S.Tominoff

Fullstack JavaScript разработчик

Добавляем мультиязычность в Wordpress #2: Loco Translate

Прододжаем эпопею с добавлением мультиязычности в Wordpress. В этой статье переводим надписи и элементы шаблонов Pods с помощью плагина Loco и пары хаков.

В предыдущей статье мы рассмотрели плагин WPGlobus, для переводов текстов сайта. Однако, WPGlobus не всегда удобен, поскольку расчитан на перевод содержимого сайта. Для перевода различных надписей, служебной информации и прочего, Wordpress предлагает встроенную поддержку системы переводов (i18n) на основе gettext. 

Очевидно, вести эти файлы не очень удобно, даже при использовании специального ПО, поскольку это подразумевает изменение PO/MO файлов после каждого изменения файлов темы, а также это неудобно конечному пользователю — Wordpress в первую очередь делался для людей не искушённых в программировании, поэтому было бы вполне логично ожидать, что подобная система может и должна работать без участия программиста.

Решение проблемы изменения файлов переводов берёт на себя замечательный плагин Loco Translate. Loco позволяет генерировать шаблоны переводов (POT файлы) и создавать/модифицировать переводы для тем, плагинов и даже самого Wordpress.


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

 

<?php
/* functions.php */

function my_theme_setup() {
  // убедитесь, что php имеет право на чтение/запись в каталоге /wp-content/themes/my_theme/languages
  load_theme_textdomain( 'my_theme', get_template_directory() . '/languages' );
  /* ещё какой-то код */
}
add_action('after_setup_theme', 'my_theme_setup');
?>

 

Соответственно, в каталоге languages вашей темы будут храниться файлы с переводами. Тут не важно, есть–ли у вас готовые po/mo файлы или нет, Loco либо создаст новые, либо подхватит существующие файлы.

 

 

Для удобства использования переводов (а для использования в Pods Template это будет необходимостью), рекомендую создать shortcut для функции перевода:

 

<?php
  /* functions.php */

  function my_theme_t($text) {
    return __($text, 'my_theme');
  }
?>

 

Теперь можно установить плагин Loco Translate — описывать установку нет смысла (в wp все плагины устанавливаются одинаково), однако давайте посмотрим на интерфейс Loco:

 

 

Здесь нас интересует раздел Active themes, выбираем тему и видим примерно следующее:

 

 

Следующий шаг — создание/изменение POT файла. Переходим в Create/Edit template и кликаем по кнопке Sync, чтобы Loco подобрал шаблонные строки из стандартных функций перевода сам (__, _e), после чего добавляем все свои дополнительные шаблонные строки в файл и сохраняем шаблон (Save).

 

 

После того, как шаблон был успешно сохранён, самое время позаботиться о переводах шаблонных строк. Для этого добавляем нужные языки (New language) и переходим в режим редактирования (Edit):

 

 

Сперва необходимо синхронизировать файл переводов с шаблоном — кнопка Sync, после чего можно начать забивать переводы в поле translation (используя сочетание ctrl+enter автоматически выбирается следующая непереведённая строка).

После перевода всех нужных строк сохраняем переводы кнопкой Save.

 

ОК, причём тут Pods???!

 

Как известно, Pods сам по себе очень крут и могуч, однако у Pods framework есть ещё и охрененно удобная штука под названием Pods Templates. По сути, это небольшой шаблонный движок завязанный на сущности Pods. Эти шаблоны можно использовать в шорткодах или виджетах Pods, что позволяет почти полностью избавиться от необходимости редактировать код вне админки.

 

*   *   *

Но у шаблонного движка есть один недостаток, а именно — отсутствие поддержки Wordpress i18n функций для статичного текста.

*   *   *


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

 

Но не стоит унывать! В Wordpress встроена очень мощная система хуков, поэтому мы вполне можем создать свои magic tags для шаблонов Pods!

 

<?php

/* functions.php */

// разрешаем использовать в шаблонах функцию my_theme_t
// это нужно, из соображений безопасности
// чтобы избежать применения произвольной функции к произвольному тексту
function my_theme_pods_template_echo_tag_allowed_functions($funcs) {
    return array_merge($funcs, array('my_theme_t'));
}
add_filter('pods_template_echo_tag_allowed_functions', 'my_theme_pods_template_echo_tag_allowed_functions');

/*
    Добавляем в Pods Template тег echo {@="TEXT",FILTER}
*/
function my_theme_pods_template_echo($code) {
  $allowed_functions = apply_filters('pods_template_echo_tag_allowed_functions', array() );
  $echo_tag_rx = '/{@="([^"]*)"\,?([^}]*)}/m';
  $matches = array();

  return preg_replace_callback($echo_tag_rx, function ($matches) use ($allowed_functions) {
    // return value "as is" if no valid filter passed
    $executed = $matches[1];

    if (in_array($matches[2], $allowed_functions)) {
        $executed = call_user_func($matches[2], $matches[1]);
    }

    return $executed;
  }, $code);
}
add_filter('pods_templates_pre_template', 'my_theme_pods_template_echo');


 

Я уже создал feature request в официальном репозитории, и вроде как нашёл поддержку пользователей, однако, у изначального подхода имеются некоторые проблемы с безопасностью, как указал @jamesgol, поэтому в первоначальный вариант я добавил обязательный фильтр pods_template_echo_tag_allowed_functions, контроллирующий разрешённые к исполнению функции.

Этот код добавляет в Pods Templates поддержку дополнительного тега со следующей сигнатурой:

{@="Текст для перевода",allowed_function}

А вот пример использования этого тега в шаблоне Pods:

 

[before]
<div class="b-about-us-reviews--left">
[/before]
<div class="b-review">
  <div class="b-review__quote">
	<blockquote>
	  {@post_content,floris_trim_quotes_text}
	</blockquote>
	<div class="b-review__link_container">
	  <a href="{@post_type,get_post_type_archive_link}">{@="More reviews",floris_t}</a>
	</div>
  </div>

  <div class="b-review__author">
	<img class="b-review__author_photo" src="{@post_thumbnail_url.thumbnail}">
	<div class="b-review__author_info">
	  <p>
		<strong>
		  {@post_title}
		</strong>
	  </p>
	  <p>
		<small>
		  {@position}
		</small>
	  </p>
	</div>
  </div>
</div>
[after]
</div>
[/after]

 

Ну и соответственно вывод для разных языков: