Fork me on GitHub

The WebDevil

Enjoy development

Есть такая замечательная вещь как gettext. Те, кто делал хоть раз многоязычный сайт, чувствовали, как неприятно использовать языковые константы и/или дублировать шаблоны.

Так вот от всего этого может избавить gettext.

Итак, что же это за зверь такой.
(далее я буду пояснять материал с http://www.onlamp.com/pub/a/php/2002/06/13/php.html)

Представим себе английскую версию (не вникая в код – о нем речь пойдет дальше) вот такого простого скрипта.

<?php
// I18N support information here
$language = 'en';
putenv("LANG=$language");
setlocale(LC_ALL, $language);

// Set the text domain as 'messages'
$domain = 'messages';
bindtextdomain($domain, "./locale");
textdomain($domain);

echo gettext("A string to be translated would go here");
?>

Явно видно что используется некий текст, который в многоязычной версии должен переводиться.
Почему не константы? А потому, что такой текст намного приятнее читается. Можно даже заменить последнюю строку на вот такое:

echo _("A string to be translated would go here");

для краткости.

И насколько удобнее было бы если для смены языковой версии понадобится всего-лишь в $language = 'en'; установить 'ru'.

Итак, как же мы можем это все перевести.

В консоли введем

xgettext -n *.php

И получим на выходе файл-исходник для перевода – messages.po. Скачав утилиту poedit мы можем приступить к переводу – благо все ясно и понятно. Потом, когда перевод завершен, нужно выполнить

msgfmt messages.po

и мы получим скомпилированную версию, готовую к употреблению, которую ложим в locale//LC_MESSAGES/.

И теперь при $language = 'ru'; gettext попытается взять locale/ru/LC_MESSAGES/messages.mo. Если же не найдет его – текст останется оригинальный. Что весьма удобно.

Примечание: у меня использовалась локаль ‘ru_RU.utf8′ и посему файлики ложил в locale/ru_RU/LC_MESSAGES/messages.mo. Список локалей можно получить командой

locale -a

а добавить новую -

locale-gen <localename>

, где localename – одна из перечисленных в /usr/share/i18n/SUPPORTED.

Smarty

А теперь о том как это связать со Смарти.

Пост на форуме смарти показал приятный на вид и в реализации метод:

[[Text to be translated]]

и реализацию метода:

...

$smarty->register_prefilter('translate_template');

...

function translate_template($tpl_source, &$smarty) {
  return preg_replace_callback('/\[\[(?:([\w-_]+)\!)?(.*?)\]\]/',
    'translate_template_item', $tpl_source);
}

function translate_template_item($matches) {
  if ($matches[1] != '') {
    return dgettext($matches[1], $matches[2]);
  } else {
    return gettext($matches[2]);
  }
}

Что показалось мне довольно простым.

Однако, я не смог распарсить xgettext-ом шаблоны, и был вынужден прибегнуть к записи в откомпилированный шаблон вызова gettext-а, что я сделал через postfilter и измененную замену:

function translate_template_item($matches) {
  if($matches[2])
    $txt = str_replace('"', '\"', $matches[2]);
  if ($matches[1] != '') {
    return "<?php echo dgettext(\"$matches[1]\", \"$txt\");?>";
  } else {
    return "<?php echo _(\"$txt\");?>";
  }
}

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

Внимание, вопрос:
Как обойти такой некрасивый метод и заставить gettext съесть эти строки прямо в виде [[string to translate]]?

10 Responses to “Gettext and Smarty”

  1. А почему нельзя использовать дополнительно функцию перевода с тэгами {t}translate me{/t}? Дописать в плагины отдельно и всё.

    Артём Курапов

  2. Вообще субъективно приятнее [[text]] чем {t}text{/t}.

    Но вопрос в том, как xgettext натравить на любой из этих вариантов ;)

    dm

  3. А я другого не понимаю – почему не воспользоваться config_load, что уже встроено в смарти и не юзать ленг файлы разные для разных языков?

    anycolor

  4. А потому, что не хочется возвращаться к тому как было когда-то:

    <h1>{WELCOME}</h1>
    <h2>{PAGE_XXX_NAME}</h2>
    <input type=text value={LOGIN_INPUT} />

    dm

  5. dm: в случае с {t}text{/t} никакой xgettext не нужен. Это уже решается стандартными средствами Smarty. Код для блока {t} лежал на Smarty wiki еще год назад, а сейчас я его найти не смог, только ссылка осталась…
    Вот что удалось найти по кускам кода:
    https://oss.gonicus.de/labs/gosa/browser/trunk/...

    Предупреждаю, что оно пытается само переводить с помощью babelfish. Лично я это безобразие сразу же отключил :)

    y0prst

  6. О!!! Вот это самое оно. Только заменить бы как-то {t} на [[.

    dm

  7. А я так и не смог заставить под Убунту xgettext создавать po файлы =
    А вообще была мысль использовать {$text|gettext} модификатор.

    akira

  8. Вот на форуме phpclub.ru нашел такое:

    Зачем:
    1) Не нужно лезть в lang файл, чтобы добавить строку
    2) При отсутствии какого-либо перевода автоматом берется оригинал
    3) Очень удобные утилиты для работы с po файлами.
    4) Перекодировка на лету из кодировки po файла в нужную в данный момент времени

    by Profic

    dm

  9. A jesli dostupa k PHP net i Gettext ne vklu4en? Polu4ajetsa nichego rabotat’ ne budet? config_load – hot’ i nekrasivo, i staro, no nadezhno..

    Zoltan

  10. А для этого, тов. Zoltan, обзаводимся своими серверами на которых можно поднять хоть черта лысого. Шаред хостинг – решение для начинающих, арендовать сервер (да хоть и виртуальный) уже совершенно не проблематично.

    dm