Localizing UWP apps with Android resources

Tomáš Bezouška
Step Up Labs
Published in
8 min readSep 17, 2018

Recently, we finally released the Windows 10 version of Settle Up — app for managing group expenses. The app is now available for Android, iOS, Windows 10 and web. Since we want to reach as many users as possible, we localized everything we could — the app itself, screenshots, store descriptions, etc. The easiest way to localize your app into many languages is to crowdsource the process — we use getlocalization.com where people who want to use the app in their language help us out by translating the English phrases we provide.

Store listing of Settle Up in Spanish

All our localization resources are divided into several files. They contain the store descriptions, sample data for screenshots and of course resources for the app itself. As the Android version was developed first, the resources are kept in the Android strings.xml format. This isn’t supported by the UWP platform, which requires the resw/resx format.

Android strings.xml vs. UWP ResX

Another thing is taking screenshots. Microsoft Store requires you to provide localized screenshots for each language your app supports, otherwise you risk not passing the app certification process. For us, this means creating a sample group, filling it with localized data and taking several screenshots of the app. Repeat for more than 15 languages. You can imagine that this can get very tedious.

In this blog post, I would like to show you how we convert the Android strings.xml files into Windows resx format so they are usable in UWP. In the second part of this post, I will show you the process of taking screenshots of the app in several languages. All scripts are in powershell, but there is nothing too complex about them so you should follow easily even if you have never used powershell before.

Using resx resources in a UWP app

There are several ways to localize your UWP app. The offical Microsoft docs suggest using resw resource files and then decorating your xaml controls with x:Uid markup. I don’t like this approach one bit because it gives you zero design-time, compile-time and intellisense support.

Instead, I prefer to use AppResources.resx with a combination of a strongly-typed ‘designer’ class which can be used both in code and in xaml bindings. See example of both:

Strongly typed c# designer file
Binding to the specific resource in xaml

As you can see, the AppResourcesclass is a simple strongly-typed wrapper around ResourceManager. This approach gives you design-time and intellisense support as well as compile-time checks when you access the class in code-behind.

The way I decided to structure the app is to have all the localization resources in a separate project (~assembly) and reference it from other projects. This way you can also reference it from your background services or other places you might need them. The language resource files are named “AppResources.{language}.resx” (language being “de”, “cs-CZ” etc). The default resource (English in our case) is simply called “AppResources.resx”.

There is one more thing— the UWP package generator (which turns your project into appx / appxbundle / appxupload files) needs to know which languages your app supports. It does so by looking for specific files in your main project — /Strings/{language}/Resources.resw. These files need to contain at least one (even dummy) phrase for the package generator to mark that your app supports this language. Since our resources live in different assembly under different name, they won’t be picked up by the package generator. You need to create those dummy resw files manually (or via a script of course). But this needs to be done only once so then you don’t need to touch those dummy resw files anymore.

That’s the theory, now let's see how we can generate everything we need from Android strings.

Downloading resources from getlocalization.com

The first thing you need to do is to get the Android strings.xml files from somewhere. As I mentioned, we use getlocalization.com. We certainly don’t want to manually download all files each time a phrase is added, fortunately, they offer API anyone can use to get their localized files.

See sample code:

Download localized resources from getlocalization.com

As you can see, downloading the localized files is pretty simple, you just need to base64-encode your credentials you normally use to log in and set them as your Authorization header.

The only file I couldn’t download with their API is the original English (default) one, I still have to download that manually. If you know how to download it via API, let me know in the comments.

Transforming strings.xml to AppResources.resx

The next step is to generate the AppResources.resx along with a corresponding c# ‘designer’ file for strong types. Visual studio actually has a built-in generator (PublicResXFileCodeGenerator) but it’s missing a few features I wanted to use. One such thing is support for plural forms — you want to be able to correctly localize “1 orange”, “2 oranges” and “many oranges” (in some languages, like Czech, the plural form for “a few” and “many” differs). Android strings.xml support this:

This is used like this (Kotlin snippet):

As far as I know, this isn’t natively supported in UWP, but it turns out that adding some basic support for plural forms is not that difficult:

The main logic is in the “WithCount” method. As you can see, it is very basic and could probably be extended to be more granular and better suit other languages, but so far, it suffices.

Ok, enough theory, let's take a look at the powershell generator.

As you can see, it's no rocket science. The Convert-Everything function first generates the designer file from the default resource. This is done by iterating over the /resources/string and /resources/plurals nodes of the supplied XML file. The functions Write-DesignerHeader and Footer are static and not so interesting. You can find them here.

Next, let's take a look at the Convert-StringsToResX function. Its job is to convert the strings.xml file into AppResouces.resx.

Again, it's not too difficult because the code is similar to the one generating the designer file. The main pain-point was converting all phrases correctly into valid XML values. This turned out to be pretty complex because there are many things to handle, as you can see. It’s not just special characters, but also string interpolation symbols, which are different in Java and C# (%s vs {0}). Also, the source strings were inconsistent in using the CDATA container, so I decided to use it to wrap all phrases for consistency. Just like before, the Header and Footer functions are not so interesting, you can find them here.

And that’s it, you have all you need to localize your UWP app. In reality, we actually have another file with Windows-only phrases, and the script just simply merges it with the main resource file. For the sake of brevity (the post is already pretty long), this isn’t included in the examples, but you get the idea.

Automating screenshots

The next step is taking screenshots of your app in all the languages it supports. It might seem as too much work for something that is done once and takes only a few minutes, but believe me, you will be thankful for having this process automated for each new language / feature / redesign / typo fix.

To make the screenshots, I found it easiest to use the CodedUI tests project in Visual Studio. There is a pretty good tutorial about them on Microsoft Docs so check that out first.

In short, you run the Coded UI Test Builder and then click through different parts of your app. As you go, you “save” various controls in the app into the builder, which at the end will generate code you can use to remotely manipulate those controls (click button ‘a’, set textbox’s text to ‘foo’ etc.). One thing to stress out is that if the controls you need don’t have an x:Name or AutomationProperties.AutomationId set, the builder will try to find them during runtime by some other property+value combination, which might fail (e.g. it tries to look for a TextBlock with value of Text set to “Login”, but during localization scenarios, this value will be different for all languages other than English). Therefore, always set the Name or AutomationId to make sure your code works.

Another thing to note is that CodedUI tests are supported only in VS Enterprise and only with MSTest (v1). With these limitations in mind, let's take a look at the code at a high level.

The [TestMethod]attribute and corresponding method need to be used for each language because MSTest (v1) doesn’t support [Theory]and similar attributes you might know from in XUnit / NUnit / MSTest V2.

This UI test basically does 4 things —starts the app, logs in, takes the screenshots of different screens, logs out. Setting the current language of the app is done in the LoginAsync method:

There are two not so obvious things going on here:

  • You cannot pass a parameter to the app when it's starting. Therefore, you need to come up with a different way to set the app language to the one you want. I placed an invisible (opacity = 0) TextBox on the Login form with a TextChanged event handler which sets the language via AppResources instance. The TextBox is invisible to the user, but the Coded UI tests have a reference to it and can manipulate it.
  • Everything under this.UIMap.UISettleUpWindow was generated by the UI Test builder. Accessing those properties will do a lookup for the respective controls in the running app. If they are not found within the default timeout, exception will be thrown.
  • Login in the app is done under different accounts for each language. Each account has a localized group with localized expenses (e.g. “Skiing trip” makes sense for someone in Canada, but not so much for someone in Mexico).

Going around the app is very similar to what is done in LoginAsync — basically just a combination of Tapping and Keyboard SendKeys. That leaves us just one final thing — taking the screenshots:

Again, let me point out the not-so-obvious things:

  • I couldn’t determine the DPI of my screen by any available method so I just decided to hard-code it. I will run the UI tests only from my laptop anyway.
  • The code assumes the app is running in maximized mode (screenshot is of the entire working area — excluding the task bar).

And that’s it. The screenshots will be saved in c:\screenshots\{lanugage} where you can find them and use them in your submission to Microsoft Store.

--

--