Библиотека сайта rus-linux.net
Программирование с использованием gtkmm 3. Интернационализация и локализация
Оригинал: Programming with gtkmm 3Авторы: Murray Cumming, Bernhard Rieder, Jonathon Jongsma, Ole Laursen, Marko Anastasov, Daniel Elstner, Chris Vine, David King, Pedro Ferreira, Kjell Ahlstedt
Дата публикации: 15 Октября 2013 г.
Перевод: А.Панин
Дата перевода: 10 Апреля 2014 г.
27. Интернационализация и локализация
Приложения на основе gtkmm могут без лишних сложностей поддерживать множество языков, включая такие неевропейские языки, как китайский, а также такие языки с письмом справа налево, как арабский. Корректно разработанное и переведенное приложение на основе gtkmm будет выбирать подходящий язык в процессе работы на основе настроек пользовательского окружения.
Процесс разработки пригодного для перевода исходного кода называется интернационализацией (internationalization
), а для его обозначения используется аббревиатура i18n
. Процесс локализации (localization
), для обозначения которого иногда используется аббревиатура l10n
, заключается в переводе на другие языки текста, сформированного благодаря обработке данного исходного кода.
Основным действием в рамках процесса интернационализации является поиск видимых для пользователей строк и подготовка их к переводу. Вам не придется заниматься переводом всех доступных строк - в том случае, если вы корректно подготовите необходимую инфраструктуру для проекта, ваше приложение будет работать вне зависимости от того, как много строк вы перевели.
Для записи строк в исходном коде должен использоваться английский язык, причем все строки должны быть помещены в макрос. После этого утилита gettext (или intltool) сможет извлечь отмеченные строки для перевода, а также появится возможность подстановки переведенного текста в процессе работы приложения.
27.1. Подготовка вашего проекта
В инструкциях ниже предполагается, что вы не будете использовать утилиту gettext напрямую, а вместо нее будете использовать утилиту intltool, которая была разработана специально для проекта GNOME. Утилита intltool использует утилиту gettext, которая, в свою очередь, извлекает строки из файла исходного кода, но при этом intltool также может объединять строки из различных файлов, например, из файлов меню рабочего стола и файлов описания графического интерфейса пользователя, таких, как файлы приложения Glade и помещать их в файлы стандартного формата gettext с расширениями .pot/.po. Мы также предполагаем, что вы используете утилиты из набора autotools (т.е., утилиты automake и autoconf) для сборки вашего проекта, причем вы используете сценарий http://git.gnome.org/browse/gnome-common/tree/autogen.sh, который, в том числе, заботится об инициализации утилиты intltool. |
Следует создать поддиректорию с именем po
в корневой директории вашего проекта. Эта директория в конечном счете будет содержать все ваши переводы. В ней следует создать файл с именем LINGUAS
и файл с именем POTFILES.in
. Обычной практикой является также создание файла с именем Changelog
в директории po
для того, чтобы переводчики имели возможность отслеживать изменения в переводах.
LINGUAS
содержит отсортированный по алфавиту список кодов, идентифицирующих языки, на которые переведена ваша программа (строки комментариев, начинающиеся с символа #
, игнорируются). Каждый код языка, находящийся в файле LINGUAS
, должен соответствовать файлу с расширением .po
. Таким образом, в том случае, если ваша программа переведена на немецкий и японский языки, ваш файл LINGUAS
должен выглядеть примерно следующим образом:
# данный список файлов должен быть отсортирован по алфавиту и содержать по одному коду в строке de ja
(В дополнение в вашей директории po
должны находится файлы ja.po
и de.po
, которые должны содержать переводы на немецкий и японский языки соответственно.)
POTFILES.in
является списком путей ко всем файлам, которые содержат отмеченные для перевода строки, начиная с корневой директории проекта. Таким образом, к примеру, в том случае, если исходные коды вашего проекта были размещены в поддиректории с именем src
и у вас есть два файла, которые содержат строки для перевода, ваш файл POTFILES.in
должен выглядеть примерно следующим образом:
src/main.cc src/other.cc
В том случае, если вы используете утилиту gettext напрямую, вы можете извлечь строки для перевода только в том случае, если они расположены в файле исходного кода. Однако, в том случае, если вы используете утилиту intltool, вы можете извлечь строки для перевода из файлов множества других форматов, включая файлы описания пользовательского интерфейса Glade, файлы формата xml, файлы меню с расширением .desktop и файлы некоторых других форматов. Следовательно, в том случае, если вы спроектировали часть пользовательского интерфейса приложения с помощью приложения Glade, вы также можете добавить ваши файлы с расширением .glade
в список, расположенный в файле POTFILES.in
.
configure.ac
, заменив 'programname' на имя вашей программы:
IT_PROG_INTLTOOL([0.35.0]) GETTEXT_PACKAGE=programname AC_SUBST(GETTEXT_PACKAGE) AC_DEFINE_UNQUOTED([GETTEXT_PACKAGE], ["$GETTEXT_PACKAGE"], [Домен для использования совместно с gettext]) AM_GLIB_GNU_GETTEXT PROGRAMNAME_LOCALEDIR=[${datadir}/locale] AC_SUBST(PROGRAMNAME_LOCALEDIR)
В данном случае переменная PROGRAMNAME_LOCALEDIR
будет использоваться позднее в файле Makefile.am
для описания макроса, который, в свою очередь, будет использоваться при инициализации библиотеки gettext в вашем исходном коде.
Makefile.am
следует:
- Добавить
po
в качестве параметра переменнойSUBDIRS
. Без этого ваши переводы не будут скомпилированы и установлены в процессе сборки вашей программы. - Объявить макрос
INTLTOOL_FILES
как:INTLTOOL_FILES = intltool-extract.in \ intltool-merge.in \ intltool-update.in
- Добавить макрос
INTLTOOL_FILES
в список файловEXTRA_DIST
. Это гарантирует то, что в момент выполнения командыmake dist
, описанные команды будут включены в архив исходного кода. - Обновить заначение макроса
DISTCLEANFILES
:DISTCLEANFILES = ... intltool-extract \ intltool-merge \ intltool-update \ po/.intltool-merge-cache
src/Makefile
следует обновить описание макроса AM_CPPFLAGS
, добавив следующее описание макроса препроцессора:
AM_CPPFLAGS = ... -DPROGRAMNAME_LOCALEDIR=\"${PROGRAMNAME_LOCALEDIR}\"
Этот макрос будет использован в момент инициализации библиотеки gettext
в вашем исходном коде.
27.2. Отметка строк для перевода
Для размещенных в исходном коде строк должен использоваться английский язык, при этом эти строки должны быть аргументами при вызове функции gettext()
. После этого данные строки будут извлекаться для перевода, причем переведенные строки могут использоваться в процессе работы приложения вместо оригинальных строк на английском языке.
Пакет GNU gettext
позволяет вам отмечать строки в исходном коде, извлекать эти строки для перевода и использовать переведенные строки в вашем приложении.
gettext()
, являющиеся укороченными обертками, которые проще использовать. Для использования этих макросов следует подключить заголовочный файл <glibmm/i18n.h>
, после чего, к примеру, заменить вызов:
display_message("Getting ready for i18n.");
display_message(_("Getting ready for i18n."));
my-strings
выполните следующую команду в директории исходного кода:
xgettext -a -o my-strings --omit-header *.cc *.h
main.cc
для инициализации библиотеки gettext:
bindtextdomain(GETTEXT_PACKAGE, PROGRAMNAME_LOCALEDIR); bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8"); textdomain(GETTEXT_PACKAGE);
27.2.1. Как работает пакет gettext
Сценарий intltool/xgettext извлекает строки и помещает их в файл <имя программы>.pot
. Переводчики вашего приложения создают свои переводы, копируя этот файл с расширением .pot
и переименовывая его в <имя локализации>.po
. Локализация устанавливает язык и кодировку для данного языка, включая форматы даты и чисел. Позднее, когда текст в вашем файле исходного кода изменяется, сценарий msmerge
может использоваться для обновления файлов <имя локализации>.po
на основе повторно сгенерированного файла с расширением .pot
.
В процессе установки файлы с расширением .po
конвертируются в файлы бинарного формата (с расширением .mo
), которые размещаются в системной директории для файлов локализации, например, в директории /usr/share/locale/
.
При запуске приложения библиотека gettext проверяет системную директорию в поисках файла с расширением .mo
, соответствующего локализации пользовательского окружения (вы можете изменить текущую локализацию, к примеру, выполнив команду "export LANG=de_DE.UTF-8"
в консоли bash). Позднее, в момент, когда в программе будет осуществлен вызов функции gettext()
, будет выполнен поиск перевода определенной строки. В том случае, если строка не найдена, будет использована оригинальная строка.
27.2.2. Тестирование и добавление переводов
intltool-update --pot
С помощью данной команды будет создан файл с именем <имя программы>.pot
. Теперь следует скопировать этот файл и переименовать его в <код языка>.po
, например в de.po
или hu.po
. Также следует добавить данный код языка в файл LINGUAS
. Файл с расширением .po
содержит заголовок и список строк на английском языке с полями для переводов этих строк, которые должны быть заполнены. Убедитесь в том, что вы установили кодировку UTF-8 для файла с расширением .po
(она задается в заголовке, причем все строки также должны использовать эту кодировку).
Возможны ситуации, в которых определенные строки в файле с расширением .po будут иметь пометку "fuzzy" . Эти переведенные строки не будут подменять оригинальные строки. Для того, чтобы они использовались, просто уберите пометки "fuzzy" . |
27.2.3. Ресурсы
27.3. Использование кодировки UTF8
Корректно интернационализированное приложение не должно делать предположений о количестве байт, использующихся для кодирования одного символа строки. Это значит, что вы не должны использовать арифметику с указателями для обхода символов в строке и это также значит, что вы не должны использовать класс std::string
и такие стандартные функции языка C, как strlen()
, так как они делают такие же предположения.
Однако, вы, скорее всего, уже избегаете использования обычных символьных массивов char*
и арифметики с указателями, используя класс std::string
, поэтому вам придется всего лишь перейти к использованию класса Glib::ustring
вместо него. Обратитесь к главе "Базовые сведения" для получения информации о классе Glib::ustring
.
27.3.1. Классы Glib::ustring и std::iostreams
Glib::ustring
в специфичную для локализации кодировку (которая обычно не является кодировкой UTF-8) в том случае, если вы передаете их в поток вывода с помощью оператора <<
. Аналогично, при извлечении строк типа Glib::ustring
из потока ввода с помощью оператора >>
происходит преобразование в противоположном направлении. Но эта схема дает сбой в том случае, если вы используете класс std::string
в качестве промежуточного звена, т.е., извлекаете текст из потока в строку типа std::string
и затем непосредственно преобразовываете строку к типу Glib::ustring
. В том случае, если строка содержала не относящиеся к таблице ASCII символы, а также текущая локализация не использовала кодировку UTF-8, в результате получится поврежденная строка типа Glib::ustring
. Вы можете обойти этот недостаток, осуществив преобразование в ручном режиме. Например, для извлечения строки типа std::string
из строки типа std::ostringstream
может использоваться следующий код:
std::ostringstream output; output.imbue(std::locale("")); // использование пользовательской локализации для этого потока output << percentage << " % выполнено"; label->set_text(Glib::locale_to_utf8(output.str()));
27.4. Часто встречающиеся ошибки
Существует несколько часто встречающихся ошибок, с которыми в конечном счете столкнетесь и вы. Этот раздел может помочь вам избежать их.
27.4.1. Одни и те же строки с разными семантиками
Иногда две строки на английском языке идентичны, но имеют различные значения в различных контекстах, поэтому они, скорее всего, не должны быть идентичными при переводе. Так как строки на английском языке используются в качестве ключей для поиска переведенных строк, возникают проблемы.
"jumps[noun]"
(для существительного "переходы") и "jumps[verb]"
(для глагола "переходит") вместо "jumps"
для отделения переводов вне вызова функции библиотеки gettext
. В том случае, если вы добавите дополнительные символы, вы также должны добавить комментарий для переводчиков перед вызовом функции библиотеки gettext
. Такие комментарии будут размещаться в файлах переводов с расширением .po
. Например, комментарий может быть следующим:
// примечание для переводчиков: не переводите часть строки "[noun]" - она используется // исключительно для отделения текущей строки от другой строки "jumps" text = strip(gettext("jumps[noun]"), "[noun]");
27.4.2. Объединение строк
Разработчики, использующие язык программирования C, применяют функцию sprintf()
для форматирования и объединения строк. Язык программирования C++ предоставляет в распоряжение разработчика потоки, но, к сожалению, их использование затрудняет процесс перевода, так как каждый фрагмент текста переводится отдельно и у переводчиков нет возможности изменения порядка следования фрагментов переведенной строки в зависимости от грамматики языка.
std::cout << _("Current amount: ") << amount << _(" Future: ") << future << std::endl; label.set_text(_("Really delete ") + filename + _(" now?"));
std::cout << Glib::ustring::compose( _("Current amount: %1 Future: %2"), amount, future) << std::endl; label.set_text(Glib::ustring::compose(_("Really delete %1 now?"), filename));
27.4.3. Предварительная оценка длины используемых строк
Вы никогда не сможете узнать заранее о том, как много места на экране займет строка после перевода. Вполне вероятно, что переведенная строка будет в два раза длиннее оригинальной строки на английском языке. К счастью, у большинства виджетов из состава gtkmm есть возможность расширения до необходимого размера в процессе работы приложения.
27.4.4. Необычные слова
Вы должны избегать использования неизвестных аббревиатур, слэнга и жаргона. Строки с подобными словами обычно сложно переводить, причем эти слова обычно плохо понятны даже тем людям, для которых английский является родным языком. Например, для обозначения приложения предпочтительнее использовать слово "application", а не "app".
27.4.5. Использование не-ASCII символов в строках
На данный момент библиотека gettext не поддерживает символы из исходного кода, не относящиеся к таблице ASCII (т.е., символы с кодами больше 127). Например, вы не сможете использовать символ, предназначенный для обозначения авторских прав (©).
Для обхода этого ограничения вы должны написать комментарий в исходном коде прямо над строкой, сообщающий переводчикам о том, что следует использовать определенный специальный символ в том случае, если он доступен в их языках. В случае английского языка вам следует создать перевод на американский английский язык, в котором будет использоваться данный специальный символ.
27.5. Содействие в создании переводов
В том случае, если ваша программа является свободным программным обеспечением, у вас есть возможность участия в отдельном подпроекте проекта GNOME под названием GNOME Translation Project, предназначенном для содействия в создании переводов.
Этот проект работает следующим образом: вы загружаете ваш исходный код в репозиторий git, в котором переводчики будут иметь доступ к нему, после чего регистрируетесь в списке рассылки gnome-i18n и обращаетесь с просьбой о добавлении вашей программы в список модулей для перевода.
После этого вы должны убедиться в том, что обновили файл POTFILES.in
в поддиректории po/
(команда intltool-update -M
может помочь в этом) для того, чтобы переводчики имели доступ к всегда обновленным файлам <имя программы>.pot
и просто "замораживать" строки как минимум за несколько дней перед выпуском нового релиза, объявляя об этом в списке рассылки gnome-i18n. В зависимости от количества строк в исходном коде вашей программы и ее популярности, впоследствии силами переводчиков начнут создаваться переводы в виде файлов <имя языка>.po
.
Учтите, что большинство команд переводчиков приложений на определенный язык состоит из 1-3 человек, поэтому в том случае, если ваша программа содержит большое количество строк для перевода, может пройти некоторое время перед тем, как кто-либо найдет время для работы над ее переводом. Также большинство переводчиков не не желает терять свое время (перевод занимает очень много времени), поэтому в том случае, если они не считают ваш проект действительно серьезным (в смысле проработки и поддержки), они могут решить потратить свое время на работу над каким-либо другим проектом.
Следующий раздел : Собственные виджеты.