Adventures in UWP – Globalization & Localization

In a previous post I created a “default” UWP blank application, which did nothing. But we did look at some of the key parts of the template code, manifest etc.

Usually I would go the “Hello World” route and just display a TextBlock or similar to display the text “Hello World”, so let’s do that here and then look to make this post a little more useful by taking a look at globalization & localization.

Obviously if we were to actually deploy an application to the Window’s store it would be good to have it available in different languages etc.

I’m not intending to spend too long on this as it’s quite a large subject in and of itself, but let’s look at the basics.

In MainPage.xaml, add a TextBlock between the Grid (here’s the code including the Grid)

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
   <TextBlock Text="Hello World" 
      HorizontalAlignment="Center" 
      VerticalAlignment="Center"/>
</Grid>

In the above code we’ve effectively hard coded “Hello World” into our application.

Globalization & Localization

Internationalization (also known as i18n) is the combination of Globalization & Localization.

Globalization tends to mean the implementation of code within your application which can change automatically based upon the locale, so for example formatting of numbers or dates.

Localization tends to mean the changes to an application for a different locale, such as translation etc.

Note: I covered some i18n coding for WPF previously in my post A quick look at WPF Localization Extensions.

Let’s localize our app.

Okay, we don’t have a lot of text to translate (or images or the likes), so this is very much an overview of the process we’d undertake on our application. Let’s change our “Hello World” into a format that can be localized.

To do this we use the x:Uid markup element on our TextBlock, for example

<TextBlock x:Uid="helloWorld" 
   HorizontalAlignment="Center" 
   VerticalAlignment="Center"/>

Note: In another post I intend to cover accessibility, such as the narrator. In some instances we might wish to add AutomationProperties.Name text to a control but we’ll also want that to be localized and so whilst a x:Uid=”hellWorld” might be used for a control, in the .resw file we’d have the name/key helloWorld.AutomationProperties.Name and put our localized text in here.

We don’t actually need to have the Text property, but you might find having something displayed at design time useful and so you can just as easily leave the Text=”Hello World” markup and it’ll be overwritten by our localization.

The x:Uid differs from the x:Name markup as it’s aimed specifically for localization.

Now we need to create our resources, in our project we simply create a folder (at the same level as our Assets) with the name of the locale we want to store resources for, so for example we can create en for general English language, or en-GB, en-CA, en-US for example, for British, Canadian and US variants of English.

See Supported Languages for a list of language codes.

With our folder in place we simple add a new item of type Resources file (.resw) and this gives us a simply grid entry screen for key/value pairs along with a comment for passing onto our translators (for example).

Within the grid, the key (actually it’s the header Name) is our x:Uid but in the format uid.PropertyName, where the property name for our TextBlock is Text. Therefore remember if the UI element changes from a TextBlock to a Button (for example) this PropertyName would need to also change from Text to Content.

We can (and should) also store string representations of colours, such as “Red” etc. as values for those elements where the colour may need to change for the different locale.

Testing our localization

So let’s assume we’ve created several folders and localized our application strings etc. How do we test the different localization without having to change our Windows setup.

There’s actually a Default language option in the app’s manifest, but changing this doesn’t appear to have the desire affect.

A simple way to change our locale is within the App constructor, simply place the following

ApplicationLanguages.PrimaryLanguageOverride = "fr-FR";

We could also have an option within the application for switching languages, in which case we’ve need to reload the UI, but I’m not going to cover that within this post.

Using the Multilingual App Toolkit

An alternate route to translation is using the MAT (Multiligual App Toolkit) which can be used in WPF or UWP.

The Multilingual app toolkit 4.0 for VS 2015 or Multilingual app toolkit for VS 2017. Give us another way to handle translations/localization.

MAT seems a little tricky to get working, but it’s not too bad once you know what’s required (ofcourse that statement can be used for most things).

  1. Select Tools | Multilingual App Toolkit and Enable Selection
  2. Depending upon your manifests Default Language (mine’s en-GB) you’ll need a folder of the same name, i.e. en-GB with a Resources.resw file in it before you can add translations via MAT. As the default language, the key’s and default strings will be taken from this file. So add our helloWorld.Text name and “Hello” as the value to this resource file.
  3. You should build the project here just to make sure all is working
  4. Now you can right mouse click on the project and select Multilingual App Toolkit and now Add translation languages will be enabled, from here you can select multiple languages.
  5. Let’s select fr-FR for some French translation. This will create another folder fr-FR, as you’d expect, along with a Resources.resw for this translation. Do not edit any new translation files, only the default language file
  6. Along with the Resource.rews a MultiligualResources folder will appears with xlf files matching the appname.language format, i.e. HelloWorld.fr-FR.xlf. It is the XLF file that we translate our text into.

Whilst we can edit our XLF in Visual Studio as it’s just an XML file, we can also double click on it from File Explorer to load via the Multilingual Editor. This editor could, ofcource, be used by a translator to go through each resource and supply the translation. Once saved MAT will again sync. but this time it will automatically supply the strings and identifiers to the fr-FR Resource.resw (in my case).

Each time we finished adding new strings to our default locale (in my case the en-GB Resource.resw) then rebuild to get MAT to resync. Then translate when you’re ready.

In most cases we’d wait until all (or most) of our application is in ready before sending the XLF to translators ofcourse, but for us to test things, just translate and rebuild to resync.

Pseudo language with MAT

Whilst the MAT includes many locales that you’ll recognise, it also includes a Pseudo language which is generated from your default language strings but with alterations to the string, for example taking “OK” and creating a longer string with semi-random characters as well as the O and K, so it’s still readable in English (my default language).

To get translations in the pseudo language, open the *.qps-ploc.xlf file and for each string you wish to translate click the Translate button or for all strings, click the Translate button’s drop down and select Translate All. This will then create translations for testing your layouts etc.

Using resource strings in the manifest, for the Display Name

In some cases we might wish to localize the Display name, i.e. Title of the main Window.

From the manifest we can reference our resource string using

ms-resource:myTitle

where myTitle is a key/name within the Resources.

We can (sort of) handle this in code by accessing using

ApplicationView.GetForCurrentView().Title = "My Title";

but actually this doesn’t replace the Display Name from the manifest but instead prepends the text.

Finally, using our resource in code

Whilst we obviously want to do most of our UI work in XAML, we still need to store strings etc. for use in code, for example when displaying a message box of the likes.

To use the resources in code we can declare the strings within the .resw. Interestingly, not sure if this is just me or how things work, but for strings that will be used in code, the name/key of the string does not include the .PropertyName format. Hence “hello.Text” fails to work when used in code, whereas “hello” as a key does work.

Here’s the code that we can use to get the string from the default resources

var rl = ResourceLoader.GetForCurrentView();
var r = rl.GetString("hello");
// r will now be our translated string

References

Globalization and localization
Put UI strings into resources