{31 день с Mango} День 29: глобализация

Это День 29 в серии статей 31 день с Mango (перевод оригинальной серии 31 Days of Mango), и был написан приглашенным автором Matt Eland. Вы можете найти Matt в твиттере @integerman.

Люди часто путаются при обсуждении глобализации и локализации. Оба решения направлены на представление содержания приложения в удобной для пользователя форме по всему миру , но различие в том, что глобализация занимается форматированием элементов, таких как время, дата, валюта и числа таким образом, в каком виде пользователь привык с ними работать; в то время как как локализация отвечает за отображение пользовательского интерфейса на родном для пользователя языке. Эта статья будет касаться применения обоих методов для создания приложений, которые могут получить большую аудиторию путём удобным и полезным для пользователей.

Мы создадим простое приложение в этой статье, которое поддерживает как глобализацию, так и локализацию. Это приложение быстро создает сообщение электронной почты, которое позволяет быстро сообщить, что вы опаздываете на встречу.

После создания нового C# проекта для Windows Phone, мы должны сделать несколько действий, чтобы настроить приложение для поддержки локализации.

Определение нейтрального языка для сборки

Так как мы локализируем наше приложение, мы должны указать в проекте языковую локаль по-умолчанию. Чтобы это сделать, мы перейдем в окно свойств проекта и нажмите на кнопку “Assembly Information…” и укажем нейтральный язык для нашей сборки — то есть язык, который будет использоваться, если не определены языковые ресурсы, соответствующие локали пользователя. В этом примере мы установим наш нейтральный язык на английский (США).

Индикация поддерживаемых культур

Далее мы должны указать какие языки поддерживает проект. Visual Studio в настоящее время не дает доступа к этой части информации о проекте, но мы можем легко редактировать его. Убедитесь, что вы сохранили все изменения, а также что любые изменения в файле проекта с расширением .csproj сохранены на диск, прежде чем вы начнете редактировать файл. Далее зайдите в папку проекта на диск, щелкнув правой кнопкой по проекту и выбрав пункт «Открыть папку в Обозревателе». Выберите .сsproj файл вашего приложения (будьте осторожны, чтобы не выбрать .csproj.user файл) и откройте его с помощью блокнота или вашего любимого текстового редактора.

Найдите элемент <SupportedCultures></SupportedCultures> и добавьте коды культур, для культур которые вы хотели, чтобы поддерживало приложение; коды культур разделяйте точкой с запятой. Этот список не должен включать нейтральный язык сборки. Прочтите  http://msdn.microsoft.com/en-us/library/hh202918(v=VS.92).aspx , чтобы узнать список культур поддерживаемых разными версиями Windows Phone. Для этого примера, мы обеспечим поддержку испанского, упрощенного китайского и французского языков в дополнение к языку по-молчанию в этом проекте — английскому (США), поэтому наш элемент SupportedCultures будет выглядеть так:

<SupportedCultures>es-ES;zh-CN;fr-FR</SupportedCultures>

После внесения изменений, сохраните .csproj файл и перейдите обратно в Visual Studio. Нажмите кнопку reload, когда Visual Studio сообщит вам, что проект был изменен и предложит, чтобы изменения в файл проекта вступили в силу.

Создание ресурсного файла

Теперь у нас есть культура по умолчанию, а также список других поддерживаемых культур, мы можем начать определять культурно-специфические ресурсы. Мы начнём с создания файла с ресурсами для культуры по-умолчанию и перейдем к добавлению ресурсов для других культур. В соответствии с лучшими практиками при локализации любые текстовые элементы пользовательского интерфейса должны быть включены в эти файлы, а не жёстко заданы в XAML разметке или в коде программы.

Нам нужно добавить файл ресурсов проекта, который определяет строки пользовательского интерфейса приложения для языка по умолчанию.  Чтобы сделать это, нажмите правой кнопкой мыши на наш проект в обозревателе решений и выбирите «Add -> New Item». Отсюда мы добавим файл ресурсов. Этот файл можно назвать как угодно, но в этом примере мы назовем его Strings.

Добавление файла переносит нас в редактор ресурсов для этого файла ресурсов. Редактор ресурсов содержит таблицу с тремя столбцами: Name, Value и Comment. Name — это автоматически сгенерированное кодированное имя ресурса и служит уникальным ключом для идентификации ресурса. Value — культурно-специфическое значение для этого ресурса и то, что мы будем использовать для хранения текста отображаемого пользователю. Comment — комментарий не используется приложением, но он помогает отмечать для каждого ресурса где он используется, и может сильно помочь при переводе на другие языки. Вы также увидите, модификатор доступа в верхней части редактора ресурсов. Он является внутренним по умолчанию, но мы должны изменить его на Public чтобы мы могли привязаться к этим значениям из XAML позже.

Вот наш например после добавления соответствующих строк и изменения модификатора доступа на общедоступный:

Создание ресурсных файлов для отдельных культур

Теперь, когда у нас созданы ресурсы по умолчанию, мы можем приступить к созданию остальных ресурсов необходимых для проекта. Мы начнем с создания ресурсов этого приложения для испанского языка. Захватите удерживая кнопку мыши и перетащите файл Strings.resx в Обозревателе решений для создания копии String.resx, затем переименуйте «Копия Strings.resx» в «Strings.es-ES.resx» (es-ES — это код для испанского языка). Это важно, чтобы новый файл начинался с того же имени, что и предыдущий файл ресурсов, но с добавлением соответствующего кода языка в конце, иначе этот файл не будет сопоставлен локали соответствующим образом. После того как вы переименовали этот файл, откройте файл Strings.es-ES.resx и изменитt значения в столбце Value для каждого ресурса. Хорошим источником для переводов может стать Переводчик Bing, хотя лучше проверить перевод с помощью носителей языка до загрузки приложения. Важно, чтобы столбцы Name в соответствующих файлах ресурсов соответствовали между собой.

Как тоолько вы это закончите, пройдите такую ​​же процедуру для всех локалей, которые вы хотите поддерживать; убедитесь что они имеют модификатор доступа — Public, имеют такое же имя и такой же постфикс из соответствующего кода культуры. Важно также отметить, что редактор ресурсов может неправилно отображать некоторые иностранные наборы символов после копировани и вставки из Переводчика Bing в редактор ресурсов, но эти символы должны правильно выглядеть в эмуляторе и на реальном устройстве.

Создание не локализованного пользовательского интерфейса

Теперь у нас есть набор локализованных строк, самое время начать строить пользовательский интерфейс, который сможет использовать их. Наш пример приложения будут иметь несколько полей, панель приложения (application bar) и стандартный заголовок. Так как мы хотим включить даты в этот пример, чтобы показать глобализацию, мы подключим библиотеку Silverlight Toolkit for Windows Phone и используем контрол TimePicker из её набора. В этой статье мы не будем вдаваться в подробности о загрузке, установке и использовании этой сборки, но она находится в свободном доступе и помощь для нее доступна в Интернете.

Наш нелокализованный MainPage.xaml выглядит следующим образом:

<phone:PhoneApplicationPage
    x:Class="PhoneApp1.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
    xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:Controls="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone.Controls.Toolkit" mc:Ignorable="d" d:DesignWidth="480" d:DesignHeight="696"
    FontFamily="{StaticResource PhoneFontFamilyNormal}"
    FontSize="{StaticResource PhoneFontSizeNormal}"
    Foreground="{StaticResource PhoneForegroundBrush}"
    SupportedOrientations="PortraitOrLandscape" Orientation="Portrait"
    shell:SystemTray.IsVisible="True">
    <phone:PhoneApplicationPage.ApplicationBar>
        <shell:ApplicationBar IsMenuEnabled="False">
            <shell:ApplicationBarIconButton IconUri="/icons/appbar.feature.email.rest.png"
                                            IsEnabled="True"
                                            Text="send"
                                            Click="HandleSendClick" />
        </shell:ApplicationBar>
    </phone:PhoneApplicationPage.ApplicationBar>
    <!--LayoutRoot is the root grid where all page content is placed-->
    <Grid x:Name="LayoutRoot"
          Background="Transparent">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <!--TitlePanel contains the name of the application and page title-->
        <StackPanel x:Name="TitlePanel"
                    Grid.Row="0"
                    Margin="12,17,0,28">
            <TextBlock x:Name="ApplicationTitle"
                       Text="I'm Running Late"
                       Style="{StaticResource PhoneTextNormalStyle}" />
            <TextBlock x:Name="PageTitle"
                       Text="Send Message"
                       Margin="9,-7,0,0"
                       Style="{StaticResource PhoneTextTitle1Style}"
                       TextWrapping="Wrap" />
        </StackPanel>
        <ScrollViewer Margin="12,0,12,0"
                      Grid.Row="1">
            <StackPanel x:Name="ContentPanel">
                <TextBlock TextWrapping="Wrap"
                           Text="To"
                           Style="{StaticResource PhoneTextSubtleStyle}" />
                <TextBox x:Name="txtTo"
                         TextWrapping="Wrap"
                         InputScope="EmailUserName" />
                <HyperlinkButton Content="Choose a contact"
                                 HorizontalContentAlignment="Left"
                                 Foreground="{StaticResource PhoneAccentBrush}"
                                 Click="HandleChooseContactClick"
                                 Margin="{StaticResource PhoneVerticalMargin}" />
                <TextBlock TextWrapping="Wrap"
                           Text="Subject"
                           Style="{StaticResource PhoneTextSubtleStyle}" />
                <TextBox x:Name="txtSubject"
                         TextWrapping="Wrap"
                         Text="I'm Running Late"
                         InputScope="Text" />
                <CheckBox x:Name="checkIncludeReason"
                          Content="Include a reason" />
                <TextBox x:Name="txtReason"
                         TextWrapping="Wrap"
                         Text="Traffic"
                         InputScope="Text"
                         IsEnabled="{Binding IsChecked, ElementName=checkIncludeReason}" />
                <CheckBox x:Name="checkIncludeETA"
                          Content="I should arrive by" />
                <Controls:TimePicker x:Name="timeArrival"
                                     IsEnabled="{Binding IsChecked, ElementName=checkIncludeETA}"
                                     Margin="0,-12,0,0" />
                <CheckBox x:Name="checkIncludeDiagnosticData"
                          Content="Include extra data" />
            </StackPanel>
        </ScrollViewer>
    </Grid>
</phone:PhoneApplicationPage>

Очевидно, что этот XAML содержит ряд жестко заданных строк, и это не подходит нам для локализации приложения. Нам необходимо получить пользовательский интерфейс, который позволит воспользоваться нашими строками ресурсов.

Получение строк ресурсов в XAML

К счастью, создавая ресурсы мы работаем с уже автоматически сгенерированными классами для доступа к этим ресурсам. К сожалению, мы не можем с легкостью связываться с ними в XAML, так как автоматически сгенерированный класс Strings имеет внутренний конструктор и статические свойства, так что мы должны создать объект оболочки ресурсов, который даёт возможность выполнить привязку.

Добавьте в ваш проект новый файл кода под названием StringProvider.cs и вставьте в него следующий код:

namespace PhoneApp1
{
    public class StringProvider
    {
        private readonly Strings _resources = new Strings();
        public Strings Resources
        {
            get { return _resources; }
        }
    }
}

Теперь перейдите в файл App.xaml вашего проекта и добавьте новый ресурс для приложения используя XMLNS описания. Этот ресурс будет доступен во всём приложении и обеспечит легкий доступ к строкам локализации. Когда это сделано, ваш App.xaml будет выглядеть так:

<Application
    x:Class="PhoneApp1.App"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"       
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
    xmlns:local="clr-namespace:PhoneApp1">     <!--Application Resources-->
    <Application.Resources>
        <local:StringProvider x:Key="Strings" />
    </Application.Resources>     <Application.ApplicationLifetimeObjects>
        <!--Required object that handles lifetime events for the application-->
        <shell:PhoneApplicationService
            Launching="Application_Launching" Closing="Application_Closing"
            Activated="Application_Activated" Deactivated="Application_Deactivated"/>
    </Application.ApplicationLifetimeObjects> </Application>

Теперь, когда мы определили наш StringProvider как ресурс, мы можем привязаться к нему на главной странице. В этом случае мы привяжемся к дочернемум свойству от свойства ресурсов, представляющее имя ресурса, которое мы определили ранее в коллекции ресурсов и использующее объект приложения StringProvider как источник для привязки. Например, наш заголовок страницы TextBlock будет выглядеть так:

<TextBlock x:Name="PageTitle" Text="{Binding Resources.PageTitleSendMessage, Source={StaticResource Strings}}" TextWrapping="Wrap" />

Среды разработки Blend и Visual Studio распознают эту связку и отобразят эту строку на нейтральном языке (значение в Strings.resx) в конструкторе, если проект был заново собран после того, как ресурс был добавлен.

При создании локализуемых приложений, имейте в виду, что во многих языках строк ресурсов будет больше, чем в локали по умолчанию. Из-за этого, важно, что вы установили параметр TextWrapping=»Wrap» (автоперенос текста) в соответствующих случаях и использовали гибкие структуры дизайна, такие как ScrollViewer и StackPanel, которые могут адаптироваться к многострочному содержимому в случае необходимости. Из-за потенциально длинных строки ресурса, рекомендуется установить параметр SupportedOrientations=»PortraitOrLandscape» на странице элементе страница  там, где это необходимо.

Важно также иметь в виду, что не все локали поддерживают различные шрифты. Лучший выбор для локализованных приложений — избегать фиксированного указания шрифта размера шрифта, а вместо этого использовать встроенные стили. Прочтите http://msdn.microsoft.com/en-us/library/hh202920(v=VS.92).aspx для получения более подробной информации.

Ссылки на локализованные строки из программного кода

 Этот подход будет работать для большинства строк, но руководящие принципы дизайна Metro требуют чтобы заголовок приложения в верхней части страницы был набран заглавными буквами, а в нашей строке в файле ресурса заглавные только первые буквы. Мы могли бы определить новый ресурс только для  метки заголовка или написать пользовательский преобразователь значения строки в верхний регистр  для привязки, но вместо этого мы просто зададим заголовок в коде, чтобы мы могли продемонстрировать доступ к локализованным ресурсам из кода, добавив следующую строку в конструктор MainPage:

public MainPage()
{
    InitializeComponent();
    // Ensure that the app title uses all caps
    ApplicationTitle.Text = Strings.AppTitle.ToUpper(CultureInfo.CurrentCulture);
    // Specify the text explicitly on the app bar using our resource string.
    var button = (ApplicationBarIconButton)ApplicationBar.Buttons[0];
    button.Text = Strings.ButtonSend;
    // By default, we're going to be 15 minutes later than now
    timeArrival.Value = DateTime.Now.AddMinutes(15);
}

Теперь у нас есть локализованное приложение. Тестирование приложения в эмуляторе с использованием настройки французского языка приводит к следующему:

Поддержка глобализации

Теперь, когда у нас есть локализованное приложения, давайте перейдем к его глобализации. Поскольку глобализация имеет дело с соблюдением культурных настроек пользователя, важно, чтобы обеспечить правильный IFormatProvider для различных методов форматирования строк, когда это необходимо. К счастью, .NET предоставляет метод CultureInfo.CurrentCulture который выдает текущие культурные пользовательские настройки и может использоваться для форматирования строки в пользовательском интерфейсе. текущий культурных пользовательских настроек и может использоваться для форматирования строки для пользовательского интерфейса. При выполнении стандартных сравнений или выпонении сериализации или других не предназначенных  для пользователей операций, важно использовать метод CultureInfo.InvariantCulture чтобы обеспечить правильную работу вашего приложения во всех региональных параметрах.

В отличие от локализации, вам не нужно ничего делать для поддержки той или иной культуры, а .NET Framework будет заботиться о большинстве операций  форматирования для различных культур в вашем приложении. По-прежнему рекомендуется предоставить соответствующие форматы  строки и указать CultureInfo.CurrentCulture как IFormatProvider где это необходимо.

Например, код нашего приложения, создающего сообщение электронной почты  явного использует метод CurrentCulture и форматы строк:

private void HandleSendClick(object sender, EventArgs e)
{
    // Build the E-Mail body from the user's selections
    var body = new StringBuilder();
    body.AppendLine(Strings.EmailHeader);
    // Include reason if applicable
    var culture = CultureInfo.CurrentCulture;
    if (checkIncludeReason.IsChecked == true)
    {
        body.AppendLine();
        body.AppendLine(string.Format(culture, "{0}: {1}", Strings.EmailReason, txtReason.Text));
    }
    // Include eta if applicable
    if (checkIncludeETA.IsChecked == true)
    {
        body.AppendLine();
        // Since we've specified our ValueFormatString for the Time Picker, we can just rely on the ValueString here.
        body.AppendLine(string.Format(culture, "{0}: {1}", Strings.CheckShouldArriveBy, timeArrival.ValueString));
    }
    // Include extra globalization examples if applicable
    if (checkIncludeDiagnosticData.IsChecked == true)
    {
        body.AppendLine();
        // this is the standardized culture name such as en-US or zh-CH
        body.AppendLine(culture.Name);
        body.AppendLine(string.Format(culture, "pi: {0}", Math.PI));
        body.AppendLine(string.Format(culture, "number: {0}", -1));
        body.AppendLine(string.Format(culture, "currency: {0:c}", 4200.00));
        body.AppendLine(string.Format(culture, "date: {0:D}", DateTime.Today));
        body.AppendLine(string.Format(culture, "time: {0:t}", DateTime.Now));
    }
    // Now that we have our message body, do something with it. What we do depends on what we're running on.
    if (Microsoft.Devices.Environment.DeviceType == DeviceType.Emulator)
    {
        // The emulator doesn't currently support sending E-Mails so we'll just output the text to a message box
        MessageBox.Show(body.ToString());
    }
    else
    {
        // Compose the E-Mail and show it to the user to preview before sending
        var task = new EmailComposeTask {Subject = txtSubject.Text, To = txtTo.Text, Body = body.ToString()};
        task.Show();
    }
}

Поскольку глобализация отделена от локализации, запуск приложения на языке, при отсутствии  строк соответствующих выбранной локали приведет к правильной глобализации некоторых значений, в соответствии с настройками для Германии, как изображено ниже:

Задание формата строки в XAML

Иногда вы хотите иметь возможность задать формат строки в XAML либо в качестве параметра в конвертере или использовать для создания собственного контрола. В нашем примере мы явно зададим формат строки контрола TimePicker для короткого формата времени текущего языка (“t”). Чтобы сделать это, мы запишем наш формат строки с парой фигурных скобок, как показано здесь:

<Controls:TimePicker x:Name="timeArrival" ValueStringFormat="{}{0:t}" />

Также возможно встроить это в программный код:

 div class="code">var info = CultureInfo.CurrentCulture.DateTimeFormat; 
timeArrival.ValueStringFormat = "{0:" + info.ShortTimePattern + "}";

или, более кратко:

timeArrival.ValueStringFormat = "{0:t}"; 

Тестирование для различных культур

К этому моменту мы создали полнофункциональное приложение, которое поддерживает глобализацию и локализацию. Вы можете озадачиться, как тестировать приложения для различных языков. Реальные телефоны Windows Phone могут не позволить вам изменить язык телефона, но, к счастью Microsoft предусмотрела эту возможность в эмуляторе.

Чтобы настроить эти параметры, войдите в меню приложений эмулятора на стартовом экране и выбирите пункт Settings. В нём нажмите на region+language.

Раздел region & language позволяет изменить отображаемыый язык на телефоне, выбрав в пункте Display language язык, для которого вы хотите протестировать ваше приложение, и нажав на гиперссылку “tap here to accept changes” (нажатие на которую приведет к подтверждению этих настроек). Это приведет к перезапуску эмулятора Windows Phone, принятию языка и глобальных настроек, которые вы выбрали. Это может ввести в заблуждение, при использовании настроек языка, который вы не знаете, так что это хорошая возможность изучить расположение экрана региональных настроек перед применением изменений.

При загрузке в маркетплейс приложения, которое поддерживает различные языки и культуры, не забудьте настроить рынки, на которых будет распространяться приложени выбрав распространение во всем мире при публикации вашего приложения и настройке цены или выбирите отдельные регионы, которые вы явно намерены поддерживать.

Теперь вы знаете, как создать от начала до конца приложение, которое будет реализовывать лучший опыт правильной глобализации и локализованных приложений для мировой аудитории, использующей Windows Phone.

Вы можете скачать рабочую версию проекта, описанного в этой статье: скачать.

Завтра мы поговорим как использовать локальную базу данных в ваших приложениях. До встречи!

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

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