Wednesday, March 21, 2012

Building Global Windows 8 Metro XAML Apps Part 1–Localizing Strings


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.

project-hierarchy

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");

ResourceManager class located in Windows.ApplicationModel.Resources.Core namespace exposes more powerful API to deal with resources, but requires more intimate knowledge of the internal structure of PRI files and resource maps. This is a native class part of WinRT API and different from System.Resources.ResourceManager. To get a string using ResourceManager first navigate to the subtree containing the resource, invoke GetValue or TryGetValue method. GetValue returns a ResourceCandidate that can be converted to a string or to a file depending on resource type. The following snippet shows using ResourceManager to load a string in action


   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.

4 comments:

Christian Resma Helle said...

I just started a project called ResW File Code Generator for generating a code behind strongly typed helper class for accessing localized strings in a .resw file that might be interesting for you and other readers o this article

Christian Resma Helle said...
This comment has been removed by the author.
Shivam Bhardwaj said...

Hi , i need to update resource file value for any control like textBlock or lable , From Other Source like Services or database at runtime , suppose String1 value is USERNAME in resource file i want to update This USERNAME as "USER" at run time in my Windows8 metro application C# with XAML . any idea About this problem .i will be thanxful to you .

Shivam Bhardwaj said...

Hi , i need to update resource file value for any control like textBlock or lable , From Other Source like Services or database at runtime , suppose String1 value is USERNAME in resource file i want to update This USERNAME as "USER" at run time in my Windows8 metro application C# with XAML . any idea About this problem .i will be thanxful to you .