Использование строк
Итак, сначала мы подключили к нашему приложению модуль i18n-abide и обернули строки в вызовы gettext. Затем наша команда переводчиков перевела эти строки и у нас есть готовый перевод для каждого языка.
Теперь давайте заставим наше приложение Node.js работать с локализованными строками. Переводы в виде po-файлов лежат в файловой системе в таком виде:
locale
en
LC_MESSAGES
messages.po
de
LC_MESSAGES
messages.po
es
LC_MESSAGES
messages.po
Во время выполнения нашего приложения нужно извлекать переведённые строки из этих файлов. Есть два способа сделать это:
- на стороне сервера с помощью всё той же функции gettext из модуля i18n-abide;
- на стороне клиента с помощью скрипта gettext.js, который также входит в состав i18n-abide.
Оба этих метода требуют, чтобы строки были в формате JSON. При переводе на стороне сервера они загружаются при старте приложения, а клиент загружает их с помощью HTTP-запросов (или можно включить их в минифицированный файл JavaScript в процессе сборки).
Так как наша система перевода совместима с GNU Gettext, есть ещё и третий путь — модуль node-gettext. Он довольно эффективен при переводе на стороне сервера.
Дальше мы будем рассматривать первый способ, так как это наиболее привычный способ использования i18n-abide.
compile-json
Как мы извлекаем строки из po-файлов и переводим их в формат JSON? Наш скрипт для этой цели называется compile-json.
Предположим, что наши файлы переводов лежат в папке locale в корне нашего проекта, а файлы JSON должны лежать в static/i18n:
$ mkdir -p static/i18n
$ ./node_modules/.bin/compile-json locale static/i18n
Мы получим такую файловую структуру:
static
i18n
en
messages.json
messages.js
de
messages.json
messages.js
es
messages.json
messages.js
compile-json проходит по всем po-файлам и вызывает для каждого из них скрипт po2json.js, который генерирует соответствтующие файлы .json и .js.
Возьмём к примеру испанский перевод:
# Spanish translations for PACKAGE package.
# Copyright (C) 2013 THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# Austin King <ozten@localhost>, 2013.
#
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2012-06-24 09:50+0200\n"
"PO-Revision-Date: 2013-04-24 16:42-0700\n"
"Last-Translator: Austin King <ozten@nutria.localdomain>\n"
"Language-Team: Spanish\n"
"Language: es\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: /home/ozten/abide-demo/views/homepage.ejs:3
msgid "Mozilla Persona"
msgstr "Mozilla Personidada"
Он примет такой вид:
"messages": {
"": {
"Project-Id-Version": " PACKAGE VERSION\nReport-Msgid-Bugs-To: \nPOT-Creation-Date: 2012-06-24 09:50+0200\nPO-Revision-Date: 2013-04-24 16:42-0700\nLast-Translator: Austin King <ozten@nutria.localdomain>\nLanguage-Team: German\nLanguage: de\nMIME-Version: 1.0\nContent-Type: text/plain; charset=UTF-8\nContent-Transfer-Encoding: 8bit\nPlural-Forms: nplurals=2; plural=(n != 1);\n"
},
"Mozilla Persona": [
null,
"Mozilla Personidada"
]
}
}
Мы можем использовать этот файл как на стороне сервера, в приложении Node.js, так и на стороне клиента, подтянув его ajax-запросом.
Директория static открыта для доступа из интернета, так что для получения испанского перевода нужно запросить файл /i18n/es/messages.json.
Директория static — это общепринятое соглашение фреймворка express, вы можете использовать любую другую. Вы можете отдавать файлы переводов как с помощью Node.js, так и любого веб-сервера, например Nginx.
Сами po-файлы можно, но совершенно необязательно выкладывать в продакшн.
Конфигурация
i18n-abide нуждается в конфигурации, чтобы знать, какие языки доступны, и где лежат файлы переводов. Пример такой конфигурации приводился в первой части:
app.use(i18n.abide({
supported_languages: ['en-US', 'de', 'es', 'zh-TW'],
default_lang: 'en-US',
translation_directory: 'static/i18n'
}));
- supported_languages здесь указывает приложению, что есть переводы на английский, немецкий, испанский и традиционный китайский.
- translation_directory говорит, что json-файлы с переводами лежат в директории static/i18n.
Обратите внимание, что параметр translation_directory нужен только для gettext на стороне сервера.
i18n-abide постарается выбрать из языков, перечисленных в supported_languages, наиболее подходящий.
Заводим моторы
Итак, когда конфигурация задана и готов перевод хотя бы для одной локали, можно начинать. В браузере меняем предпочитаемый язык на тот, который хотим проверить в работающем приложении:
Заходим на одну из страниц приложения. Он должна отображаться в переводе. Вот, например, греческая версия Persona:
gobbledygook
Для того, чтобы протестировать многоязычное приложение ещё до того, как готовы переводы, мы создали специальный модуль, навеянный клипом Дэвида Боуи "Лабиринт".
Чтобы ипсользовать его, достаточно просто добавить локаль, для которой перевод ещё не готов, например it-CH в параметры supported_languages и debug_lang:
app.use(i18n.abide({
supported_languages: ['en-US', 'de', 'es', 'zh-TW', 'it-CH'],
debug_lang: 'it-CH',
...
Теперь, если переключить браузер на использование швейцарского итальянского, i18n-abide будет использовать gobbledygook для этого языка.
Это удобный способ убедиться в том, что дизайн сайта нормально поддерживает в том числе и языки с написанием справа налево, например иврит:
Идём глубже
Мы лишь коснулись поверхности интернационализации и локализации. Разрабатывая многоязычное приложение на Node.js вы столкнётесь со множеством интересных нюансов и неожиданных ловушек. Вот некоторые из них.
Интерполяция строк
В i18n-abide есть функция format, которую можно использовать как на стороне клиента, так и на стороне сервера. Она принимает форматированную строку и подставляет значения параметров во время выполнения. Её можно использовать в двух вариантах:
- %s – format вызывается с форматированной строкой и массивом параметров, которые будут подставляться по порядку.
- %(имя)s – параметры передаются в виде объекта, названия полей которого должны совпадать с именами параметров в строке.
format можно использовать чтобы меньше засорять po-файлы разметкой HTML. Возьмём три примера:
<%= gettext('<p>Buy <a href="/buy?prod=blue&type=ticket">Blue Tickets</a> Now!</p>') %>
<p><%= format(gettext('Buy <a href="%s">Blue Tickets</a> Now!'), ['/buy?prod=blue&type=ticket']) %></p>
<p><%= format(gettext('Buy <a href="%(url)s">Blue Tickets</a> Now!'), {url: '/buy?prod=blue&type=ticket'}) %></p>
В po-файле строки для каждого из них будут выглядеть так:
msgid "<p>Buy <a href=\"/buy?prod=blue&type=ticket\">Blue Tickets</a> Now!</p>"
msgid "Buy <a href="%s">Blue Tickets</a> Now!"
msgid "Buy <a href="%(url)s">Blue Tickets</a> Now!"
В первом примере строка включает в себя тег
<p>
. Ужас! Если вы когда-нибудь измените разметку, вам придётся обновлять каждый файл перевода. Плюс этот уродливый URL.Использование format позволяет избежать случаев, когда переводчики, незнакомые с HTML, могут по незнанию испортить код, а так же упрощает поддержку приложения.
Именованные параметры хороши тем, что они самодокументированы. Переводчик будет видеть, что под значением имеется в виду именно URL. Интерполяция строк — общепринятый приём в локализации ПО.
Ещё один вариант применения интерполяции — включение в строки информации, доступной только во в время выполнения:
<p><%= format(gettext('Welcome back, %(user_name)s'), {user_name: user.name}) %></p>
Избегайте негибкого дизайна
Мы должны учитывать необходимость локализации на самых ранних этапах разработки, ещё когда создаётся предварительный макет сайта.
Избегайте картинок с надписями. Лучше использовать CSS, чтобы позиционировать слова поверх изображений.
Тщательно отлаживайте CSS. Немецкое слово может оказаться в несколько раз длиннее английского и порвать ваш дизайн в клочья. Вот, попробуйте этот букмарклет.
Замораживание строк
При каждом релизе нам придётся координировать свои действия с командой переводчиков. Придется мириться либо с тем, что необходимо будет дожидаться 100%-й готовности всех переводов перед публикацией очередных изменений, либо с тем, что на некоторые языки приложение будет переведено лишь частично.
Команде переводчиков может понадобиться несколько недель, если число строк для перевода велико. Это нужно учитывать в планировании разработки.
Для переводчиков стоит сделать внутреннюю версию сайта с живым предпросмотром изменений, чтобы они могли контролировать свою работу.
Продолжение следует...
This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at fivefilters.org/content-only/faq.php#publishers. Five Filters recommends:
- Massacres That Matter - Part 1 - 'Responsibility To Protect' In Egypt, Libya And Syria
- Massacres That Matter - Part 2 - The Media Response On Egypt, Libya And Syria
- National demonstration: No attack on Syria - Saturday 31 August, 12 noon, Temple Place, London, UK
Комментариев нет:
Отправить комментарий