Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

How to Localize WPF pages

0.00/5 (No votes)
17 Apr 2015 1  

Introduction

Today there are many ways WPF localization projects mainly based on bindings.
This approach has its advandages and disadvandages. I do not like this approach because a huge number of bindings in xaml markup, additional delay when the page loads. Just more time to search for a string in the source code, that is when I see the text in the running program, first I need to find this line in the resx resources, and after only xaml containing this key.

Background

Recently, we have connected Elas for localization of our project. Elas extracts all the xaml markup element which attribute values marked x:Uid and puts them into xlf file for later translation. I'll show you a simple example how to do it.

Using the Elas

Windows 8, Visual Studio 2013

So create a new WPF project.

And add a few controls to the main window.

MainWindow.xaml

XML
<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow"
        Width="525"
        Height="350">
    <Grid>

        <Menu Height="22" VerticalAlignment="Top">
            <MenuItem Header="File">
                <MenuItem Header="New" />
                <MenuItem Header="Open" />
                <Separator />
                <MenuItem Header="Exit" />
            </MenuItem>
            <MenuItem Header="Help">
                <MenuItem Header="About" />
            </MenuItem>
        </Menu>
        <TabControl Margin="10,40,10,10">
            <TabItem Header="File">
                <Grid>
                    <Button Width="97"
                            Height="21"
                            Margin="11,26,0,0"
                            HorizontalAlignment="Left"
                            VerticalAlignment="Top"
                            Content="Add" />
                    <Button Width="97"
                            Height="21"
                            Margin="11,53,0,0"
                            HorizontalAlignment="Left"
                            VerticalAlignment="Top"
                            Content="Remove" />
                    <ListBox Margin="122,28,13,38" />
                    <TextBlock Height="26"
                               Margin="6,0,6,6"
                               VerticalAlignment="Bottom">
                        <TextBlock>
                            Selected Item:<Run Text="{Binding SelectedItem}" />
                        </TextBlock>
                    </TextBlock>
                </Grid>
            </TabItem>
            <TabItem Header="Directory">
                <Grid>
                    <TextBox Height="21"
                             Margin="14,16,24,0"
                             VerticalAlignment="Top" />
                </Grid>
            </TabItem>
        </TabControl>
    </Grid>
</Window>

Add Elas Core Nuget package.

Pay attention in the solution added a new file ".elas \ Elas Configuration.props"

This configuration file of the Elas where you can add the language(s) in which you wish to receive a localization.

Run build.

After the build, now we have a xliff file for "MainWindow.xaml":

But it does not have any trans-unit because we have not set any x:Uid for elements.

Add x:Uid for each element.

MainWindow.xaml

XML
<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow"
        Width="525"
        Height="350">
    <Grid>

        <Menu x:Uid="Menu"
              Height="22"
              VerticalAlignment="Top">
            <MenuItem x:Uid="Menu.File" Header="File">
                <MenuItem x:Uid="Menu.File.New" Header="New" />
                <MenuItem x:Uid="Menu.File.Open" Header="Open" />
                <Separator x:Uid="Menu.File.Separator" />
                <MenuItem x:Uid="Menu.File.Exit" Header="Exit" />
            </MenuItem>
            <MenuItem x:Uid="Menu.Help" Header="Help">
                <MenuItem x:Uid="Menu.Help.About" Header="About" />
            </MenuItem>
        </Menu>
        <TabControl x:Uid="TabControl" Margin="10,40,10,10">
            <TabItem x:Uid="TabControl.File" Header="File">
                <Grid x:Uid="TabControl.File.Grid">
                    <Button x:Uid="TabControl.File.Add"
                            Width="97"
                            Height="21"
                            Margin="11,26,0,0"
                            HorizontalAlignment="Left"
                            VerticalAlignment="Top"
                            Content="Add" />
                    <Button x:Uid="TabControl.File.Remove"
                            Width="97"
                            Height="21"
                            Margin="11,53,0,0"
                            HorizontalAlignment="Left"
                            VerticalAlignment="Top"
                            Content="Remove" />
                    <ListBox Margin="122,28,13,38" />
                    <TextBlock x:Uid="TabControl.File.Bottom"
                               Height="26"
                               Margin="6,0,6,6"
                               VerticalAlignment="Bottom">
                        <TextBlock x:Uid="TabControl.File.Bottom.SelectedItem">
                            Selected Item:<Run x:Uid="TabControl.File.Bottom.SelectedItem.Run" Text="{Binding SelectedItem}" />
                        </TextBlock>
                    </TextBlock>
                </Grid>
            </TabItem>
            <TabItem x:Uid="TabControl.Directory" Header="Directory">
                <Grid x:Uid="TabControl.Directory.Grid">
                    <TextBox x:Uid="TabControl.Directory.TextBox"
                             Height="21"
                             Margin="14,16,24,0"
                             VerticalAlignment="Top" />
                </Grid>
            </TabItem>
        </TabControl>
    </Grid>
</Window>

Build again. And now we can begin localization.

If you want to work with "MainWindow.xaml.xlf" file directly in Visual Studio, then it will be more convenient to add xml schema "xliff-core-1.2-transitional.xsd" into Visual Studio. This file can be found in "%SolutionDir%\packages\DevUtils.Elas.Core.XXX\schemas\xliff-core-1.2-transitional.xsd" and add it to Visual Studio.

Consider the "MainWindow.xaml.xls" file.

This file contains the keys (1) (x:Uid) and the initial value (2) to be translated. Translation added to the target element and the value of state is changed to "translated". Elements for which you do not want to do the translation set translate into "no" and state into the "final".

That's what I get.

MainWindow.xaml.xlf

XML
<xliff version="1.2" xmlns:elas="urn:devutils:names:tc:xliff:document:1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
    <file original="MainWindow.xaml" source-language="en-US" target-language="ru-RU" datatype="xml">
        <header>
            <tool tool-version="0.0.8.0" tool-name="ELAS" tool-company="DevUtils.Net" tool-id="DevUtils.Elas.Tasks.Core, Version=0.0.8.0, Culture=neutral, PublicKeyToken=3cae0f4d0d366709" />
        </header>
        <body>
            <group id="Menu">
                <trans-unit id="Menu.$Content" translate="no">
                    <source xml:space="preserve">#Menu.File;#Menu.Help;</source>
                    <target xml:space="preserve" state="final"></target>
                </trans-unit>
                <group id="File">
                    <trans-unit id="Menu.File.$Content" translate="no">
                        <source xml:space="preserve">#Menu.File.New;#Menu.File.Open;#Menu.File.Separator;#Menu.File.Exit;</source>
                        <target xml:space="preserve" state="final"> </target>
                    </trans-unit>
                    <trans-unit id="Menu.File.Header" translate="yes">
                        <source xml:space="preserve">File</source>
                        <target xml:space="preserve" state="translated">Файл</target>
                    </trans-unit>
                    <group id="New">
                        <trans-unit id="Menu.File.New.Header" translate="yes">
                            <source xml:space="preserve">New</source>
                            <target xml:space="preserve" state="translated">Новый</target>
                        </trans-unit>
                    </group>
                    <group id="Open">
                        <trans-unit id="Menu.File.Open.Header" translate="yes">
                            <source xml:space="preserve">Open</source>
                            <target xml:space="preserve" state="translated">Открыть</target>
                        </trans-unit>
                    </group>
                    <group id="Exit">
                        <trans-unit id="Menu.File.Exit.Header" translate="yes">
                            <source xml:space="preserve">Exit</source>
                            <target xml:space="preserve" state="translated">Выход</target>
                        </trans-unit>
                    </group>
                </group>
                <group id="Help">
                    <trans-unit id="Menu.Help.$Content" translate="no">
                        <source xml:space="preserve">#Menu.Help.About;</source>
                        <target xml:space="preserve" state="final"></target>
                    </trans-unit>
                    <trans-unit id="Menu.Help.Header" translate="yes">
                        <source xml:space="preserve">Help</source>
                        <target xml:space="preserve" state="translated">Помощь</target>
                    </trans-unit>
                    <group id="About">
                        <trans-unit id="Menu.Help.About.Header" translate="yes">
                            <source xml:space="preserve">About</source>
                            <target xml:space="preserve" state="translated">О программе</target>
                        </trans-unit>
                    </group>
                </group>
            </group>
            <group id="TabControl">
                <group id="File">
                    <trans-unit id="TabControl.File.$Content" translate="no">
                        <source xml:space="preserve">#TabControl.File.Grid;</source>
                        <target xml:space="preserve" state="final"></target>
                    </trans-unit>
                    <trans-unit id="TabControl.File.Header" translate="yes">
                        <source xml:space="preserve">File</source>
                        <target xml:space="preserve" state="translated">Файл</target>
                    </trans-unit>
                    <group id="Add">
                        <trans-unit id="TabControl.File.Add.Content" translate="yes">
                            <source xml:space="preserve">Add</source>
                            <target xml:space="preserve" state="translated">Добавить</target>
                        </trans-unit>
                    </group>
                    <group id="Remove">
                        <trans-unit id="TabControl.File.Remove.Content" translate="yes">
                            <source xml:space="preserve">Remove</source>
                            <target xml:space="preserve" state="translated">Удалить</target>
                        </trans-unit>
                    </group>
                    <group id="Bottom">
                        <trans-unit id="TabControl.File.Bottom.$Content" translate="no">
                            <source xml:space="preserve">#TabControl.File.Bottom.SelectedItem;</source>
                            <target xml:space="preserve" state="final"></target>
                        </trans-unit>
                        <group id="SelectedItem">
                            <trans-unit id="TabControl.File.Bottom.SelectedItem.$Content" translate="yes">
                                <source xml:space="preserve">Selected Item:#TabControl.File.Bottom.SelectedItem.Run;</source>
                                <target xml:space="preserve" state="translated">Выбранный элемент:#TabControl.File.Bottom.SelectedItem.Run;</target>
                            </trans-unit>
                        </group>
                    </group>
                </group>
                <group id="Directory">
                    <trans-unit id="TabControl.Directory.$Content" translate="no">
                        <source xml:space="preserve">#TabControl.Directory.Grid;</source>
                        <target xml:space="preserve" state="final"></target>
                    </trans-unit>
                    <trans-unit id="TabControl.Directory.Header" translate="yes">
                        <source xml:space="preserve">Directory</source>
                        <target xml:space="preserve" state="translated">Директория</target>
                    </trans-unit>
                </group>
            </group>
        </body>
    </file>
</xliff>

Build again. Check whether no warnings or errors.

Next, switch on the Russian locale in Windows or in the program

(I added into the class constructor "App"

C#
CultureInfo.DefaultThreadCurrentUICulture = new CultureInfo("ru-RU");

)

And we get a localized application to Russian.

P.S. Next time I'll tell you how to use the Elas to localize C++ (Windows resources) applications.

History

Initial release

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here