Локализация приложений Node.js. Часть 2: инструментарий и процесс

в 15:59, , рубрики: .pot, i18n-abide, javascript, mozilla persona, node.js, node.js holiday season, Блог компании Нордавинд, интернационализация, локализация

Локализация приложений Node.js. Часть 2: инструментарий и процессОт переводчика: Это деcятая статья из цикла о Node.js от команды Mozilla Identity, которая занимается проектом Persona.


В прошлой статье о локализации приложений Node.js мы узнали, как использовать модуль i18n-abide в нашем коде. Наша работа, как программистов, фактически закончилась на том, что мы обернули строки в шаблонах и коде приложения в вызовы gettext(). Но работа по локализации и переводу приложения только начинается.

Инструментарий

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

Проекту Mozilla уже почти 15 лет, и наша команда локализаторов и переводчиков одна из самых больших (и клёвых) в мире Open Source. Поэтому у нас широко используются давно привычные, можно даже сказать старинные и причудливые инструменты.

Gettext

GNU Gettext — это набор инструментов, предназначенный для локализации настольных и веб-приложений. Когда вы пишете код и шаблоны для Node, вы используете везде английские фразы, но оборачиваете каждую из них в вызов gettext().

gettext делает две вещи:

  • во время сборки составляет каталог всех строк, встреченных в приложении;
  • во время выполнения заменяет их на локализованные варианты.

Все извлечённые строки хранятся в текстовых файлах с расширением .po В дальнейшем мы будем называть их po-файлы.

Po-файлы

Po-файлы — это текстовые файлы определённого формата, которые gettext может читать, писать и объединять.

Вот пример содержимого po-файла zhTW/LCMESSAGES/messages.po:

#: resources/views/about.ejs:46
msgid "Persona preserves your privacy"
msgstr "Persona 保護您的隱私"

Подробнее мы его рассмотрим позже, а сейчас нам важно понимать, что msgid — это английская строка, а msgstr — её перевод на китайский. Всё, что начинается с # — комментарий. Комментарий в этом примере указывает на расположение этой строки в коде.

Gettext предоставляет множество других инструментов для работы со строками и po-файлами. Мы коснёмся и их.

Почему именно этот инструментарий?

Прежде чем мы погрузимся в более подробное изучение модулей Node.js для работы с gettext, мы должны спросить себя, почему мы выбрали именно этот набор инструментов?

Год назад я подробно исследовал существующие модули Node.js для интернационализации и локализации. Большинство из них изобретали собственные велосипеды и основанные на JSON форматы для хранения строк.

С другой стороны, в Mozilla давно и успешно используются такие инструменты, как POEdit, Verbatim, Translate Toolkit и Pootle. Вместо того, чтобы заставлять людей переучиваться, мы решили разработать для них инструменты, совместимые с привычными стандартами и процессами.

Po-файлы — общепринятый формат обмена и сотрудничества наших переводчиков. Именно в этом формате они должны получать от нас строки для перевода, и отдавать нам готовый текст.

Имея большой опыт разработки в Mozilla ещё на PHP и Python, я нахожу Gettext очень удобным. По мере того, как веб-приложение растёт и содержит всё больше текста, проявляется всё больше нюансов, которые требуют применения хорошо проверенных инструментов и API Gettext.

Создаём po-файлы для переводчиков

Итак, мы разметили наш код вызовами gettext. Что дальше? В дело вступает тот, кого мы называем «строководом». Это можете быть вы сами, переводчик или администратор. Что делает строковод?

  • Извлекает строки, впервые появившиеся в приложении.
  • Находит новые, изменившиеся строки или помечает удалённые в последующих релизах.
  • Готовит po-файлы для каждой команды переводчиков.
  • Разрешает конфликты и помечает изменённые или удалённые строки переводов.

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

msginit, xgettext, msgfmt и другие инструменты GNU Gettext — это мощный набор для работы с каталогами строк. С этими инструментами работает только строковод. Большинство разработчиков могут оставаться в блаженном неведении относительно них.

Создание дерева файлов для локали:

$ mkdir -p locale/templates/LC_MESSAGES

В этой директории хранятся шаблоны po-файлов — файлы .pot. Они будут использованы gettext в дальнейшем.

Извлечение строк

В прошлой статье мы установили i18n-abide:

$ npm install i18n-abide

Среди других инструментов командной строки, abide предоставляет extract-pot. Эта команда используется для извлечения строк в директорию локали:

mkdir -p locale/templates/LC_MESSAGES
$ ./node_modules/.bin/extract-pot --locale locale

Скрипт пройдёт по всему исходному коду приложения, находит строки и записывает их в файл шаблона po.

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

Jsxgettext ищет в коде вызовы gettext() и извлекает из них строковый аргумент, затем он форматирует строки в формат, совместимый с инструментарием gettext. Вот отрывок такого pot-файла:

#: resources/views/about.ejs:46
msgid "Persona preserves your privacy"
msgstr ""
 
#: resources/views/about.ejs:47
msgid ""
"Persona does not track your activity around the Web. It creates a wall "
"between signing you in and what you do once you're there. The history of "
"what sites you visit is stored only on your own computer."
msgstr ""
""
 
#: resources/views/about.ejs:51
msgid "Persona for developers"
msgstr ""

Позже на основе этого шаблона будут созданы po-файлы с переводом. Они будут выглядеть так:

#: resources/views/about.ejs:46
msgid "Persona preserves your privacy"
msgstr "Persona 保護您的隱私"
 
#: resources/views/about.ejs:47
msgid ""
"Persona does not track your activity around the Web. It creates a wall "
"between signing you in and what you do once you're there. The history of "
"what sites you visit is stored only on your own computer."
msgstr ""
"Persona 只是連結您登入過程的一座橋樑,不會追蹤您在網路上的行為。您的網頁瀏覽"
"紀錄只會留在您自己的電腦當中。"
 
#: resources/views/about.ejs:51
msgid "Persona for developers"
msgstr "Persona 的開發人員資訊"

Чтобы лучше почувствовать тему, вы можете взглянуть на полный вариант po-файла для китайского языка.

Создание локали

Команда msginit из набора Gettext используется для создания po-файла для конкретной локали, основанного на файле шаблона:

$ for l in en_US de es; do
    mkdir -p locale/${l}/LC_MESSAGES/
    msginit --input=./locale/templates/LC_MESSAGES/messages.pot 
            --output-file=./locale/${l}/LC_MESSAGES/messages.po 
            -l ${l}
  done

Мы только что создали po-файлы для американского английского, немецкого и испанского языков.

Po-файлы

Итак, мы извлекли строки и создали папки локалей. Вот так выглядит наше дерево файлов:

locale/
  el/
    LC_MESSAGES/
      messages.po
  en_US
    LC_MESSAGES/
      messages.po
  es
    LC_MESSAGES/
      messages.po
  templates
    LC_MESSAGES/
      messages.pot

К этим частям вашего приложения можно дать доступ переводчикам. К примеру, испанская команда будет иметь доступ к locale/es/LC_MESSAGES/messages.po. Если у вас очень большой проект, могут быть даже две отдельные локали для испанского и аргентинского вариантов испанского языка: es-ES и es-AR.

Со временем могут добавляться новые локали.

Слияние изменений строк

Релиз за релизом вы будете добавлять новые, менять и удалять старые строки. Вам будет необходимо обновлять все po-файлы в соответствии с этими изменениями. В gettext есть мощные инструменты для этого. Для себя мы сделали скрипт-обертку merge-po.sh, который использует команду msgmerge из пакета GNU Gettext.

Добавим инструменты i18n-abide в системные пути:

$ export PATH=$PATH:node_modules/i18n-abide/bin

и запустим процесс слияния строк:

$ ./node_modules/.bin/extract-pot --locale locale .
$ merge_po.sh ./locale

Как и в первый раз, extract-pot собирает все строки и создаёт шаблон. Затем merge-po.sh обновляет все po-файлы, приводя их в соответствие с текущей версией приложения. После этого команды переводчиков могут снова браться за работу.

Gettext против синдрома «изобретено не здесь»

Нет ничего сложного в том, чтобы изобрести свой велосипед на основе JSON вместо gettext. Большинство авторов модулей Node пошли именно этим путём. Но по мере роста приложения и добавления новых и новых языков мелкие неприятности будут нарастать как снежный ком. Например, без merge-po.sh вам рано или поздно придётся писать и отлаживать собственные инструменты для слияния. Вручную обновить 30 файлов для 30 локалей, ничего при этом не потеряв и не перепутав — та ещё морока.

А в gettext всё необходимое уже есть и это экономит нам кучу времени и нервов.

Заключение

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

Итак, строки переведены, и в следующей статье мы узнаем, как работает локализация во время выполнения приложения.


Продолжение следует...

Автор: ilya42

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js