Локализация WPF страниц

от автора

Сегодня существует много способов локализации WPF проектов в основном основанных на биндинге.
В этом подходе есть свои плюсы и минусы. Меня не устраивает в этом подходе это огромное количество биндингов в xaml разметке, дополнительная задержка при загрузке страницы. Так же дополнительное время для поиска строки в исходном коде т.е. когда я вижу строку в запущенной программе, сначала я должен найти эту строчку в resx ресурсах, а после только xaml содержащий этот ключ.
Недавно мы подключили Elas для локализации нашего проекта. Elas вытаскивает из xaml разметки все значения атрибутов элемента помеченного x:Uid и помещает их в xlf файл для последующего перевода. Расскажу на простом примере как это делается.

Windows 8, Visual Studio 2013

И так создадим новый WPF проект.

И несколько элементов на главном окне.

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> 

Добавим Elas Core Nuget пакет.

Обратите внимание в солюшене появился новый файл ".elas\ElasConfiguration.props"

Это конфигурационный файл Elas где вы можете задать языки на которые желаете получить перевод.

Далее запускаем билд.

И после билда у нас теперь есть xliff файл для «MainWindow.xaml»

Но он не имеет ни одного trans-unit поскольку мы не задали ни одного x:Uid для элементов.

Добавим x:Uid для каждого элемента.

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>   

Снова билд. И теперь мы можем приступить к локализации.

Перед локализацией

Если вы собираетесь работать с «MainWindow.xaml.xlf» файлом самостоятельно в Visual Studio, то для этого будет удобнее добавить xml схему «xliff-core-1.2-transitional.xsd» в Visual Studio. Этот файл можно найти в "%SolutionDir%\packages\DevUtils.Elas.Core.X.X.X\schemas\xliff-core-1.2-transitional.xsd" и добавить его в Visual Studio.

Рассмотрим файл «MainWindow.xaml.xlf».

Этот файл содержит ключи (1) (x:Uid) и исходное значение (2) которое необходимо перевести. Перевод добавляется в элемент target и значение state меняется на «translated». Элементы для которых вы не желаете делать перевод установите translate в «no» и state в «final»

Вот что получилось у меня.

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> 

Снова билд. Проверяем нет ли предупреждений или ошибок.

Далее переключаем локаль на русский в Windows или в программе (Я добавил в конструктор класса «App»

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

).

И получаем локализованное приложение на русский.

P.S.
В следующий раз я расскажу как с помощью Elas локализовать C++ (Windows resources) приложения.

ссылка на оригинал статьи http://habrahabr.ru/post/255809/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *