Diederik Krols

The XAML Brewer

Globalizing and Localizing a WPF application

This article describes how to localize a WPF application using the WPF Localization Extension project from Codeplex. On one hand, this is a fine tool. On the other hand, it's a crying shame that WPF developers need to rely on external sources for something as basic as localization. As you know, localization support is nicely integrated in Microsoft's other development stacks, like WinForms and ASP.NET. So far for the ranting, let's get to the solution.


Globalization is making your application ready to be localized. This boils down to separating culture-specific elements (texts, images, colors, ...) from the code. The globalization code analysis rules may help you to do this.

Localization is tailoring your application toward a specific language and region by providing these culture-specific elements (e.g. in a resource file or a database).

Options for WPF localization

These are a couple of options for localizing a WPF application:

  • use the clumsy and error prone LocBaml tool. Don't get me wrong: I really like the idea of skipping the globalization step and then do localization as an afterthought. But just read the intro of this article for a list of reasons NOT to use LocBaml,
  • use classic RESX files, or
  • use WPF ResourceDictionaries.

More details about the pros and cons of these alternatives can be found here.

The WPF Localization Extension project

The WPF Localization Extension allows you to localize your application using classic .resx files. The project revolves around a custom markup extension that allows you to declaratively (in XAML) bind control properties to localized resources. The library offers a lot of functionality, but more important: it is well-written and well-documented (at least in its source code). I built a small sample application that walks through some of its features. Here's how this application looks like. You find its source code at the end of this article:

The project is decorated with the following resource files: 

Declarative Localization

The markup extension and its namespaces are registered by the following declaration on top your XAML file (you might want to change the project's source code to choose a more appropriate namespace):

xmlns:lex="http://schemas.root- project.org/xaml/presentation"

You can now set up a binding from a control's property to a resource, like this:

<TextBlock Text="{lex:LocText Key=Tomato, Dict=Translations, Assembly=U2UConsult.WPF.Localization.Sample}" />

It's possible to ignore the current culture, and force a specific culture for a binding, like this:

<TextBlock Text="{lex:LocText Key=Duck, Dict=Translations, Assembly=U2UConsult.WPF.Localization.Sample, ForceCulture=nl}" />

My sample application only fetches string resources, but WPF Localization Extension> supports the following resource types out of the box:

  • Brush,
  • Double,
  • FlowDirection,
  • Image,
  • Text, and
  • Thickness.

For other data types you'll need to plug in a Converter.

Programmatic Localization

Sometimes we need to localize a control in source code, e.g. when the control is dynamically created. In this case we can set the binding programmatically, like this:

LocTextExtension loc = new LocTextExtension("U2UConsult.WPF.Localization.Sample:Translations:Pumpkin");

loc.SetBinding(this.PumpkinTextBlock, TextBlock.TextProperty);

If data binding is not needed (or not possible), the you can fallback to a lookup in the .resx file using GetLocalizedObject, like this:

string pinguin = LocalizeDictionary.Instance.GetLocalizedObject<string>(





this.PinguinTextBlock.Text = pinguin;


Design time support

Localization at run time is one thing, but as a developer we like to see the result without running the application. The LocalizeDictionary.DesignCulture dependency property provides a Culture for Visual Studio's designers. It can be set in XAML, as follows:


[By the way: the design time support only started working on my VS2010 after I recompiled the codeplex source with VS2010.]

On top of that, the markup extension provides a DesignValue property that sets the value in Visual Studio's Designer, regardless of the (Design-)Culture. Here's a sample:

<TextBlock Text="{lex:LocText Key=Tomato, Dict=Translations, Assembly=U2UConsult.WPF.Localization.Sample, DesignValue=#Tomato#}" />

Here's how these two features look like at design time:

Finally, the markup extension also provides a InitialValue for Blend support.

Missing cultures and missing translations

I guess we all agree that our application should never totally crash if a string can not be found in a resource file, or if the user has an unexpected or unsupported locale. Here are some techniques to get around this.

Missing resources

It's not a good idea to just fallback to a default language for missing entries, without a warning to the end user. In the best case this will only create confusion. But just take a look at the following interlingual homographs and consider what would happen if they appear in the middle of a half-translated window:

  • coin is french for corner,
  • file is dutch for traffic jam, and
  • gift is german for poison

So the invariant culture should NOT be English (or whatever 'real' language), but still remain comprehensible. In my app, the invariant resource file looks like this:

When a translation (or the whole culture) falls back to the invariant culture, the user interface looks like this (Tomato and Pumpkin are not translated):

Missing cultures

If a specific culture is not found (e.g. 'fr-NL') then it makes sense to first check if the language exists ('fr'), and only use the invariant culture as a last resort. That's exactly what the following code does. The 'French' and 'Dzongkha' buttons in the sample application test all code paths:

private void SwitchCulture(string culture)


    CultureInfo ci = CultureInfo.InvariantCulture;



        ci = new CultureInfo(culture);


    catch (CultureNotFoundException)




            // Try language without region

            ci = new CultureInfo(culture.Substring(0, 2));


        catch (Exception)


            ci = CultureInfo.InvariantCulture;





        LocalizeDictionary.Instance.Culture = ci;

        this.CultureTextBlock.Text = ci.EnglishName;




Source code

Last but not least, here's the source code of the small sample project: U2UConsult.WPF.Localization.Sample.zip (2,41 mb)


WCF Data Services 4.0 in less than 5 minutes

WCF Data Services 4.0 (formerly known as ADO.NET Data Services, formerly known as Astoria) is one of the ways to expose an Entity Data Model from Entity Framework 4.0 in a RESTful / OData way. This article explains how to create such a data service and how to consume it with a browser and with a WPF client.

The Data Service

Start with an empty ASP.NET Application:

Add a WCF Data Service to it:

Also add an Entity Data Model to the ASP.NET project:

Follow the Model Wizard to create a model containing entities on top of the Employee and Person tables from the AdventureWorks2008 database:

In the designer, you should have something like this:

A lot of code was generated, let's add our own 50ct in the service's code behind. First let it inherit from DataService<AdventureWorks2008Entities>:

public class WcfDataService : DataService<AdventureWorks2008Entities>

{ .. }


Then modify the InitializeService method as follows. This exposes all operations and grants all access rights (not really a production setting):

public static void InitializeService(DataServiceConfiguration config)


    config.SetEntitySetAccessRule("*", EntitySetRights.All);

    config.SetServiceOperationAccessRule("*", ServiceOperationRights.All);

    config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V2;


Believe it or not, we're done (for the first part): the entity model is now exposed in a RESTful way. At the root URL you get an overview of the exposed entities. In the attached sample the root URL is "http://localhost:1544/WcfDataService.svc", but you may of course end up with another port number:

At the "/Employees" address you find all employees:

In your browser this list of employees may appear like this:

This means it's time to -at least temporarily- disable your rss feed reading view. Here's how to do this in IE:



To reach an individual entity, just type its primary key value in parentheses at the end of the URL, like "http://localhost:1544/WcFDataService.svc/Employees(1)":

You can navigate via the relationships between entities. This is how to reach the Person entity, connected to the first Employee. The URL is "http://localhost:1544/WcfDataService.svc/Employees(1)/Person":

Other OData URI options options can be found here, including:

  • Filtering: http://localhost:1544/WcfDataService.svc/Employees?$filter=JobTitle eq 'Chief Executive Officer'
  • Projection: http://localhost:1544/WcfDataService.svc/Employees?$select=JobTitle,Gender
  • Client side paging: http://localhost:1544/WcfDataService.svc/Employees?$skip=5&$top=2

Version 4.0 also includes support for server side paging. This gives you some control over the resources. Add the following line in the InitializeService method:

config.SetEntitySetPageSize("Employees", 3);

Only 3 employees will be returned now, even if the client requested all:

A Client

Enough XML for now. WCF Data Services also expose a client side model that allows you to use LINQ.

Create a new WPF application:

Add a Service Reference to the WFC Data Service:

Decorate the Window with two buttons and a listbox. It should look more or less like this:

The ListBox will display Employee entities through a data template (OK, that's XML again):



   ItemTemplate="{StaticResource EmployeeTemplate}"



Here's the template. It not only binds to Employee properties, but also to Person attributes:




        <StackPanel Orientation="Horizontal">


               Text="{Binding Path=Person.FirstName}"


               Padding="0 0 2 0"/>


               Text="{Binding Path=Person.LastName}"



        <StackPanel Orientation="Horizontal">


               Text="{Binding Path=JobTitle}"



               Text="{Binding Path=VacationHours}"


               TextAlignment="Right" />


               Text=" vacation hours taken." />




The Populate-Button fetches some Employee entities together with their related Person entity, and binds the collection to the ListBox (in version 4.0 two-way bindings are supported for WPF):

private void Populate_Click(object sender, RoutedEventArgs e)


    AdventureWorks2008Entities svc =

        new AdventureWorks2008Entities(

            new Uri("http://localhost:1544/WcfDataService.svc"));


    this.employeesListBox.ItemsSource =

        svc.Employees.Expand("Person").Where(emp => emp.BusinessEntityID < 100);


Here's the result:

The Update-Button updates the number of vacation hours of the company's CEO. It fetches the Employee, updates its VacationHours property, then tells the state manager to update the employee's state, and eventually persists the data:

private void Update_Click(object sender, RoutedEventArgs e)


    AdventureWorks2008Entities svc =

        new AdventureWorks2008Entities(

            new Uri("http://localhost:1544/WcfDataService.svc"));


    Employee employee =

        svc.Employees.Where(emp => emp.BusinessEntityID == 1).First();







If you now repopulate the listbox, you will see the increased value:

Source Code

Here's the full source code of this sample (just requires VS2010 with no extra downloads): U2UConsult.WcfDataServices.Sample.zip (96,59 kb)