Альтернативная локализация в Silverlight

в 1:33, , рубрики: silverlight, метки: ,

Технология создания интерактивных 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 не полный. Предполагается, что разработчик сам расширит его.

Здесь не сделан акцент и не приведен пример создания, загрузки и отображения списка поддерживаемых языков в приложении — это сделать совсем просто.

Автор: диванный аналитик

Источник

Поделиться

* - обязательные к заполнению поля