
При разработке CMF я столкнулся с необходимостью грамотно реализовать i18n (мультиязычность), и стал рассматривать различные варианты…
Сначала, исходя из прошлого опыта, я хотел сделать «языковые константы» и мытарствах с хаками для числительных. Но потом к счастью остановил свой выбор на GNU gettext, на мощном и популярном (в Unix-среде) инструменте. Вскоре я понял что нет смысла излишне нагружать сервер переводами фраз, которые не индексируются поисковиками, и что в ряде случаев лучше переводить на клиенте. Однако, необходима была унифицированная система, позволяющая делать переводы в едином формате.
Прогуглив gettext javascript, я увидел несколько реализаций.
Первой попалась — code.google.com/p/gettext-js
Плюс в том что она не требует дополнительной переконвертации исходного po-файла, минус — нету ngettext.
Потом я нашел plugins.jquery.com/project/gettext
Её я и решил использовать. Однако, для работы плагина требуется подготовка специального JSON-файла из MO-файла.
Для конвертации приводится функция на Python:
import simplejson as enc
import gettext
def gettext_json(domain, path, lang = [], indent = False):
try:
tr = gettext.translation(domain, path, lang)
# for unknown reasons, instead of having plural entries like
# key: [sg, pl1...]
# tr._catalog has (key, n): pln,
keys = tr._catalog.keys()
keys.sort()
ret = {}
for k in keys:
v = tr._catalog[k]
if type(k) is tuple:
if k[0] not in ret:
ret[k[0]] = []
ret[k[0]].append(v)
else:
ret[k] = v
return enc.dumps(ret, ensure_ascii = False, indent = indent)
except IOError:
return None
Пришлось потратить… дцать минут на гуглеж и изучение доки, чтоб поправить код и заставить работать. В результате родил нормальную Unix-программу.
gettext2json
#!/usr/bin/python
import sys
import simplejson as enc
import gettext
def gettext_json(domain, path, lang = [], indent = False):
try:
tr = gettext.translation(domain, path, lang)
# for unknown reasons, instead of having plural entries like
# key: [sg, pl1...]
# tr._catalog has (key, n): pln,
keys = tr._catalog.keys()
keys.sort()
ret = {}
for k in keys:
v = tr._catalog[k]
if type(k) is tuple:
if k[0] not in ret:
ret[k[0]] = []
ret[k[0]].append(v)
else:
ret[k] = v
return enc.dumps(ret, ensure_ascii = True, indent = indent)
except IOError as (errno, strerror):
print "I/O error({0}): {1}".format(errno, strerror)
print gettext_json(sys.argv[1],sys.argv[2],[sys.argv[3]], True)
Также, решил автоматизировать процесс создания бинарных MO-файлов из текстовых PO-файлов:
BuildLocales:
#!/usr/bin/php -q
<?php
chdir(__DIR__);
$lcPath = './locale';
$jsPath = './static/locale';
foreach (glob($lcPath.'/*/LC_MESSAGES/*.po') as $poFile) {
$locale = pathinfo(dirname(dirname($poFile)), PATHINFO_FILENAME);
$domain = pathinfo($poFile, PATHINFO_FILENAME);
$moFile = dirname($poFile).'/'.$domain.'.mo';
$jsFile = $jsPath.'/'.$locale.'/'.$domain.'.json';
shell_exec('mkdir -p '.escapeshellarg($jsPath.'/'.$locale));
shell_exec('msgfmt -o '.escapeshellarg($moFile).' '.escapeshellarg($poFile));
$cmd = 'gettext2json '.escapeshellarg($domain).' '.escapeshellarg($lcPath ).' '.escapeshellarg($locale).' > '.escapeshellarg($jsFile);
shell_exec($cmd);
}
Таким образом, для подготовки всех файлов достаточно лишь создать/изменить текстовый .po файлы в папке locale и запустить скрипт BuildLocales.
Для подключения gettext к Javascript необходимо указать атрибут lang у тега html, добавить в head элемент link с путем до json-файла и подгрузить jquery.gettext.js.
Начало HTML-кода страницы будет выглядеть примерно так:
<!DOCTYPE html>
<html lang="ru">
...
<link href="/locale/ru/mydomain.json" lang="ru" rel="gettext"/>
<script type="text/javascript" src="/js/jquery.gettext.js" />
...
Затем можно вызывать функцию _(«Hello world!») и наслаждаться. Упс! Не работает!
Придется кое-что поправить в jquery.gettext.js, накладываем patch:
63,66c63,70
< try {
< var messages = eval('(' + data + ')');
< } catch(e) {
< return;
---
> if (typeof(data) == 'object') {
> var messages = data;
> } else {
> try {
> var messages = eval('(' + data + ')');
> } catch(e) {
> return;
> }
Те кому лень накладывать патч берут jquery.gettext.js.
Надеюсь жаркое вам понравилось, спасибо за внимание.