Технология создания интерактивных web-приложений Silverlight является одним из современных средств взаимодействия пользователя с ресурсами сайта в сети Интернет. На сегодня успешные программы давно освоили механизмы адаптации своего интерфейса к разноязыковой публике — т.е. локализации. Например, в технологии WPF применяется механизм динамических ресурсов (Расширение разметки DynamicResource), позволяющих изменять надписи и изображения интерфейса при их отображении, а не компиляции программы. Так, при реализации интерфейса WPF достаточно указать в требуемом свойстве элемента через привязку (Binding) имя ресурса. Однако, не смотря на схожесть способа реализации графического интерфейса технологии WPF в Silverlight нет динамических ресурсов. Стоит отметить об известном способе автоматизации этой задачи основанный на INotifyPropertyChanged — как сказано здесь «заключается он в создании класса с набором свойств, являющихся ресурсами». Данный способ сложен, требует вводить новое свойство в класс для указания ссылки на него в интерфейсе под каждый элемент ресурса, а запись привязки (Binding) к свойствам такая же громоздкая, тем более, что каждое привязанное свойство необходимо программно с помощью цикла или поименно «заставлять» обновляться. Предлагается альтернативный способ локализации в Silverlight основанный на метках (Tag) элементов интерфейса и рекурсивном цикле принудительной замены значений их свойств (например, текстов надписей и картинок). Такой подход обеспечивает независимость от предлагаемых Микрософтом способах локализации, предоставляет надежный программный механизм гарантирующий замену значений указанных свойств элементов, является простым и компактным в реализации.
Реализация
Первым делом нужно расставить метки (Tag) в элементах интерфейса, нуждающихся в локализации, например:
<TextBlock Text="Масштаб" Tag="m_Scale"/>
Далее, создать отдельно для каждого поддерживаемого языка обычный текстовый файл в кодировке UFT-8, родной для Silverlight, описывающий перевод меток, например, для английского:
m_Scale=Scale
и русского:
m_Scale=Масштаб
где одна метка — одна строка.
Запрограммировать небольшой код рекурсивного цикла изменения указанных свойств, например, в виде класса:
public static class TLanguage
{
public static void RenameForm(DependencyObject root)
{
//Это элементы типа Control (Добавьте свои)
if (root is TabControl)
foreach (TabItem i in (root as TabControl).Items)
{
i.UpdateLayout();//обновляем отображение
string t = i.Tag + "";//текущая метка
if (!dic.ContainsKey(t)) continue;//проверяем наличие перевода метки
i.Header = dic[t];//переводим, локализуем
RenameForm(i.Content is FrameworkElement ? i.Content as FrameworkElement : i.Content as TabControl);//идем внутрь элемента
}
if (root is ScrollViewer)
{
var a = (root as ScrollViewer).Content;
RenameForm(a is FrameworkElement ? a as FrameworkElement : a as Control);
}
if (root is ChildWindow)
RenameForm((root as ChildWindow).Content as FrameworkElement);
if (!String.IsNullOrEmpty((root as FrameworkElement).Tag + ""))
{
var t = (root as FrameworkElement).Tag + "";//метка
if (!dic.ContainsKey(t)) return;//наличие
//Локализуем элементы
if (root is TextBlock) (root as TextBlock).Text = dic[t];
if (root is ChildWindow) (root as ChildWindow).Title = dic[t];
if (root is CheckBox) (root as CheckBox).Content = dic[t];
}
//Эти функции видят только элементы типа UIElement
int count = VisualTreeHelper.GetChildrenCount(root);
for (int i = 0; i < count; i++)
RenameForm(VisualTreeHelper.GetChild(root, i));
}
//Ассоциативный массив для переводов меток
static Dictionary<string, string> dic = new Dictionary<string, string>();
//Изменяемое свойство класса, нужно для загрузки текстового файла переводов меток
public static CultureInfo Language
{
get
{
return System.Threading.Thread.CurrentThread.CurrentUICulture;
}
set
{
if (value == null) throw new ArgumentNullException("value");
//if (value == System.Threading.Thread.CurrentThread.CurrentUICulture) return;
//1. Меняем язык приложения:
System.Threading.Thread.CurrentThread.CurrentUICulture = value;
//2. Загружаем лист перевода для новой культуры
string[] l = null;
//Проще, если название файла совпадает с принятым сокращением названия языка
try { l = Utils.Resource.LoadTextLines("Resource/Language/" + value.Name + ".txt"); }
catch { l = Utils.Resource.LoadTextLines("Resource/Language/ru-RU.txt"); }
if (l == null) return;
dic.Clear();
foreach (string s in l)
if (s.Length > 0 && s[0] != '#')//обход комментария
//заполняем массив переводов
dic.Add(s.Substring(0, s.IndexOf('=')).Trim(), s.Substring(s.IndexOf('=') + 1).Trim());
}
}
}
При запуске приложения нужно указать (вызвать функцию загрузки) используемый язык, например:
TLanguage.Language = System.Threading.Thread.CurrentThread.CurrentUICulture;
Теперь, для каждого окна в конструкторе нужно добавить вызов функции локализации, например:
TLanguage.RenameForm(this);
Набор поддерживаемых для локализации элементов интерфейса в приведенной процедуре RenameForm не полный. Предполагается, что разработчик сам расширит его.
Здесь не сделан акцент и не приведен пример создания, загрузки и отображения списка поддерживаемых языков в приложении — это сделать совсем просто.
Автор: диванный аналитик