Методики и приемы экстернализации строк в командных сценариях UNIX

Адаптируем командные сценарии вашего продукта для всемирной аудитории

В этой статье мы дадим практические советы и поделимся опытом экстернализации сообщений, выводимых в командных сценариях. Также рассматриваются важные моменты, которые необходимо учитывать подготовке к переводу на другие языки с точки зрения глобализации.
Статья предназначена для разработчиков программного обеспечения, которым необходимо подготовить командные сценарии к переводу на другие языки. Прочитав эту статью, вы узнаете, что необходимо учитывать при экстернализации сообщений в командных сценариях, получите полное представление об экстернализации и переводе строк, а также узнаете о некоторых известных проблемах и способах их решения.

Наоми Ву, разработчик программного обеспечения, IBM

Наоми Ву (Naomi YM Wu) возглавляет группу тестирования глобализации IBM на протяжении 5 лет. Она возглавляла проекты проверки глобализации (GVT) для различных линеек продуктов IBM, включая ECM, Rational и Industry Solution. Также работала над проектами Globalization Assessment и Globalization Enablement в качестве инженера по глобализации. Одним из главных направлений работы Наоми является реализация возможности перевода.



Энди Ву, разработчик программного обеспечения, IBM

Энди Ву (Andy MT Wu) работает разработчиком программного обеспечения и менеджером проектов в IBM на протяжении 10 лет. Принимал участие в работе над такими общекорпоративными проектами, как IBM WebSphere, IBM Tivoli, Business Analytics, Industry Solutions и Systems and Technology Group. Имеет опыт работы в области реализации возможности перевода, тестирования и разработки на основе сервис-ориентированной архитектуры (SOA).



Зак Ли, разработчик программного обеспечения, IBM

Зак Ли (Zach TL Lee) возглавляет группу тестирования глобализации центра Globalization Shared Services Center (GSSC), CDL, IBM. Отвечает за тесты GVT и тестирование пригодности к переводу (TVT) для продуктов IBM Tivoli. Также работал над проектами Globalization Assessment и Globalization Enablement в качестве инженера по глобализации. Основными направлениями работы Зака являются реализация возможности перевода и глобализации.



07.07.2014

1. Для чего нужен перевод в командных оболочках?

Командные сценарии в UNIX®-подобных операционных системах представляют собой последовательности команд, применяемые для автоматического выполнения задач. Командные сценарии являются важнейшими инструментами системных администраторов и разработчиков. Но, как правило, эти сценарии выводят сообщения только на английском языке, что может быть неудобно для неанглоязычных пользователей. Для таких пользователей, работающих с вашими приложениями по всему миру, реализация перевода в командных сценариях может оказаться чрезвычайно полезной. В таблице 1 даны определения некоторых часто используемых в этой статье терминов.

Таблица 1. Терминология
ТерминОпределение
Экстернализация строк (string externalization) / Извлечение строк (string extraction) Процесс выделения в исходном коде переводимых сообщений для их последующего перевода.
Локаль (locale) Параметр, определяющий язык, географическое положение и пользовательские предпочтения (язык сообщений, формат вывода даты, денежных единиц и т. д.).
Глобализация (G11N) Реализация единого программного продукта с поддержкой различных культурных традиций и доступного на одном или нескольких языках.
Программно-интегрированная информация (Program Integrated Information, PII) Выводимый пользователям текст, содержащийся в программном продукте и интегрированный в его код. Сюда входят пользовательский интерфейс и различные сообщения.

1.1 Подход с использованием экстернализации строк

Прежде чем переводчики смогут приступить к переводу сообщений, разработчикам необходимо реализовать такую возможность. Широко применяемой библиотекой для поиска перевода является библиотека GNU gettext. Эта библиотека содержит инфраструктуру с набором инструментов, позволяющих реализовать многоязыковую поддержку. Реализация включает различные языки программирования – Java, C/C++, командные сценарии, Perl и другие. В этой статье мы расскажем, как использовать gettext для экстернализации PII-строк, содержащихся в командных сценариях.

1.2 Что необходимо учитывать при экстернализации строк

Идентификатор сообщения: что использовать – английскую фразу или уникальный идентификатор?

Одно английское слово или фраза могут иметь несколько значений или являться различными частями речи. Также они могут иметь различные значения, которые по-разному переводятся на другие языки. Поскольку в PII-файле может использоваться только один идентификатор сообщения, то использование английской фразы в качестве идентификатора может привести к тому, что во всех контекстах будет использоваться один и тот же перевод. Если же вместо этого использовать уникальный идентификатор, то переводчик сможет использовать различный перевод для каждого случая.

Жестко закодированные сообщения

В прошлом разработчики нередко включали английские сообщения в исходный код. Разделение переводимых сообщений и исполняемого кода является фундаментальным правилом при подготовке к переводу. Соблюдение этого правила позволяет переводчикам добавлять новые языки или изменять существующие файлы перевода без изменения исходного кода.

Конкатенация

Переводимые сообщения должны представлять собой законченные предложения. Составление предложений из фрагментированных PII-сообщений приводит к тому, что при переводе эти фрагменты выстраиваются в том же порядке. В некоторых языках это может противоречить грамматическому строю. Внедрение переменных в предложения помогает переводчикам менять порядок слов.

Несколько абзацев

Избегайте включения нескольких абзацев в одно PII-сообщение. Рекомендуется ограничивать PII-сообщение одним абзацем. Причина заключается в том, что PII сообщение является наименьшей единицей перевода, и если одно сообщение содержит несколько абзацев, то при повторном переводе переводчикам может потребоваться перепроверять целое PII-сообщение при изменении лишь одного английского слова, а это занимает лишнее время.


2. Этапы подготовки командного сценария к переводу

В этом разделе будут описаны подробные шаги по подготовке командных сценариях к переводу. Весь процесс начинается с извлечения строк и заканчивается интеграцией перевода. Чтобы сделать сообщения пригодными для перевода, на этапе извлечения строк используются команды библиотеки GNU gettext и файлы .po (portable object – портируемый объект).

Как говорилось в разделе Что необходимо учитывать при экстернализации строк, существуют разные мнения относительно того, что следует использовать в качестве идентификатора PII-сообщения – английскую фразу или уникальный идентификатор. Оба подхода имеют свои преимущества и недостатки. Тем не менее для выполнения требований G11n относительно контекстно-зависимого перевода одних и тех же английских фраз мы будем придерживаться альтернативного подхода gettext и использовать в качестве идентификатора PII-сообщения уникальный идентификатор, а не английскую фразу. Это позволит использовать в различных контекстах свои уникальные определенные сообщения.

В оставшейся части статьи мы будем предполагать, что вы являетесь разработчиком, ответственным за реализацию возможности перевода в существующем приложении, которое на данный момент поддерживает только английский язык. В этом сценарии будет целиком описан весь процесс подготовки исходного кода, экстернализации строк и интеграции перевода.

Подготовка командного сценария к переводу состоит из пяти основных этапов.

  1. Подготовка командного сценария.
  2. Извлечение переводимых сообщений.
  3. Генерация файлов переводимых сообщений.
  4. Перевод файлов сообщений.
  5. Упаковка переведенных файлов сообщений в текущую сборку программы.

Более подробно эти этапы будут рассмотрены в следующих подразделах.

2.1 Подготовка командных сценариев

Прежде чем перейти к извлечению сообщений из командных сценариев, убедитесь в том, что в подлежащих переводу сообщениях в исходном коде отсутствуют нижеперечисленные форматы, упомянутые в руководстве GNU gettext (дополнительную информацию см. в разделе Ресурсы)

  1. Доступ к аргументам (пример: $0, $1, …)
  2. Часто изменяющиеся переменные среды (пример: $?)
  3. Командные подстановки (пример: "`...`" or "$(...)")
  4. Доступ к переменным со значениями по умолчанию (пример: ${значение-переменной-по-умолчанию})

Если переводимые сообщения в исходном коде содержат вышеперечисленные форматы, то необходимо изменить код таким образом, чтобы устранить это. Ниже приведен простой пример.

Замените

echo "Usage: $0 [OPTION] FILE..."

на

name=$0
echo "Usage $name [OPTION] FILE..."

После того как вы проверите исходный код, следует настроить командные сценарии на использование команд gettext путем добавления объявления в заголовок каждого файла сценария.

Сначала добавим в заголовок '.gettext.sh', что позволит использовать такие команды, как gettext и eval_gettext. Пример показан на рисунке 1.

Далее определим переменные TEXTDOMAIN и TEXTDOMAINDIR, чтобы указать расположение переводимого файла .mo (machine object – машинный объект). Файлы формата Machine Object – это двоичные файлы, генерируемые из файлов формата Portable Object. Обратите внимание на то, что переменные TEXTDOMAIN и TEXTDOMAINDIR используются для указания имени файла и пути к файлу соответственно (как показано на рисунке 2).

Рисунок 1. Объявление "gettext"
Рисунок 1. Объявление gettext
Рисунок 2. Указание местоположения файла перевода
Рисунок 2. Указание местоположения файла перевода

2.2 Извлечение переводимых сообщений

После подготовки файла командного сценария можно переходить к извлечению переводимых сообщений при помощи команд gettext и eval_gettext.

Если сообщения не содержат никаких переменных, то для замены английских фраз уникальными идентификаторами сообщений можно использовать команду gettext. Команда имеет следующий формат::

gettext "<msgid>"; echo

Переменная <msgid> представляет собой комбинацию имени sh-файла и краткого описания самого сообщения. Если вы будете придерживаться такого правила именования, то получите следующие преимущества:

  1. Информация об имени файла позволяет разработчикам быстро определять источник сообщения.
  2. При таком подходе поддерживается определенная степень читабельности, чего нет при использовании числовых идентификаторов. В листинге 1 приведен пример использования команды gettext.

Листинг 1. Пример использования gettext

<До>

echo "This program will use following command to
install the EGO RPM packages on the system."

<После>

gettext "Platform_EGO_install_msg"; echo

В процессе преобразования команд echo в команды gettext рекомендуется вести список сопоставлений идентификаторов сообщений и исходных английских фраз с целью отслеживания. Таким образом, вы всегда будете иметь возможность отыскать в этом списке дублирующиеся идентификаторы или же объединить сообщения с одинаковым содержанием (убедившись, что их смысл одинаков в различных контекстах на всех языках, на которые они переводятся) в один идентификатор. Вам также не придется искать какое-то определенное сообщение во всех файлах. Это может оказаться полезным в процессе подготовки английских ресурсов к переводу, описанном в разделе Генерация файлов переводимых сообщений.

В случаях, когда сообщения содержат одну или несколько переменных, необходимо использовать команду eval_gettext. Поскольку в этом случае используется подход с использованием уникальных идентификаторов, вы можете просто заменить исходную строку уникальным идентификатором, как показано в листинге 2.

Листинг 2. Пример использования eval_gettext

<До>

echo "The host is $BINARY_TYPE"

<После>

eval_gettext "Platform_binary_type_msg"; echo

Если же вы предпочитаете использовать подход на основе английских фраз (т. е. использовать в качестве идентификаторов английские фразы), то вам необходимо выделять символы $ escape-последовательностями, как показано в листинге 3.

Листинг 3. Пример использования eval_gettext (с использованием английской фразы в качестве идентификатора)

<До>

echo "The host is $BINARY_TYPE"

<После>

eval_gettext "The host is \$BINARY_TYPE"; echo

Как говорилось в разделе Подготовка командных сценариев, в некоторых случаях сообщения необходимо перестроить. Переменные, содержащие аргументы, необходимо извлечь с помощью команды eval_gettext, как показано в листинге 4.

Листинг 4. Пример реорганизации переменных, содержащих аргументы

<До>

echo "Port numbers ($param_name=$1) must be integers."

<После>

port=$1
eval_gettext "Install_cmn_port_int_error"; echo

После экстернализации всех переводимых сообщений можно приступать к генерации po-файлов с помощью команды xgettext.

xgettext <filename.sh>

Если вам требуется пакетная обработка нескольких командных сценариев, то создайте текстовый файл со списком этих сценариев и запустите следующую команду:

xgettext -f <filename-list.txt>

Эта команда довольно удобна, если у вас имеется множество сценариев и вы хотите объединить содержащиеся в них сообщения в один po-файл. Пример файла filename-list.txt показан в листинге 5.

Листинг 5. Пример файла со списком сценариев
./sym-wrapper-header.sh
./platform.sh
./instlib/install_common.sh
./instlib/post-lsf.sh
./instlib/post-ego.sh

После извлечения сообщений с помощью команды xgettext будет сгенерирован po-файл, содержащий уникальные идентификаторы сообщений (msgid) и пустые строки сообщений (msgstr). Пример такого файла (messages.po) показан в листинге 6.

Листинг 6. Файл messages.po, сгенерированный командой xgettext
#: instlib/post-lsf.sh:452
#, sh-format
msgid "Post_lsf_installed_msg"
msgstr ""

#: instlib/post-lsf.sh:458
msgid "Post_lsf_mgmt_host_msg"
msgstr ""

#: instlib/post-ego.sh:93
#, sh-format
msgid "Post_ego_remove_success_msg"
msgstr ""

#: instlib/post-ego.sh:96
#, sh-format
msgid "Post_ego_install_success_msg"
msgstr ""

На этом этапе процедура извлечения строк завершена. Далее файлы с полученными сообщениями необходимо преобразовать в файлы переводимых сообщений и выполнить их перевод.

2.3 Генерация файлов переводимых сообщений

Процедуры подготовки файлов переводимых сообщений различаются в случаях использования подхода на основе уникальных идентификаторов и подхода на основе английских фраз. Обратите внимание на то, что в случае использования подхода на основе английских фраз нет необходимости создавать файл с английскими фразами, поскольку английский перевод используется в самих идентификаторах. Этот подход подразумевает, что английский язык является языком по умолчанию. В то же время при использовании подхода на основе уникальных идентификаторов каждый язык считается целевым языком перевода. По этой причине необходимо создать файл с английскими фразами, что позволит программе выполнить конвертацию уникальных идентификаторов в соответствующие сообщения в английской локали. Теперь опишем подготовительные процедуры. Сначала необходимо открыть файл messages.po, сгенерированный программой xgettext и содержащий только идентификаторы сообщений (msgid). Далее необходимо заполнить пустые строки сообщений (msgstr) соответствующими английскими фразами и сохранить файл под новым именем. Для этого можно использовать список сопоставлений, созданный на этапе экстернализации. По завершении вы получите файл messages.po, показанный в листинге 7 (мы назвали этот файл messages_en.po, чтобы не спутать его с файлом message.po).

Листинг 7. Пример файла, содержащего английский перевод
#: instlib/post-lsf.sh:452
#, sh-format
msgid "Post_lsf_installed_msg"
msgstr ""
"Platform LSF $prdversion is installed at $topdir.\n"
"To make LSF take effect, you must set your environment on this host: \n"
"source ${topdir}/cshrc.platform \n"
"or \n"
". ${topdir}/profile.platform "

#: instlib/post-lsf.sh:458
msgid "Post_lsf_mgmt_host_msg"
msgstr ""
"This is a management host. To complete the installation on this host, you "
"must run: \n"
"egoconfig mghost lsf"

#: instlib/post-ego.sh:93
#, sh-format
msgid "Post_ego_remove_success_msg"
msgstr ""
"Platform EGO version $_ego_version is successfully removed from RPM database."

#: instlib/post-ego.sh:96
#, sh-format
msgid "Post_ego_install_success_msg"
msgstr "Platform EGO version $egoversion is successfully installed."

2.4 Перевод файлов сообщений

На этом этапе файл идентификаторов сообщений (messages.po) и файл английских фраз (messages_en.po) готовы к переводу. Сам процесс перевода выходит за рамки этой статьи и здесь не рассматривается.

После завершения процесса перевода вы получите набор файлов сообщений (messages.po), переведенных на все языки.

Также у вас имеется один файл с английскими сообщениями, сгенерированный вами самостоятельно (при использовании подхода на основе уникальных идентификаторов).

2.5 Упаковка файлов переведенных сообщений в текущую сборку программы

Теперь нам доступны все файлы переведенных сообщений. Последний этап – это интеграция переведенных файлов в программный продукт. Прежде всего необходимо организовать все переведенные po-файлы в определенную структуру. Вот как она выглядит:

$TEXTDOMAINDIR/<locale>/LC_MESSAGES/

Здесь параметр <locale> подчиняется следующему правилу именования: <"Код языка"_"Код страны">">. Полный список этих кодов вы найдете в разделе Ресурсы.

Переменная $TEXTDOMAINDIR – это то значение, которое было указано в начале каждого sh-файла. Например, если вы присвоили переменной $TEXTDOMAINDIR значение /opt/ego/nls, а файл содержит перевод для традиционного китайского языка, то следует разместить его в директории /opt/ego/nls/zh_TW/LC_MESSAGES/. Также проследите, чтобы все po-файлы использовали одинаковое имя $TEXTDOMAIN.po, что позволит командным сценариям корректно определять файлы перевода. На рисунке 3 приведен пример правильной структуры файлов сообщений.

Рисунок 3. Пример структуры файлов переведенных сообщений
Рисунок 3. Пример структуры файлов переведенных сообщений

Далее, чтобы программа смогла прочесть перевод, необходимо из каждого po-файла сгенерировать mo-файл с помощью следующей команды.

msgfmt messages.po

В результате выполнения команды msgfmt будет сгенерирован файл messages.mo. Файлы .mo должны располагаться в той же файловой структуре, что и файлы .po. Пример показан на рисунке 4.

Рисунок 4. Структура файлов переведенных сообщений после выполнения команды msgfmt
Рисунок 4. Структура файлов переведенных сообщений после выполнения команды msgfmt

Вы можете либо преобразовывать po-файлы в mo-файлы вручную с помощью команды msgfmt, либо интегрировать этот этап в процесс сборки. Обычно рекомендуется использовать последний способ. Автоматизация выполнения команды msgfmt не только устраняет необходимость ручного запуска, но и гарантирует, что сборка всегда будет содержать самую последнюю версию перевода. Пример сценария сборки приведен в листинге 8.

Листинг 8. Пример сценария для интеграции этапа msgfmt в процесс сборки

#Parse .po files under nls folder and compile to .mo files.
find ../nls -name '*.po' | while read -r file; do
PODIR=`dirname $file`; POFILE=`basename $file`;
PONAME=`echo $POFILE | cut -d'.' -f1`; 
msgfmt -o $PODIR/$PONAME.mo $file; done

Теперь ваши командные сценарии успешно переведены. Язык, на котором будут выводиться сообщения в сценариях, определяется переменной среды LANG.


3. Распространенные проблемы и способы их решения


В этом разделе мы рассмотрим некоторые распространенные проблемы, возникающие при подготовке командных сценариев к переводу, и способы их решения.

3.1 Проблема перевода строки

Иногда при генерации mo-файлов с помощью команды msgfmt возникают следующие сообщения об ошибках:

Листинг 9. Пример сообщений об ошибках команды msgfmt

./en_US/LC_MESSAGES/messages.po:160: 'msgid' and 'msgstr' entries do not both begin with '\n'
./en_US/LC_MESSAGES/messages.po:172: 'msgid' and 'msgstr' entries do not both end with '\n'

Эти ошибки могут возникать по следующим причинам:

  • При работе с английскими фразами:
    Если в качестве идентификаторов сообщений (msgid) используются английские фразы и в начале или конце сообщения присутствует символ перевода строки, то при переводе сообщения на другой язык переводчик может случайно или намеренно удалить этот символ. В этой ситуации возникает ошибка, показанная в листинге 9, поскольку символ перевода строки присутствует в начале или конце идентификатора сообщения, но отсутствует в строке сообщения.
  • При использовании уникальных идентификаторов:
    Если в качестве идентификатора сообщения используется уникальный идентификатор (msgid), и в начале или конце переводимой строки присутствует символ перевода строки (msgstr), то вы также получите ошибку.

Таким образом, рекомендуется избегать использования символов перевода строки в начале или конце сообщений – как в полях msgid, так и в полях msgstr. При необходимости использовать символ новой строки просто удалите символ перевода строки из po-файлов, а взамен вставьте в командный сценарий команду echo.

Рисунок 5. Пример удаления символа “\n” из po-файла
Рисунок 5. Пример удаления символа n из po-файла

Кликните, чтобы увидеть увеличенное изображение

Рисунок 5. Пример удаления символа “\n” из po-файла

Рисунок 5. Пример удаления символа n из po-файла

3.2 Использование резервной локали

Не каждое приложение содержит перевод на все языки. Если в качестве идентификатора msgid используется английская фраза, то по умолчанию вместо отсутствующего перевода выводится английское сообщение. Однако в случае отсутствия перевода для текущей локали при использовании уникального идентификатора сообщения этого не происходит. В этом случае вместо английского сообщения выводится уникальный идентификатор сообщения.

Рисунок 6. Вывод идентификатора сообщения при отсутствии перевода
Рисунок 6. Вывод идентификатора сообщения при отсутствии перевода

Для решения этой проблемы необходимо предусмотреть механизм перехода на локаль по умолчанию, что позволит избежать вывода уникальных идентификаторов сообщений при отсутствии перевода для текущей локали. Это делается при помощи переменной среды под названием LANGUAGE.. После добавления кода из листинга 10 в начало каждого командного сценария при отсутствии перевода для текущей локали будет использоваться английская локаль.


Листинг 10. Пример настройки локали по умолчанию

# Setting locale fallback mechanism if no translation found
LANGUAGE=$LC_ALL:$LANG:en_US    export LANGUAGE

Сначала GNU gettext найдет значение переменной $LC_ALL и попытается найти директорию локали, точно соответствующей этому значению. Если совпадения не будет найдено, будет использована переменная $LANG. Если же соответствующая директория также не будет найдена, то переменной LANGUAGE будет присвоено значение en_US. Такой подход гарантирует, что пользователи увидят как минимум английское сообщение.

3.3 Проблема подстановки переменной в методе eval_gettext

Если строки задаются уникальным идентификатором, то исходный метод GNU eval_gettext не сможет выполнить подстановку переменной. Для решения этой проблемы необходимо переписать метод eval_gettext и поместить его в каждый командный сценарий, подлежащий интернационализации. В листинге 11 представлен исходный и конечный код.

Листинг 11. Пример подстановки переменной при использовании уникального идентификатора

<До>

eval_gettext () {
gettext "$1" | (export PATH `envsubst --variables "$1"`; envsubst "$1")
}

<После>

eval_gettext () {
_tmp=`gettext "$1"`; 
gettext "$1" | (export PATH `envsubst --variables "$_tmp"`; envsubst "$_tmp");
unset _tmp }

3.4 Особый случай – компонент установщика

Как правило, установщик представляет собой сжатый исполняемый файл (т. е. файл с расширением .bin), содержащий все необходимые директории и файлы. Первым шагом установщика является распаковка. Однако доступ к PII-коду невозможно получить до тех пор, пока распаковка не будет завершена, поскольку все файлы, содержащиеся в установщике (в том числе и PII-файлы) сжаты. Таким образом, пользователям будут недоступны сообщения о статусе распаковки на их родном языке. Мы предлагаем два варианта решения этой проблемы:

  • Создать дополнительную директорию, содержащую файлы перевода для стадии распаковки.
  • Попросить пользователей выполнить распаковку вручную с помощью команд UNIX или Linux. В этом случае за вывод сообщений будет отвечать операционная система.

Ресурсы

Комментарии

developerWorks: Войти

Обязательные поля отмечены звездочкой (*).


Нужен IBM ID?
Забыли Ваш IBM ID?


Забыли Ваш пароль?
Изменить пароль

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Профиль создается, когда вы первый раз заходите в developerWorks. Информация в вашем профиле (имя, страна / регион, название компании) отображается для всех пользователей и будет сопровождать любой опубликованный вами контент пока вы специально не укажите скрыть название вашей компании. Вы можете обновить ваш IBM аккаунт в любое время.

Вся введенная информация защищена.

Выберите имя, которое будет отображаться на экране



При первом входе в developerWorks для Вас будет создан профиль и Вам нужно будет выбрать Отображаемое имя. Оно будет выводиться рядом с контентом, опубликованным Вами в developerWorks.

Отображаемое имя должно иметь длину от 3 символов до 31 символа. Ваше Имя в системе должно быть уникальным. В качестве имени по соображениям приватности нельзя использовать контактный e-mail.

Обязательные поля отмечены звездочкой (*).

(Отображаемое имя должно иметь длину от 3 символов до 31 символа.)

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Вся введенная информация защищена.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=AIX и UNIX
ArticleID=976703
ArticleTitle=Методики и приемы экстернализации строк в командных сценариях UNIX
publish-date=07072014