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
- Create normal WPF App, and use a ResourceDictionary for strings
- Use MergedDictionary in App.resources
- Add [assembly: NeutralResourcesLanguage(”en-US”, UltimateResourceFallbackLocation.Satellite)] in AssemblyInfo.cs
- Add new <UICulture>en-US</UICulture> in all ProjectGroup tags for current project
- Open command prompt at project level, run msbuild /t:updateuid <PROJECT NAME>.csproj
- Open command prompt at project level, run msbuild /t:checkuid <PROJECT NAME>.csproj
- Build it from VS
- Obtain 1st cultures CSV file, LocBaml.exe /parse en-US/<PROJECT NAME>.resources.dll /out:<PROJECT NAME>.csv
- Edit CSV save as new csv file name, such as <PROJECT NAME><nn-XX CULTURE STRING>.csv
- Need to do translation for new culture. Do something like
- LocBaml.exe /generate en-US/HelloWorld.resources.dll /trans :HelloWorldfr-CA.csv /out:c:\ /cul:fr-CA
- Create new <nn-XX CULTURE STRING> directory in Bin folder
- Copy <PROJECT NAME>.resources.dll from step 10, which was placed on C: to the folder created in step 11
- 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
- Create normal WPF App
- 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
- Within any Window that you want to use localized text, add a clr-namespace such as
- xmlns:properties=”clr-namespace:HelloWorldUsingResXFiles.Properties”
- Hook up a items text to a resource file text, like
- <Button Margin=”10″ Name=”button1″
- Content=”{x:Static properties:Resources.MainButtonText}”></Button>
- 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
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.
- Create normal WPF App, and use a ResourceDictionary for strings
- Use MergedDictionary in App.resources
- Make sure any localizable controls using a DynamicResource like
<Label Content=”{DynamicResource label1}”/>
- Create a new culture assembly with a sensible name, such as Culture_fr-CA
- For assembly created in step 4, create mirror image ResourceDictionary that matches the original Assemblies ResourceDictionary. but with translated strings
- Compile the culture assembly to some folder under main Assemblies bin\debug folder. This demo assumes this folder is called “CultureFiles”
- When main app runs, get current culture and Load the matching Assembly from disk
- From the currently loaded Culture Assembly, extract the ResourceDictionary
- Using the extracted ResourceDictionary, replace the current Application.MergedResources dictionary with the newly extracted culture ResourceDictionary
- 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

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

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

After

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.

Advantages Of This Method
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
| You must Sign In to use this message board. |
|
|
 |
 | LocBaml  Reinhard Ostermeier | 1:47 11 Sep '09 |
|
 |
Hi, I'm currently working on a bunch of tools which follows the LocBaml workflow. Instead of using cvs files for translations I will use xml files. With the main tool you can setup projects to combine multiple c# or vb projects into one 'solution'. Then you decide into which languages you want to translate your assemblies. You can split and merge this main xml file into files with a subset of languages - one for every team member who does translations. These translations can be done with an seperate editor that has a lot of filter capabilities. Dictionaries for auto translation will be supported as well. You can implement your own auto translation sources by implementing an .net interface (to use some online translation service for example). A the end you click a generate button to get the satelite dll's.
By this tools the disadvantages you mentioned for LocBaml should be solved.
I'm still in the beginning of this project, but as soon as I have a working version I will host it on codeplex.com. So have a look for bloc.codeplex.com in the near future. As soon as I find some more time, I will write an small article for CodeProject as well.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
 |
Cool
Sacha Barber- Microsoft Visual C# MVP 2008/2009
- Codeproject MVP 2008/2009
Your best friend is you. I'm my best friend too. We share the same views, and hardly ever argue My Blog : sachabarber.net
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Hey Sacha,
Great article, it's really helped me understand WPF Localization!
Just to make you aware, your 9th point which reads: "9.Resources.&lt;nn-XX CULTURE STRING&gt;.resx" didn't work for me. It appears that WPF uses the same naming conventions as Windows Forms:
Resources.resx Resources.de.resx Resources.de-DE.resx etc...
Another point I'd like to mention is that Step 10 forces my locale to whichever culture I'm added to it. (As you would expect). By removing this and leaving my App.xaml.cs file empty, I can set the UICulture to be that of my CurrentThread's culture and achieve my desired results!
I know it's a little academic to bring it up, I just thought I'd let you know!
Thanks again for a great article!
Rob
PS, I've rewritten your steps for this part of your article for your to paste back in. (Assuming I'm not completely misunderstanding your 9th step and I'm providing an incorrect step!)
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=cclr-namespace:HelloWorldUsingResXFiles.Properties" 5.Hook up a items text to a resource file text, like 6.&lt;Button Margin="10" Name="button1" 7.Content="{x:Static properties:Resources.MainButtonText}"&gt;&lt;/Button&gt; 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
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Ah ok cool, Ill ammend the article now
Sacha Barber- Microsoft Visual C# MVP 2008/2009
- Codeproject MVP 2008/2009
Your best friend is you. I'm my best friend too. We share the same views, and hardly ever argue My Blog : sachabarber.net
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
By the way if you were using the MVVM pattern here is another idea
public class LocalizableViewModelBase : ViewModelBase {
private static Resources resources = new Resources(); private static CultureInfo cachedCulture;
public Resources LocalizedText { get { return resources; } }
protected void SetCulture(string userCulture, bool cacheCurrent) { … if (Thread.CurrentThread.CurrentUICulture.Name != userCulture) { Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo(userCulture); this.RaisePropertyChanged("LocalizedText"); } } }
Which would allow a XAML binding like this
<TextBlock Text="{Binding LocalizedText.UserName, Mode=OneWay}"
TextWrapping="Wrap"
Height="15"
VerticalAlignment="Bottom"
Grid.Column="1" />
Sacha Barber- Microsoft Visual C# MVP 2008/2009
- Codeproject MVP 2008/2009
Your best friend is you. I'm my best friend too. We share the same views, and hardly ever argue My Blog : sachabarber.net
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Hi again,
I'm trying to implement your MVVM approach to locale and I'm not having much luck...
I've crunched both the ViewModelBase class and the LocalizableViewModelBase into the same class to produce:
private static Resources resources = new Resources(); private static CultureInfo cachedCulture;
public Resources LocalizedText { get { return resources; } }
protected void SetCulture(string userCulture, bool cacheCurrent) { if (Thread.CurrentThread.CurrentUICulture.Name != userCulture) { Thread.CurrentThread.CurrentUICulture = new CultureInfo(userCulture); RaisePropertyChanged("LocalizedText"); } }
public event PropertyChangedEventHandler PropertyChanged; private void RaisePropertyChanged(string property) { if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(property)); }
And as suggested, I'm using your XAML to perform the binding, what's the obvious (and presumably embarrassing) mistake I'm making?
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
To be honest that idea was not my own, I just thought that approach may be of interest to you. It looked like it would work, have you got some Resources set up in the apps Resource file?
I have never tried it, if I find some time, which I have to say it not that likely, as I have a tonne of other stuff on right now, Ill give it a go.
For now you may like to read this document
http://wpflocalization.codeplex.com/[^]
Which is probably the best document on WPF localization out there.
Sacha Barber- Microsoft Visual C# MVP 2008/2009
- Codeproject MVP 2008/2009
Your best friend is you. I'm my best friend too. We share the same views, and hardly ever argue My Blog : sachabarber.net
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
|