[Include] Multi-Languages
#1

Доброго времени суток.
Перед новым годом принято дарить подарки и я хочу представить результат проделанной мной работы.
Хочу представить вам небольшой include, который позволит вам без труда сделать мультиязычный сервер. На написание includ-а меня сподвиг tutorial Мультиязычный интерфейс.
В данном tutorial-е рассмотренный классические схемы построения мультиязычного сервера, но данные схемы неудобны при написании больших модов, некие схемы с точки зрения производительности пагубны для сервера, третьи имеют плохую плотность данных, происходит выделение большого количества памяти для хранения текста, которые в свою очередь не заполняет всю память, выделенной для её хранения. Пример:
pawn Code:
new str[128] = "123";
-Первым направлением было, решение плотности хранения данных (текста). Для это пригодились особенности строкового типа, а именно определения конца текста. Был создан одно длинное хранилище всех текстов. Особенностью решения является то, что все тексты пишутся друг за другом - потоком. Достигается высокая плотность хранения данных, так как под каждый текст не выделяется своя переменная заданной длины.
-Вторым направление было, повышение удобства программирования. Каждый из вас согласится с тем, что запоминать кучу идентификаторов текста, как число, очень не удобно и даже отталкивает. Возьмём пример из tutorial-а
pawn Code:
SendClientMessage(playerid, 0xFDE39DFF, Language[GetPVarInt(playerid, "Language")][0]);
Попробуй пойми, что там за текст хранится, а постоянно подглядывать в файл та ещё забава. Другое дело запомнить идентификатор как текст на много легче, создаются ассоциации. Пример:
pawn Code:
#define RUS_TEXT_TWO "Текст %s текст"
#define ENG_TEXT_ONE "Text %s text"
Но тут мы сталкиваемся с проблемами, как мы все знаем текст будет подставятся препроцессором компилятора, и тогда чтобы осуществить мультиязычность - нужны условия, какой язык сервер будет показывать. Так же, стандартный компилятор не может скушать текст более 512 символов, что не позволительно в некоторых диалоговых окнах. Так же в tutorial-е была реализация подстановки текста по имени ключа в файле, но мы же с вами грамотные люди, мы знаем к чему всё это приводит. И тут, многим покажется, что решений больше нет, но тут я применил маленькую хитрость. Особенность enumenator-a, поля в нём имеют текстовое представление и они уже изначально индексируются. Объясню наглядно:
pawn Code:
enum Text
{
    Text_1[32],
    Text_2[32],
};
new var[2][Text];
//...
var[1][Text_2] = "123"; /*тоже самое что и*/ var[1][1] = "123";
//Пример:
SendClientMessage(playerid, 0xFDE39DFF, Language[GetPVarInt(playerid, "Language")][Text_2]);
Теперь уже мы интуитивно понимаем, что за текст там может содержатся. Но остаётся вопрос, как же это всё связывается.
Так как у нас буффер большой длины и нужно извлекать необходимые тексты, требуется знать место(ячейку) с которого начинается текст. Для этого создана переменная, которая хранит указатель на ячейку с которого начинается текст, текст у нас ассоциируется с полем enumenator-a, а само поле выступает в качестве идентификатора. (смотри код в includ-e )
-Третьим направление было, хранение текстов и их размещение. Самым удобным вариантом, конечно же является хранения текстов в файле, его можно в любое время открыть, подредактировать. Обычно структура таких файлов является: [ключ]текст, но это нужно писать обработчик, поиск ключей, проверять ключ по условию чтобы положить в нужную переменную и т.д. Решение оказалось на удивление простым. Был взят enumenator и вынесен в отдельный файл, а рядом с полем написаны тексты. Выглядит это всё следующим образом:
pawn Code:
enum E_Lng {
Error_Localization/*системная строка*/,//Ошибка локализации.
Localization,//Русский
asdasd_2,//резервная строка
Line_1,//Я строка номер %s, моя длина %d символов.
Line_2,//Я строка номер %s, моя длина %d символов и я длиннее.
Line_3,//Я строка номер %s, моя длина %d символов,\r\nкто следующий?.
};
Ассоциация и рядом сам текст, здорово, не правда ли. Причём вы можете перемещать строки не боясь о том что потеряете идентификатор, так как Ассоциация(поле enumenator-а) автоматически индексируется. Дополнительно к этому при удалении строки или её отсутствии, не позволит программисту сделать ошибку, если данный текст ещё используется в коде. Размер длины текста не имеет значения, главное что бы текст был положен в одну строку. Возможность оставлять комментарии и много других не явных функций. Основным условием является формирование строки вида: *,//*
Закончим вводную часть.

Преимущества данного includ-а:
  1. Поддержка большого количества локализаций, по умолчанию до 256.
  2. Локализация хранится в файле.
  3. Возможность быстрой правки локализации и задействование её не перезагружая сервер.
  4. Хорошая плотность данных.
  5. Стабильная и достаточна быстрая работа.
  6. Лёгкость в программировании, по сравнению с другими схемами.
  7. Система контроля ошибок.
Недостатки:
  1. Нельзя добавлять тексты(строка) без перекомпиляции мода. Особенно если текст(строка) добавлен в середине файла локализации - вызывает смещение данных.
  2. Для того, чтобы на ходу делать правки, расширять тексты, нужно предварительно выделить больше ячеек памяти. отвечает за это #define MAX_ML_CHARACTER
  3. При добавлении новых локализаций, необходимо учесть два минуса, указанных выше и соответственно вписать файл в ML_Text_Files

Функции:
pawn Code:
ML_Load(); //Загружает/перезагружает локализации.
ML_Text(lngid, textid); //Возвращает текст. Параметры: (ид языка, ид текста)
ML_Player_Text(playerid, textid); //Возвращает текст c учётом установленного языка для игрока Параметры: (ид игрока, ид текста)
ML_S_Text(language, E_Lng:text, source[], len = sizeof(source)); //Безопасное перекладывание текста. Возвращает истину если успешно переложено. Параметры: (ид языка, ид текста, название массив, длина массива)
ML_S_Player_Text(playerid, E_Lng:text, source[], len = sizeof(source)); //Безопасное перекладывание текста c учётом установленного языка для игрока. Возвращает истину если успешно переложено. Параметры: (ид языка, ид текста, название массив, длина массива)
ML_SetPlayerLanguage(playerid, language); //Установка языка игроку, Вернёт истуну в случае успеха. Параметры: (ид игрока, ид языка)
Установка:
  1. Скачать архив.
  2. Распаковать в папку с сервером.
  3. В моде подключить следующим образом: #include "../include/ML.inc"
  4. Подстроить настройки в includ-e
Пример использования:
pawn Code:
#include <a_samp>

#include "../include/ML.inc" // Мультиязычность.

public OnFilterScriptInit()
{
    ML_Load(); // Загрузим локализации

    print(ML_Text(0, Localization)); // Выведем в консоль текст

    new source[16];
   
    if(ML_S_Text(0, Line_1, source)) //Безопасно извлекаем строку
    {
        print(source); // Выведем в консоль текст
    }
    else
    {
        print("Строка не была извлечена!");
    }

    new source_2[64];
    format(source_2, sizeof(source_2), ML_Text(0, Line_2), "два" ,strlen(ML_Text(0, Line_1)));// Формируем текст
    print(source_2); // Выведем в консоль текст
   
    print(ML_Text(1, Line_3));
   
    return 1;
}
Скачать:
Версия 1.0: >>> solidfiles.com инклуд+локализации+пример

Просмотреть:
Версия 1.0: >>> pastebin.com
Reply
#2

не легче просто короткие сообщения дефайнить, а длинные в массиве хранить? использование то всё равно одинаковое
Reply
#3

translate.inc удобней же
Reply
#4

Имхо у меня в файлах удобнее.
Reply
#5

Quote:
Originally Posted by Jon_De
View Post
не легче просто короткие сообщения дефайнить, а длинные в массиве хранить? использование то всё равно одинаковое
И как ты собрался сделать отображение текстов на том или ином языке в зависимости от желания игрока. Одновременно сервер получается на нескольких языках.

Quote:
Originally Posted by OKStyle
View Post
Имхо у меня в файлах удобнее.
Файл постоянно читать нужно, не есть хорошо.
Reply


Forum Jump:


Users browsing this thread: 1 Guest(s)