Click here to Skip to main content
15,867,330 members
Articles / Desktop Programming / WPF

Advanced WPF Localization

Rate me:
Please Sign up or sign in to vote.
4.85/5 (27 votes)
1 Oct 2017Public Domain6 min read 261.2K   7K   74   138
Provides a solution for localization of WPF application both in XAML and in code-behind

Introduction

In this article, I'm presenting an easy way to localize a WPF application. Localization can be done in XAML, code-behind or both.

This solution enables you to:

  • Localize text, images, brushes, margins, control width and height, flow direction (or any other enumeration) and virtually any property that can be converted from text. Localization is done directly in XAML by using a markup extension.
  • Localize custom controls located in a class library assembly
  • Localize properties in code behind and change the values of localized properties
  • Localize both dependency and non-dependency properties
  • Change the current language on-the-fly with immediate results
  • Use multiple resource files in the same Window/User control
  • Use multiple languages (cultures) in the same Window/User control
  • Support multiple UI threads
  • Support code-behind localization from a non-UI thread
  • Support data templates and can be used in styles

Added 2011-09-10

  • Localization of bindings: Format data values

Localization in XAML

To localize your text, images, etc. in XAML, do the following:

  1. Open the default resource file created by VS in your WPF application project. It is located in "Properties\Resources.resx".
  2. Create entries in the string resources of the file. For example enter "HelloWorld" for name and "Hello World!" for value.
  3. Use the following syntax to display the text in your application:
XML
<TextBlock Text="{Loc HelloWorld}" />

That's it. You can localize any string in your application that way.

To localize an image (if you need culture-specific images), add the image in the "Images" section of the resource file and set its name. Then reference the image in your application by its name. For example, if you add an image and name it "MyImage", use the following in XAML to display it:

XML
<Image Source="{Loc MyImage}" />   

In case you need to localize the size of a control, its color, margin, flow direction, font, etc., you can do that too:

XML
<Grid Name="myGrid" Height="{Loc myGrid_Height}"
	Width="{Loc myGrid_Width}" Margin="{Loc myGrid_Margin}"
	FlowDirection="{Loc myGrid_FlowDirection}">
    <TextBlock Name="myText" FontFamily="{Loc myText_FontFamily}"
	FontBold="{Loc myText_FontBold}" Foreground="{Loc myText_Color}"/>
</Grid>

Resource file:

C#
myGrid_Height = 50
myGrid_Width = 100
myGrid_Margin = 20,10,20,10
myGrid_FlowDirection = LeftToRight
myText_FontFamily = Arial
myText_FontBold = True
myText_Color = Red

In the resource file, you put exactly the same values you would put in XAML.

You can localize pretty much any value. Primitive values (char, boolean, numbers), DateTime and enumerations are supported out of the box. Other values like margins (the .NET "Thickness" type), brushes and font families are supported if they have an associated TypeConverter that can be used to convert them from their string representation.

If you need, you can also use a custom converter the same way you use it in bindings:

XML
<Image Source="{Loc MyImage, Converter={StaticResource MyConverter},
	ConverterParameter=10}"/>

Resource Files

By default, the solution looks for resources in the resource file created automatically by VS in your WPF application project. It is located in "Properties\Resources.resx". If you want to use a different resource file, you must explicitly specify it in XAML.

To do this, you must assign a value to the "LocalizationScope.ResourceManager" attached property. As the name implies, the "LocalizationScope" type exposes several attached properties that control localization in a part of your window or user control. You can set these properties on any element in XAML and their values will affect the localization of that element and all its children.

XML
<Window ...>
   ...
   <Grid LocalizationScope.ResourceManager="...">...</Grid>
   ...
</Window>

To assign a value to the LocalizationScope.ResourceManager property in XAML, you must use the "ResourceManager" markup extension included in the solution. There are two ways to specify a resource file by using the extension - by referring to its corresponding auto-generated type or by entering an assembly name and base name.

Auto-generated Type

VS automatically generates a code-behind file for any resource file. The code-behind file contains an autogenerated class that exposes all resources contained in the file. While this autogenerated class is not used by the localization solution, its type is used to initialize a System.Resources.ResourceManager instance. The "ResourceManager" type uses the namespace and name of the resource file's class to locate the resources in the assembly.

To refer to the type, you must:

  1. Make the type public (open the resource file and change the "Access Modifier" from "Internal" to "Public")
  2. Declare a namespace in XAML referring to the type's namespace:
    XML
    <Window ... xmlns:res="clr-namespace:MyProject.Properties">
       ...
       <Grid LocalizationScope.ResourceManager="{ResourceManager res:MyResources}">...
       </Grid>
       ...
    </Window> 

    In the above example, the resource file is named "MyResources" and is located in the "Properties" folder of the project "MyProject".

Assembly Name and Base Name

The second way to specify a resource file is to enter the name of the assembly containing the resources and the base name of the resources.

XML
<Window ...>
   ...
   <Grid LocalizationScope.ResourceManager="{ResourceManager AssemblyName='MyProject',
	BaseName='MyProject.Properties.MyResources'}">...</Grid>
   ...
</Window> 

A small advantage in this case is that you do not have to make the auto-generated type public.

Bindings

Databound properties can be localized. Localization can be used to add localized text to a value and/or to ensure that a date or a number is formatted according to the selected language.

  • Only dependency properties of type "System.String" and "System.Object" are supported.
  • The format string can be stored in resources or in XAML.
  • A special "LocBinding" extension is used to localize bindings.
XML
<TextBlock>
    <TextBlock.Text>
        <LocBinding ResourceKey="MyNameIs">
            <Binding Path="FirstName"/>
            <Binding Path="LastName"/>
        </LocBinding>
    </TextBlock.Text>
    <TextBlock.Text>
        <LocBinding ResourceKey="CurrentDate">
            <Binding Source="{x:Static sys:DateTime.Now}" Path="."/>
        </LocBinding>
    </TextBlock.Text>
</TextBlock>
XML
<TextBlock>
    <TextBlock.Text>
        <LocBinding StringFormat="My name is {0} {1}">
            <Binding Path="FirstName"/>
            <Binding Path="LastName"/>
        </LocBinding>
    </TextBlock.Text>
    <TextBlock.Text>
        <LocBinding StringFormat="Today is {0:d}">
            <Binding Source="{x:Static sys:DateTime.Now}" Path="."/>
        </LocBinding>
    </TextBlock.Text>
</TextBlock> 

A value of the "ContentControl.Content" property (Label, ListBoxItem, etc.) cannot be localized directly. The reason is that WPF ignores MultiBinding.StringFormat for this property. To localize it, use the following:

XML
<ListBoxItem>
    <ListBoxItem.Content>
        <TextBlock>
            <TextBlock.Text>
                <LocBinding ResourceKey="...">
                    ...
                </LocBinding>
            </TextBlock.Text>
        </TextBlock>
    </ListBoxItem.Content>
</ListBoxItem>

Localization in code-behind

Dependency and non-dependency properties of any descendant of the "DependencyObject" type can be localized in code-behind.

See the demo project. I'll cover this in a new article when I have enough time. The demo project is pretty self explaining.

Setting the Current Language

The .NET framework exposes two properties to set the current culture - Thread.CurrentCulture and Thread.CurrentUICulture. The CurrentCulture property controls formatting dates and numbers. The CurrentUICulture property controls which resources are accessed. To set the current language, set one or both of these properties and then call the LocalizationManager.UpdateValues() method.

C#
Thread.CurrentThread.CurrentCulture = myNewCulture;
Thread.CurrentThread.CurrentUICulture = myNewCulture;
LocalizationManager.UpdateValues();

Other Features

Missing Resources

Missing string resources are replaced with the following string: "[MyResourceKey]". Therefore, if you have...

XML
<TextBlock Text="{Loc HelloWorld}" /> 

...and forget to enter "HelloWorld" in resources, you will see "[HelloWorld]" on the screen.

All other types of resources are replaced with their default value (null for reference types, zero for numbers, etc.).

Multiple Languages

You can set the Culture and UICulture properties on any element in XAML via the "LocalizationScope.Culture" and "LocalizationScope.UICulture" attached properties. Setting these properties define the cultures used by the control and its children.

XML
<Grid Name="myGrid" LocalizationScope.UICulture="English (United States)">
...
</Grid>
C#
LocalizationScope.SetUICulture(myGrid, myNewCulture);
// Update the localized values
LocalizationManager.UpdateValues();

For more information, see the demo project. I'll cover this in a new article when I have enough time.

Multithreading

Updating values in code-behind from threads other than the UI thread is supported. Multiple UI threads are supported.

For more information, see the demo project. I'll cover this in a new article when I have enough time.

History

  • 2011-09-09
    • Initial version
  • 2011-09-10
    • Bug fix: Fixed a bug in the demo project: "LocalizationManager.UpdateValues()" does not have to be called when setting properties in code-behind.
    • Bug fix: "[MyResourceKey]" is not displayed when localizing the "ContentControl.Content" property (Label, Button, etc.) and the resource is not found.
    • Added "CallbackParameter" when a callback is used for localization in code-behind.
    • Added support for localization of bindings.

License

This article, along with any associated source code and files, is licensed under A Public Domain dedication


Written By
Software Developer (Senior)
Bulgaria Bulgaria
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionSolved Multilingual from code behind Pin
ankitCsharpAsp20-Apr-20 4:42
ankitCsharpAsp20-Apr-20 4:42 
QuestionHow to update the calendar when changing the language? Pin
rom-nik21-Nov-19 4:02
rom-nik21-Nov-19 4:02 
QuestionWhy don't you need a namespace for the markup extension Pin
Phil Jollans19-Mar-19 11:14
Phil Jollans19-Mar-19 11:14 
AnswerRe: Why don't you need a namespace for the markup extension Pin
Phil Jollans22-Mar-19 20:27
Phil Jollans22-Mar-19 20:27 
GeneralMy vote of 5 Pin
Kithoras Carzyl13-Aug-18 4:53
Kithoras Carzyl13-Aug-18 4:53 
QuestionUsing in commercial product Pin
Member 470024620-Mar-17 11:55
Member 470024620-Mar-17 11:55 
AnswerRe: Using in commercial product Pin
Jecho Jekov1-Oct-17 0:30
Jecho Jekov1-Oct-17 0:30 
Hi,

I am sorry for this very late reply - for some reason I do not receive notifications to my e-mail when a new message is posted.

The code is free to use in any way and there is no need to include any license or any reference to the original.

Regards,
Jecho
SuggestionGreat. Have modified purge though Pin
mikipatate9-Aug-16 21:13
mikipatate9-Aug-16 21:13 
GeneralRe: Great. Have modified purge though Pin
Jecho Jekov3-Dec-16 10:12
Jecho Jekov3-Dec-16 10:12 
GeneralRe: Great. Have modified purge though Pin
mikipatate14-Dec-16 4:34
mikipatate14-Dec-16 4:34 
QuestionThank you, great work Pin
Henka.Programmer9-Jun-16 4:20
Henka.Programmer9-Jun-16 4:20 
AnswerRe: Thank you, great work Pin
Jecho Jekov3-Dec-16 10:15
Jecho Jekov3-Dec-16 10:15 
GeneralRe: Thank you, great work Pin
Henka.Programmer4-Dec-16 2:18
Henka.Programmer4-Dec-16 2:18 
QuestionThank you, great work Pin
Henka.Programmer29-May-16 11:41
Henka.Programmer29-May-16 11:41 
AnswerRe: Thank you, great work Pin
Jecho Jekov3-Dec-16 10:15
Jecho Jekov3-Dec-16 10:15 
PraiseIncredible work, thank you! Pin
AWriter22-Mar-16 2:09
AWriter22-Mar-16 2:09 
GeneralRe: Incredible work, thank you! Pin
Jecho Jekov3-Dec-16 9:47
Jecho Jekov3-Dec-16 9:47 
GeneralThank You, Great Job Pin
Henka.Programmer2-Mar-16 10:10
Henka.Programmer2-Mar-16 10:10 
GeneralRe: Thank You, Great Job Pin
Jecho Jekov3-Dec-16 9:19
Jecho Jekov3-Dec-16 9:19 
QuestionThx for your excellent project. Why am I not seeing [ResourceKey] for missing resources? Pin
JamesHurst3-Aug-15 21:25
JamesHurst3-Aug-15 21:25 
AnswerRe: Thx for your excellent project. Why am I not seeing [ResourceKey] for missing resources? Pin
Jecho Jekov3-Dec-16 9:16
Jecho Jekov3-Dec-16 9:16 
BugBug when project name contains whitespaces Pin
Member 1152504514-Mar-15 8:09
Member 1152504514-Mar-15 8:09 
GeneralRe: Bug when project name contains whitespaces Pin
Jecho Jekov15-Mar-15 23:28
Jecho Jekov15-Mar-15 23:28 
QuestionLoc is not admitted in Microsoft Expression Blend 4 Pin
Domingo López29-Jan-15 6:19
Domingo López29-Jan-15 6:19 
AnswerRe: Loc is not admitted in Microsoft Expression Blend 4 Pin
Jecho Jekov30-Jan-15 4:37
Jecho Jekov30-Jan-15 4:37 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.