Как стать автором
Обновить

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

Время на прочтение 8 мин
Количество просмотров 7.5K
Сегодня существует много способов локализации 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) приложения.
Теги:
Хабы:
+9
Комментарии 2
Комментарии Комментарии 2

Публикации

Истории

Работа

.NET разработчик
66 вакансий

Ближайшие события

Московский туристический хакатон
Дата 23 марта – 7 апреля
Место
Москва Онлайн
Геймтон «DatsEdenSpace» от DatsTeam
Дата 5 – 6 апреля
Время 17:00 – 20:00
Место
Онлайн
PG Bootcamp 2024
Дата 16 апреля
Время 09:30 – 21:00
Место
Минск Онлайн
EvaConf 2024
Дата 16 апреля
Время 11:00 – 16:00
Место
Москва Онлайн