WinRT provides good support for building global aware applications especially when compared to classic Windows API , but has some limitations compared to WPF features. In this series of posts we will explore the provided features with some workarounds to the issues in the current release.
ResW Files
WinRT uses ResW files to store localized strings, which are XML files used to store key value . ResW is the same as ResX schema. But unlike ResX built-in tooling in Visual Studio for WPF and Silverlight no strongly typed classes will be generated for ResW files. ResW files are compiled to binary .pri files using makpri.exe, the same tool can be used to dump the data to XML. Dumping pri files provides useful information about how and what resources are stored.
The resource files can be loaded manually using ResourceLoader or ResourceManager, but the runtime will load the files automatically if the file is named Resources.resw and placed in a folder named with National Language Tag. The following screenshot shows an example of this structure.
The folder name is divided into two chunks, the first is the culture identifier for example en to English, es to Spanish, the second is the country qualifier, for example en-US for or en-UK. Determining the right resource to load depends on a heuristic process depending on the user Language Preference list.
Language Preferences
Traditionally .NET applications used the CurrentCutlure to determine the resource to load. This allowed only two options, either the culture specific resource or the fallback resource. With Windows 8 comes the concept of Language preferences which can include multiple language. The resource engine will try loading resources for the preferred language and if it can’t find any it will try the preferred language culture then the second preferred language and so on and if no match from the preferred languages is found, the engine will default to the fallback language. You can get the language preferences of the current user with Windows.System.UserProfile.GlobalizationPreferences.Languages property.
You can programmatically override the preferred language using Windows.Globalization.ApplicationPreferences.PreferredLanguage property. The PreferredLanguage setting is persisted, an will live across applications sessions. To clear it to use the system default language preferences instead, set it to an empty string.
Loading Strings in XAML
The resource engine can bind strings automatically to XAML elements if the following steps are followed:
1- Add x:Uid attribute to your XAML element, for example <TextBlock x:Uid="HelloWorld" /> 2- In the Resources.resw file add a value with its key uses the convention [Uid].[Property] for example HelloWorld.Text or HelloWorld.FontSize to set the Text and FontSize properties of the control with x:Uid HelloWorld.
x:Uid’s is not required to be unique. If two elements have the same Uid, both will receive the property settings. The property type is not limited to strings. Any type with built in converter can be used, for example Color properties can be set to Green or #00FF00 for example. Custom converters are not supported using Uid. If you need custom value converters for localized resource strings, you can use Binding to a class that loads the resources manually.
Attached properties can also be set in resw files but requires the fully qualified namespace for the class defining the property, for example the syntax for setting the Grid.Column property for an element would be MyElement.[using:Windows.UI.Xaml.Controls]Grid.Column
Loading Strings Manually
The ResourceLoader class simplifies the task of retrieving a string in two steps. First you instantiate a ResourceLoader then invoke GetString method. Instantiating ResourceLoader without any parameters loads the resources located in Resources.resw class. Passing a name loads the resource in the specified file as shown in the following example
1: var loader = new ResourceLoader();
2: loader.GetString("HelloWorld");
3: var customLoader = new ResourceLoader("Custom");
4: var str = customLoader.GetString("MyString");
1: var manager = Windows.ApplicationModel.Resources.Core.ResourceManager.Current;
2: var subtree = manager.MainResourceMap.GetSubtree("Custom");
3: var resource = subtree.GetValue("MyString");
4: var resourceValue = resource.ToString();
Responding to System Preferences Changes
ResourceManager exposes an event that can be used to detect the system preference changes. The following snippet shows how to use this event and get the current language preferences.
1: var manager = Windows.ApplicationModel.Resources.Core.ResourceManager.Current;
2: manager.DefaultContext.QualifierValues.MapChanged += (s, m) => {
3: string currentLangValue;
4: ResourceManager.Current.DefaultContext.QualifierValues.TryGetValue("Language", out currentLangValue);
5: var langs = Windows.System.UserProfile.GlobalizationPreferences.Languages;
6: };
In the next post we will dig deeper into PRI files and their internal structure, and see how to load localized files and custom layouts.