Использование серверного gettext в WordPress
Еще один способ экономии серверных ресурсов
Тема использования «родного» для PHP gettext не нова, и неоднократно уже поднималась: на форуме, в блогах. Но все представленные решения оказались неудовлетворительными, так как они не поддерживают больше одного домена локализации.
Тем не менее, у меня получилось побороть этот недостаток — ценой изменений кода WordPress.
Внимание: всё написанное проверялось на WordPress 2.8.5. Тем не менее, приведенное решение является экспериментальным. Вы можете его повторить на свой страх и риск.
Начнём с требований к серверу:
- Должно быть установлено расширение gettext;
- На сервере должна быть установлена нужная локаль. Те, у кого есть shell-доступ, могут проверить следующим образом: например, требуется локаль ru_RU. Чтобы проверить, установлена ли она, нужно выполнить такую команду:
[-]View Code Bashlocale -a | grep ru_RU
Если выдача пуста (что характерно для американских серверов), и у вас есть административные привилегии, то не всё потеряно: нужно выполнить следующие команды от имени пользователя
root:[-]View Code Bashecho "ru_RU.UTF-8 UTF-8" >> /etc/locale.gen
locale-gen
Если все требования выполняются, то приступим.
Для начала нужно создать несколько дополнительных каталогов. Основной языковой файл WordPress обычно располагается в каталоге wp-content/languages. В этом каталоге нужно создать подкаталог ru_RU (полагая, что нам нужен русский язык), в нём — LC_MESSAGES. В консоли это можно сделать одной командой (предполагая, что текущий каталог — wp-content/languages):
В каталог wp-content/languages/ru_RU/LC_MESSAGES нужно скопировать (или переместить, кому как больше нравится) файл ru_RU.mo из wp-content/languages и переименовать его в default.mo:
Вся эта чехарда связана с особенностями GNU gettext: функции gettext ожидают увидеть .mo-файлы в строго определённом месте.
Далее нужно внести изменения в три файла.
Начнём с wp-config.php.
В нём нужно найти строку
define ('WPLANG', 'ru_RU');
После неё добавить:
setlocale(LC_ALL, 'ru_RU.utf8');
Второй аргумент функции setlocale() — системное имя локали. Оно совпадает с выводом команды locale -a. У меня на сервере locale -a выдаёт
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/l10n.php.
В нём нужно изменить несколько функций (они идут не друг за другом):
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);
}
Затем нужно определиться с тем, что мы собираемся делать с плагинами и темами: где будут храниться переводы.
Есть два очевидных пути:
- Хранить все переводы в
wp-content/languages/xx_YY/LC_MESSAGES; - Создавать нужную иерархию каталогов в каталоге темы/плагина.
В любом случае языковые файлы нужно переименовывать вручную.
Имя языкового файла плагина состоит из двух частей: домен-локаль.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:
$locale = get_locale();
$path = ( empty( $path ) ) ? get_template_directory() : $path;
$mofile = "$path/$domain.mo";
return load_textdomain($domain, $mofile);
}
Если на первом, то изменить нужно две функции:
$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 будет такой:
$ct = $context . "\004" . $text;
$res = dgettext($domain, $ct);
$res = ($res == $ct) ? $text : $res;
return apply_filters( 'gettext_with_context', $res, $text, $context, $domain);
}
Вложения:
- l10n-2 (application/zip)
- l10n-1 (application/zip)
- native-gettext-2 (application/zip)
- native-gettext-1 (application/zip)
Окт
2009
Комментарии к статье «Использование серверного gettext в WordPress» (4) »
Пожалуйста, не используйте эту форму для комментирования! Данная форма предназначена исключительно для ботов.
Оставить комментарий к записи «Использование серверного gettext в WordPress»
गते गते पारगते पारसंगते बोधि स्वाहा
Меня зовут Владимир, я программист-фрилансер, специализирующийся на Web-программировании и програмировании под Linux.
По совместительству занимаюсь администрированием LAMP/LNMP-серверов и техническим переводом.


[...] the original post here: Использование серверного gettext в WordPress Related [...]
Всё сделал по Вашей статье.
Вроде побыстрее стало работать, замеров не производил но Вашему результату доверяю.
Спасибо за статьи и чудесный блог.
Одно уточнение: например, у меня,
gettextопределяет папку с локалью не по установленной локалиsetlocale(), а …*барабанная дробь*
по переменной окружения LANGUAGE, которую можно задать так:
putenv("LANGUAGE=ru_RU");Попробовал второй вариант – похоже, что случаются ошибки при изменении файлов перевода, видимо, их прежнее содержимое где-то на сервере может оставаться.