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
<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
<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
<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"
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