Introduction
Localisation is something everyone should really care about. Creating your applications, or websites, in a manner that can be easily localised for different languages and cultures is not only “good practice”, but it may also provide additional opportunities.
Luckily the .NET framework, and WPF, has rich support for localisation, with WPF even having “baked in” support for switching flow direction for right to left languages. On top of that Rick Strahl and Michele Leroux Bustamante compiled an excellent guidance document detailing several technique for localising WPF applications. The documentation, along with sample code, can be found in the WPF Localisation Guidance project on CodePlex.
The document discusses several techniques for localising applications using RESX (resource) files, which is my preferred approach; but each of the techniques has its drawbacks:
- Static Binding. Simple and easy, but lacks support for dynamically switching culture.
- Attached Properties. Look powerful, but lacks support for value convertors and it’s a bit inefficient.
- Markup Extension. A new instance of the helper (complete with event wireup for locale switching) is created for every control, which doesn’t sound ideal and may lead to memory leaks.
All of the techniques are perfectly workable solutions, but with Binding and INotifyPropertyChanged WPF already contains a powerful mechanism for mapping and automatically updating UI elements with data; surely there’s some way we can leverage those? My goal was to attempt to find a way to localise an application with the following criteria:
- Use the standard Binding syntax so we can support value convertors.
- No complicated markup extensions.
- Provide a mechanism for code to “register” resources that can be consumed anywhere in the application, even from other PRISM modules.
- Be able to cleanly switch locales, without restarting the application or closing/reopening screens.
- Support the design experience with a minimum of fall back values when in design mode.
The Result – Localisation Using Binding
If you just want to see the code, there’s a basic implementation, and a PRISM demonstration, at the end of the article. Both implementations use the same basic “moving parts”:
LocalisationHelper
In essence this is our ViewModel. There are two key pieces of code here, first being:
public string this[string Key] { get { if (!validKey(Key)) throw new ArgumentException(@"Key is not in the valid [ManagerName].[ResourceKey] format"); if (DesignHelpers.IsInDesignModeStatic) throw new Exception("Design mode is not supported"); return _resourceManager.GetResourceString(GetManagerKey(Key), GetResourceKey(Key)); } }
This allows us to bind elements to the LocalisationHelper and provide a “key” which will be used to lookup the correct resource manager and resource string. Our binding uses a little known syntax that looks like this (take note of the initial “.”):
{Binding Path=.[MyResourceManager.MyResourceString]}
To provide design support we throw an exception if we detect design mode (using code from Laurent Bugnion’s MVVM Light Toolkit) so the FallbackValue can be used instead. I’m not too keep on throwing an exception, but I couldn’t see a cleaner way to “fail” the binding.
The other important code hooks an event that fires when the locale changes, and fires a NotifyPropertyChangedEvent with an empty property string. This triggers a refresh for all controls that have bindings to the LocalisationHelper.
ResourceManagerService
The ResourceManagerService provides several functions:
- The ability to register ResourceManagers – these are automatically generated by Visual Studio when you create RESX files and are used to load the locale specific strings.
- Retrieve a resource string from a given ResourceManager.
- Get and set the current locale. A locale consists of an IETF language tag (such as en-GB) and a boolean to indicate whether the locale uses a right to left flow direction.
- A event that is fired when the locale changes. This is hooked by the LocalisationHelper, for firing PropertyChanged events, and also by the main Window which uses the right to left flag to set the flow direction.
Give Me The Code Already!
There are two samples attached. The first is a simple application that uses a static ResourceManagerService that shows the basic implementation. The second is a PRISM based application that uses the container/service locator, EventAggregator, weak references, and several different modules to give a more “advanced” example.
Conclusion
I haven’t yet used this solution in anger, but it certainly seems to “tick all the boxes” from my initial requirements. Comments, suggestions, criticisms and flames are welcomed 🙂