Интернационализация WordPress i18n: Готовим плагин к переводу

В предыдущей статье я уделил внимание основам интернационализации WordPress (сокращенно i18n); рассказал, как установить локализованную версию WordPress и легко перевести работающий сайт на локализованную версию WordPress.

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

Разница между интернационализацией и локализацией

На протяжении многих лет разработчики, как правило, неверно истолковывали смысл этих терминов - интернационализация и локализация:

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

Стоит отметить, что интернационализацию часто сокращенно обозначают i18n (потому что в этом слове (Internationalization) между ‘i’ и ‘n’ 18 букв), а локализацию сокращают l10n (потому что аналогично между ‘l’ и ‘n’ размещается 10 букв).

Для чего нужна интернационализация?

Ответ прост - WordPress используется во всем мире на разных языках. Когда плагины интернационализированы, они привлекают большую аудиторию из разных частей мира, которая, очевидно, получает большую пользу от плагина на их родном языке.

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

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

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

Установка заголовков перевода

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

Заголовки перевода - это Text Domain и Domain Path.

Text Domain используется для обозначения всего текста, принадлежащего плагину.

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

Text Domain должен совпадать со slug плагина. Например, если ваш плагин представляет собой один файл, который называется sample-plugin.php, или он содержится в папке sample-plugin, то Text Domain должен быть sample-plugin.

Уточнение по Text Domain

В text domain должно использоваться тире, и недопустимо использование подчеркивания.

Помните, я сказал, что text domain должен соответствовать slug плагина? На самом деле это не совсем так. Я провел небольшой эксперимент с одним из моих плагинов, вместо slug плагина, я использовал уникальный текст, и он работал без проблем.

Вывод: обеспечьте уникальность Text Domain, так чтобы он не конфликтовал с другими плагинами.

Domain Path - это папка, в которой WordPress будет искать файлы перевода .mo.

По умолчанию, WordPress ищет файлы переводов в корневой папке плагина. Однако размещение файла перевода в корневой папке может дезорганизовать структуру плагина.

Если вы хотите хранить файлы перевода в другой папке, например: /languages, необходимо сообщить об этом WordPress, используя заголовок Domain Path.

Ниже приводится стандартная шапка плагина WordPress, в том числе и заголовков перевода:

<?php
/*
 Plugin Name: Enable Shortcode and PHP in Text widget
 Plugin URI: http://w3guy.com/shortcode-php-support-wordpress-text-widget/
 Description: Enable shortcode support and execute PHP in WordPress's Text Widget
 Author: Agbonghama Collins
 Version: 1.2
 Author URI: http://w3guy.com
 Text Domain: espw-plugin
 Domain Path: /languages/
 */

Загрузка Text Domain

Чтобы WordPress загружал файл перевода, если он существует для языка пользователя, мы можем использовать функцию load_plugin_textdomain() function.

Ниже приведена структура данной функции:

<?php load_plugin_textdomain( $domain, $abs_rel_path, $plugin_rel_path ) ?>

Первый параметр $domain должен представлять text domain; $abs_rel_path - устаревший параметр и его значение должно быть установлено на false; и последний параметр $plugin_rel_path - относительный путь к файлам перевода.

Если MO-файлы перевода находятся в папке плагина, используется следующий код:

load_plugin_textdomain( 'espw-plugin', false, dirname( plugin_basename( __FILE__ ) ) );

Если MO-файлы перевода находятся в папке языков плагина, используется следующее:

load_plugin_textdomain( 'espw-plugin', false, dirname( plugin_basename( __FILE__ ) ) . '/languages/' );

Вы не просто вызываете функцию load_plugin_textdomain, она должна вызываться в вашем плагине, тогда же, когда вызывается действие plugins_loaded, следующим образом:

function load_plugin_textdomain() {
  load_plugin_textdomain( 'espw-plugin', FALSE, basename( dirname( __FILE__ ) ) . '/languages/' );
}

add_action( 'plugins_loaded', 'load_plugin_textdomain' );

Подробно об интернационализации плагина

Теперь, когда заголовки Text Domain и Domain Path установлены, пора научиться интернационализировать плагин.

Эту часть статьи мы разделим на следующие разделы:

  • Перевод строк;
  • Использование заполнителей;
  • Перевод HTML;
  • Работа с множественными формами;
  • Дизамбигуация контекста;
  • Безопасность строк перевода.

Пожалуйста, обратите внимание: строка espw-plugin будет использоваться в качестве text domain для этой статьи.

1. Перевод строк

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

$text =  __( 'Hello, SitePoint Readers!', 'espw-plugin' );

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

_e( 'Hello, SitePoint Readers!', 'espw-plugin' );

2. Использование заполнителей

Я полагаю, вы знаете, что представляют собой заполнители. Для получения дополнительной информации вы можете бегло просмотреть страницы документации PHP sprintf и printf().

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

echo 'Your city is $city.'

Корректно следующее использование функции printf():

printf(
    __( 'Your city is %s.', 'espw-plugin' ),
    $city
);

Разобрав код некоторых плагинов, размещенных в хранилище плагинов WordPress, я увидел там следующее:

echo __('Your city is $city', 'espw-plugin');
	_e('Your city is $city', 'espw-plugin');

Здесь переводимы не только строки, но и переменная $city.

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

Функция sprintf похожа на printf тем, что они обе форматируют строки, используя заполнители, только printf выводит отформатированную строку, а sprintf возвращает ее.

Пример: следующий код присваивает отформатированную строку переменной $text:

$text = sprintf( __('Your city is %s.', 'espw-plugin'), $city );

3. Перевод HTML

Включение HTML в переводимые строки зависит от контекста.

В качестве примера можно привести ссылку (отделено от окружающего ее текста):

<div class="site-info">
  <a href="http://wordpress.org/" >< ?php _e( 'Proudly powered by WordPress.', 'espw-plugin' ); ?></a>
</div>

Другим примером является ссылка в параграфе (не отделенная от текста вокруг нее):

<?php
$url = 'http://example.com';
$link = sprintf( __( 'Check out this link to my <a href="%s">website</a> made with WordPress.', 'espw-plugin' ), $url );
echo $link;
?>

4. Работа с множественными формами

Строка, которая изменяется, когда изменяется количество элементов, может быть интернационализирована с помощью функции _n().

Эта функция принимает 4 аргумента:

  • Singular - единственная форма строки;
  • Plural - множественная форма строки;
  • Count - количество объектов, которые будут определять, должна возвращаться единственная или множественная форма;
  • Text Domain - text domain плагина.

Давайте рассмотрим несколько примеров, чтобы понять, как работает функция _n().

В английской версии у вас есть "One comment" и "Two comments". В других языках вы можете иметь несколько форм для множественных чисел.

Приведенный ниже код демонстрирует, как справиться с таким сценарием, используя функцию _n():

printf(
    _n(
        'One comment',
        '%s comments',
        get_comments_number(),
        'espw-plugin'
    ),
    number_format_i18n( get_comments_number() )
);

Пояснение приведенного выше кода состоит из этих трех функций - printf, _n и number_format_i18n.

Для удобства, код функции будет расчленен на пояснение каждого компонента функции:

_n(
       'One comment',
       '%s comments',
       get_comments_number(),
       'espw-plugin'
   )

Первым аргументом, передаваемым в функцию _n, является текст, который будет отображаться, когда количество комментариев единично.

Второй - текст, который отображается, когда количество комментариев больше единицы.

Заполнитель %s будет содержать значение number_format_i18n(get_comments_number()), которое я рассмотрю чуть позже.

Третьим аргументом get_comments_number() считается функция, которая возвращает количество комментариев.

Если она возвращает 1, тогда функцией printf выводится первый аргумент, в противном случае, если значение больше единицы, возвращается второй аргумент %s comments.

Пожалуйста, обратите внимание: заполнитель %s заменяется целым числом, возвращаемым number_format_i18n( get_comments_number() ), что в свою очередь является вторым аргументом, переданным в функцию printf.

Наконец, четвертый аргумент - это text domain перевода.

Функция number_format_i18n() преобразует количество комментариев в формат, исходя из локации перевода. Для получения дополнительной информации ознакомьтесь с документацией.

Подобно number_format_i18n() date_i18n затребует дату в локализованном формате, на основе временной метки.

В то же время функция _n(), приведенная ниже, демонстрирует еще один вариант того, как это работает:

printf( _n( 'We deleted one spam message.', 'We deleted %d spam messages.', $count, 'my-text-domain' ), $count );

Если переменная $count возвращает 1, будет отображаться текст We deleted one spam message; но если возвращаемое значение больше 1, тогда текст We deleted %d spam messages будет отображаться с заполнителем %d, который заменяется на целое значение $count.

5. Дизамбигуация контекста

Иногда термин используется в нескольких значениях. Хотя в английском языке это одно и то же слово, на других языках оно должно переводиться по-разному.

Например, слово Post может использоваться как в качестве глагола во фразе "Click here to post your comment", и как существительное: "Edit this post".

В таких случаях следует использовать функцию _x или _ex.

Они похожи на функции __() и _e(), но имеют дополнительный аргумент - $context:

_x( 'Post', 'noun', 'espw-plugin' );
_x( 'Post', 'verb', 'espw-plugin' );

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

Когда строки, сделанные переводимыми с помощью функции _x(), разбираются инструментами перевода, такими как Poedit, аргумент контекста предоставляет переводчику указание на контекст, в котором использована строка / текст.

На немецкий Post как существительное переводится Beitrag, а как глагол - verbuchen.

Ниже приведен скриншот из Poedit, на котором показан перевод строки Post на немецкий с контекстом, взятым в квадратные скобки.

5.	Дизамбигуация контекста

В то время как _x() возвращает строки перевода, _ex() выводит их на экран.

6. Безопасность строк перевода

WordPress имеет ряд функций для проверки и обеспечения безопасности данных.

Список функций для безопасности текстов переводов включает в себя: esc_html(), esc_html_e(), esc_html_x(), esc_attr(), esc_attr_e() and esc_attr_x(). Более подробную информацию вы можете получить, ознакомившись с разделом Кодекса WordPress по каждой из этих функций.

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

Заключение

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

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

В заключительной части приведено пошаговое руководство того, как сделать плагин готовым к переводу. Также вы узнали, как локализовать плагин.

Я надеюсь, вы почерпнули из этой статьи много интересного.

Удачи всем!

РедакцияПеревод статьи «WordPress i18n Make Your Plugin Translation Ready»