View source code on GitHub
 
In first part of WPF Localization & Translation I described the requirements to localization library. In this article I will show WPF localization at runtime using ResX files.
Lets start with simple case and try to localize a text on a Button.

We want changing language to German causes “Save” changing to “Speichern” on the fly without restarting application. The second our desire is to make life easy for developers and designers who will write localizable XAML. I think next XAML is quite simple for this purpose:

History

The idea and solution was initially compiled from several sources such as:
WPF Runtime localization
WPF Localization Using RESX Files
Localization of a WPF application using a custom MarkupExtension
and other similar articles.

Since the beginning some chalenging tasks were solved and now our library successfully functioning in quite large server and client .NET solutions (80 projects, 2000+ XAML files).

Layers

In spite of simple XAML

the translation system consists of some layers that responsible for their own part of localization:
WPF Localization at runtime using ResX files
1. Storage with all localized strings in all languages. For example *.resx files. But it can be any other form of storage: DB or XML-file
2. Manager that chooses appropriate string from Storage depending on current language and returns it
3. XAML Markup Extension provides convenient using for developers and designers
4. Layer between XAML and Manager.

Storage. WPF localization at runtime using resx files.

We made a *.resx file as storage for our resources for several reasons.

  • It’s natively supported by .NET Framework. You can easy get resource from *.resx file via .NET classes.
  • Visual Studio has it’s own resource editor.
  • There are many third party tools for resource manipulating. Such as ResX Resource Manager. Or you can write your own. It’s not so difficult.
  • It’s easy to divide resources by language putting them into different *.resx files: Lib.resx, Lib.de.resx, Lib.ru.resx. Below is the screenshot of one of the resource editor implementation
  • ResX file resource editor

    ResManager

    It’s nonWPF class that can retrieve resources from storage by key. It’s methods such as GetResourceString(“Lib_Save”), GetResourceObject(“Lib_SomeObjectKey”) return localized resources.

    Here we have to decide about important thing – resources organization in files.
    Having localization library in large solution with more than 20 developers we very quickly fall into swelling our resx file to something that impossible to manage. There are advices to create one resx file per xaml file. It’s not good for some solutions I worked on, with 2000+ XAML files. You get 2000+ additional resx files for each language you supported. For 2 languages it will be 4000+ files, for 3 – 6000+ etc.
    How to organize your own resources depends on your project. Below I describe the kind of storage that successfully works with such big project I described above.

    Resources storage organization

    Almost all application consists of two parts: common library and application itself. Common library is a set of projects with common classes. “Common” means classes and other stuff that can be used in many applications. For example Reflection helpers, collection helpers, Linq extensions, common dialogs, WPF controls, converters and much more each company have in it’s arsenal. This common library can be used by any application. It’s the base for any application. So all resouces that belong to common library and invariant for all applications we can put to independent file Lib.*.resx. Such strings as “Save”, “Cancel”, “Close”, “Do you really want to delete this row?”, “ID”, “Name” and so on.

    Application resources we can divide into groups too. If your app get entities from DB it’s probably you have a deal with some form of codegeneration. Entity Framework as example. And you can generate resource files for entities. With T4 Text Templates or Codesmith Generator. Here we get another resx file: Entity.*.resx

    Other application resources we put into third MyApp.*.resx

    Keys

    How should we ask the ResManager to get text for Save button that was put in Lib.*.resx file? It would be convenient to say

    Our ResManager, depending on current UI Culture, would search through the resource files and find appropriate resource in Lib.*.resx. But here we stumble upon two points:
    1. Performance. It’s not good searching in all files
    2. Ambiguity. You can’t be sure that one or another of developers put resource string with “Save” key into another resx file. Of course you can rely upon developer’s awareness and attention. But I’ve found more than 30 “Save” strings in “MyApp”.resx file in our application. Developers put them there for Save buttons in views they’ve created.

    To solve these two problems we make our keys consist of several parts:
    Prefix_Name_Suffix
    Suffix can be complex and I will describe it in the next articels when will show complex cases. For most cases we are interesting in Prefix and Name. Prefix defines file(for file based storage) where resource is stored. Name is the name of resource. For Save button in Lib.*.resx file it would be stored with Key = “Lib_Save”.

    For entities in Entity.*.resx file we have resources with keys Entity_Order_IssueDate, Entity_Order_Status, Entity_Customer_ShortName, Entity_Customer_Phone. For ID and Name it’s better to have special keys Entity_Base_Id, Entity_Base_Name as they are used in almost all entities.

    But there are huge amount of resources for the MyApp application. How can we manage this heap? We can make our keys smarter:
    Prefix_ViewName_Name_Suffix
    Lets imagine button on OrderView.xaml with text “Close order”. A key for it will be:

    Default resource file

    95% of text on the view is taken from MyApp.*.resx file. It’s not good for developer or designer to write “MyApp_…” on each TextBlock or Button. We could define and register one of resx files as default with no prefix. So 95% of resources would start without prefix but with View name.

    XAML Markup Extension

    How can we provide such short form as {Res …} for Button.Content or TextBlock.Text properties? Only through MarkupExtension:

    Layer between XAML and Manager

    It works fine but has one flaw. ProvideValue() method returns localized object which wouldn’t change when current UI culture is changed. We need to make ProvideValue() more complex to return BindingExpression from it. This will allow us not only to react on culture changing but to react on key changing in case of dynamic keys(I’ll explain dynamic keys later in the next article).
    I wouldn’t perplex reader with details how we can accomplish this task. You can look at ResExtension.ProvideValue() and ResConverter class in the source code

    Make it shorter

    Now we need to write namespace in each case:

    which may be tedious. To workaround this we can use the next trick. Create XmlnsDefinitions.cs file and write code:

    It’s not honest trick but successfully works with no side effects. Now we can write short form:

    View source code on GitHub
     

    Summary

    In this article I described main parts of localization library and storage details that need just to put tranlated text to Button.Content. In the next article we consider other more complex localization cases: enums, dynamic keys, formatted strings and so on.