A quick look at WPF Localization Extensions

Continuing my look into localizing WPF application…

I’ve covered techniques used by theme’s to change a WPF application’s resources for the current culture. I’ve looked at using locbaml for extracting resources and localizing them. Now I’m looking at WPF Localization Extensions.

The WPF localization extension take the route of good old resx files and markup extensions (along with other classes) to implement localization of WPF applications.

Getting Started

  • Create a WPF application
  • Using NuGet, install the WPFLocalizeExtension
  • Open the Properties section and copy Resources.resx and rename the copy Resources.fr-FR.resx (or whatever culture you wish to support)

As with my other examples of localizing WPF applications, I’m not going to put too much effort into developing a UI as it’s the concepts I’m more interested in at this time.

First off let’s add the following XAML to MainWindow.xaml within the Window namspaces etc.

xmlns:lex="http://wpflocalizeextension.codeplex.com"
lex:LocalizeDictionary.DesignCulture="en"
lex:LocalizeDictionary.OutputMissingKeys="True"
lex:ResxLocalizationProvider.DefaultAssembly="Localize2"
lex:ResxLocalizationProvider.DefaultDictionary="Resources"        

The DefaultDictionary needs to have the name of the resource file, whether it’s Resources (exluding the .resx) or if you’ve created one names Strings or whatever, just exclude the extension.

The DefaultAssembly is the name of the assembly to be used as the default for resources, i.e. in this case it’s the name of my project’s assembly.

Next up, within the Grid, we’re going to have this

<TextBlock Text="{lex:Loc Header}" />

Header is a key – obviously on a production ready application with more than one string to translate, we’d probably implement a better naming convention.

A tell tale sign things are wrong is you’ll see the text displayed as Key: Header, this likely points to one of the namespace values being incorrect, such as the DefaultDictionary name.

That’s it for the UI.

In the Resources.resx, add a string named Header and give it a Value, for example mine should default to English and hence will have Hello as the value. In the Resources.fr-FR.resx, add a Header name and give it the value Bonjour.

That’s the extent of our strings for this application. If you run the application you should see the default Resources string, “Hello”. So now let’s look at testing the fr-FR culture.

In App.xaml.cs create a default constructor and place this code within it

LocalizeDictionary.Instance.SetCurrentThreadCulture = true;
LocalizeDictionary.Instance.Culture = new CultureInfo("fr-FR");

Run the application again and the displayed string will be take from the fr-FR resource file.

To allow us to easily switch, at runtime, between cultures, we can use the LocalizeDictionary. Here’s a combo box selector to do this (taken from the sample source on the WPF Localization Extensions github page).

<ComboBox ItemsSource="{Binding Source={x:Static lex:LocalizeDictionary.Instance}, Path=MergedAvailableCultures}"
   SelectedItem="{Binding Source={x:Static lex:LocalizeDictionary.Instance}, Path=Culture}" DisplayMemberPath="NativeName" />

We also need to be able get strings from the selected resource in our code, here’s a simple static class from StackOverflow which allows us to get a string (or other type) from the currently selected resources

public static class LocalizationProvider
{
    public static T GetLocalizedValue<T>(string key)
    {
        return LocExtension.GetLocalizedValue<T>
(Assembly.GetCallingAssembly().GetName().Name + ":Resources:" + key);
    }
}

The string “Resources” should obviously be changed to the name of your resource files (for example if you’re using just strings in a “Strings” resource etc).

This is certainly simpler to set-up than locbaml, the obvious drawback with this approach is that the strings at design time are not very useful. But if, like me, you tend to code WPF UI primarily in XAML then this probably won’t concern you.