Главная
Блог разработчиков phpBB
 
+ 17 предустановленных модов
+ SEO-оптимизация форума
+ авторизация через соц. сети
+ защита от спама

Локализация планов на .NET с интерпретатором функций

Anna | 18.06.2014 | нет комментариев

Начну с того, что за много лет работы программистом я многократно сталкивался с задачей внедрения в план локализации в том либо другом виде, но обыкновенно это были решения, работающие на основе подгружаемого словаря с парами ключ-значение. Такой подход абсолютно оправдан для маленьких планов, но имеет ряд значительных недостатков:

  1. Трудность внедрения в присутствующий план.
  2. Неимение средств форматирования локализованных сообщений (за исключением стандартного string.Format).
  3. Неосуществимость встраивания культурно-зависимых функций. Скажем, нормальную задачу, — подстановку требуемой формы слова в зависимости от значения числа, — одними словарями значений не позволить.

Проанализировав эти задачи, я пришел к итогу о необходимости создания собственной библиотеки для локализации планов, которая будет лишена перечисленных выше недостатков. В этой статье я расскажу о тезисах ее работы с примерами кода на C#.

Состав библиотеки

Ссылка на план SourceForge: https://sourceforge.net/projects/open-genesis/?source=navbar

В сборку входят следующие планы:

  • Genesis.Localization — основная библиотека локализации.
  • Ru — реализация русской локализации (пример).
  • En — реализация английской локализации (пример).
  • LocalizationViewer — программа для демонстрации вероятностей библиотеки с вероятностью редактирования локализаций.image

Основные тезисы

image

Администратор локализаций

Библиотека построена на базе плагинов и работает дальнейшим образом: при запуске приложения создается администратор локализаций (LocalizationManager), которому указывается путь к каталогу, где он будет изготавливать поиск доступных пакетов локализаций (LocalizationPackage), всякий из которых отвечает за некоторую культуру (пакет русской локализации, английской и т.п.). Позже этого дается команда на поиск и загрузку дескрипторов всех пакетов, каждый код инициализации выглядит приблизительно дальнейшим образом:

// инициализация администратора локализаций
LocalizationManager = new LocalizationManager();
LocalizationManager.BasePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Localization");
LocalizationManager.Initialize();
try
{
    LocalizationManager.DetectAllLocalizations();
}
catch (LocalizationException ex)
{
    MessageBox.Show(ex.Message, "Оплошность локализации", MessageBoxButtons.OK, MessageBoxIcon.Error);
    return;
}

Если все прошло без ошибок, в администраторе появится список доступных локализаций в виде их коротких изложений (дескрипторов, LocalizationDescriptor). Эти дескрипторы не содержат в себе какой-либо логики, а служат только лишь изложением того либо другого пакета, тот, что дозволено загрузить и начать использовать в программе.

Список всех локализаций дозволено получить из администратора:

manager.Localizations

Скажем, мы захотели подключить русскую локализацию, для этого нужно загрузить ее непринужденно в администратор:

LocalizationPackage package = manager.Load("ru");

Позже загрузки с локализацией дозволено трудиться — получать из нее строки, источники и т.д., а если она больше не необходима, ее дозволено выгрузить:

manager.Unload("ru");

Значимо! Дозволено загружать и выгружать неограниченное число локализаций, т.к. все они создаются в собственных доменах (AppDomain).

Пакет локализации

Всякая локализация представляет из себя комплект файлов в отдельном каталоге, корневым для всех является тот, тот, что был выбран при загрузке администратора локализаций. l>

  • Включение довода в строку.Формат инструкции:
    %index%
    

    Итог: встраивание в строку довода под номером index

    Пример применения:

    package.GetFormattedString("%1% = %0%%%", 80, "КПД");
    

    Итог:

    КПД = 80%
    

    Обратите внимание, что символ %, будучи служебным, должен экранироваться иным таким же символом, как в этом примере.

  • Включение функцийФормат инструкции:
    %Func(arg1, arg2, ..., argN)%
    

    Доводами могут быть числастроки в двойных кавычках (сами кавычки экранируются, как и %, двойным повтором), доводы строки по их номеру (%index) либо вызовы других функций.

    Пример применения:

    package.GetFormattedString("Игрок %1% наносит по вам %Upper(Random("мощный", "сокрушительный", "сильный"))% удар, отнимая %0% %Plural(%0, "единицу", "единицы", "единиц")% здоровья.", 55, "MegaDeath2000");
    

    Итог:

    Игрок MegaDeath2000 наносит по вам СОКРУШИТЕЛЬНЫЙ удар, отнимая 55 единиц здоровья.
    

Встроенные функции и интеграция

В классе LocalizationPackage встроено несколько «стандартных» функций, часть была использована в примере выше:

  • Plural(int, var1, var2, …, varN) — встраивание формы слова в зависимости от числа, данный способ уникален для всякой культуры и должен быть переопределен. В частности, в русском языке есть три формы числа (скажем: «1 единица», «2 единицы», «8 единиц»).
  • Random(var1, var2, …, varN) — выбор случайного значения среди заданных.
  • Upper(string) — приведение к верхнему регистру.
  • Lower(string) — приведение к нижнему регистру.
  • UpperF(string) — приведение к верхнему регистру только первой буквы («словечко» => «Словечко»).
  • LowerF(string) — приведение к нижнему регистру только первой буквы.

Если вам требуется добавить новые функции, сделать это дозволено двумя методами.

  1. В переопределенном классе пакета дозволено объявить новые функции и пометить их признаком [Function], тогда они будут механически включены в интерпретатор для определенной локализации. Встроенные функции определены именно этим методом, скажем так выглядят функции Plural и Random:
    [Function("P")]
    protected abstract object Plural(int count, params object[] forms);
    
    [Function]
    protected virtual object Random(params object[] variants)
    {
        if (variants.Length == 0)
        {
            return null;
        }
        else
        {
            return variants[_rnd.Next(variants.Length)];
        }
    }
    

    Обратите внимание, что для функции возможно задание списка ее псевдонимов (для короткой записи), скажем Plural может вызываться как через основное имя (Plural), так и через псевдоним (P), при этом регистр в наименованиях функций не имеет значения.

  2. Интеграция собственных функций, для этого применяется способ InjectFormatterFunction, пример применения:
    var package = LocalizationManager.Load("ru");
    package.InjectFormatterFunction(new Func<int, int, int>((a, b) => Math.Min(a, b)), "Min");
    package.InjectFormatterFunction(new Func<int, int, int>((a, b) => Math.Max(a, b)), "Max");
    package.GetFormattedString("%min(%0, max(%1, %2))%", 10, 8, 5);
    

    Итог:

    8
    

    В качестве довода для InjectFormatterFunction может быть передан способ (MethodInfo) либо делегат (в примере выше передаются делегаты).

Добавочные вероятности

Помимо основных функций, библиотека предоставляет еще два дополнения.

Отладочный режим

В Debug-версии библиотеки включена вероятность не только приобретения локализованных строк описанными выше методами, но и их непосредственная запись:

var package = LocalizationManager.Load("ru");
package["New Key"] = "Новое значение";
package.Save();

В этом случае будет сделана новая локализованная строка с указанным ключом и значением (либо перезаписана теснее имеющаяся), а сам пакет сохранен на диск. Также в режиме отладки при попытке чтения строки с отсутствующим ключом, будет возвращено пустое значение, но при этом будет сделана новая запись. Это комфортно на исходном этапе разработки — нам не необходимо заботится о наполнении словаря — он сам будет пополняться пустыми значениями, которые мы потом заполним данными.

В релизе функции записи недостижимы, что абсолютно разумно — индустриальная программа не должна уметь пополнять свой словарь локализации.

Маппинги

Это наш десерт. Предназначение — стремительная локализация форм, контролов и других трудных объектов.
Данная функция применяется в демонстрационном плане LocalizationViewer.

Приведу отрывок изложения основной формы:

[LocalizableClass("Text", "CAPTION")]
public partial class frmMain : Form
{
        ...

        [LocalizableClass]
        private System.Windows.Forms.ToolStripButton cmdExit;

        [LocalizableClass]
        private System.Windows.Forms.ToolStripButton cmdSave;

        [LocalizableClass]
        private System.Windows.Forms.ToolStripLabel lblSearch;

        ...

        /// <summary>
        /// применяем локализацию
        /// </summary>
        private void Localize()
        {
            LocalizationMapper mapper = new LocalizationMapper();
            mapper.Current = manager["ru"];
            mapper.Localize(this);
        }

        ...
}

LocalizationMapper, разрешает локализовать всякий объект, переданный ему в функции Localize, применяя признаки [Localizable] и [LocalizableClass] на полях и свойствах локализуемого объекта (в данном случае — формы). Скажем, признак [LocalizableClass] без параметров обозначает, что нужно локализовать качество по умолчанию (Text), при этом будет использован механический ключ вида <class>.<subclass>.<field>. Для поля Text кнопки cmdExit ключ будет таким:

LocalizationViewer.frmMain.cmdExit_Text

Завершение

Библиотека в скором времени будет опробована в одном из моих планов, так что скорее каждого будут кое-какие доработки, в основном направленные на растяжение базового функционала пакетов. Следите за обновлениями на SourceForge и пишите свои комментарии и мысли по последующему становлению библиотеки.

P.S.

Допустимо, вы скажете, что я изобретаю велосипед. Пускай так, но изобретать велосипеды — это мое хобби…
К тому же, это гораздо увлекательнее и пригоднее в плане самосовершенствования в программировании.
По этой же причине тут не будет ссылок на литературу и прочие источники информации — все было написано с нуля.

Источник: programmingmaster.ru
Оставить комментарий
Форум phpBB, русская поддержка форума phpBB
Рейтинг@Mail.ru 2008 - 2017 © BB3x.ru - русская поддержка форума phpBB