Интернационализация тем и плагинов для WordPress

Дата публикации:Март 30, 2013

Интернационализация (на сленге разработчиков: «i18n») – это процесс создания приложения, готового к трансляции (переводу на другой язык). Странный вид аббревиатуры «i18n» объясняется очень просто: между «i» и «n» в слове internationalization стоит 18 букв. В случае с WordPress под интернационализацией подразумевается специальная маркировка строк, поддающихся переводу на тот или иной язык.

Введение в Gettext

Система WordPress использует для интернационализации библиотеки gettext и некоторые дополнительные инструменты. Если вы перейдете на сайт, то вы увидите функцию _(), которая является родной для PHP функцией трансляции, совместимой с gettext. Вместо нее вы должны использовать определенную в WordPress функцию __(), состоящую из двух знаков подчеркивания.

Строки, поддающиеся переводу

Чтобы сделать строку поддающейся переводу в вашем приложении, вы должны просто поместить исходную строку в функцию __():

$hello =  __( 'Hello, dear user!', 'my-text-domain' );

Если вам необходимо вывести переведенную строку в браузере, используйте вместо __() функцию _e():

_e( 'Your Ad here', 'my-text-domain' )

POT-файлы

После того, как строки будут помечены для перевода в исходных файлах, можно будет воспользоваться специальным средством xgettext, которое извлечет оригинальные строки и сформирует шаблонный файл перевода POT. Ниже представлен пример POT-файла:

#: wp-admin/admin-header.php:49
msgid "Sign Out"
msgstr ""

PO-файлы

Переводчик получает POT-файл и транслирует msgstr секции на определенный язык. В итоге получается PO-файл, который имеет тот же самый формат, что и POT, однако содержит в себе переводы строк и определенные заголовки.

MO-файлы

Из переведенного PO-файла создается MO-файл – двоичный файл, содержащий в себе оригинальные строки и их переводы. Все это хранится в формате, подходящем для быстрого извлечения. Преобразование выполняется через инструмент msgfmt.

Типичная команда msgfmt выглядит следующим образом:

msgfmt -o filename.mo filename.po

Если у вас есть много PO-файлов, готовых к конвертации, вы можете использовать их групповое преобразование. В качестве примера приведем конвертацию с помощью команды bash:

# Find PO files, process each with msgfmt and rename the result to MO
for file in `find . -name "*.po"` ; do msgfmt -o `echo $file | sed s/\.po/\.mo/` $file ; done

Текстовые домены

Если вы переводите плагин или тему, вам понадобится использовать текстовый домен для обозначения всего текста, принадлежащего плагину/теме. Это позволяет увеличить портативность и помогает установить связь с уже существующими инструментами WordPress.

Приложение может использовать неограниченное количество доступных для перевода модулей и различных MO-файлов соответственно. Домен – это дескриптор каждого модуля, имеющего отдельный MO-файл.

Пометка строк для перевода

Строки для перевода могут быть обернуты в одну функцию или в набор функций. Чаще всего используется функция __(). Она просто возвращает перевод своего аргумента:

echo '<h2>' . __( 'Blog Options' ) . '</h2>';

Еще одна простая функция — _e(). Она выводит перевод своего аргумента. Вместо длинной записи:

echo __( 'Using this option you will make a fortune!' );

вы можете использовать короткий вариант:

_e( 'Using this option you will make a fortune!' );

Заполнители

echo 'We deleted $count spam messages.'

Как бы вы провели интернационализацию данной строки? Давайте попробуем сделать это вместе:

_e( 'We deleted $count spam messages.' );

Не работает! Учтите, что строки для перевода извлекаются из разных источников, потому транслятор будет работать с фразой: «We deleted $count spam messages». Однако в приложении функция _e будет вызываться с аргументом, поэтому мы увидим строку: «We deleted 49494 spam messages». Естественно, gettext не найдет подходящего перевода для данной строки и просто выведет ее аргумент: «We deleted 49494 spam messages». К сожалению, данный перевод не является корректным.

Решение заключается в использовании семейства функций printf. Особенно полезны для нас функции printf и sprintf. Вот как будет выглядеть правильное решение проблемы с подсчетом спама:

printf( __( 'We deleted %d spam messages.' ), $count );

Заметьте, что в данном случае строка для перевода – это просто шаблон «We deleted %d spam messages», который в процессе выполнения остается таким же, как и в источнике.

Если в строке стоит несколько заполнителей, рекомендуется использовать свопинг аргументов. В таком случае одинарные кавычки (‘) являются обязательными: двойные кавычки (») заставят PHP интерпретировать $s как переменную s, что не является нужным для нас действием.

printf( __( 'Your city is %1$s, and your zip code is %2$s.' ), $city, $zipcode );

В представленном примере zip-код будет выводиться после названия города. В некоторых языках отображение zip-кода и города должно производиться в обратном порядке (сначала zip-код, потом город). Использование префикса %s допускает такой случай. Перевод может быть записан следующим образом:

printf( __( 'Your zip code is %2$s, and your city is %1$s.' ), $city, $zipcode );

Запись множественных чисел

Давайте вернемся к примеру со спамом. Что будет, если мы удалим только одно спам-сообщение? Сообщение будет таким: «We deleted 1 spam messages». Если переводить на русский язык, то это сообщение будет выглядеть следующим образом: «Мы удалили 1 спам-сообщений». Написано некорректно.

В WP вы можете использовать функцию _n().

printf( _n( 'We deleted %d spam message.', 'We deleted %d spam messages.', $count ), $count );

Функция _n() принимает 4 аргумента:

  • Singular – форма единственного числа строки
  • Plural – форма множественного числа строки
  • Count – число, которое будет определять, какая форма строки должна быть возвращена (есть языки, имеющие более двух форм).
  • Текстовый домен – четвертый необязательный аргумент.

Функция возвращает корректную переведенную форму строки, зависящую от введенного числа.

Многозначность

Иногда один и тот же термин в английском языке может нести в себе разный смысл в разном контексте употребления, а значит, и по-разному переводиться на другие языки. К примеру, слово Post может использоваться и как глагол (Click here to post your comment), и как существительное (Edit this post). В таких случаях необходимо обращаться к функции _x(). Она во многом напоминает __(), однако имеет дополнительный аргумент – контекст:

if ( false === $commenttxt ) $commenttxt = _x( 'Comment', 'noun' );
if ( false === $trackbacktxt ) $trackbacktxt = __( 'Trackback' );
if ( false === $pingbacktxt ) $pingbacktxt = __( 'Pingback' );
...
// some other place in the code
echo _x( 'Comment', 'column name' );

С помощью данного метода в обоих случаях мы получим строку Comment для оригинальной версии, однако переводчики будут видеть две строки Comment, доступных для трансляции, каждая из которых будет стоять в своем собственном контексте.

Текстовый домен – третий необязательный параметр.

echo _x( 'Comment', 'column name', 'my-plugin-domain' );

Обратите внимание, что функция _x() также имеет свою «echo»-версию: _ex(). Предыдущий пример можно записать следующим образом:

_ex( 'Comment', 'column name', 'my-plugin-domain' );

Используйте любой вариант, который вам удобен.

Описания

Вы думаете, переводчик знает, как транслировать данную строку?

__( 'g:i:s a' )

В таком случае вы можете добавить пояснения в исходный код. Начинаются комментарии со слова «translators:». Это должен быть последний PHP-комментарий перед вызовом gettext. Ниже представлен пример:

/* translators: draft saved date format, see http://php.net/date */
$draft_saved_date_format = __( 'g:i:s a' );

С помощью комментария, помеченного как «translators:», вы можете написать личное сообщение переводчикам, дабы разъяснить им, как переводить строку.

Символы новой строки

Gettext не любит видеть символы «\r» (ASCII-код: 13) в переводимых строках, поэтому используйте вместо них \n.

Пустые строки

Пустая строка зарезервирована для внутреннего использования Gettext, поэтому не стоит пытаться ее интернационализировать. К тому же, это нецелесообразно еще и потому, что переводчики не будут знать контекста ее использования.

Если вы столкнулись со случаем, когда вам требуется интернационализировать пустую строку, укажите контекст, который поможет всем: и системе Gettext, и переводчикам.

Обработка Javascript-файлов

Используйте wp_localize_script() для добавления переведенных строк или других серверных данных к поставленному в очередь скрипту:

wp_enqueue_script( 'script-handle', … );
wp_localize_script( 'script-handle', 'objectL10n', array(
	'speed'  => $distance / $time,
	'submit' => __( 'Submit', 'my-plugin-domain' ),
) );

Затем в Javascript-файле, соответствующему дескриптору скрипта, вы можете использовать переменную «objectL10n.»:

$('#submit').val(objectL10n.submit);
$('#speed').val('{speed} km/h'.replace('{speed}', objectL10n.speed));

Советы при интернационализации

Устоявшаяся практика интернационализации позволяет выделить следующие правила:

  • Точный английский стиль — минимум сленга и сокращений
  • Полные предложения – в большинстве языков порядок слов отличается от использования в английском.
  • Разбиение на абзацы – объединяйте связанные предложения, правда, без перебора; не стоит вставлять целую страницу сплошного текста, набранного в одной строке.
  • Используйте форматирование строк вместо их конкатенации: запись sprintf(__(‘Replace %1$s with %2$s’), $a, $b); лучше, чем __(‘Replace ‘).$a.__(‘ with ‘).$b;
  • Избегайте использования ненужной разметки и управляющих символов – не включайте теги, окружающие текст, и не оставляйте URL для трансляции.
  • Не оставляйте в переводимой фразе начальные или конечные пробелы.

Перевод плагинов

Если вы хотите перевести плагин, придерживайтесь представленных выше советов. Также к ним можно добавить следующие рекомендации:

  • Вы должны использовать домен, который загружается в хуке вашего плагина
  • Каждый вызов перевода должен иметь следующий вид: __(‘text’, ‘domain-name’)
  • Ваше доменное имя, которое чаще всего напоминает название плагина или совпадает с ним, не должно содержать в себе знаков подчеркивания.

Выбор и загрузка домена

Текстовый домен – это уникальный идентификатор, который необходим для того, чтобы система WordPress могла различать загруженные переводы. В качестве текстового домена лучше всего использовать название плагина.

Пример: если ваш плагин представляет собой отдельный файл под названием shareadraft.php, или плагин содержит в себе папку shareadraft, то в таком случае подходящим доменным именем будет «shareadraft». В случае с темами доменное имя выбирается по названию директории.

Текстовый домен может также использоваться для формирования названия MO-файла с переводами ваших плагинов. Вы можете загрузить их, вызывая функцию load_plugin_textdomain в действии plugins_loaded.

load_plugin_textdomain( $domain, $path_from_abspath, $path_from_plugins_folder )

Пример:

function myplugin_init() {
 $plugin_dir = basename(dirname(__FILE__));
 load_plugin_textdomain( 'my-plugin', false, $plugin_dir );
}
add_action('plugins_loaded', 'myplugin_init');

В данном примере мы пытаемся загрузить my-plugin-{locale}.mo из базовой директории с плагинами. Здесь locale – это код языка или код страны, который вы определили в константе WPLANG в файле wp-config.php. Скажем, locale для немецкого языка — ‘de’, для датского — ‘da_DK’. Датские MO- и PO-файлы будут иметь следующий вид: my-plugin-da_DK.mo и my-plugin-da_DK.po.

Для WordPress 2.6 и выше третий параметр – это каталог, содержащий .mo-файл (относительно директории плагинов). Он должен заканчиваться наклонной чертой. Если ваш плагин не требует совместимости со старыми версиями WP, вы можете оставить второй параметр пустым.

Для более ранних версий WordPress второй параметр должен содержать в себе каталог, содержащий .mo-файл относительно ABSPATH. Третий параметр должен быть пустым.

Для тем процесс очень похож:

load_theme_textdomain('my_theme');

Поместив вызов данной функции в файл functions.php. Эта функция будет искать файл locale.mo в вашей папке с темой и загружать его (где locale – ваш текущий язык, к примеру, pt_BR.mo).

Предостережения:

  • Всегда называйте ваш MO-файл как Locale.mo (к примеру, da_DK.mo)
  • Не называйте ваш MO-файл как my_theme-da_DK.mo

Интернационализация виджетов для WordPress 2.8+

WordPress 2.8+ использует новый интерфейс Widget API, который позволяет расширить стандартный класс виджетов и некоторые его функции. В результате API не нужно использовать функцию init. После кодировки виджета с помощью функций widget(), form() и update(), его нужно зарегистрировать. После регистрации виджета загружается текстовый домен.

Пример:

// register Foo_Widget widget
function Foo_Widget_init() {
    return register_widget( 'Foo_Widget' );
}
add_action( 'widgets_init', 'Foo_Widget_init' );

$plugin_dir = basename( dirname( __FILE__ ) );
load_plugin_textdomain( 'foo_widget', null, $plugin_dir );

В данном примере регистрируется виджет под названием Foo_Widget, затем задается переменная с директорией плагинов и происходит попытка загрузки файла foo_widget-locale.po.

Пометка строк в темах и плагинах

В данном случае работают все правила, определенные выше для пометки строк, однако есть также еще одно дополнительное. Оно утверждает, что вы должны добавить ваш домен в виде аргумента к каждому вызову __, _e и __ngettext, иначе ваши переводы не будут работать.

Примеры:

  • __(‘String’) переходит в __(‘String’, ‘text_domain’)
  • _e(‘String’) переходит в _e(‘String’, ‘text_domain’)
  • _n(‘String’, ‘Strings’, $count) переходит в _n(‘String’, ‘Strings’, $count, ‘text_domain’)

Добавление домена вручную – тяжкий труд, потому вы можете его автоматизировать:

  • Если ваш плагин зарегистрирован в официальном хранилище, перейдите к странице администратора и выберите Add Domain to Gettext Calls.

Или:

  • Получите скрипт add-textdomain.php и выполните его:
    php add-textdomain.php -i domain phpfile phpfile ...

После того, как вы это сделаете, домен будет добавлен во все вызовы gettext в файлах.

Генерация POT-файла

Помните ли вы, что POT-файл – это именно то, что вы должны отдать переводчикам, чтобы они могли сделать свою работу?

Есть несколько способов, как можно сгенерировать POT-файл для вашего плагина:

1. Если ваш плагин зарегистрирован в официальном хранилище, перейдите на страницу администратора и выберите Generate POT file.

2. Если ваш плагин не зарегистрирован в хранилище, вы можете заглянуть в директорию wordpress-i18n в SVN и запустить скрипт makepot.php:

php makepot.php wp-plugin your-plugin-directory

Вам понадобится установить программу gettext на свой сервер перед тем, как запускать команду, приведенную выше (также вы должны использовать SVN для проверки средств интернационализации wordpress). Как только все будет сделано, вы увидите POT-файл в текущей директории.

Чтобы сгенерировать POT-файл для темы, используйте:

php makepot.php wp-theme your-theme-directory

Поставлять POT-файл вместе с вашим плагином или темой – хорошая идея.

К тому же, если вы добавите данную строку в заголовок плагина, система WordPress интернационализирует метаданные плагина при выводе его в панели администратора:

Text Domain: your-text-domain

Источник: codex.wordpress.org

Поделиться

Оставить комментарий

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

Получать новые комментарии по электронной почте. Вы можете подписаться без комментирования.