Автор: Subb98
Введение:
Данная статья предназначена для тех, кто хотел бы сделать свой сервер, либо отдельный плагин мультиязычным. Для этого существуют словари — файлы, содержащие в себе набор ключей, каждому из которых присвоено своё значение (слово или фраза на одном или нескольких языках). Эта статья как раз и посвящена словарям, точнее, работе с ними. Не будем углубляться в теорию, тем более, что, в данном случае, её почти и нет, разберём всё подробно на практике...
Практическая часть (основная):
Для начала нам необходимо зарегистрировать наш файл словаря:
Введение:
Данная статья предназначена для тех, кто хотел бы сделать свой сервер, либо отдельный плагин мультиязычным. Для этого существуют словари — файлы, содержащие в себе набор ключей, каждому из которых присвоено своё значение (слово или фраза на одном или нескольких языках). Эта статья как раз и посвящена словарям, точнее, работе с ними. Не будем углубляться в теорию, тем более, что, в данном случае, её почти и нет, разберём всё подробно на практике...
Практическая часть (основная):
Для начала нам необходимо зарегистрировать наш файл словаря:
- Код: Выделить всё
#include <amxmodx>
#pragma semicolon 1
public plugin_init() {
register_plugin("Test Dictionary", "0.1", "[amx-x.ru] #team");
register_dictionary("test_dictionary.txt"); // регистрация нового файла словаря с именем 'test_dictionary.txt'
}
По умолчанию файл должен находиться в директории <GAME_DIR>/addons/amxmodx/data/lang. В файле словаря (test_dictionary.txt) пишем текст следующего содержания:
- Код: Выделить всё
[ru]
TD_HELLO = Привет, мир!
[en]
TD_HELLO = Hello, world!
Сохраняем файл в кодировке UTF-8 без BOM. Рассмотрим, что же мы написали. Мы создали файл словаря, который поддерживает перевод на два языка: русский и английский. ru и en — это значения ключа lang (setinfo "lang" "en"). TD_HELLO — это ключ файла словаря, Привет, мир! и Hello, world! — его значения для русского и английского языков, соответственно. Ключи, расположенные ниже строки [ru], относятся к переводу на русский язык. Соответственно, ключи, расположенные ниже строки [en], относятся к переводу на английский язык. Теперь зарегистрируем команду, при выполнении которой всем игрокам в чат будет выводиться наше сообщение:
- Код: Выделить всё
#include <amxmodx>
#pragma semicolon 1
public plugin_init() {
register_plugin("Test Dictionary", "0.1", "[amx-x.ru] #team");
register_dictionary("test_dictionary.txt");
register_clcmd("say /hello", "CmdHello"); // регистрация команды /hello для отправки сообщения всем игрокам (общий чат)
register_clcmd("say_team /hello", "CmdHello"); // регистрация команды /hello для отправки сообщения всем игрокам (командный чат)
}
Добавим функцию обработки команды /hello (вывода сообщения):
- Код: Выделить всё
#include <amxmodx>
#pragma semicolon 1
public plugin_init() {
register_plugin("Test Dictionary", "0.1", "[amx-x.ru] #team");
register_dictionary("test_dictionary.txt");
register_clcmd("say /hello", "CmdHello");
register_clcmd("say_team /hello", "CmdHello");
}
public CmdHello() {
client_print(0, print_chat, "%L", LANG_PLAYER, "TD_HELLO"); // отправка нашего сообщения всем игрокам
return PLUGIN_HANDLED; // блокировка отправки команды /hello
}
Мы получили простейшую функцию вывода сообщения 'Привет, мир!' или 'Hello, world!' (в зависимости от языковых настроек игрока-получателя сообщения) по команде /hello. Как вы, наверное, уже поняли, символы %L в сообщении заменяются на то значение, которое соответствует ключу TD_HELLO. Добавим в сообщение никнейм игрока, который вызывает эту функцию (выполняет команду /hello):
- Код: Выделить всё
#include <amxmodx>
#pragma semicolon 1
public plugin_init() {
register_plugin("Test Dictionary", "0.1", "[amx-x.ru] #team");
register_dictionary("test_dictionary.txt");
register_clcmd("say /hello", "CmdHello");
register_clcmd("say_team /hello", "CmdHello");
}
public CmdHello(const id) {
new szName[32]; // объявление нового массива szName
get_user_name(id, szName, charsmax(szName)); // получение никнейма игрока, который выполнил команду /hello, в массив szName
client_print(0, print_chat, "%s: %L", szName, LANG_PLAYER, "TD_HELLO");
return PLUGIN_HANDLED;
}
Теперь наше сообщение имеет вид: 'Player: Привет, мир!' или 'Player: Hello, world!'. В данном случае мы использовали константу LANG_PLAYER (также можно использовать константу LANG_SERVER, индекс получателя, либо значение ключа lang, но об этом чуть позже). Обратите внимание, что константа LANG_PLAYER располагается всегда перед ключом TD_HELLO и между ними не должно быть никаких иных аргументов. То есть, подобные варианты:
- Код: Выделить всё
client_print(0, print_chat, "%s: %L", LANG_PLAYER, szName, "TD_HELLO");
- Код: Выделить всё
client_print(0, print_chat, "%s: %L", szName, "TD_HELLO", LANG_PLAYER);
ошибочны и, либо не будут работать вовсе, либо будут работать некорректно. Также для каждого ключа должна быть объявлена своя константа. Рассмотрим на конкретном примере. Добавим в файл словаря ещё один ключ:
- Код: Выделить всё
[ru]
TD_HELLO = Привет, мир!
TD_SAID = сказал
[en]
TD_HELLO = Hello, world!
TD_SAID = said
Добавим новый ключ в наше сообщение:
- Код: Выделить всё
#include <amxmodx>
#pragma semicolon 1
public plugin_init() {
register_plugin("Test Dictionary", "0.1", "[amx-x.ru] #team");
register_dictionary("test_dictionary.txt");
register_clcmd("say /hello", "CmdHello");
register_clcmd("say_team /hello", "CmdHello");
}
public CmdHello(const id) {
new szName[32];
get_user_name(id, szName, charsmax(szName));
client_print(0, print_chat, "%L (%L %s)", LANG_PLAYER, "TD_HELLO", LANG_PLAYER, "TD_SAID", szName);
return PLUGIN_HANDLED;
}
Теперь сообщение приняло следующий вид: 'Привет, мир! (сказал Player)' или 'Hello, world! (said Player)'. С основной частью разобрались, переходим к деталям. Разберём, в каких случаях необходимо использовать константы LANG_PLAYER, LANG_SERVER, индекс получателя и значение ключа lang.
LANG_PLAYER необходимо использовать в тех случаях, когда индекс получателя заранее неизвестен. Например, при отправке сообщения всем игрокам, что мы и делали выше. Во всех остальных случаях желательно использовать индекс получателя. Рассмотрим пример. Добавим в словарь ещё один ключ:
LANG_PLAYER необходимо использовать в тех случаях, когда индекс получателя заранее неизвестен. Например, при отправке сообщения всем игрокам, что мы и делали выше. Во всех остальных случаях желательно использовать индекс получателя. Рассмотрим пример. Добавим в словарь ещё один ключ:
- Код: Выделить всё
[ru]
TD_HELLO = Привет, мир!
TD_SAID = сказал
TD_HEALTH = Ваше здоровье
[en]
TD_HELLO = Hello, world!
TD_SAID = said
TD_HEALTH = Your health
Немного изменим наш код:
- Код: Выделить всё
#include <amxmodx>
#pragma semicolon 1
public plugin_init() {
register_plugin("Test Dictionary", "0.1", "[amx-x.ru] #team");
register_dictionary("test_dictionary.txt");
register_clcmd("say /health", "CmdHealth"); // замена команды /hello на /health (общий чат)
register_clcmd("say_team /health", "CmdHealth"); // замена команды /hello на /health (командный чат)
}
public CmdHealth(const id) {
client_print(id, print_chat, "%L: %d", id, "TD_HEALTH", get_user_health(id)); // отправка сообщения игроку, выполнившему команду /health
return PLUGIN_HANDLED;
}
Теперь наше сообщение имеет вид: 'Ваше здоровье: 100' или 'Your health: 100'. Как видите, вместо константы LANG_PLAYER перед ключом TD_HEALTH расположен индекс получателя, id, на языковые настройки которого и будет сориентирована система при отправке сообщения.
LANG_SERVER необходимо использовать в тех случаях, когда вы хотите, чтобы при отправке сообщения или его форматировании система была сориентирована на языковые настройки сервера. Также эту константу можно использовать в тех же случаях, что и LANG_PLAYER (то есть, если индекс получателя заранее неизвестен). На практике используется достаточно редко, но, иногда, бывает весьма полезной для решения нестандартных задач.
Значение ключа lang нужно использовать в тех случаях, когда необходимо отправить клиенту сообщение на конкретном языке (например, немецком). Рассмотрим на примере. Добавим для наших ключей ещё один перевод:
LANG_SERVER необходимо использовать в тех случаях, когда вы хотите, чтобы при отправке сообщения или его форматировании система была сориентирована на языковые настройки сервера. Также эту константу можно использовать в тех же случаях, что и LANG_PLAYER (то есть, если индекс получателя заранее неизвестен). На практике используется достаточно редко, но, иногда, бывает весьма полезной для решения нестандартных задач.
Значение ключа lang нужно использовать в тех случаях, когда необходимо отправить клиенту сообщение на конкретном языке (например, немецком). Рассмотрим на примере. Добавим для наших ключей ещё один перевод:
- Код: Выделить всё
[ru]
TD_HELLO = Привет, мир!
TD_SAID = сказал
TD_HEALTH = Ваше здоровье
[en]
TD_HELLO = Hello, world!
TD_SAID = said
TD_HEALTH = Your health
[de]
TD_HELLO = Hallo, Welt!
TD_SAID = sagte
TD_HEALTH = Ihre Gesundheit
Изменим наш код:
- Код: Выделить всё
#include <amxmodx>
#pragma semicolon 1
public plugin_init() {
register_plugin("Test Dictionary", "0.1", "[amx-x.ru] #team");
register_dictionary("test_dictionary.txt");
register_clcmd("say /health", "CmdHealth");
register_clcmd("say_team /health", "CmdHealth");
}
public CmdHealth(const id) {
client_print(id, print_chat, "%L: %d", "de", "TD_HEALTH", get_user_health(id));
return PLUGIN_HANDLED;
}
Как видите, вместо константы/индекса получателя мы использовали значение ключа lang, "de", и теперь, вне зависимости от настроек языка клиента/сервера, сообщение будет отправлено именно на том языке, который мы указали (в данном случае, на немецком).
Практическая часть (дополнительная):
Сейчас мы рассмотрим некоторые дополнительные "приёмы", которые можно использовать при работе со словарями.
1. Работа с аргументами, содержащимися в значениях ключей словаря. О чём речь — разберём на примере. Изменим уже имеющиеся у нас файл словаря и код. В файле словаря оставим перевод на два языка (русский и английский), значение ключа TD_HEALTH немного изменим:
Практическая часть (дополнительная):
Сейчас мы рассмотрим некоторые дополнительные "приёмы", которые можно использовать при работе со словарями.
1. Работа с аргументами, содержащимися в значениях ключей словаря. О чём речь — разберём на примере. Изменим уже имеющиеся у нас файл словаря и код. В файле словаря оставим перевод на два языка (русский и английский), значение ключа TD_HEALTH немного изменим:
- Код: Выделить всё
[ru]
TD_HELLO = Привет, мир!
TD_SAID = сказал
TD_HEALTH = Ваше здоровье: %d
[en]
TD_HELLO = Hello, world!
TD_SAID = said
TD_HEALTH = Your health: %d
Изменим также и сам код:
- Код: Выделить всё
#include <amxmodx>
#pragma semicolon 1
public plugin_init() {
register_plugin("Test Dictionary", "0.1", "[amx-x.ru] #team");
register_dictionary("test_dictionary.txt");
register_clcmd("say /health", "CmdHealth");
register_clcmd("say_team /health", "CmdHealth");
}
public CmdHealth(const id) {
client_print(id, print_chat, "%L", id, "TD_HEALTH", get_user_health(id));
return PLUGIN_HANDLED;
}
Как, вероятно, вы заметили, при форматировании сообщения мы указали лишь символы для подстановки значения ключа словаря TD_HEALTH (%L), а между тем в сообщении также присутствует аргумент в виде получения здоровья игрока (get_user_health(id)). Символы для этого аргумента (%d) будут подставлены непосредственно из самого значения ключа словаря TD_HEALTH. Это работает для любых аргументов (целых чисел, дробных чисел, строк и т.д.). Таким же способом можно реализовать подстановку значения одного ключа словаря в значение другого ключа. Чтобы было понятно, о чём речь, рассмотрим это на ещё одном примере. Изменим в очередной раз наши файл словаря и код:
- Код: Выделить всё
[ru]
TD_HELLO = Привет, мир! (%L)
TD_SAID = сказал %s
TD_HEALTH = Ваше здоровье
[en]
TD_HELLO = Hello, world! (%L)
TD_SAID = said %s
TD_HEALTH = Your health
- Код: Выделить всё
#include <amxmodx>
#pragma semicolon 1
public plugin_init() {
register_plugin("Test Dictionary", "0.1", "[amx-x.ru] #team");
register_dictionary("test_dictionary.txt");
register_clcmd("say /hello", "CmdHello");
register_clcmd("say_team /hello", "CmdHello");
}
public CmdHello(const id) {
new szName[32];
get_user_name(id, szName, charsmax(szName));
client_print(0, print_chat, "%L", LANG_PLAYER, "TD_HELLO", LANG_PLAYER, "TD_SAID", szName);
return PLUGIN_HANDLED;
}
В результате мы увидим уже знакомое нам сообщение: 'Привет, мир! (сказал Player)' или 'Hello, world! (said Player)'.
2. Работа с многострочными значениями ключей словаря. Для большего удобства (при работе с объёмными значениями параметров ключей словаря) существует возможность писать значение ключа словаря в несколько строк:
2. Работа с многострочными значениями ключей словаря. Для большего удобства (при работе с объёмными значениями параметров ключей словаря) существует возможность писать значение ключа словаря в несколько строк:
- Код: Выделить всё
[ru]
TD_HELLO:
Привет, мир! (
сказал %s)
:
[en]
TD_HELLO:
Hello, world! (
said %s)
:
Немного изменим наш код:
- Код: Выделить всё
#include <amxmodx>
#pragma semicolon 1
public plugin_init() {
register_plugin("Test Dictionary", "0.1", "[amx-x.ru] #team");
register_dictionary("test_dictionary.txt");
register_clcmd("say /hello", "CmdHello");
register_clcmd("say_team /hello", "CmdHello");
}
public CmdHello(const id) {
new szName[32];
get_user_name(id, szName, charsmax(szName));
client_print(0, print_chat, "%L", LANG_PLAYER, "TD_HELLO", szName);
return PLUGIN_HANDLED;
}
Результатом выполнения команды /hello будет всё то же сообщение: 'Привет, мир! (сказал Player)' или 'Hello, world! (said Player)'. Важной особенностью многострочных значений ключей словаря является тот факт, что из строк автоматически удаляются все CR и LF символы, благодаря чему данный способ присваивать значение ключам словаря будет особенно удобен, например, для создания мультиязычных MOTD-окон. И, разумеется, сразу же пример. Файл словаря:
- Код: Выделить всё
[ru]
TD_TITLE = Сообщение дня
TD_LIST:
<li>Очень длинное предложение, настолько длинное, что не помещается в одну строку...</li>
<li>Вторая строка является продолжением первого предложения, но этого тоже мало...</li>
<li>Третья строка есть завершение первых двух предложений и этого, пожалуй, достаточно.</li>
:
[en]
TD_TITLE = Message of the Day
TD_LIST:
<li>Very long sentence, so long, that does not fit in one line...</li>
<li>The second line is a continuation of the first sentence, but it is also small...</li>
<li>The third line is the culmination of the first two sentences and this is a fairly.</li>
:
Код плагина:
- Код: Выделить всё
#include <amxmodx>
#pragma semicolon 1
public plugin_init() {
register_plugin("Test Dictionary", "0.1", "[amx-x.ru] #team");
register_dictionary("test_dictionary.txt");
register_clcmd("say /motd", "CmdMotd");
register_clcmd("say_team /motd", "CmdMotd");
}
public CmdMotd(const id) {
new szText[1024], szTitle[32]; // объявление новых массивов под содержимое MOTD-окна и его заголовок
formatex(szText, charsmax(szText), "<html><body><center><h3>%L</h3></center><ul>%L</ul></body></html>",
id, "TD_TITLE", id, "TD_LIST"); // форматирование содержимого MOTD-окна
formatex(szTitle, charsmax(szTitle), "%L", id, "TD_TITLE"); // форматирование заголовка MOTD-окна
show_motd(id, szText, szTitle); // отображение MOTD-окна игроку
return PLUGIN_HANDLED;
}
В результате выполнения команды /motd мы получим MOTD-окно с примерно следующим содержанием:
или:
Важные детали:
И в завершении статьи мы поговорим о некоторых нюансах, которые также следует знать, чтобы полноценно работать со словарями.
1. Сохранение файла в кодировке UTF-8 без BOM. Для начала определим, что такое BOM и почему при преобразовании файла словаря в другую кодировку следует сохранять его именно в формате без BOM. Итак, BOM (от англ. Byte Order Mark) — маркер последовательности байтов или метка порядка байтов — юникод-символ, используемый для индикации порядка байтов текстового файла. Если говорить проще, то BOM представляет из себя три нечитаемых символа, которые смещают ваши данные от начала файла. Как раз "благодаря" этому смещению, AMX Mod X не может корректно прочесть первую строку словаря и в силу этого начинает считать его недействительным (возможные ошибки типа Invalid multi-lingual line (file "...dictionary.txt" line n) как раз могут быть вызваны в связи с неверной кодировкой файла словаря). Стандартный блокнот Windows всегда сохраняет файлы в кодировке UTF-8 с BOM, следовательно, приводит файлы словарей в негодность. Сохранять файлы в кодировке UTF-8 без BOM следует при помощи "продвинутых" текстовых редакторов, таких как Вы должны зарегистрироваться, чтобы видеть ссылки. или Вы должны зарегистрироваться, чтобы видеть ссылки., или любых других редакторов, располагающих данным функционалом.
2. Как правильно менять язык сервера. Если речь идёт о мультиязычном сервере, то плагин multilingual.amxx должен быть включен, а значение квара amx_client_languages в amxx.cfg должно быть равно 1. Сам язык сервера вы сможете настроить через меню (amx_langmenu) или же отредактировав значение параметра server_language в файле vault.ini. Если же язык на сервере должен быть один (например, русский), то плагин multilingual.amxx должен быть отключен, значение квара amx_client_languages должно равняться 0, ну и в файле vault.ini для параметра server_language необходимо указать соответствующее значение.
3. Как при составлении файла словаря правильно называть ключи. Я не случайно в начале каждого ключа указал префикс TD_ (первые буквы от названия плагина, 'Test Dictionary'). Ключи с одинаковыми именами могут "пересекаться" между собой в силу того, что все словари располагаются в общем кэше, в результате чего мы можем наблюдать в сообщениях плагина совсем не тот текст, который указан в нашем файле словаря. Чтобы этого не случалось, нужно просто не допускать существования ключей с одинаковыми именами в файлах словаря. И ещё один небольшой, но не менее важный нюанс, о котором следует знать и помнить: имена ключей чувствительны к регистру символов, то есть, TD_HEALTH и TD_health — это два разных ключа и при подобных несовпадениях мы получим ошибку типа ML_NOTFOUND: TD_HEALTH, которая означает, что ключ TD_HEALTH не найден в файле словаря.
Благодарности:
Сообщение дня
- Очень длинное предложение, настолько длинное, что не помещается в одну строку...
- Вторая строка является продолжением первого предложения, но этого тоже мало...
- Третья строка есть завершение первых двух предложений и этого, пожалуй, достаточно.
или:
Message of the Day
- Very long sentence, so long, that does not fit in one line...
- The second line is a continuation of the first sentence, but it is also small...
- The third line is the culmination of the first two sentences and this is a fairly.
Важные детали:
И в завершении статьи мы поговорим о некоторых нюансах, которые также следует знать, чтобы полноценно работать со словарями.
1. Сохранение файла в кодировке UTF-8 без BOM. Для начала определим, что такое BOM и почему при преобразовании файла словаря в другую кодировку следует сохранять его именно в формате без BOM. Итак, BOM (от англ. Byte Order Mark) — маркер последовательности байтов или метка порядка байтов — юникод-символ, используемый для индикации порядка байтов текстового файла. Если говорить проще, то BOM представляет из себя три нечитаемых символа, которые смещают ваши данные от начала файла. Как раз "благодаря" этому смещению, AMX Mod X не может корректно прочесть первую строку словаря и в силу этого начинает считать его недействительным (возможные ошибки типа Invalid multi-lingual line (file "...dictionary.txt" line n) как раз могут быть вызваны в связи с неверной кодировкой файла словаря). Стандартный блокнот Windows всегда сохраняет файлы в кодировке UTF-8 с BOM, следовательно, приводит файлы словарей в негодность. Сохранять файлы в кодировке UTF-8 без BOM следует при помощи "продвинутых" текстовых редакторов, таких как Вы должны зарегистрироваться, чтобы видеть ссылки. или Вы должны зарегистрироваться, чтобы видеть ссылки., или любых других редакторов, располагающих данным функционалом.
2. Как правильно менять язык сервера. Если речь идёт о мультиязычном сервере, то плагин multilingual.amxx должен быть включен, а значение квара amx_client_languages в amxx.cfg должно быть равно 1. Сам язык сервера вы сможете настроить через меню (amx_langmenu) или же отредактировав значение параметра server_language в файле vault.ini. Если же язык на сервере должен быть один (например, русский), то плагин multilingual.amxx должен быть отключен, значение квара amx_client_languages должно равняться 0, ну и в файле vault.ini для параметра server_language необходимо указать соответствующее значение.
3. Как при составлении файла словаря правильно называть ключи. Я не случайно в начале каждого ключа указал префикс TD_ (первые буквы от названия плагина, 'Test Dictionary'). Ключи с одинаковыми именами могут "пересекаться" между собой в силу того, что все словари располагаются в общем кэше, в результате чего мы можем наблюдать в сообщениях плагина совсем не тот текст, который указан в нашем файле словаря. Чтобы этого не случалось, нужно просто не допускать существования ключей с одинаковыми именами в файлах словаря. И ещё один небольшой, но не менее важный нюанс, о котором следует знать и помнить: имена ключей чувствительны к регистру символов, то есть, TD_HEALTH и TD_health — это два разных ключа и при подобных несовпадениях мы получим ошибку типа ML_NOTFOUND: TD_HEALTH, которая означает, что ключ TD_HEALTH не найден в файле словаря.
Благодарности: