Pull to refresh

Локализация в Silverlight

Silverlight *
Локализация когда-то приходит в ваш интернациональный дом. Что бы вы ни построили — большой небоскреб или хижину дяди Тома — надо уметь разговаривать на языке жителей этого дома.

Если ваш Silverlight дом нуждается в локализации, милости просим, я постараюсь дать краски и кисточку, а плакаты рисуйте сами.

Проблема.


Для разработки настольного приложения проблем в использовании ресурсов нет, давно появилась возможность создания ResX файлов и использования второстепенных(satellite) сборок для конкретного языка. Silverlight эти функции достались по наследству, но в отличии от настольных приложений, необходимо помнить, что увеличивая количество локализированных ресурсов вы увеличиваете размер сборки. Вот и получается, что пользователь ждет загрузки, а часть загруженной информации ему и вовсе не нужна. С другой стороны, я про сервер, чем больше файл, тем больше нагрузка на сервер. Представьте, вам надо поправить ресурсы, а это означает что надо еще раз собирать и выкладывать полную сборку и пользователи будут опять её загружать, ибо с кеша вытянуть уже не получится. А лежали бы ресурсы отдельно зашел, поменял и все.
Обобщаем: любой лишний ресурс сборки требует лишнего времени пользователя, сервера, разработчика.

Обзор


Есть общие подходы, которые я использую в своём решение и с которыми я хотел бы ознакомить Вас.

Создание ресурса просто, но все же кратко об этом:
— добавляем новый элемент из контекстного меню на нужной папке в Solution Explorer (Add -> New Item);
— выделяем вкладку General;
— выбираем элемент Resources Files (или ищем в полном списке элемент с таким названием);
— вводим имя — ProjectResources;
— нажимаем add (или просто жмем Enter).
В нужной папке создался ресурс. Открываем дизайнер ресурсного файла двойным нажатием на файле в Solution Explorer. В появившейся сетке, в первой колонке пишем имя нашего ресурса — «Message», в следующую колонку вносим значение — «Hello World!».
Обязательно! не забыть сделать наш ресурс Public, для чего сменить в «Access Modifier» состояние Internal на Public.

На этом создание основного (neutral) ресурса заканчивается и мы можем переходить к следующей части «Мерлезонского балета» — созданию провайдера для ресурсов. Данный подход уже упоминался в топике Программируем Reversi на Silverlight и заключается он в создании класса с набором свойств, являющихся ресурсами.

  1. public class ResProvider : INotifyPropertyChanged
  2. {
  3.    static ProjectResources resources = new ProjectResources();
  4.  
  5.    public ProjectResources ProjectResources
  6.    {
  7.     get
  8.     {
  9.      return resources;
  10.     }
  11.    }
  12.  
  13.    #region INotifyPropertyChanged Members
  14.    // Implemention of interface.
  15.    #endregion
  16. }
* This source code was highlighted with Source Code Highlighter.

Используется в markup это так:

  1. <UserControl>
  2.   <UserControl.Resources>
  3.     <Resources:ResProvider x:Key="ResProvider"/>
  4.   </UserControl.Resources>
  5.   <Grid>
  6.     <TextBlock Text="{Binding Source={StaticResource ResProvider},Path=ProjectResources.Message}"/>
  7.   </Grid>
  8. </UserControl>
* This source code was highlighted with Source Code Highlighter.

При чем, если надо использовать данный провайдер для всего приложения, вы просто определяете его на уровне Application, то есть в App.xaml.

Основной ресурс готов, провайдер есть, теперь надо расширить ресурс дополнительными языками.

Для тех кто спросил «Как?»:
— вызываем контекстное меню на нужной папке в Solution Explorer (Add -> New Item);
— выделяем закладку General;
— создаем Resources Files с именем основного ресурса (в нашем случае ProjectResources);
— в конце добавляем имя нужной культуры — ProjectResources.ru-Ru.resx;
— нажимаем Add.
Создастся файл с нужным именем. Открываем ресурс в дизайнере, в первой колонке пишем имя совпадающие с базовым ресурсом, в нашем случае это — «Message», вторую колонку заполняем переведенным значением — «Привет Мир!».

Ничего нового я не придумал, фактически своим постом я описал два документа:
  1. How to: Add Resources to a Silverlight-based Application
  2. Silverlight and localizing string data
Для тех кто внимательно читал первый(или уже сталкивался с ним), из приведенных выше документов, скажу, что я специально умолчал о добавлении в xml проекта специальной секции <SupportedCultures />, определяющей список языковых сборок, необходимых для включения в скомпилированный xap файл. Если вы добавите эту секцию, то ни как не уйдете от той проблемы, с которой я начал повествование — от использования лишних ресурсов.

Решение


Способ, который я разработал, к сожалению, не подходит тем, кто хочет одним состоянием чекбокса включить поддержку ленивой (lazy) загрузки ресурсов. О как закрутил. Короче — придется потрудиться что бы осушить пруд и добраться до желанной рыбки.

Во-первых, скачать последнюю версию библиотеки ResourceExtension с codeplex и добавить её в референсы к Silverlight проекту.

Создание ресурсов не отличается от приведенного выше, поэтому я его опущу. Единственное, что требуется для ресурсов это добавить pre-build событие. Вызываем свойства Silverlight проекта. Открываем вкладку Build Events. В Pre-build вносим:
@ECHO off
DEL "$(TargetPath)"
FOR /F "delims=" %%i IN ('findstr /M /L /S /C:"global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager" "$(ProjectDir)*.Designer.cs"') DO (
DEL resource.gen
  FOR /F "usebackq delims=" %%j IN ("%%i") DO (
    ECHO. %%j > resourcecurrentrow.gen
    FOR /F "tokens=1,2* delims=(=" %%k IN (resourcecurrentrow.gen) DO (
      IF "%%l" == " new global::System.Resources.ResourceManager" (ECHO %%k = new ResourceExtension.ResourceXManager(%%m)>>resource.gen ELSE (ECHO. %%j) >>resource.gen
      ))
XCOPY resource.gen "%%i" /Q /Y /R /K
)

* This source code was highlighted with Source Code Highlighter.

Этим действием, автоматически, во всех ресурсах проекта, подменяется класс ResourceManager на ResourceXManager, реализованный в библиотеке ResourceExtension.

К сожалению, этот код не работает без дополнительной секции <UseHostCompilerIfAvailable>FALSE</UseHostCompilerIfAvailable> в файле проекта. Добавить этот код надо в секцию <PropertyGroup>.

Markup не претерпел ни каких изменений, все осталось на своих местах.

А вот провайдер видоизменился, теперь он является наследником класса CultureResourceProvider и приобрел метаданные на свойствах:
  1. using ResourceExtension;
  2. using ResourceExtension.Implementation;
  3.  
  4. namespace ResourceExtensionExample.Resources
  5. {
  6.   public class ResProvider: CultureResourcesProvider
  7.   {
  8.     private static ProjectResources projectResources = new ProjectResources();
  9.  
  10.     [ResourceProperty]
  11.     public ProjectResources ProjectResources
  12.     {
  13.       get { return projectResources; }
  14.     }
  15.   }
  16. }
* This source code was highlighted with Source Code Highlighter.

Финальный аккорд. Собираем. Включаем галочку для просмотра всех файлов и находим папку bin\Debug\ru-RU, которую мы должны переместить в папку ClientBin и положить рядом с xap файлом.
Так должно получиться
И, «легкое превращение в элегантные шорты»:
  1. public partial class MainPage : UserControl
  2. {
  3.   public MainPage()
  4.   {
  5.     InitializeComponent();
  6.   }
  7.  
  8.   private void Switch_Click(object sender, RoutedEventArgs e)
  9.   {
  10.     ((ResProvider) Resources["ResProvider"]).Options.CultureInfo = new CultureInfo("ru-Ru");
  11.   }
  12. }
* This source code was highlighted with Source Code Highlighter.

позволит вам удобно загрузить ресурсы с русской локалью.

Если у вас, что-либо не получилось. Вы можете скачать пример и посмотреть, как там все реализовано.

Компонент находится в стадии alpha, поэтому ни каких претензий, вы используете его на свой страх и риск.
В данной реализации работают только строковые(strings) ресурсы.

Почему был выбран именно такой подход? Что внутри? Как работает? Я попытаюсь рассказать об этом во второй статье «Silverlight Resource Extension. Часть 2. Описательная», которую планирую закончить в обозримом будущем.
Tags:
Hubs:
Total votes 59: ↑37 and ↓22 +15
Views 1.7K
Comments Comments 23