Click here to Skip to main content
Click here to Skip to main content

WPF Localization

, 10 Sep 2009
Rate this:
Please Sign up or sign in to vote.
recently at work I was asked to look into Localization techniques when working with WPF/XAML. There are some excellent sources around that cover the various different techniques such asthis excellent article which outlines the following techniquesUsing LocbamlLocbaml is a localization tool that M

recently at work I was asked to look into Localization techniques when working with WPF/XAML. There are some excellent sources around that cover the various different techniques such as

this excellent article which outlines the following techniques

Using Locbaml

Locbaml is a localization tool that Microsoft have as a free download, available from http://msdn.microsoft.com/en-us/library/ms771568.aspx. This tool may be used to translate compiled XAML (binary XAML, BAML) resources into a CSV and may be used to translate a CSV file into a new Dll.

Here are the steps involved with doing this

  1. Create normal WPF App, and use a ResourceDictionary for strings
  2. Use MergedDictionary in App.resources
  3. Add [assembly: NeutralResourcesLanguage(”en-US”, UltimateResourceFallbackLocation.Satellite)] in AssemblyInfo.cs
  4. Add new <UICulture>en-US</UICulture> in all ProjectGroup tags for current project
  5. Open command prompt at project level, run msbuild /t:updateuid <PROJECT NAME>.csproj
  6. Open command prompt at project level, run msbuild /t:checkuid <PROJECT NAME>.csproj
  7. Build it from VS
  8. Obtain 1st cultures CSV file, LocBaml.exe /parse en-US/<PROJECT NAME>.resources.dll /out:<PROJECT NAME>.csv
  9. Edit CSV save as new csv file name, such as <PROJECT NAME><nn-XX CULTURE STRING>.csv
  10. Need to do translation for new culture. Do something like
  11. LocBaml.exe /generate en-US/HelloWorld.resources.dll /trans :HelloWorldfr-CA.csv /out:c:\ /cul:fr-CA
  12. Create new <nn-XX CULTURE STRING> directory in Bin folder
  13. Copy <PROJECT NAME>.resources.dll from step 10, which was placed on C: to the folder created in step 11
  14. Create a constructor for App class like follows:
public App()
{
    string culture = “fr-CA”; //Where this is the culture you want to use
    System.Threading.Thread.CurrentThread.CurrentUICulture =
        new System.Globalization.CultureInfo(culture);
    System.Threading.Thread.CurrentThread.CurrentCulture =
        new System.Globalization.CultureInfo(culture);
}

Advantages Of This Method

  • You can do it at the end of the project
  • You don’t have to interfere with your main Assembly to install new cultures

Disadvantages Of This Method

· It is laborious and error prone

· Not well suited for teams of developers as a new CSV file is created each time you use the LocBaml tool, so you need to know what was different between newly exported CSV file and the translation CSV file

Using RESX Files

An alternative approach is to not use Locbaml at all and simply use RESX (resource files).

Here are the steps involved with doing this

  1. Create normal WPF App
  2. There will already be a resx file within the Properties folder, so simply add a string value to that for the text you want to localize. Make sure to select public from Access Modifier drop down
  3. Within any Window that you want to use localized text, add a clr-namespace such as
  4. xmlns:properties=”clr-namespace:HelloWorldUsingResXFiles.Properties”
  5. Hook up a items text to a resource file text, like
  6. <Button Margin=”10″ Name=”button1″
  7. Content=”{x:Static properties:Resources.MainButtonText}”></Button>
  8. 8.Copy and paste the existing resx file within the Properties folder to a new file with the following name
    9.Resources.nn-XX.resx OR Resources.nn.resx
    10.Modify the App.cs file to include whatever culture should be used, for example
public App()
{
    HelloWorldUsingResXFiles.Properties.Resources.Culture = new CultureInfo(”fr-CA”);
}

Advantages Of This Method

  • Very easy to implement

Disadvantages Of This Method

  • All RESX files must be stored in Main Assembly
  • As all cultural resource files are in Main Assembly, may make doing automated build harder

Another Way : Use Dynamically Loaded Assemblies With ResourceDictionaries

I just couldn’t help but think there was a better way, so I had a think about this, and it seems just like the way skins are applied in WPF. You wire certain control properties to DynamicResources where a new XAML file (the skin) is loaded which effects the control that is referencing the resource. So I had a think about it and thought why not do the same for Localization strings. So that’s what I did. Here is the idea

Keep a Main Assembly which expects to get localization resources from a ResourceDictionary, and then at runtime load in an extra Assembly which contains just the ResourceDictionaries for the matching CurrentCulture where the application is running.

The loaded Assemblies ResourceDictionaries will be used to replace the default one within the main Assembly.

  1. Create normal WPF App, and use a ResourceDictionary for strings
  2. Use MergedDictionary in App.resources
  3. Make sure any localizable controls using a DynamicResource like

    <Label Content=”{DynamicResource label1}”/>

  4. Create a new culture assembly with a sensible name, such as Culture_fr-CA
  5. For assembly created in step 4, create mirror image ResourceDictionary that matches the original Assemblies ResourceDictionary. but with translated strings
  6. Compile the culture assembly to some folder under main Assemblies bin\debug folder. This demo assumes this folder is called “CultureFiles”
  7. When main app runs, get current culture and Load the matching Assembly from disk
  8. From the currently loaded Culture Assembly, extract the ResourceDictionary
  9. Using the extracted ResourceDictionary, replace the current Application.MergedResources dictionary with the newly extracted culture ResourceDictionary
  10. All controls that refer to the Dynamic resources should be ok now with new cultural strings

In my main App I simply have the following ResourceDictionary

   1:  <ResourceDictionary xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation”
   2:      xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml”
   3:      xmlns:system=”clr-namespace:System;assembly=mscorlib”>
   4:      <system:String x:Key=”label1″>label1</system:String>
   5:      <system:String x:Key=”label2″>label2</system:String>
   6:      <system:String x:Key=”label3″>label3</system:String>
   7:      <system:String x:Key=”label4″>label4</system:String>
   8:      <system:String x:Key=”label5″>label5</system:String>
   9:  </ResourceDictionary>

Then in the App file I have the following

   1:  <Application x:Class=”LooseXAML.App”
   2:      xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation”
   3:      xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml”
   4:      StartupUri=”Window1.xaml”>
   5:  
   6:      <Application.Resources>
   7:          <ResourceDictionary>
   8:              <ResourceDictionary.MergedDictionaries>
   9:                  <ResourceDictionary Source=”Dictionary_en-US.xaml” />
  10:              </ResourceDictionary.MergedDictionaries>
  11:          </ResourceDictionary>
  12:      </Application.Resources>
  13:  
  14:  </Application>

Then somewhere I may reference these resources like

   1:  <Label Content=”{DynamicResource label1}”/>

So that is all pretty standard stuff. Next I created a new Assembly with a single ResourceDictionary in it. I am using the culture string as part of the Assembly name and or the actual ResourceDictionary

image10.png

The actual ResourceDictionary MUST contain the name items as the main Assembly

   1:  <ResourceDictionary xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation”
   2:      xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml”
   3:      xmlns:system=”clr-namespace:System;assembly=mscorlib”>
   4:      <system:String x:Key=”label1″>french label1</system:String>
   5:      <system:String x:Key=”label2″>french label2</system:String>
   6:      <system:String x:Key=”label3″>french label3</system:String>
   7:      <system:String x:Key=”label4″>french label4</system:String>
   8:      <system:String x:Key=”label5″>french label5</system:String>
   9:  </ResourceDictionary>

From here I compile this assembly and put it somewhere visible from the main Assembly, such as bin\debug\CultureFiles

image-thumb11.png

From there is just a question of loading the seperate culture Assemblies and extracting the contained ResourceDictionary and replacing the current Main Assemblies ResourceDictionary. Thus forcing all resourced referencing controls to update with the new resource data.

There is a small demo application that demonstrated this approach here which you may test by clicking on the the fr-CA listbox item

Before

image-thumb12.png

After

image-thumb13.png

But you MUST ensure that your current culture is set to French-CANADA via control panel for it to work. This means change region Options AND languages. If you don’t do this the CurrentCulture will not be correct for the demo code to work.

image-thumb14.png

Advantages Of This Method

  • Very easy to implement

Disadvantages Of This Method

  • Lots of different Assemblies to manage
  • Culture Assemblies ResourceDictionary entries must match Main Assembly otherwise a lookup may fail at runtime

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Sacha Barber
Software Developer (Senior)
United Kingdom United Kingdom
I currently hold the following qualifications (amongst others, I also studied Music Technology and Electronics, for my sins)
 
- MSc (Passed with distinctions), in Information Technology for E-Commerce
- BSc Hons (1st class) in Computer Science & Artificial Intelligence
 
Both of these at Sussex University UK.
 
Award(s)

I am lucky enough to have won a few awards for Zany Crazy code articles over the years

  • Microsoft C# MVP 2014
  • Codeproject MVP 2014
  • Microsoft C# MVP 2013
  • Codeproject MVP 2013
  • Microsoft C# MVP 2012
  • Codeproject MVP 2012
  • Microsoft C# MVP 2011
  • Codeproject MVP 2011
  • Microsoft C# MVP 2010
  • Codeproject MVP 2010
  • Microsoft C# MVP 2009
  • Codeproject MVP 2009
  • Microsoft C# MVP 2008
  • Codeproject MVP 2008
  • And numerous codeproject awards which you can see over at my blog

Comments and Discussions

 
GeneralUsing RESX Resources - Steps 9 PinmemberBertron 510-Sep-09 23:15 
GeneralRe: Using RESX Resources - Steps 9 PinmvpSacha Barber10-Sep-09 23:37 
GeneralRe: Using RESX Resources - Steps 9 PinmvpSacha Barber10-Sep-09 23:39 
GeneralRe: Using RESX Resources - Steps 9 PinmemberBertron 511-Sep-09 0:28 
GeneralRe: Using RESX Resources - Steps 9 PinmvpSacha Barber11-Sep-09 1:30 
GeneralRe: Using RESX Resources - Steps 9 PinmemberBertron 511-Sep-09 1:51 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web03 | 2.8.140721.1 | Last Updated 11 Sep 2009
Article Copyright 2009 by Sacha Barber
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid