Сегодня существует много способов локализации WPF проектов в основном основанных на биндинге.
В этом подходе есть свои плюсы и минусы. Меня не устраивает в этом подходе это огромное количество биндингов в xaml разметке, дополнительная задержка при загрузке страницы. Так же дополнительное время для поиска строки в исходном коде т.е. когда я вижу строку в запущенной программе, сначала я должен найти эту строчку в resx ресурсах, а после только xaml содержащий этот ключ.
Недавно мы подключили Elas для локализации нашего проекта. Elas вытаскивает из xaml разметки все значения атрибутов элемента помеченного x:Uid и помещает их в xlf файл для последующего перевода. Расскажу на простом примере как это делается.
Windows 8, Visual Studio 2013
И так создадим новый WPF проект.
И несколько элементов на главном окне.
<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>
Добавим Elas Core Nuget пакет.
Обратите внимание в солюшене появился новый файл ".elasElasConfiguration.props"
Это конфигурационный файл Elas где вы можете задать языки на которые желаете получить перевод.
Далее запускаем билд.
И после билда у нас теперь есть xliff файл для «MainWindow.xaml»:
Но он не имеет ни одного trans-unit поскольку мы не задали ни одного x:Uid для элементов.
Добавим x:Uid для каждого элемента.
<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>
Снова билд. И теперь мы можем приступить к локализации.
Рассмотрим файл «MainWindow.xaml.xlf».
Этот файл содержит ключи (1) (x:Uid) и исходное значение (2) которое необходимо перевести. Перевод добавляется в элемент target и значение state меняется на «translated». Элементы для которых вы не желаете делать перевод установите translate в «no» и state в «final»
Вот что получилось у меня.
<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>
Снова билд. Проверяем нет ли предупреждений или ошибок.
Далее переключаем локаль на русский в Windows или в программе (Я добавил в конструктор класса «App»
CultureInfo.DefaultThreadCurrentUICulture = new CultureInfo("ru-RU");
).
И получаем локализованное приложение на русский.
P.S. В следующий раз я расскажу, как с помощью Elas локализовать C++ (Windows resources) приложения.
Автор: Gargoni