Pull to refresh

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

Здесь не сделан акцент и не приведен пример создания, загрузки и отображения списка поддерживаемых языков в приложении — это сделать совсем просто.
Tags:
Hubs:
You can’t comment this publication because its author is not yet a full member of the community. You will be able to contact the author only after he or she has been invited by someone in the community. Until then, author’s username will be hidden by an alias.