Использование серверного gettext в WordPress

Еще один способ экономии серверных ресурсов

Тема использования «родного» для PHP не нова, и неоднократно уже поднималась: на форуме, в блогах. Но все представленные решения оказались неудовлетворительными, так как они не поддерживают больше одного домена локализации.

Тем не менее, у меня получилось побороть этот недостаток — ценой изменений кода .

Внимание: всё написанное проверялось на WordPress 2.8.5. Тем не менее, приведенное решение является экспериментальным. Вы можете его повторить на свой страх и риск.

Начнём с требований к серверу:

  1. Должно быть установлено расширение gettext;
  2. На сервере должна быть установлена нужная локаль. Те, у кого есть shell-доступ, могут проверить следующим образом: например, требуется локаль ru_RU. Чтобы проверить, установлена ли она, нужно выполнить такую команду:
    [-]
    View Code Bash
    locale -a | grep ru_RU

    Если выдача пуста (что характерно для американских серверов), и у вас есть административные привилегии, то не всё потеряно: нужно выполнить следующие команды от имени пользователя root:

    [-]
    View Code Bash
    echo "ru_RU.UTF-8 UTF-8" >> /etc/locale.gen
    locale-gen

Если все требования выполняются, то приступим.

Для начала нужно создать несколько дополнительных каталогов. Основной языковой файл WordPress обычно располагается в каталоге wp-content/languages. В этом каталоге нужно создать подкаталог ru_RU (полагая, что нам нужен русский язык), в нём — LC_MESSAGES. В консоли это можно сделать одной командой (предполагая, что текущий каталог — wp-content/languages):

[-]
View Code Bash
mkdir -p ru_RU/LC_MESSAGES

В каталог wp-content/languages/ru_RU/LC_MESSAGES нужно скопировать (или переместить, кому как больше нравится) файл ru_RU.mo из wp-content/languages и переименовать его в default.mo:

[-]
View Code Bash
cp ru_RU.mo ru_RU/LC_MESSAGES/default.mo

Вся эта чехарда связана с особенностями GNU gettext: функции gettext ожидают увидеть .mo-файлы в строго определённом месте.

Далее нужно внести изменения в три файла.

Начнём с wp-config.php.

В нём нужно найти строку

define ('WPLANG', 'ru_RU');

После неё добавить:

setlocale(LC_ALL, 'ru_RU.utf8');

Второй аргумент функции setlocale() — системное имя локали. Оно совпадает с выводом команды locale -a. У меня на сервере locale -a выдаёт

[-]
View Code Text
C
en_US.utf8
POSIX
ru_RU.utf8

поэтому я использую ru_RU.utf8. В других системах это могут быть ru_RU, ru_RU.CP1251, ru_RU.KOI8-R, ru_RU.UTF-8, ru_RU.ISO-8859-5. Варианты для локали ru_UA: ru_UA, ru_UA.KOI8-U, ru_UA.UTF-8.

Следующий файл — wp-includes/.php.

В нём нужно изменить несколько функций (они идут не друг за другом):

[-]
View Code PHP
function translate( $text, $domain = 'default' ) {
       return apply_filters('gettext', dgettext($domain, $text), $text, $domain);
}

function translate_with_gettext_context( $text, $context, $domain = 'default' ) {
       return apply_filters( 'gettext_with_context', dgettext( $domain, $text ), $text, $context, $domain);
}

function _n($single, $plural, $number, $domain = 'default') {
       $translation = dngettext( $domain, $single, $plural, $number );
       return apply_filters( 'ngettext', $translation, $single, $plural, $number, $domain );
}

function _nx($single, $plural, $number, $context, $domain = 'default') {
       $translation = dngettext( $domain, $single, $plural, $number );
       return apply_filters( 'ngettext_with_context ', $translation, $single, $plural, $number, $context, $domain );
}

function load_textdomain($domain, $mofile) {
       bindtextdomain($domain, dirname($mofile));
       bind_textdomain_codeset($domain, 'UTF-8');
       return true;
}

function load_default_textdomain() {
       bindtextdomain('default', WP_LANG_DIR);
       bind_textdomain_codeset('default', 'UTF-8');
       textdomain('default');
       return true;
}

function &get_translations_for_domain( $domain ) {
       assert(false);
}

Затем нужно определиться с тем, что мы собираемся делать с плагинами и темами: где будут храниться переводы.
Есть два очевидных пути:

  1. Хранить все переводы в wp-content/languages/xx_YY/LC_MESSAGES;
  2. Создавать нужную иерархию каталогов в каталоге темы/плагина.

В любом случае языковые файлы нужно переименовывать вручную.
Имя языкового файла плагина состоит из двух частей: домен-локаль.mo. Домен обычно уникален для каждого плагина (он регистрируется при вызове функции load_plugin_textdomain()/load_theme_textdomain()). Так, в имени all_in_one_seo_pack-ru_RU.mo домен all_in_one_seo_pack, локаль ru_RU.

В первом случае файл должен быть перемещен в wp-content/languages/ru_RU/LC_MESSAGES/all_in_one_seo_pack.mo, во втором — в wp-content/plugins/all-in-one-seo-pack/ru_RU/LC_MESSAGES/all_in_one_seo_pack.mo. Да, такова плата за меньшее потребление памяти и более высокую производительность.

В случае с темой дела обстоят несколько иначе: имя файла не содержит домен (например, ru_RU.mo). Домен придется искать вручную, это будет аргумент функции load_theme_textdomain(), обычно она вызывается из файла functions.php темы. Например, у темы Mandigo домен (сюрприз!) mandigo.

В первом случае файл должен быть перемещен в wp-content/languages/ru_RU/LC_MESSAGES/mandigo.mo, во втором — в wp-content/themes/theme/mandigo/ru_RU/LC_MESSAGES/all_in_one_seo_pack.mo.

Если вы остановились на втором варианте, то нужно изменить еще одну функцию в файле wp-includes/l10n.php:

[-]
View Code PHP
function load_theme_textdomain($domain, $path = false) {
    $locale = get_locale();

    $path = ( empty( $path ) ) ? get_template_directory() : $path;

    $mofile = "$path/$domain.mo";
    return load_textdomain($domain, $mofile);
}

Если на первом, то изменить нужно две функции:

[-]
View Code PHP
function load_plugin_textdomain($domain, $abs_rel_path = false, $plugin_rel_path = false) {
    $mofile = WP_LANG_DIR . '/something.mo';
    return load_textdomain($domain, $mofile);
}

function load_theme_textdomain($domain, $path = false) {
    $mofile = WP_LANG_DIR . '/something.mo';
    return load_textdomain($domain, $mofile);
}

Третий файл, который нужно изменить — это wp-settings.php. В принципе, можно и не изменять.
Изменение простое: удалить или закомментировать эту строку:

include_once(ABSPATH . WPINC . '/pomo/mo.php');

Это минус 27 КиБ для парсера.

Что даёт это решение? На тестовом сайте потребление памяти снизилось с 53.81 до 45.2 МиБ (более 8.5 МиБ) и уменьшилось время генерации страницы (с 0.614 до 0.401 сек). Разница довольно ощутимая.

Для тех, кто любит патчи (unified diff, для WordPress 2.8.5):

Для тех, кто патчи не любит (пропатченные файлы, WordPress 2.8.5):

UPDATE 27/12/2009: правильная реализация функции translate_with_gettext_context будет такой:

[-]
View Code PHP
function translate_with_gettext_context( $text, $context, $domain = 'default' ) {
    $ct = $context . "\004" . $text;
    $res = dgettext($domain, $ct);
    $res = ($res == $ct) ? $text : $res;
    return apply_filters( 'gettext_with_context', $res, $text, $context, $domain);
}

Вложения:

Автор: ; опубликовано в: WordPress; метки: gettext, l10n, WordPress, патч
26
Окт
2009

RSS Комментарии к статье «Использование серверного gettext в WordPress» (4)  »

  1. [...] the original post here: Использование серверного gettext в WordPress Related [...]

  2. Davnozdu

    Всё сделал по Вашей статье.
    Вроде побыстрее стало работать, замеров не производил но Вашему результату доверяю.
    Спасибо за статьи и чудесный блог.

  3. Одно уточнение: например, у меня, gettext определяет папку с локалью не по установленной локали setlocale(), а …

    *барабанная дробь*

    по переменной окружения LANGUAGE, которую можно задать так:
    putenv("LANGUAGE=ru_RU");

  4. Попробовал второй вариант – похоже, что случаются ошибки при изменении файлов перевода, видимо, их прежнее содержимое где-то на сервере может оставаться.

Пожалуйста, не используйте эту форму для комментирования! Данная форма предназначена исключительно для ботов.

Оставить комментарий к записи «Использование серверного gettext в WordPress»

Ваш e-mail не будет опубликован. Обязательные поля помечены *

*

Можно использовать следующие HTML-теги и атрибуты: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Оставляя комментарий, вы выражаете своё согласие с Правилами комментирования.

Подписаться, не комментируя

गते गते पारगते पारसंगते बोधि स्वाहा