Локализация когда-то приходит в ваш интернациональный дом. Что бы вы ни построили — большой небоскреб или хижину дяди Тома — надо уметь разговаривать на языке жителей этого дома.
Если ваш 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 и заключается он в создании класса с набором свойств, являющихся ресурсами.
Используется в markup это так:
При чем, если надо использовать данный провайдер для всего приложения, вы просто определяете его на уровне Application, то есть в App.xaml.
Основной ресурс готов, провайдер есть, теперь надо расширить ресурс дополнительными языками.
Для тех кто спросил «Как?»:
— вызываем контекстное меню на нужной папке в Solution Explorer (Add -> New Item);
— выделяем закладку General;
— создаем Resources Files с именем основного ресурса (в нашем случае ProjectResources);
— в конце добавляем имя нужной культуры — ProjectResources.ru-Ru.resx;
— нажимаем Add.
Создастся файл с нужным именем. Открываем ресурс в дизайнере, в первой колонке пишем имя совпадающие с базовым ресурсом, в нашем случае это — «Message», вторую колонку заполняем переведенным значением — «Привет Мир!».
Ничего нового я не придумал, фактически своим постом я описал два документа:
Для тех кто внимательно читал первый(или уже сталкивался с ним), из приведенных выше документов, скажу, что я специально умолчал о добавлении в xml проекта специальной секции <SupportedCultures />, определяющей список языковых сборок, необходимых для включения в скомпилированный xap файл. Если вы добавите эту секцию, то ни как не уйдете от той проблемы, с которой я начал повествование — от использования лишних ресурсов.
Способ, который я разработал, к сожалению, не подходит тем, кто хочет одним состоянием чекбокса включить поддержку ленивой (lazy) загрузки ресурсов. О как закрутил. Короче — придется потрудиться что бы осушить пруд и добраться до желанной рыбки.
Во-первых, скачать последнюю версию библиотеки ResourceExtension с codeplex и добавить её в референсы к Silverlight проекту.
Создание ресурсов не отличается от приведенного выше, поэтому я его опущу. Единственное, что требуется для ресурсов это добавить pre-build событие. Вызываем свойства Silverlight проекта. Открываем вкладку Build Events. В Pre-build вносим:
Этим действием, автоматически, во всех ресурсах проекта, подменяется класс ResourceManager на ResourceXManager, реализованный в библиотеке ResourceExtension.
К сожалению, этот код не работает без дополнительной секции <UseHostCompilerIfAvailable>FALSE</UseHostCompilerIfAvailable> в файле проекта. Добавить этот код надо в секцию <PropertyGroup>.
Markup не претерпел ни каких изменений, все осталось на своих местах.
А вот провайдер видоизменился, теперь он является наследником класса CultureResourceProvider и приобрел метаданные на свойствах:
Финальный аккорд. Собираем. Включаем галочку для просмотра всех файлов и находим папку bin\Debug\ru-RU, которую мы должны переместить в папку ClientBin и положить рядом с xap файлом.

И, «легкое превращение в элегантные шорты»:
позволит вам удобно загрузить ресурсы с русской локалью.
Если у вас, что-либо не получилось. Вы можете скачать пример и посмотреть, как там все реализовано.
Компонент находится в стадии alpha, поэтому ни каких претензий, вы используете его на свой страх и риск.
В данной реализации работают только строковые(strings) ресурсы.
Почему был выбран именно такой подход? Что внутри? Как работает? Я попытаюсь рассказать об этом во второй статье «Silverlight Resource Extension. Часть 2. Описательная», которую планирую закончить в обозримом будущем.
Если ваш 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 и заключается он в создании класса с набором свойств, являющихся ресурсами.
- public class ResProvider : INotifyPropertyChanged
- {
- static ProjectResources resources = new ProjectResources();
-
- public ProjectResources ProjectResources
- {
- get
- {
- return resources;
- }
- }
-
- #region INotifyPropertyChanged Members
- // Implemention of interface.
- #endregion
- }
* This source code was highlighted with Source Code Highlighter.
Используется в markup это так:
- <UserControl>
- <UserControl.Resources>
- <Resources:ResProvider x:Key="ResProvider"/>
- </UserControl.Resources>
- <Grid>
- <TextBlock Text="{Binding Source={StaticResource ResProvider},Path=ProjectResources.Message}"/>
- </Grid>
- </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», вторую колонку заполняем переведенным значением — «Привет Мир!».
Ничего нового я не придумал, фактически своим постом я описал два документа:
Для тех кто внимательно читал первый(или уже сталкивался с ним), из приведенных выше документов, скажу, что я специально умолчал о добавлении в 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 и приобрел метаданные на свойствах:
- using ResourceExtension;
- using ResourceExtension.Implementation;
-
- namespace ResourceExtensionExample.Resources
- {
- public class ResProvider: CultureResourcesProvider
- {
- private static ProjectResources projectResources = new ProjectResources();
-
- [ResourceProperty]
- public ProjectResources ProjectResources
- {
- get { return projectResources; }
- }
- }
- }
* This source code was highlighted with Source Code Highlighter.
Финальный аккорд. Собираем. Включаем галочку для просмотра всех файлов и находим папку bin\Debug\ru-RU, которую мы должны переместить в папку ClientBin и положить рядом с xap файлом.

И, «легкое превращение в элегантные шорты»:
- public partial class MainPage : UserControl
- {
- public MainPage()
- {
- InitializeComponent();
- }
-
- private void Switch_Click(object sender, RoutedEventArgs e)
- {
- ((ResProvider) Resources["ResProvider"]).Options.CultureInfo = new CultureInfo("ru-Ru");
- }
- }
* This source code was highlighted with Source Code Highlighter.
позволит вам удобно загрузить ресурсы с русской локалью.
Если у вас, что-либо не получилось. Вы можете скачать пример и посмотреть, как там все реализовано.
Компонент находится в стадии alpha, поэтому ни каких претензий, вы используете его на свой страх и риск.
В данной реализации работают только строковые(strings) ресурсы.
Почему был выбран именно такой подход? Что внутри? Как работает? Я попытаюсь рассказать об этом во второй статье «Silverlight Resource Extension. Часть 2. Описательная», которую планирую закончить в обозримом будущем.