Сделать приложение многоязычным, с неограниченным количеством поддерживаемых локалей, без изменения кода логики программы не просто, а очень просто!
Рассмотрим локализацию приложения пр помощи GNU утилит gettext на конкретном примере. В качестве препарируемого приложения я выбрал roguelike игру Cataclysm (git-hub: https://github.com/Whales/Cataclysm, forum: http://whalesdev.com/forums/). Код написан на c++ и открыт. Текстовые сообщения в ней безобразно размазаны по всему коду, так что код отлично подойдет для примера :)
Для начала немного слов о механизмах, предоставляемых gettext. Для локализации приложения необходимо все текстовые сообщения, выводимые на экран, нужно "обернуть" специальной функцией gettext. Например:было:
printf ("The amount is %d\n", amount);стало:printf (gettext ("The amount is %d\n"), amount);И этого достаточно для неограниченного количества локализаций! необходимо, только инициализировать текущую локаль ОС. Но, как и обещал, будем все рассматривать на конкретном примере.
Описание прототипа функции gettext находится в файле libintl.h поэтому его необходимо подключить в исходниках. Для Cataclysm, наилучшее место его включения, это файл будет "game.h". Добавляем в его начало (разумеется, после #define _GAME_H_):
#include <libintl.h> #define _(String) gettext (String)Мы также определили макрос _(String), чтобы не писать много букв gettext(...), на каждое сообщение в игре, а просто _(...). Также нам понадобятся две константы, это название нашего приложения (на самом деле это будет имя файла с локализацией приложения) и директория размещения его локализаций.
#define PACKAGE "cataclysm" #define LOCALEDIR "locale"
в конкретном случае директория "locale" у нас находится в директории самого приложения. По умолчанию же в debian системах это "/usr/share/locale" где лежат локализации для различных языков всех приложений, поэтому, если мы будем размешать локализации в директории по умолчанию, то значение PACKAGE должно быть уникальным.
Далее инициализируем локаль и задаем значения для локализаций. Это необходимо сделать вначале функции main(), в cataclysm она описана в файле "main.cpp":
setlocale (LC_ALL, ""); bindtextdomain (PACKAGE, LOCALEDIR); textdomain (PACKAGE);Теперь самая нудная часть, необходимо просмотреть ВСЕ исходники и изменить все строки с сообщениями программы. Например в файле game.cpp в строке 110.было:
mvwprintz(w_open, 0, 1, c_blue, "Welcome to Cataclysm!");стало:
mvwprintz(w_open, 0, 1, c_blue, _("Welcome to Cataclysm!"));После того, как исходный код изменен, создаем директории для русской локали:
mkdir locale mkdir locale/ru mkdir locale/ru/LC_MESSAGES
создаем темплейтый файл для перевода:
xgettext --default-domain=cataclysm --output-dir=./locale --language=C++ --from-code=UTF8 --keyword=_ --directory=./ *.cpp *.h
В результате, в директории locale, у нас создастся файл cataclysm.po, содержащий все строки из исходного кода, которые мы обернули макросом _(String).
создаем po файл для русского перевода:
msginit -i locale/cataclysm.po -o locale/ru/LC_MESSAGES/cataclysm.po --locale=ru_RU.UTF8
Теперь нам нужно отредактировать полученный файл locale/de/LC_MESSAGES/cataclysm.po, а именно, добавить в него перевод, можно редактировать в любом текстовом редакторе, или же, воспользоваться программой poedit (http://www.poedit.net).
Пример части отредактированного po файла:
msgid "" msgstr "" "Project-Id-Version: cataclysm\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2012-03-13 15:54+0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Kanstantsin Ashuika <EMAIL@ADDRESS>\n" "Language-Team: Russian <LL@li.org>\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" #: game.cpp:110 game.cpp:204 msgid "Welcome to Cataclysm!" msgstr "Добро пожаловать в Катаклизм!" #: game.cpp:111 game.cpp:205 msgid "" "This alpha release is highly unstable. Please report any crashes or bugs to\n" "fivedozenwhales@gmail.com." msgstr "Эта альфа версия очень сырая. Пожалуйста, сообщайте о всех найденных ошибках на\n" "fivedozenwhales@gmail.com." #: game.cpp:130 msgid "Could not make './save' directory" msgstr "Невозможно создать директорию './save'" #: game.cpp:155 msgid "No message today." msgstr "Сегодня нет сообщений" #: game.cpp:167 game.cpp:219 msgid "MOTD" msgstr "Сообщение" #: game.cpp:168 game.cpp:220 msgid "New Game" msgstr "Новая Игра" #: game.cpp:169 game.cpp:221 msgid "Load Game" msgstr "Загрузить" #: game.cpp:170 game.cpp:222 msgid "Tutorial" msgstr "Обучение" #: game.cpp:171 game.cpp:223 msgid "Help" msgstr "Помощь" #: game.cpp:172 game.cpp:224 msgid "Quit" msgstr "Выход" #: game.cpp:229 game.cpp:268 msgid "Custom Character" msgstr "Создать персонажа" #: game.cpp:231 game.cpp:269 msgid "Preset Character" msgstr "Выбрать ранее созданного" #: game.cpp:233 game.cpp:270 msgid "Random Character" msgstr "Случайный персонаж" #: game.cpp:285 msgid "No save games found!" msgstr "Не найдено сохраненной игры"
После изменения po файла необходимо его скомпилировать в mo файл командой:
msgfmt locale/ru/LC_MESSAGES/cataclysm.po -o locale/ru/LC_MESSAGES/cataclysm.mo
Если вы будете редактировать файл с использованием программы poedit, то он автоматически его откомпилирует при сохранении.
теперь при запуске приложения из под локали ru_RU и наличии файла locale/ru/LC_MESSAGES/cataclysm.mo текстовые сообщения из исходников будут заменены на сообщения из mo файла. Точно также можно добавить файлы локализации для других языков. При наличии файла локализации под конкретную локаль будет вставляться соответствующий перевод.
Тут стоит упомянуть еще об одном моенте. разумеется, если выставлена по умолчанию английская локаль, то русского перевода мы не увидим. Для того чтобы из под английской локали увидеть наш перевод необходимо запустить приложение из локали ru. Например при помощи следующей команды:
LANGUAGE="ru_RU.utf8" ./cataclysmРезультат:
Что осталось за кадром
Очевидно, что при внесении изменений в исходные файлы программы, необходимо пересоздать файл темплейтов. Чтобы добавить новые строки из вновь созданного файла темплейтов в существующий вариант перевода, необходимо выполнить команду:
msgmerge -N -U locale/ru/LC_MESSAGES/cataclysm.po locale/cataclysm.po
В данном примере мы задавали константы PACKAGE и LOCALEDIR прямо в исходниках, возможно, более правильно было бы их задавать в Makefile. Для cataclysm это бы выглядело так:
Makefile... # добавили: DEFS = -DLOCALEDIR=\"locale\" -DPACKAGE=\"cataclysm\" ... # заменили строку $(CXX) $(CFLAGS) -c $< -o $@ $(CXX) $(CFLAGS) $(DEFS) -c $< -o $@ ...Также, для того чтобы ncurses нормально отображал многобайтные символы, необходимо при линковке использовать библиотеку ncursesw а не ncurses.


Хороший подход
ОтветитьУдалитьВеликолепно!
ОтветитьУдалитьНо что делать с падежами?
Например wolf corpse при таком подходе превратится в волка труп, plastic bootle of water в пластиковая бутылка с вода и т.д.
С этими проблемами я и столкнулся, когда пытался локализовать.
Можно в описание класса добавить падежи, потом исправить еще много чего и получить на выходе нормальный русский текст. Или смириться и ждать финального релиза.
На самом деле я тут описал самый простой способ с минимум усилий. Для качественного перевода необходимо все же править больше кода, а не просто обертывать фразы. Возможностей существует много. Но и работы больше, более подробно все описано в документации: http://www.gnu.org/software/gettext/manual/gettext.html
ОтветитьУдалитьЯ немного повозился. Результат уже начинает нравиться.
ОтветитьУдалитьТут https://github.com/Frolik/Cataclysm
А скомпилированные версии есть?
Удалитьскомпилировать под линукс - одна команда. make, под винду были билды, но не играбельно из за файловой системы винды и формата хранения карты мира в катаклизме.
УдалитьТо есть под винду никак? Тогда можете deb-пакет скинуть? Сам скомпилить не могу.
УдалитьЕсли вопросы по катаклизму, а тем более по его переводу, то лучьше сюда: http://rlgclub.ru/forum/viewtopic.php?f=6&t=724. По поводу создания деб пакета: а какой версии катаклизма деб нужен? те изменения что я вносил - почти годовой давности. версия Frolik-а еще старше, но зато с добавленным частичным переводом. По ссылке на пост форума (что выше), были еще куски переведены, но тоже давно...
УдалитьЗа статью спасибо, интересно.
ОтветитьУдалитьВместо 3-ёх команд mkdir можно было использовать mkdir -p locale/ru/LC_MESSAGES
да. верно. спасибо за замечание.
Удалить