Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Simple WPF Localization

0.00/5 (No votes)
9 Jul 2009 2  
An article on how to easily and effectively fully localize a WPF application.

Introduction

After searching the Internet for a while on how to localize a WPF application, I found two very nice solutions which can be found here and here. However, both solutions looked too complicated to me, and I decided to implement a solution that will just do what I need with simplicity and effectiveness.

My solution by far does not have the features of the above solutions, but it still does its job perfectly: to localize the text in my project.

Background

The basic goals are:

  • Easy to use localization that allows localizing the XAML directly during design time
  • Use of embedded resources
  • The language can be changed at runtime
  • Ability to see the localized resources in the designer of Visual Studio
  • The implementation to be as simple as possible and to not introduce performance issues

Easy to Use

To reach the first goal, I borrowed the great idea from the other two solutions to use a WPF extension. Extensions can be used directly in XAML to assign values to properties, and are extremely useful for the purpose.

For example, the localization of a TextBlock in XAML would look like this:

<TextBlock Text="{Loc Text_Hello}"/>

Use Embedded Resources

The second goal can be reached by connecting a resource manager to the localization component.

When you create a WPF application in Visual Studio, Visual Studio creates a Resources.resx file in the Properties folder of the project. Because this looks like a perfect location to put all your localizable resources in, I chose to attach the localization component to this resource file by default. This is the same resource file you normally access by using the Properties.Resources.MyResourceName expressions in your code-behind.

For flexibility, an arbitrary resource manager can be explicitly connected to the localization component at any time. This will also cause all of the localized content to be updated with the new values.

Design Time Support

Design time support is limited only to the default culture (i.e., the resources you put in the Properties/Resources.resx file). This means you can see any localized resource rendered in the designer but only those you have put in the mentioned file. Any culture specific resource you put in additional resource files (like Resources.fr-FR.resx) will not be visible during design time unless you temporarily rename the files to see your changes. If you find any simple solution to this problem, please let me know.

Design Time Issues

As simple as the code is, you may have some issues during design time, like you may not see your recent changes or you may not see any localized resources at all.

The first issue is easily solved by recompiling the project which causes Visual Studio to reload the main assembly of the project and then to refresh the designer area.

The second issue is more subtle.

At runtime, the default resource file is easily found by obtaining the main entry assembly of the application and creating a resource manager that references the file. But this does not work during design time because an assembly is deemed to be the entry assembly of an application only if the application has either been started by it or by the AppDomain.ExecuteAssembly method (refer to the documentation of the System.Windows.Application.ResourceAssembly property for more information). Therefore, the localization component uses the following procedure to try to find the main assembly of the application during design time: obtain the list of all assemblies loaded in the current domain and search for the first assembly that contains both the Main method and a MyAssemblyName.App class that derives directly or indirectly from System.Windows.Application. If the component finds such an assembly, it creates a resource manager connected to the MyAssemblyName.Properties.Resources resource file of the assembly.

If you have the above issue during design time, you can directly load the assembly of your application by replacing some of the code in the LocalizationManager.GetResourceManager class with code similar to Assembly.Load("MyassemblyName") and use it as the resource assembly.

The third and the fourth goals are strictly an implementation issue and are not covered in this article. The only thing I would mention is that the implementation consists of only two very simple classes and does not add any performance penalty to your application.

Using the Code

To use the solution, you need to do the following:

  1. Add a reference to the WPFLocalization assembly, or better - include the two source code files into one of your class library projects that accompany your applications. The second solution is better because having a library that consists of only two classes adds cost to your application.
  2. If you choose to include the two source code files in your application, note the following:

    1. I advise you to add the classes in one of your class libraries instead of to the main application project as you may have design time issues every time there are any compilation errors (i.e., Visual Studio may fail to load the assembly and raise an error in the designer).
    2. If you change the name of the namespace from "WPFLocalization" to something else, you have to make the same change on the second argument of the System.Windows.Markup.XmlnsDefinition assembly attributes at the beginning of the LocExtension.cs file.
  3. Extract all your localizable resources for the default culture and put them into the Properties/Resources.resx file. Of course, you can place the resources in a resource file anywhere else as well, but this will break the design time support unless you make changes to the code.
  4. For every additional culture, add a corresponding resource file to the Properties folder (e.g., Properties/Resources.fr.resx) and create a localized version of the resources.

  5. Use the localization extension in XAML to reference the resources.

While steps 1 and 2 are straightforward, step 3 needs additional explanation.

Using the Localization Extension

To use the localization extension, you simply need to apply it in XAML on the properties you want to localize, similar to the following code:

<TextBlock Text="{Loc MyResourceKey}"/>  

MyResourceKey is the name of the resource in the Properties/Resources.resx file you want to reference.

To use formatting, use code like:

<TextBlock Text="{Loc CreatorName, Format='Created by {0}'}"/>  

You do not have to add any namespace references (like xmlns:loc="...") at the beginning of the XAML file, because the extension registers to the default Microsoft namespaces at startup (another idea I have borrowed from one of the other solutions).

Images

I have not tested the extension with images and I have no idea if it will work. Handling images most probably requires additional effort and is something I have not planned.

Missing Resources

If a resource you reference is not found, then the following applies:

  • If the application is a debug version, the resource is substituted by the text "[Resource: ResourceKey]".
  • If the application is a release version, the resource is substituted by an empty string.

Changing the Current Culture at Runtime

The current culture can be changed during runtime at any moment by assigning a value to the LocalizationManager.UICulture property. A change to the property does the following:

  1. Changes the UI culture of the current thread to the specified culture (i.e., assigns the value to Thread.CurrentThread.CurrentUICulture).
  2. Updates all the properties localized by the extension.

Using a Different Resource Manager

To use a different resource manager, simply assign a value to the LocalizationManager.ResourceManager property. Assigning a value will also update the localized properties.

Conclusion

Any comments and/or suggestions are welcome.

History

  • 2008-10-10: Initial version.
  • 2008-11-28: Added support for templates.

Updated Version

  • 2009-07-10: Added support for non-dependency properties (i.e., standard properties) to support the Microsoft.Windows.Controls.Ribbon control.
  • 2009-07-10: If a resource is not found during design time, an ArgumentOutOfRangeException exception is thrown instead of showing a [Resource: ResourceKey] message. This makes missing resources easier to spot.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here