Resources and Localization - Programming Windows Store Apps with C# (2014)

Programming Windows Store Apps with C# (2014)

Chapter 13. Resources and Localization

In this chapter we look at how we can localize our apps—that is, make them functional in different languages.

The general premise of localization remains unchanged from years gone by. Operationally, we have usually done this by creating a table containing strings, which are then used to replace static text on the UI. You then create copies of the table for each language that you want to support. Windows works out which table to use based on the user’s systemwide language preferences.

The story in WinRT for handling strings is different from how it was in .NET. You now add .resw files to projects, rather than .resx files. The APIs for loading the strings are, as you’ve probably guessed, different. The process by which strings are packaged along with the app is also different. Strings are not embedded into the DLL but are combined into a .pri file that is deployed along with the app. So, we’ll look in detail at how that works as we go.

Then, we’ll look at how to replace strings in the XAML markup, how to explicitly load strings, and how we can localize images. We’ll also cover a special feature that allows us to package multiple image resources that are selected out depending on the DPI of the display.

.pri Files

.pri files, or Package Resource Index files, are the new file format used with WinRT and in Windows Store app development. They are a binary format that represents the hierarchy of resources used in your app and any dependencies. In Windows Store apps, we define resources by marking them with a Build Action of Content. All resources are included in this file, not just string resources.

If you have a standalone module with no dependencies other than Windows.winmd and .NET Core, you will end up with a resources.pri file on compilation that contains any strings that are defined within that app, as well as references to any resources. We define strings by adding one or moreResources.resw files into the app, one per required language locale.

Back in Chapter 8 we built some scratch applications to support that particular chapter’s discussion. One of those was called ImageShareScratch. If you look in the build output of that, you’ll find a resources.pri file. Figure 13-1 illustrates.

The resources.pri file for the ImageScratch.exe Windows Store app

Figure 13-1. The resources.pri file for the ImageScratch.exe Windows Store app

The Windows 8 SDK ships with a utility called makepri.exe that can both make .pri files and dump out their contents. (I’ll show you the command-line arguments you need to do this shortly.) If you dump out the contents of that resources.pri, you’ll find all of the images and XAML files that were packaged along with the app. Here’s a snippet of the output:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>


<ResourceMap name="8c96f52a-1d50-4d5a-a5f5-27f950cf4765" version="1.0"



<ResourceMapSubtree name="Files">

<NamedResource name="App.xaml"


<Candidate type="Path">




<NamedResource name="MainPage.xaml"


<Candidate type="Path">




<ResourceMapSubtree name="Assets">

<NamedResource name="Logo.png"


<Candidate type="Path">




<!-- file continues as you might expect... -->



Where this story gets interesting is that as you combine modules to make bigger apps—such as by including the Bing Maps component, the UI-agnostic StreetFoo.Client assembly, JSON.NET, and so on—each dependency has its own resources.pri file, which is used by makepri.exe to eventually build one master resources.pri file that contains references that are universal across the whole install.


It’s worth remembering that if you’re struggling to find the path to a resource in your app, use makepri.exe to dump out the resource structure. You can then find the resource path (or even just confirm that it’s there) and copy and paste the path. This is the best way to troubleshoot resource references.

If you look in the XML I’ve just presented, you’ll see one reference to a ResourceMap and ResourceSubMap within. Each submap represents one component. Were we to run makepri.exe and dump out the resources for the app as it exists in the last chapter, we’d have entries as shown (rendered by IE) in Figure 13-2. (I’ll just illustrate the output here—I’ll show you how to run makepri.exe in a moment. I’ve rendered it in IE so that I can collapse sections down and make it easier to understand.)

The structure of the resources.pri file as per the app as it was at the end of

Figure 13-2. The structure of the resources.pri file as per the app as it was at the end of Chapter 12

Incidentally, the value shown in the URI after the ms-resource: protocol directive (569e8a16-efb8-4992-ada5-7407fecb3dee) is the package name as given in the manifest. We thus far haven’t changed that package name from its default, which happens to be a GUID.

Hopefully, you can see how the entire app’s state is laid out. We can now add strings into our project, and ultimately we’ll see that reflected in the resources.pri data.

Adding Strings

Start by creating a new Windows Store class library project called StreetFoo.Client.Resources.

Resources are managed by convention in WinRT. The convention for string resources is that you create a folder called Strings at the root of the project, and then folders per locale.

The way that locales are referenced in Windows hasn’t changed for years. Locale codes—or more properly, Windows Language Code Identifiers (LCIDs)—typically look like this: en-US, fr-FR, de-DE, etc. The first part is the language code (which happens to adhere to ISO standard 639-1), and the second part is a country/region code (which happens to adhere to ISO standard 3166-1). For example:


English language, United States country/region


English language, Great Britain country/region


French language, France country/region

...and so on.

Why I’m belaboring this point is that Windows will “fail over” to languages depending on what the app supports and what your system supports. For example, I’m in the UK, so my machine is set to en-GB. If I have an app that defines resources for en-US but not en-GB, Windows will assume that en-US is a good enough match. Similarly, if I have an app that supports only fr-FR and a system set to en-GB, because Windows knows the app doesn’t support any English language resources, it will use fr-FR because it has no other option.

What happens at a deeper level—which we’ll see—is that Windows can be configured with a set of language preferences and will try to get the best fit out of what the machine supports, what the user chooses as her preferences, and what a given app can actually do.

Within the locale folders, you create a Resources.resw file. Figure 13-3 illustrates.

Layout of the Resources project with an en-US resources file

Figure 13-3. Layout of the Resources project with an en-US resources file

What we want to do first is prove that we can see strings that we create in the resources.pri file. Open up the .resw file and any string pair that you like. Figure 13-4 illustrates my choice.

A resource string

Figure 13-4. A resource string


If you’re new to string translation, the “Comment” field is used for notes if you want to give the file over to a translation bureau for translation.

Now we need to reference the resources project from the main Windows Store app project. We do this in the usual way. Build the app, and a new resources.pri file will be created.

To run makepri.exe, you need to run the Developer Command Prompt for VS2012.


If you haven’t done so already, it’s worth pinning the Developer Command Prompt to your taskbar and setting it to run with administrator rights.

Run the prompt and navigate to the project directory. Execute this command line to create an XML file dump of the resources.pri contents. (Remember, this file will be in the ~/bin/Debug folder of the project.)

makepri dump /if resources.pri

This will create a resources.pri.xml file. You can open this in your favorite XML visualizer. Figure 13-5 shows the location of the new string.

Our new string and ResourceMapSubTree

Figure 13-5. Our new string and ResourceMapSubTree

The fact that the string exists in its own ResourceMapSubTree is important. When working in this mode, where resources are in a separate assembly, you have to fully qualify the identifiers in all cases. When you have resources in the same assembly, you generally don’t have to do that. I’ll explain more about that we as go on.

Now that we know how resources are collated and organized, let’s look at how we can use them.

Localizing Strings

Now, how can we actually localize strings within our apps? Let’s look at how we can localize strings first in XAML using the automatic string replacement capability, and then how we can explicitly load up strings.

First, we need to look at an issue with the project setup.

Default Project Locales

When the project is created in Visual Studio, Visual Studio takes the logged-on user’s locale and writes it into the file as the default locale. You may need to manage that default; however, Visual Studio doesn’t let you edit it through one of its tools. You need to change it using Notepad or a text editor of your choice.

In each of the three project files, open the .csproj and find the DefaultLanguage entry. (It’ll be near the top.) For our purposes, as our default locale will be en-US, make sure it’s en-US.



When you do this, take care not to accidentally set Notepad to be the default handler for .csproj files. Nothing bad will happen, but it can be a pain.

You don’t have to choose en-US on your projects—choose whichever is appropriate. The important point is that you control the locale, rather than assuming whatever one Visual Studio has set is correct.

Localizing Strings in XAML

To test the string localization, we’ll create a copy of the string table for French. (This is the fr-FR locale.) The first thing we’ll do is change the caption at the top of the reports page.

We’ll start by replacing the string in the language table. Rather than presenting screenshots when we do this, I’ll present the string table as text. Table 13-1 shows the en-US table with the string. I’ll discuss the naming shortly:

Table 13-1. en-US string table





The easiest thing to do at this point is present how this works, and we’ll then go back and talk about the naming convention.

The XAML replacement magic works by having you decorate controls with an x:Uid attribute. This is then used to find matching values in the string table. Notice I say “values” there. I’ve specified Text in Table 13-1. In fact, it can replace any property you like. This is helpful when you need to adjust the UI to accommodate language strings, but we’ll get to that.

To see this working, we’ll change the Reports page so that the caption is localized. To do this, we need to add the x:Uid attribute to the TextBlock control used for the caption. Here’s the change:

<!-- Modify code in ReportsPage.xaml -->



<ColumnDefinition Width="Auto"/>

<ColumnDefinition Width="*"/>


<Button x:Name="backButton" Click="GoBack" IsEnabled="{Binding Frame

.CanGoBack, ElementName=pageRoot}"

Style="{StaticResource BackButtonStyle}"/>

<TextBlock x:Name="pageTitle" Grid.Column="1" x:Uid=

"/StreetFoo.Client.Resources/Resources/Reports_Caption" Text="Reports" Style="

{StaticResource PageHeaderTextStyle}"/>


When we specify the x:Uid, we have to provide a fully qualified resource path. The way we are doing it is slightly trickier as compared to most of the examples on MSDN and in the community because we have a separate resource assembly. If we had string resources directly within the Windows Store app project, we could just say ReportsCaption and let the resource loader infer the beginning of the path.

In either case, note that we don’t specify the .Text. XAML is using the value to find all the strings with the same value prior to the dot. Anything after the dot is then the subject of data binding, with a dependency property being sought out and the value replaced. Note that there is no design-time support for this; hence, we still need to specify a static value for Text so that we can see it in the designer. To validate that the string replacement is working as a requirement, you may want to use the convention of prefixing the literal strings on the UI with an x. This will allow you to see at a glance which strings have not been enlisted in the localization.

Run the project and you won’t see anything different. To see things done differently, we have to add new string tables.

To the resources assembly, add a locale for fr-FR and add a new Resources.resw. We then need to create a translation of the string that we had before. Figure 13-6 shows the solution structure. Table 13-2 shows the value of the replacement string in French.

The fr-FR locale string table in the Resources project

Figure 13-6. The fr-FR locale string table in the Resources project

Table 13-2. fr-FR string table






In situations where Windows cannot find a string in a translated language table, but that string is in the default language table, Windows will fail over and use the default string.

If you build that, the resources.pri file will be changed. Here’s the snippet of XML from that new resources.pri file where you can see the strings. The en-US and fr-FR strings are now available. Note too how the en-US one is shown as the default (isDefault).

<ResourceMapSubtree name="StreetFoo.Client.Resources">

<ResourceMapSubtree name="Resources">

<ResourceMapSubtree name="Reports_Caption">

<NamedResource name="Text"



<Candidate qualifiers="Language-FR-FR"




<Candidate qualifiers="Language-EN-US"

isDefault="true" type="String">







To try this, you need to change your language. It’s difficult to write a book for a global audience and make assumptions about the languages that each reader has installed. I’ve based this on the assumption that you have English installed but no other languages.

The language selection is done from within Windows. WinRT will pick up the locale from Windows and choose the language that best fits based on those available and the default.

However, there’s a problem. Although we can define multiple language tables in our languages assembly, the actual app doesn’t know which languages are supported. This is also done by convention. We have to create blank Resources.resw files in the Windows Store app project that match the supported locales. Figure 13-7 shows this structure.

Repeating the folder and file structure in the Windows Store app project

Figure 13-7. Repeating the folder and file structure in the Windows Store app project

To reiterate, the newly created .resw files in the StreetFoo.Client.UI project are blank.

You’re now in a position to change your language preferences and see if the localization works. You do this through Control Panel—and the easiest way to access this in Windows 8 is to press Win+R, type control, and press Return. In the search box on the window, type language and you’ll see an option for “Add a language.” In Figure 13-8, I’ve added fr-FR, but more importantly I’ve made it my preferred language by putting it at the top. Windows will choose an application’s language by cross-referencing the app’s supported languages with the preference order defined in Control Panel.

Adding the fr-FR locale and making it preferred

Figure 13-8. Adding the fr-FR locale and making it preferred

Run the app now, and the Reports page will have the French language caption. Figure 13-9 illustrates.

The replaced string

Figure 13-9. The replaced string


Although I won’t go through it here, I’ve added a de-DE language in the code download to give you more variations to play with.


Now that we’ve seen it working, we need to talk about conventions.

It’s reasonably important to have some form of convention when working with language strings. With very complicated apps, you can end up with many hundreds of strings.

You should come up with your own convention that feels comfortable, but there are some restrictions. It’s worth designing that convention so that you can see which UI aspect owns the string. In this case, I’ve proposed using Reports as the prefix for strings that relate to ReportsPage.

Because XAML uses dot notation to find properties to bind to, you cannot use dots in the part of the name that you own. Likewise, I’d recommend not using slashes because they are used in the paths that you specify to load the strings. What I’ve used in my strings is underscores.

You can also include elements—for example, perhaps you want messages that appear in MessageDialog pop ups to include Message in the name. Likewise, strings that apply to notifications might include Tile or Toast in the name.

The important thing is to have a convention; how that convention is structured is up to you.

Changing Other Properties

One of the classic problems with localization is when you need to change the UI in order to accommodate a string. German is particularly prone to using words that are often longer than those used by other languages.

It’s for this reason that the dot notation is included in the string name. You can reference any dependency properties that you like in there.

I won’t show you how to do this in these pages, as it’s obvious and there’s nowhere to put it. However, in the code download I have a button on the Reports page that changes its height depending on the locale.

Explicitly Loading Strings

To round off the discussion on strings, we need to look at how we can explicitly load strings for things like messages that are displayed in MessageDialogs and other notifications (i.e., those that are not necessarily expressed in markup).

Oftentimes when you load a string in this way you need to replace values in it—for example “I found 27 reports” may need to be replaced with “J’ai trouvé 27 déclarations.” You can easily do this by loading a string and using the loaded string as the template for use with a normalstring.Format call.

The approach I’m proposing here is to create a helper class that lets us load strings and optionally format them. Nothing about the string table setup will change.

We’ll build a class called StringHelper with a method called Format. This will take a path to a string, load it, and then call string.Format on it, passing in any parameters (if we specified any).

Whereas before when we needed to put in the x:Uid attributes we specified the full string, in StringHelper, if we assume all the strings are in a single common assembly (which is a fair assumption), we can provide just the name of the string in the common assembly. When we come to load it, we’ll assume the other elements of the path are static.

Here’s the implementation for StringHelper. This uses a ResourceLoader bound to a specific resource map, the name of which is the name of our common language assembly. When we come to build the complete path, we need to specify that it’s within the Resources branch, and then we just tack the name onto the end.

public static class StringHelper


private const string ResourceMapName = "StreetFoo.Client.Resources";

// loads a resource string and runs string.Format on it...

public static string Format(string name, params object[] args)


var buf = new ResourceLoader(ResourceMapName).

GetString("Resources/" + name);

if (args.Any())

return string.Format(buf, args);


return buf;



To test it, we’ll localize the text used in the toast displayed when we refresh the Reports page. We’ll need one string for the toast caption, and then two strings to report back on the number of items that were loaded from the server.

The first step is to build up the string table. Tables 13-3 and 13-4 show the en-US and fr-FR tables.

Table 13-3. en-US string table with toast strings






Reports refreshed


I found 1 report.


I found {0} reports.

Table 13-4. fr-FR string table with toast strings






Déclarations rafraîchies


J'ai trouvé 1 declaration.


J'ai trouvé {0} declarations.

Using the strings is just how you’d imagine it might be. Rather than using literal strings, you load the appropriate string from the table using the new StringHelper class.

Here’s the change to the constructor of ReportsPageViewModel, specifically where the RefreshCommand is initialized. I’ve omitted code from this class for brevity.

public ReportsPageViewModel(IViewModelHost host)

: base(host)


// commands...

this.RefreshCommand = new DelegateCommand(async (e) =>



await this.DoRefresh(true);

// toast...

string message = StringHelper.Format("Reports_Toast_IFound1Report");

if (this.Items.Count != 1)

message = StringHelper.Format(

"Reports_Toast_IFoundNReports", this.Items.Count);

var toast = new ToastNotificationBuilder(new string[] {

StringHelper.Format("Reports_Toast_ReportsRefreshed"), message });

toast.ImageUri = "ms-appx:///Assets/Toast.jpg";



// code omitted for brevity...


Run the project now with the fr-FR locale as your preferred locale, and you’ll see the string on the toast in French rather than English. Figure 13-10 illustrates.

Our toast notification from , but in French

Figure 13-10. Our toast notification from Chapter 6, but in French


There’s one last thing to discuss before we leave strings and move on to images. Our StringHelper.Format method doesn’t have to take arguments. There is a line of thought that says that Format isn’t the best name for this. You could create another method in StringHelper (e.g., LoadString) that didn’t take arguments. All that would do, however, is defer to Format. Its sole purpose would be stopping your code from “looking funny” because of the preconceived perception that Format should take arguments.

Localizing Images

In this next section, we’ll look at how to work with localized image resources. This is helpful in situations where the image you’re looking to convey has some localized meaning. However, the more interesting (and common) thing is how to vary the resource selection by display DPI.

We’ll start by looking at varying the resource by locale.

Varying Images by Locale

We’ve seen thus far that string resources are defined in our application using a convention-based approach. By virtue of the fact that we have resources in specific folders, makepri.exe knows that one string table belongs to the en-US locale, whereas another belongs to fr-FR.

That same convention-based approach flows through into all resource types. If we create an image with a given name (e.g., Flag.png), and if we put one in the en-US folder and another in the fr-FR folder, WinRT knows that we are defining a localized resource. Given where we are at the moment with our separate resource assembly, the easiest thing to do here is to continue this approach. (In fact, that’s what we will do after this short diversion.)

There is another way to specify a localized resource file, which is to put the language identifier in the file itself. The rule is to put .lang-<languageCode> into the filename. Figure 13-11 shows this approach; however, this isn’t the approach we’re going to use in this book.

Using the .lang-<languageCode> convention

Figure 13-11. Using the .lang-<languageCode> convention


One more thing: if you do use that approach, you may find you have to specify several language qualifiers in the string name. If you do this, separate them with underscores.

As mentioned, I don’t want to go that way, as I want to show you how to do this using our separate resource assembly. We’ll also use the approach of separating resources by locale-specific folder rather than using the filename. (You can still use the filename-based approach, but if you have a lot of resources to localize, splitting by folder can be more useful.)

Figure 13-12 shows the same flag files but in separate folders. I’ve created a root Images folder to do this.

Images broken down into specific language folders

Figure 13-12. Images broken down into specific language folders

To refer to these images in the XAML, nothing changes from the way we’ve done this before using the ms-appx: protocol in the URI.

Here’s the change to the title block of ReportsPage to display an image. As you might expect, we don’t have to do anything in order to specify that we want a localized value. It just works.

<!-- Back button and page title -->



<ColumnDefinition Width="Auto"/>

<ColumnDefinition Width="*"/>


<Button x:Name="backButton" Click="GoBack" IsEnabled="{Binding Frame.

CanGoBack, ElementName=pageRoot}"

Style="{StaticResource BackButtonStyle}"/>

<StackPanel Orientation="Horizontal" Grid.Column="1">

<TextBlock x:Name="pageTitle" x:Uid=

"/StreetFoo.Client.Resources/Resources/Reports_Caption" Text="Reports"

Style="{StaticResource PageHeaderTextStyle}"/>

<Image Source="ms-appx:///StreetFoo.Client.Resources/Images

/Flag.png" Width="25" Height="15" HorizontalAlignment="Left" Margin="0,30,0,0">




Run the app in the en-US locale, and you’ll see an image as shown in Figure 13-13.

The en-US flag

Figure 13-13. The en-US flag

Change the locale to fr-FR and do the same thing (see Figure 13-14).

The fr-FR flag

Figure 13-14. The fr-FR flag

That’s all you have to do. WinRT will do the heavy lifting in terms of the resource selection.

Varying Images by Display DPI

This section is challenging, as it’s difficult to demonstrate different pixel density in a book. However, in order to make raster images look nice in your application, you need to handle display DPI.

DPI is a reasonably complex topic, and a detailed discussion is beyond the scope of this book. However, it has to do with scaling. Imagine you have a bitmap that fills the whole screen. When you develop it, you develop on a normal DPI resolution. The image will look fine to you. However, on deployment, if the end user uses a higher DPI screen, the OS will scale up that image and as a result it may not look as good. The idea, then, is that we provide multiple images in the application and allow Windows to use bigger images when appropriate and hence perform more appropriate scaling. (Scaling will always end up looking a little rubbish, as the whole point of it is to “invent” information that isn’t there—we’re aiming to get to “good enough.”)


This doesn’t just affect full-screen renders. Windows will scale up the UI on higher DPI displays because otherwise targets get too small to touch accurately. This is why handling DPI is particularly important in Windows Store apps.

There is a sweeping recommendation with Windows Store apps that rather than using raster images at all, we’re supposed to use vector images. In Chapter 5, for example, we tried where possible to use the vector images defined in the Segoe UI Symbol font. While this is true, it’s naïve to assume the whole industry is going to pivot away from raster images to vector images for Windows Store apps. (Remember that I went into this topic in more detail in Chapter 5.)

To properly support high DPI displays, images need to be provided in 100% versions, 140% versions, and 180% versions. The 140% version is used when the DPI is between 174 and 239 DPI, and the 180% version is used for 240 DPI or above. The 100% version is for what (as of the time of writing at least) is considered to be a “normal,” non-high-DPI display.

As you’d expect, the different images are specified using a convention-based approach—specifically, .scale-<percentage>. It’s regarded as best practice to specify the 100% version explicitly. Figure 13-15 illustrates the flag resources in our solution with scale specified.

Flag image resources specified with scale

Figure 13-15. Flag image resources specified with scale

Again, in a book I can’t really show you this. However, if you were to look at the output in resources.pri, you would see the different images in there, with each one having a defined Scale qualifier:

<ResourceMapSubtree name="StreetFoo.Client.Resources">

<ResourceMapSubtree name="Images">

<NamedResource name="Flag.png"



<Candidate qualifiers="Language-FR-FR, Scale-180"





<Candidate qualifiers="Language-FR-FR, Scale-140"





<Candidate qualifiers="Language-FR-FR, Scale-100"





<Candidate qualifiers="Language-EN-US, Scale-180"

isDefault="true" type="Path">




<Candidate qualifiers="Language-EN-US, Scale-140"

isDefault="true" type="Path">




<Candidate qualifiers="Language-EN-US, Scale-100"

isDefault="true" type="Path">