Localizing a WPF application using dynamic resources

There are several options for localizating a WPF application. We can use resx files, locbaml to create resource DLL’s for us, or why not just use the same technique used in theme’s, i.e. DynamicResource and ResourceDictionary’s.

In this post I’m going to look at the DynamicResource and ResourceDictionary approach to localization. Although this technique can obviously be used with images etc., we’ll concentrate on dealing with strings, which usually are the main area of localization.

Let’s start with some code

Create a simple WPF application which will use the standard DynamicResource to set text on controls. We will create a “default” set of string resources to allow us to develop our initial application with and we will create two satellite assemblies which will contain the same string resources for en-GB and en-US resources.

  • Create a WPF Application
  • Create a class library named Resources_en-GB and another class library named Resources_en-US
  • Add the references PresnetationCore, PresentationFramework, WindowsBase and System.Xaml to these class libraries
  • Change the class library output folders for Debug and Release to match those for the WPF application so the DLL’s will be built to the same folder as the application

Now in the WPF application, add a ResourceDictionary, mine’s named Strings.xaml and this will act as our default/design-time dictionary, here’s mine

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:system="clr-namespace:System;assembly=mscorlib">

    <system:String x:Key="Header">TBC</system:String>
    
</ResourceDictionary>

and my MainWindow.xaml looks like this

<Window x:Class="WpfLocalizable.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfLocalizable"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <StackPanel>
        <TextBlock Text="{DynamicResource Header}" FontSize="24"/>
        <StackPanel Orientation="Horizontal" VerticalAlignment="Bottom">
            <Button Content="US" Width="100" Margin="10" />
            <Button Content="GB" Width="100" Margin="10" />
        </StackPanel>
    </StackPanel>
</Window>

I didn’t say this was going to be exciting, did I?

Now if you run the application as it currently stands, you’ll see the string TBC – our design-time string.

Next, copy the Strings.xaml file from the application to both the Resources_en-GB and Resources_en-US and change the string text to represent your GB and US strings for header – I used the word Colour in GB and Color in US – just to demonstrate the common language differences.

Now if you build and run the application, you’ll see the default header text still, so we now need to make the application set the resources at start-up and allow us to easily switch them. So change the buttons in MainWindow.xaml to these

<Button Content="US" Width="100" Margin="10" Click="US_OnClick"/>
<Button Content="GB" Width="100" Margin="10" Click="GB_OnClick"/>

We’re going to simply use code behind for changing the resource in this demo. So in MainWindow.xaml.cs add the following code

private void LoadStringResource(string locale)
{
   var resources = new ResourceDictionary();

   resources.Source = new Uri("pack://application:,,,/Resources_" + locale + ";component/Strings.xaml", UriKind.Absolute);

   var current = Application.Current.Resources.MergedDictionaries.FirstOrDefault(
                    m => m.Source.OriginalString.EndsWith("Strings.xaml"));


   if (current != null)
   {
      Application.Current.Resources.MergedDictionaries.Remove(current);
   }

   Application.Current.Resources.MergedDictionaries.Add(resources);
}

private void US_OnClick(object sender, RoutedEventArgs e)
{
   LoadStringResource("en-US");
}

private void GB_OnClick(object sender, RoutedEventArgs e)
{
   LoadStringResource("en-GB");
}

and finally in the constructor let’s default to en-GB, so simply add this line after the InitializeComponent

LoadStringResource("en-GB");

Now run the application, be default you should see en-GB strings and then press the US button to see the en-US version etc.

Finishing touches

In some situations we might want to switch the languages strings used via an option (very useful when debugging but also in you’re natural language is not the same as the default on your machine). In most cases, we’re likely to want to switch the language at start-up to match the machines’s culture/language.

Using ResourceDictionary might look a little more complex than CSV files, but should be easy for your translators to use and being, ultimately, XML – we could ofcourse write a simple application to allow the translators to view strings etc. in a tabular format.

We can deploy as many or as few localized resources as we need on a machine.

References

Checkout this a useful document WPF Globalization and Localization Overview from Microsoft.