Это вторая часть статьи о создании многоязыковых приложений с помощью 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 содержат множество функций, которые будут очень полезны при разработке многоязыковых приложений. Надеюсь, данная статья станет хорошим руководством при создании приложений, которыми смогут пользоваться жители самых разных стран.