Отправляет email-рассылки с помощью сервиса Sendsay

Практические советы по GTK+

  Все выпуски  

Практические советы по GTK+ (Локализация приложения)


Локализация приложения

Если Вы собираетесь писать мультиязычное приложение на GTK+, то самым простым способом будет воспользоваться
встроенными средствами перевода.
Все строки, требуемые перевода, меняем со "string" на gettext("string") или проще на _("string").
Например:
gtk_label_new(_("Latitude"));

Все переводы строк будут находится в отдельном файле. Теперь остаётся только создать этот файл.
Вам не придётся вручную искать строки для перевода в исходных файлах. Существует множество полезных утилит, облегчающих эту работу.
Вот порядок работы:
1) Создать файл с именами исходных файлов в которых есть строки, требуемые перевода:
$>ls *.c  > list.txt

2) Сделать .pot файл, в котором будут все непереведённые строки, причём они там появятся автоматически,
Вам не надо самостоятельно вытаскивать из исходных кодов строки для перевода:
$>xgettext --from-code=UTF-8 -o myapp.pot -kN_ -k_ -c -C -f list.txt

3) Переименовываем maypp.pot в maypp.po. Кажется глупостью создавать сначала .pot файла, а потом его переменовывать в .ро, не проще ли сразу сделать .po файл. На самом деле операция переименования делается только один раз, в .po файле хранится рабочий вариант перевода, а в .pot файле непереведёные строки для обновления .po файла без затирания  готовых переводов в нём.

В файле myapp.po переводим все строки на требуемый язык.
Например:
msgid "Latitude"
msgstr "Широта"

Конечно эту операцию можно провернуть в текстовом редакторе, но более простым вариантом будет использовать программу poEdit: http://sourceforge.net/project/showfiles.php?group_id=27043 с помощью которой можно редактировать переводы. Также poEdit позволяет автоматически выполнять нижепреведённые операции.

4) Сформировать бинарный файл с переводом: (обратно msgunfmt)
$>msgfmt -vo myapp.mo myapp.po


5) Скопировать полученый myapp.mo в каталог соответствующего языка:
$>cp ./myapp.mo /usr/share/locale/ru/LC_MESSAGES
В данном случае перевод копируется в каталог для русского языка.
По этой ссылке http://www.loc.gov/standards/iso639-2/php/code_list.php Вы можете узнать остальные двух-символьные коды языков.
В Windows переводы обычно хранятся в папке %GTKDIR%/share/locale или %GTKDIR%/lib/locale.

6) После обновления приложения не нужно заново переводить все строки.
После модификации исходников достаточно сформировать новый POT файл (смотри п.2) и с помощью его обновить PO файл:
$>msgmerge -U myapp.po myapp.pot

Файл с переводом создали, осталось сказать приложению, какой файл использовать в качестве перевода и откуда его брать:
Для этого нужно вызвать функцию:
bindtextdomain( PACKAGE,PACKAGE_LOCALE_DIR );
где   PACKAGE - имя файла с переводом "myapp"
        PACKAGE_LOCALE_DIR - каталог, где лежат переводы.
Потом говорим приложению, в какой кодировке наш перевод:
bind_textdomain_codeset (PACKAGE, "UTF-8");
И последее, устанавливаем текущий файл, из которого будут браться строки для переводов:
textdomain(PACKAGE);

В процессе работы приложения можно сменить файл переводов, к примеру Вам понадобилось узнать с какого дня недели начинается неделя, для этого меняем файл перевода на стандартный перевод GTK+:
#define G_(String) gettext (String)
textdomain("gtk20");
str_week_start = g_strdup(G_("calendar:week_start:0"));
textdomain(PACKAGE);// восстанавливаем файл с переводами

Вы наверное заметили здесь небольшую хитрость, для перевода мы используем функцию G_() которая аналогична _() или gettext(). Это сделано для того, чтобы утилита xgettext не добавила строку "calendar:week_start:0" в список для перевода, в нашем файле эта строка не нужна, она уже переведена в файле gtk20.mo

Может быть противоположный случай, когда строка не должна быть переведена, а в файле переводов появится должна.
Для этого используется макроопределение для N_()
#define N_(String) gettext_noop (String)
или
#define N_(String) (String)
То есть оставлять строку непереведённой. Этот трюк тоже для утилиты xgettext, запускаемой с ключём -kN_ -k_, которая просматривает исходники и берёт строки для перевода из функций N_() и _().

При запуске приложение само отпределит требуемый язык перевода, и если файла с переводом не будет, то строки останутся непереведёнными, это касается и того случая, если в файле перевода не найдётся нужной строки.
Если Вы хотите поучавствовать в процессе выбора локали, то можно выставить переменную окружения LANG в нужный язык, причём это касается и Windows, где по умолчанию такой переменной нет.
Например, LANG=en. Но будте осторожны, очень многие приложения используют такой способ выбора языка интерфейса, и они тоже поменяют свой язык.
Если Вы хотите выбрать язык отличный от умолчания, Вам следует выполнить gtk_disable_setlocale() перед gtk_init(...),
чтобы язык не был выбран по умолчанию и самому выполнить setlocale(LC_ALL,"xx"), где xx - требуемая локаль.
Правда в Windows так не прокатит, так что придётся дополнительно сделать что-то подобное:
b = MAKELANGID(LANG_GERMAN,SUBLANG_GERMAN);
a = MAKELCID(b,SORT_DEFAULT);
c = SetThreadLocale(a);


Напоследок рассмотим готовое приложение с поддержкой локализации:

#include <gtk/gtk.h>
#include <libintl.h>
#define _(String) gettext (String)
#define N_(String) gettext_noop (String)

// название файла с переводом для нашего приложения
#define PACKAGE "myapp"

// каталог, где лежат файлы с переводами
#ifdef WIN32 // для Windows подразумевается, что приложение лежит в папке bin, а рядом в папке share/locale находятся переводы
#define PACKAGE_LOCALE_DIR "../share/locale"
#else
#define PACKAGE_LOCALE_DIR "/usr/share/locale"
#endif

int main(int argc, char** argv)
{
    gchar *str=NULL;
    // настройка папок с переводами;
    str = bindtextdomain( PACKAGE,PACKAGE_LOCALE_DIR );//установить каталог, содержащий переводы
    // кодировка перевода;
    str = bind_textdomain_codeset (PACKAGE, "UTF-8");// устанавливаем файл myapp.mo текущим, из которого будут браться строки для переводов
    str = textdomain(PACKAGE);
    // продолжение работы
    ...
    // здесь программа определится, какой именно язык нужно использовать
    gtk_init(&argc,&argv) ;// Иннициализация GTK;
    ...
    gtk_main();
    return 0;
}


В избранное