Это вторая часть статьи о создании многоязыковых приложений с помощью XAML. В первой части мы говорили о выборе шрифтов и поддержке двунаправленного текста. Здесь мы рассмотрим, как использовать ресурсный механизм для поиска локализованных версий строк и ресурсов, а также для управления ими. Кроме того, мы поговорим об API для форматирования, позволяющих представить даты и числа в подходящем для пользователей вашего приложения формате.
Словари ресурсов
В XAML существует понятие словарей ресурсов, которые используются для определения стилей и других ресурсов. В Windows 8 шаблоны включали словарь ресурсов в Common/StandardStyles.xaml. В предварительной версии Windows 8.1 эти стили интегрированы в платформу и теперь являются частью generic.xaml. Для поддержки пользовательской настройки ресурсов мы добавили новый механизм поиска ресурсов ThemeResource, который пересматривается, когда тема операционной системы переключается в контрастный режим.
Например, generic.xaml определяет ресурс для шрифта по умолчанию, используемого в других стилях, следующим образом:
<FontFamily x:Key="ContentControlThemeFontFamily">Segoe UI</FontFamily>
На что затем ссылаются другие стили:
<!-- Default style for Windows.UI.Xaml.Controls.Button -->
<Style TargetType="Button">
<Setter Property="Background" Value="{ThemeResource ButtonBackgroundThemeBrush}" />
<Setter Property="Foreground" Value="{ThemeResource ButtonForegroundThemeBrush}"/>
<Setter Property="BorderBrush" Value="{ThemeResource ButtonBorderThemeBrush}" />
<Setter Property="BorderThickness" Value="{ThemeResource ButtonBorderThemeThickness}" />
<Setter Property="Padding" Value="12,4,12,4" />
<Setter Property="HorizontalAlignment" Value="Left" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="FontFamily" Value="{ThemeResource ContentControlThemeFontFamily}" />
<Setter Property="FontWeight" Value="SemiBold" />
<Setter Property="FontSize" Value="{ThemeResource ControlContentThemeFontSize}" />
<Setter Property="Template">...</Setter>
</Style>
Ресурсы темы могут быть заменены при запуске приложения с помощью:
- переопределения их в App.xaml, например:
<Application
x:Class="MyTestApp.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:MyTestApp">
<Application.Resources>
<FontFamily x:Key="ContentControlThemeFontFamily">Times New Roman</FontFamily>
</Application.Resources>
</Application>
- переопределения их в коде в App.xaml.cs, например:
protected override void OnWindowCreated(WindowCreatedEventArgs args)
{
base.OnWindowCreated(args);
SetResource("ContentControlThemeFontFamily", new FontFamily("Papyrus"));
}
void SetResource(object key, object value)
{
if (this.Resources.ContainsKey(key))
this.Resources[key] = value;
else
this.Resources.Add(key, value);
}
- загрузки словаря ресурсов из App.xaml.cs, например:
protected override void OnWindowCreated(WindowCreatedEventArgs args)
{
base.OnWindowCreated(args);
ResourceDictionary d = new ResourceDictionary();
d.Source = new Uri(“ms-appx:///Resources/Dictionary1.xaml”);
this.Resources.MergedDictionaries.Add(d);
}
Примечание. Если ваше приложение использует несколько окон, у каждого из них есть свои ресурсы и поток интерфейса. Поэтому ресурсы нужно загружать в OnWindowCreated, чтобы их можно было инициализировать для каждого окна.
Примечание. Копия generic.xaml включена в состав Windows SDK, по умолчанию она расположена в C:\Program Files (x86)\Windows Kits\8.1\Include\winrt\xaml\design\.
Таблицы строк (.resw)
XAML-проекты для Магазина Windows используют файлы resw, чтобы определять строки ресурсов в коллекции ресурсов для дальнейшей локализации. Их можно использовать для локализации разметки xaml, к ним можно также отправлять запросы через программный код.
Таблицы строк задаются в resw-файлах под названием Resources.resw, которые можно располагать в любом месте проекта (чаще всего их помещают в папку strings). Вы можете использовать шаблоны имен квалификаторов (мы поговорим о них чуть ниже), чтобы создавать версии для разных языков, регионов и т. д.
Локализация XAML-разметки
Рекомендуемый (и самый простой) способ локализации XAML –– добавление атрибутов x:Uid к элементам, значения которых нужно локализовать. Рассмотрим это на примере из MainPage.xaml:
<Page
x:Class="MyTestApp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/XAML/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/XAML"
xmlns:local="using:MyTestApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
xml:lang="en-us">
<Grid>
<StackPanel Margin="20">
<TextBlock x:Uid="mp_tb1" x:Name="TextBlock1" Text="Hello World" />
</StackPanel>
</Grid>
</Page>
При запуске вы увидите TextBlock с надписью «Hello World» внутри. Если я добавлю файл Resources.resw в мой проект, содержащий:
Редактор ресурсов в Visual Studio
и заново запущу приложение, то отобразится «Welcome!» Это происходит вот почему: если элементы имеют атрибут x:Uid, то при загрузке XAML программа сопоставит значение Uid с имеющимися строками в таблице строк, названными согласно шаблону «uid.property», и добавит или переопределит свойства в XAML. Так что если я добавлю:
Имя | Значение | Комментарий |
mp_tb1.FontSize | 40 | |
в файл ресурсов, то будет установлен FontSize (Размер шрифта) 40 пт, хотя этот атрибут не задан в XAML.
Данный метод можно использовать для замены любого атрибута в XAML, включая определения стиля и ресурсов, как показано в следующем примере из MainPage.xaml:
<Page.Resources>
<SolidColorBrush x:Key="TextBoxBrush" x:Uid="test1" Color="Blue" />
<Style TargetType="TextBox">
<Setter Property="Background" Value="{StaticResource TextBoxBrush}" />
<Setter Property="FontSize" Value="20" x:Uid="test2" />
</Style>
</Page.Resources>
Имя | Значение | Комментарий |
test1.Color | Green | |
test2.Value | 40 | |
Значения для атрибута x:Uid не зависят от названия элемента (x:Name). Значения x:Uid не проверяются на глобальную уникальность по всему проекту. Однако механизм ограничения области здесь отсутствует, поэтому одни и те же значения будут присвоены всем элементам с одинаковым x:Uid.
Поиск строк с помощью программного кода
Значения в таблице строк можно запрашивать из кода, как показано в следующем примере из MainPage.xaml.cs:
using Windows.UI.Xaml.Controls;
using Windows.ApplicationModel.Resources;
namespace MyTestApp
{
/// <summary>
/// Пустая страница может быть использована отдельно либо на нее можно перейти внутри Frame
/// </summary>
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
ResourceLoader loader = ResourceLoader.GetForCurrentView();
string s = loader.GetString("String1");
}
}
}
Это очень полезно для локализации программно-генерируемого содержимого, особенно в комбинации с string.Format() в .NET (или семейством API wsprintf()в C++) для подстановки строк на основе строки форматирования. Не полагайтесь на конкатенацию при создании строк, поскольку в других языках порядок подстановки может отличаться.
Средства локализации
Я просто обязан упомянуть набор средств для многоязычных приложений. Один раз подключив их, вы сможете создавать XLIFF-файлы для отправки на локализацию и выполнять автоматический перевод на несколько языков с помощью переводческого сервиса Bing.
Диалоговое окно «Языки» в наборе средств для многоязычных приложений
Затем программа создает файлы для каждого языка, и вы можете воспользоваться диалоговым окном «Перевод» для локализации каждого ресурса:
Многоязычный редактор в наборе средств для многоязычных приложений
Псевдолокализация
Отправка файлов на локализацию иногда занимает много времени. Вам лучше подождать и сделать локализацию тогда, когда практически завершите работу над пользовательским интерфейсом и он будет более-менее стабильным и доступным для изменения. Псевдолокализация — это методика, в рамках которой используется машинный алгоритм для локализации строк, увеличения их размера и смены кодировки; она позволяет проверить локализацию. Инструмент псевдолокализации берет строки по умолчанию и затем:
- Создает идентификатор, позволяющий увидеть строки в интерфейсе.
- Изменяет текст, благодаря чему вы понимаете, что он локализован, но все равно можете его прочитать.
- Удлиняет текст, поскольку длина предложений отличается в разных языках, например, немецкий язык гораздо многословнее английского.
Вы можете добавить qps-ploc в качестве языка Windows (вам нужно поискать его), назначить ему высший приоритет, а затем запустить свое приложение, чтобы посмотреть результат локализации. Возьмем в качестве примера проект Hub Template (Шаблон концентратора), использующий ресурсы-строки. После локализации он будет выглядеть следующим образом:
Локализованный шаблон концентратора
Квалификаторы наименований файлов
В средстве управления ресурсами для приложений Магазина Windows есть очень полезная функция, позволяющая выбрать для загрузки один из нескольких файлов на основе набора квалификаторов в названии файла или пути.
К примеру, если у вас есть ресурс Strings/Resources.resw, он загрузит другие файлы на основе квалификаторов, включающих:
- Язык и языковые стандарты
- DPI
- Ориентацию справа налево
- Высокую контрастность
Другие файловые ресурсы, которые возвратятся:
Увеличить
Данный механизм используется для сопоставления с несколькими версиями любых ресурсов, загруженных из проекта, например, с изображениями, resw, xml, html и даже XAML-файлами.
Примечание. В отличие от ресурсов .Net, неподходящие ресурсы не становятся резервными ресурсами по умолчанию для языков. Так что если вы зададите ресурс для одного языка /fr-fr/MyIcon.jpg и установите язык по умолчанию en-us, то неподходящий файл, например /MyIcon.jpg, не будет сопоставлен с этим языком. Вам нужно будет специально создать резервный ресурс для языка, например /en-us/MyIcon.jpg.
Предоставляйте одинаковые наборы ресурсов для каждого поддерживаемого языка. Если вы предоставляете набор ресурсов для французского и немецкого языков, каждый набор должен содержать идентичные единообразные ключи ресурсов (файлы и названия элементов таблиц строк).
Файловые ресурсы XAML
Рекомендуемый механизм для локализации XAML-файлов –– использование x:Uid и таблицы строк, как описано выше. Однако при необходимости можно загрузить альтернативные версии с помощью механизма квалификаторов ресурсов:
- Сохраняйте оригинальный XAML-файл для компиляции и (или) генерации кода вместе с файлом с выделенным кодом (*.xaml.cs).
- Добавляйте локализованные варианты только для XAML, идентифицируя их с помощью квалификаторов имен.
- Оставьте значение типа компиляции Page.
- Удалите x:Class из объявления XAML в заголовке файла, чтобы предотвратить генерацию кода и ошибки компиляции.
Например:
Solution Explorer демонстрирует проект с локализованными версиями App.xaml для английского,
французского и испанского языков
Пакеты ресурсов
Пакеты ресурсов –– это новая функция упаковки в Windows 8.1. Ресурсы для различных языков и DPI упаковываются в отдельные appx-файлы, за счет чего сокращается объем загрузки и место, занимаемое на диске.
Опция для создания пакетов ресурсов находится под Generate app bundle (Создать набор приложений) мастера Create App Packages (Создание пакетов приложения) в Visual Studio.
Если приложение содержит несколько ресурсов, то после выбора этого пункта будет создан appxbundle, а не пакетный файл appx. Если вы смените его расширение на .zip, то содержимое будет выглядеть примерно так:
Файл appx bundle, содержащий ресурсы для французского и испанского языков
API для форматирования
Форматы даты, времени, чисел и валюты различаются в зависимости от страны. XAML и WinRT содержат элементы управления и API, позволяющие отображать и вводить эти значения в подходящем для конечного пользователя формате.
Дата и время
В США и Европе используется григорианскийкалендарь. В других регионах применяются собственные календари, например корейский и еврейский. Количество и длительность месяцев в этих календарях отличается от григорианского, а начало года приходится на разные даты.
Даты, вводимые из различных типов календарей, обрабатывает элемент управления DatePicker, например:
<DatePicker CalendarIdentifier="JapaneseCalendar" x:Name="date1" />
Для их отображения используется DateTimeFormatter API из WinRT или DateTime.ToString из .Net.
К примеру, этот код:
public void ShowDates()
{
string[] languages = { "en-us", "en-gb", "fr-fr", "ar-sa", "zh-hans-cn", "hi-in", "ja-jp", "ko-kr", "ru-ru" };
StringBuilder sb = new StringBuilder();
DateTime now = DateTime.Now;
foreach (string lang in languages)
{
sb.AppendLine(string.Format("Language: {0}", lang));
DateTimeFormatter df = new DateTimeFormatter("longdate",new string[] {lang} );
sb.AppendLine(df.Format(now));
df = new DateTimeFormatter("shortdate", new string[] { lang });
sb.AppendLine(df.Format(now));
df = new DateTimeFormatter("longtime", new string[] { lang });
sb.AppendLine(df.Format(now));
}
Reb1.Document.SetText(TextSetOptions.UnicodeBidi, sb.ToString());
}
возвратит следующие результаты:
Будьте осторожны при синтаксическом анализе дат из строк, поскольку разные страны могут использовать свой собственный формат даты. Например, в США дата в сокращенном виде обычно записывается как месяц/день/год, а в Великобритании — день/месяц/год. Метод .Net DateTime.Parse учитывает культурные различия при анализе дат из строк.
При работе с временем важно учитывать часовые пояса. Пользователи обычно ожидают отображения времени с учетом их часового пояса.
Числа и валюты
Имеются региональные отличия в форматировании чисел, например, в качестве десятичного разделителя служит точка или запятая, разряды (тысячи, миллионы и т. д.) могут отделяться друг от друга. Не во всех языковых стандартах применяется десятичная система счисления с арабскими цифрами (0–9), в отдельных регионах используются другие системы счисления. Для форматирования чисел служит DecimalFormatter API из WinRT, поддерживающий подобные сценарии.
При работе с валютами необходимо учитывать, какой символ использовать для обозначения валюты и где его разместить по отношению к числу. Для форматирования валют служит CurrencyFormatter API.
Примечание. Средство форматирования не выполняет конвертацию валют, так что вы должны помнить об этом при сопоставлении валют разных стран.
Данный код форматирует числа:
public void ShowNumbers()
{
string[] languages = { "en-US", "en-GB", "fr-FR", "ar-SA", "zh-CN", "hi-IN", "ja-JP", "ko-KR", "ru-RU" };
StringBuilder sb = new StringBuilder();
double d = 1234567.89;
foreach (string l in languages)
{
string[] t = l.Split('-');
GeographicRegion region = new GeographicRegion(t[1]);
sb.AppendLine(string.Format("Language: {0}", l));
DecimalFormatter df = new DecimalFormatter(new string[] { l }, region.CodeTwoLetter);
df.IsGrouped = true;
sb.AppendLine(df.Format(d));
CurrencyFormatter cf = new CurrencyFormatter(region.CurrenciesInUse[0], new string[] { l }, region.CodeTwoLetter);
cf.IsGrouped = true;
sb.AppendLine(cf.FormatDouble(d));
}
Reb1.Document.SetText(TextSetOptions.UnicodeBidi, sb.ToString());
}
В результате получается:
Подведем итоги
Платформы XAML и WinRT содержат множество функций, которые будут очень полезны при разработке многоязыковых приложений. Надеюсь, данная статья станет хорошим руководством при создании приложений, которыми смогут пользоваться жители самых разных стран.