Document home

Visual C++

WPF Localization and Internationalization

Windows Presentation Foundation (Wikipedia) is the most recent framework to build GUI applications in .NET. The classic framework is Windows Forms. WPF uses .xaml and .resx files to store resource data. Soluling supports all WPF applications from .NET 3.0 to the newest version of .NET, such as .NET Core and .NET 5. Soluling localization tool and service also support XAML files.

The following chapters describe the WPF localization step by step.

Localization Process

You can localize WPF applications using the following methods:

1. Visual Studio project or solution file localization method

The first way to localize a .NET project is to add a Visual Studio project file (.csproj or .vbproj) into a Soluling project. Soluling reads the project file to locate all the resource files it uses (.resx, .xaml, images, etc.). You don't have to select all the resources files manually, but adding one single project file is enough.

If you have multiple project files, you can add a Visual Studio solution file (.sln) instead of individual project files. If you later add new projects into your solution, you don't have to add anything into your Soluling project because Soluling will automatically find the new items from the solution file and adds them to the project. The result you will get by using a Visual Studio solution file instead of individual project files is exactly the same. Your Soluling project will contain the same sources, you can configure them in the same way, and the output files will be the same. Support for Visual Studio solution file is to make things easier for you.

2. Assembly file localization method

Another way to localize a .NET project is to add an assembly file into a Soluling project. Soluling reads the assembly to locate all the resource data it uses. When building, Soluling creates satellite assembly files. There is one output file (.resources.dll) for each language. You don't need to have the source code files. The only file you need is the original assembly file.

In addition to creating satellite assembly files, Soluling can also create localized assembly files. The difference is that the localized assembly file is an identical copy of the original assembly. It contains exactly the same code segments as the original. You can replace the original assembly with localized assembly without breaking anything. You deploy the localized assembly file without the original assembly file. The satellite assembly files, on the other hand, contain only resources. They do not contain any code. They do not run alone but must always be deployed with the original assembly file. By default, Soluling creates satellite assembly files. You can change this by using the Output sheet of Source dialog.

The main UI resource of a WPF application is XAML. Visual Studio creates the original XAML files that are XML based text files. When an assembly is compiled, the compiler converts XML based XAML files into binary BAML files that are stored into assembly files as resources similar to compiled .resx files. Unfortunately, the conversion from XAML to BAML is a one-way operation. BAML file can not be fully converted back to the original XAML. However, BAML files can be localized but with limitations: not all properties can be localized, and each element must have an x:Uid attribute (not Uid). It is recommended to manually add x:Uid attributes so you can give a meaningful value for the attribute (the attribute value is used as a context value). However, if you want to use a more automated approach, you can use the following command.

MsBuild.exe /t:updateuid MyProject.csproj

MsBuild.exe tool can be found from .NET framework directory (e.g. C:\Windows\Microsoft.NET\Framework\v4.0.30319\MsBuild.exe)

Note! The assembly file localization method can be used for assemblies that use Any CPU platform target.

3. Resource file localization method

The last localization method is to localize each XAML file (.xaml) and resource file (.resx). Soluling reads the original resource file and, when building, creates localized resource files. There is one output file for each language. Even this method seems easy, it has two drawbacks. First is that you can not use localized resource files directly, but you have to put them inside an assembly file. If you use resource file localization, it is up to you to compile either the localized assembly file(s) or localized satellite assembly file(s). Second is that a typical .NET project contains dozens of resource files, and in many cases, they are scattered around several folders. You have to add them all to your Soluling project. Even using wildcards, it may be complicated. Also, every time you add a new resource file, you may have to add it to the project.

Creating a Soluling project

Start by creating a new Soluling project. Drag and drop a Visual Studio project file (.csproj or .vbproj), a Visual Studio solution file (.sln), or a .NET assembly file (.exe or .dll) to Soluling. Project Wizard appears showing first the Resources page.

WPF resources

Select the resource types you want to localize. In most cases, the default types (ResX and XAML) are all you need to localize. Click Next. If you have previously localized your application, you might have localized .resx and/or .xaml files. In this case, Soluling finds the existing localized file. If at least one file is found, Project Wizard shows the following page.

Existing files

Click Next. The Languages page appears. If no existing languages were found, the language list is initially empty. If existing languages were found, the list contains them. In either case, you can add any number of target languages.

Languages

Finally, click Finish to complete Project Wizard. Your new Soluling project is ready.

WPF project

Context value

When Soluling scans XAML data, it composes a context for each element. This context is important because it is used to identify each string and other properties. The context is formed by combining the property name with the element name. The property name is straightforward because it is the name of the attribute holding the property or Content if the value comes from the element value instead of the attribute value. However, the element name is more complex. By default, XAML elements do not have any id or name. For example

<Label>This is a sample</Label>

The above sample does not have any attributes that would give a name. This is why Soluling uses a combination of the element name and index. In this case Label[0]. If the relative position of the Label in its parent changes (e.g., from first label to second label), so does the context ("Label[0]" -> "Label[1]"). We want to avoid this. By adding a name/id attribute, we can give a specific name of the element.

<Label x:Uid="sampleLabel">This is a sample</Label>

x:Uid is the recommended way to specify the name. However, you can also use Uid, x:Name, Name, x:Key or AutomationProperties.AutomationId. However, if you plan to use assembly file localization, you must use x:Uid. If the element does not contain x:Uid its properties cannot be localized when using assembly file localization. If you use Visual Studio project or solution file localization, then you can use other id attributes too.

If you want to access the element from code, you have to add either Name or x:Name attribute.

<Label x:Name="sampleLabel">This is a sample</Label>

The above Label does not have x:Uid. This is fine if you use Visual Studio project or solution file localization, but if you use assembly file localization, you must also add x:Uid attribute.

<Label x:Uid="sampleLabel" x:Name="sampleLabel">This is a sample</Label>

This is a bit verbose but required if assembly file localization is used. The values of x:Uid and x:Name does not need to be the same.

Scan rules

Soluling uses scan rules to specify what components and properties are localized and how they are localized.

Runtime language change

Soluling extends the .NET framework by providing classes to performs runtime language change. It makes it possible to start an application in one language and later change the language on run time as many times as possible. Soluling's runtime language change is very fast and flicker-free.

Bi-directional languages on WPF

See samples in <data-dir>\Samples\WPF\BiDi to see how to add RTL support.

Localizing XAML in the hard way

When XAML was introduced there were no localization tools supporting XAML. Still, there are very few tools that can properly handle XAML and even fewer that can handle BAML resources of assembly files. This lack of direct support for XAML/BAML caused that programmers did not store strings values into XAML but instead added them into .resx file and used the ResX resource from XAML files. Let's have an example. We have the following XAML file:

<Window 
  x:Class="HelloWorld.MainWindow"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  Title="Sample" Height="200" Width="400" Loaded="Window_Loaded">
 
  <StackPanel x:Name="panel">
    <Label x:Name="label1" Content="Hello World" />
    <Label x:Name="label2" />
  </StackPanel>
</Window>

XAML has a Loaded event that updates the content of label2 on run time.

private void Window_Loaded(object sender, RoutedEventArgs e)
{
  label2.Content = "How are you?";
}

How do we localize this? Soluling gives you the easy way. You can keep your XAML files unchanged. The only thing you have to change is to remove hard code string from C# code. The above code has "How are you?" string that needs to be resourced. Double click Properties\Resources.resx to open then resource editor. Add "HowAreYou" name and give it "How are you?" value. Finally, replace the hard-coded string of the event with access to the resource string.

private void Window_Loaded(object sender, RoutedEventArgs e)
{
  label2.Content = Properties.Resources.HowAreYou;
}

This is all you have to do if you use Soluling.

If you use a tool that can not localize XAML/BAML you have to move from the XAML to the resx those string that you want to localize. So first, add another item to Properties\Resources.resx. Add "HelloWorld" name and give it "Hello World" value. Then you have to make few changes to the XAML.

<Window 
  x:Class="HelloWorld.MainWindow"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:prop="clr-namespace:HelloWorld.Properties"
  Title="Sample" Height="200" Width="400" Loaded="Window_Loaded">
 
  <StackPanel x:Name="panel">
    <Label x:Name="label1" Content="{x:Static prop:Resources.HelloWorld}" />
    <Label x:Name="label2" />
  </StackPanel>
</Window>

First, you have to add prop namespace that gives you access to the resource strings. Then you have to change the "Hello World" string with x:Static markup extension. To localize a string in XAML you have to do three changes, start editing two files instead of one, and you will make your XAML much harder to read. No wonder we call this the hard way. It is just too complicated and takes too much of your time. Do not use it!

Unfortunately, all localization tutorials and how-to-do papers found from the web recommend that you do not store string values into the XAML file. Instead, they recommend to store the values into a .resx file and use those ResX items from XAML using markup extensions (e.g. {x:Static Resources.String1}). Do not apply. Those documents were written by people that do not know that modern localization tools can easily localize XAML. Tools they use can only localize .resx resources. When you use Soluling, you can always store all strings into your XAML file and let Soluling localize the XAML file. Use .resx to store only the strings that are used in your C#/VB code. If you already have used the method described above, you don't have to remove the ResX items from XAML files. You can mix and match the plain string and .resx strings.

<data-dir>\Samples\WPF\HelloWorld directory contains three versions of the same Hello World application:

Directory Description
Original Original application that has string in XAML and hard coded string in code.
EasyWay Only hard coded string from code has been resourced into resx file. This is the method you should use.
HardWay In addition to a hardcoded string also string from XAML has been resourced into resx file. This method is too complicated. Do not use this method.

Build errors

When you perform the build operation, Soluling creates localized .resx and .xaml files. Then Soluling compiles the XML based localized .resx and .xaml files into a binary format: .resources and .baml. Sometimes the XAML to BAML compilation may fail. The most common reason for that is that Soluling can not locate one or more assemblies of dependency assemblies of your application, or the assembly file is not up to date. To ensure that your main assembly is up to date, make sure that you have compiled your project before trying to build your Soluling project. Also, make sure that your Soluling project uses the same Visual Studio configure as you used when you compiled it. The default configuration is Debug|AnyCPU, but you can change that by right-clicking the .csproj file in Soluling's project tree and choosing Properties.

WPF resources

If this does not help, it may be the output directory of your Visual Studio project does not contain all the assemblies. If this is the case, locate the directory where your assemblies are and tell that to Soluling. Enter the assembly directory path to the Assembly directory field.

WPF resources

Samples

GitHub and <data-dir>\Samples\WPF contains following WPF (.NET) samples:

Directory Description Notes
Driving Shows how to internationalize, localize and add a runtime lanugage switch for a WPF application.
Original Original English driving application that has not been internationalized.
Localized Internationalized and localized driving application.
Multilingual As above but with a runtime language switch.
Try this first!
Tutorial A tutorial sample. Read <data-dir>\Library\NET\NET.pdf to complete the tutorial.
BadSample Original application. Contains lots of hard coded strings, code that is not internationalized, and user interface that requires reworking.
GoodSample Above application internationalized and localized properly.
Try this first!
     
Core Shows how to localize a WPF application that uses .NET Core  
BiDi Shows how to localize to bi-directional languages or from bi-directional languages. Contains following projects:
BiDiArabic A sample application that uses originally Arabic and right to left layout. The application is localized to English.
BiDiEnglish As above but uses originally English and left to right layout. The application is localized to Arabic.
 
Database Shows how to implement a localized application that uses multilingual database. Requires .NET 4.0 or higher.
FileDialog Shows how to localize Filter property of OpenFileDialog and SaveFileDialog dialogs.  
Font Shows how to localize fonts.  
HelloWorld Compares ResX only localization method with XAML/ResX localization method. Contains following projects:
EasyWay Both XAML and ResX files are localized. This is the recommended way.
HardWay Only ResX files are localized. You should not use this method even it is shown on most localization best practices because it is just too complicated!
Original Original uninternationalized project.
 
LanguageSwitch Shows how to perform a runtime language switch. Runtime language switch. Requires .NET 4.0 or higher.
Plural Show how to use plural messages. Contains following projects:
MultiPlural A sample that uses two plural enabled variables.
SinglePlural A sample that uses one plural enabled variable.
 
Sport Shows how to localize a grid. Requires .NET 4.0 or higher.
SystemString Shows how to use resource dictionaries to store strings.  

Samples.sln is a Visual Studio 2019 solution file that contains all the above samples. Samples are written in Visual Studio 2019, but the files are compatible with older Visual Studio versions. Each project directory contains a Soluling project file that localizes the project file. All samples work with .NET 3.0 or higher except Database, LanguageSwitch, and Sport that requires .NET 4.0 or higher.

How to configure localization

Project Wizard ask you some options when creating a new project. However Soluling lets you to configure the localization at very detailed level. Once you have create a project you can view and modify the configuration of each file of the project. Select a file, right click and choose Options.

Configuring Visual Studio project file localization

You can configure how to localize your Visual Studio project file by selecting the item in the project tree, right-clicking, and choosing the Options menu. A source dialog appears that lets you edit the options. This source uses the following option sheets.

Configuring Visual Studio solution file localization

You can configure how to localize your Visual Studio solution file by selecting the item in the project tree, right-clicking, and choosing the Options menu. A source dialog appears that lets you edit the options. This source uses the following option sheets.

Configuring assembly file localization

You can configure how to localize your .NET assembly file by selecting the item in the project tree, right-clicking, and choosing the Options menu. A source dialog appears that lets you edit the options. This source uses the following option sheets.

Configuring resource file localization

You can configure how to localize your XAML file by selecting the item in the project tree, right-clicking, and choosing the Options menu. A source dialog appears that lets you edit the options. This source uses the following option sheets.