Разработка многоязыковых приложений на XAML (часть 2)

OSzone.net » Microsoft » Разработка приложений » Windows (до Windows 10) » Разработка многоязыковых приложений на XAML (часть 2)
Автор: Сэм Спенсер
Иcточник: MSDN
Опубликована: 19.08.2014

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

Ресурсы темы могут быть заменены при запуске приложения с помощью:

<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>
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);
}
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.FontSize40

в файл ресурсов, то будет установлен 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.ColorGreen
test2.Value40

Значения для атрибута 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, он загрузит другие файлы на основе квалификаторов, включающих:

Другие файловые ресурсы, которые возвратятся:

*
Увеличить

Данный механизм используется для сопоставления с несколькими версиями любых ресурсов, загруженных из проекта, например, с изображениями, resw, xml, html и даже XAML-файлами.

Примечание. В отличие от ресурсов .Net, неподходящие ресурсы не становятся резервными ресурсами по умолчанию для языков. Так что если вы зададите ресурс для одного языка /fr-fr/MyIcon.jpg и установите язык по умолчанию en-us, то неподходящий файл, например /MyIcon.jpg, не будет сопоставлен с этим языком. Вам нужно будет специально создать резервный ресурс для языка, например /en-us/MyIcon.jpg.

Предоставляйте одинаковые наборы ресурсов для каждого поддерживаемого языка. Если вы предоставляете набор ресурсов для французского и немецкого языков, каждый набор должен содержать идентичные единообразные ключи ресурсов (файлы и названия элементов таблиц строк).

Файловые ресурсы XAML

Рекомендуемый механизм для локализации XAML-файлов –– использование x:Uid и таблицы строк, как описано выше. Однако при необходимости можно загрузить альтернативные версии с помощью механизма квалификаторов ресурсов:

Например:

*

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


Ссылка: http://www.oszone.net/24613/