Настройка мультиязычности на основе Babel

06.08.2019
14196

Дорогие друзья! Сегодня мы с вами будем настраивать мультиязычность с помощью компонента Babel на примере одного из наших проектов. Забегая вперед скажу, что данный способ подходит и для организации мультисайтовости в MODx Revo.

Для начала устанавливаем компонент Babel из официального репозитория. При установке он у нас спросит про некоторые параметры, мы оставляем все по умолчанию и устанавливаем.

Настройки контекстов

Реализовывать мультиязычность мы с вами будем на примере украинского языка. И для начала создадим контекст с ключом UA, а имя ему дадим “Украинский”. Так как контекст WEB у нас с вами будет по умолчанию русским, его тоже можно отредактировать, дав ему имя – “Русский”.

MODX Revo - настройка мультиязычности

Теперь нам с вами необходимо настроить наши контексты. Для этого кликаем по контексту в дереве ресурсов правой кнопкой мыши и нажимаем “редактировать”. На вкладке “Настройки контекста” нам с вами необходимо переопределить некоторые глобальные настройки системы.

MODX Revo - настройка мультиязычности

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

Настройка

web

ua

base_url

/

/ua/

cultureKey

ru

ua

site_start

1

205

site_url

test4.dart.agency/

test4.dart.agency/ua/

Теперь предлагаю разобраться с параметрами:

  • base_url – базовый URL сайта относительно корня
  • cultureKey – ключ языка
  • site_start – идентификатор главной страницы сайта
  • site_url – URL сайта (если вы используете домены или поддомены, то вместо моих значений у вас должны стоять разные домены, поддомены)

Не забывайте нажать кнопку “сохранить” после выставления всех настроек.

Настройка htaccess.

После того, как мы внесем все параметры в наши контексты нам необходимо настроить так называемы роутинг для инициализации наших контекстов. И первый файл, который мы с вами поправим – это .htaccess. В нем нам необходимо закомментировать стандартный роутинг MODX и сделать такую конфигурацию:

# The Friendly URLs part
#RewriteCond %{REQUEST_FILENAME} !-f
#RewriteCond %{REQUEST_FILENAME} !-d
#RewriteRule ^(.*)$ index.php?q=$1 [L,QSA]

RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(ru|ua)/favicon.ico$ favicon.ico [L,QSA]

RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(ru|ua)/assets(.*)$ assets$2 [L,QSA]

RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(ru|ua)/images(.*)$ images$2 [L,QSA]

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(ru|ua)?/?(.*)$ index.php?cultureKey=$1&q=$2 [L,QSA] 

Обратите внимание, что я оставил закоменченный роутинг MODX, для того, чтобы вам было удобнее ориентироваться.

Если же вы используете домены или поддомены, то вам не нужно использовать настройку .htaccess. Можете сразу переходить к написанию плагина.

Смысл здесь довольно простой: сначала мы настраиваем редиректы с /ua/favicon.ico, /ua/assets/, /ua/images/ на аналогичные папки без виртуального префикса /ua/. Следующий шаг, нам необходимо отследить, какой из контекстов мы будем грузить. Также делаем это на основе виртуального префикса /ua/, передав его в MODX как GET-параметр cultureKey.

Пишем плагин-обработчик

MODX у нас не знает, что появился новый параметр. Поэтому нам необходимо написать плагин gateway на событие onHandleRequest:

<?php
if ($modx->context->key == 'mgr' || !$modx->getOption('friendly_urls') || $modx->event->name != 'OnHandleRequest') {
    return;
}
switch($_REQUEST["cultureKey"]){
    // Украинский
    case 'ua':
        $modx->switchContext('ua');
        break;
        
    // Русский
    default: 
        $modx->switchContext('web');
        break;
}
unset($_GET["cultureKey"]);

Таким образом, мы с вами научили MODX работать с новым параметром. Если же вы используете домены или поддомены, то в вашем случае плагин будет выглядеть примерно так:

<?php
/* don't execute if in the Manager */
if ($modx->context->key == 'mgr' || !$modx->getOption('friendly_urls') || $modx->event->name != 'OnHandleRequest') {
    return;
}
 
switch ($_SERVER['HTTP_HOST']) {
   // проверка домена 1
    case 'mastercentr.tmweb.ru':
        // if the http_host is of a specific domain, switch the context
        $modx->switchContext('web');
        break;
   // проверка домена 2
    case 'mso.mastercentr.tmweb.ru':
        // if the http_host is of a specific domain, switch the context
        $modx->switchContext('moika');
        break;
    default:
        // by default, don't do anything
        $modx->switchContext('web');
        break;
}

Отлично! Теперь мы настроили с вами основную часть нашей задачи. Теперь осталось дело за малым – перевести все тексты. Для статичных элементов в нашей верстке нам необходимо создать файлы лексикона MODX.

Переводы в файлах лексиконов.

Надеюсь, вы уже заметили, что у каждого компонента в папке /core/components/ есть папка /lexicon/, в которой хранятся файлы лексиконов. Мы с вами будем работать в папке компонента babel. Так как у нас там не будет находится папки /ua/ ее необходимо будет создать. Также советую туда продублировать файл default.inc.php из папки /ru/.

Для переводов непосредственно на сайте я создаю в обоих папках файл translate.inc.php, где переведем фразу “Заказать звонок” и наименования наших языков. Файл translate.inc.php из папки “ru”:

<?php
$_lang["ledsystems.buttons.callback"] = "Заказать звонок";
/* ----------- Языки -------------- */
$_lang["ledsystems.lang.ru"] = "RU";
$_lang["ledsystems.lang.ua"] = "UA";

Файл translate.inc.php из папки “ua”:

<?php
$_lang["ledsystems.buttons.callback"] = "(укр.) Заказать звонок";
/* ----------- Языки -------------- */
$_lang["ledsystems.lang.ru"] = "(укр.) RU";
$_lang["ledsystems.lang.ua"] = "(укр.) UA";

На языках лучше создавать ключи, у которых значение задано с префиксом, в моем случае “(укр.)”. После полного заполнения файла он передается заказчику, и он уже все переведет с учетом контекста изложения.

Использование лексиконов.

Далее мы можем использовать наши лексиконы в шаблонах, чанках и сниппетах. В шаблонах и чанках мы будем использовать их следующим образом в синтаксисе fenom:

{$_modx->lexicon("ledsystems.buttons.callback")} 
{"ledsystems.buttons.callback" | lexicon}

Единственное, нужно заранее загрузить файлы лексикона выше строкой (обычно я это делаю в самом верху шаблона):

{$_modx->lexicon->load('babel:translate')}

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

[[%ledsystems.buttons.callback? &namespace=`babel` &topic=`translate`]]

В сниппете же вызов будет такой:

<?php
// подгрузка лексиконов
$modx->lexicon->load('babel:translate');
// использование
$data = $modx->lexicon(‘ledsystems.buttons.callback’);

Настройка идентификаторов контейнеров, ресурсов.

Для того, чтобы передавать корректные значения parents и аналогичные, я советую использовать их через настройки контекста. Таким образом, мы сделаем работу с контекстами гибкой и удобной. Использовать настройки контекста мы сможем также, как и настройки системы. В синтаксисе fenom:

{$_modx->runSnippet("pdoMenu",[
	"parents" => $_modx->config.ledsystems_base_catalog,
	"level" => 1
])}

Стандартный синтаксис:

[[pdoMenu?
&parents=`[[++ ledsystems_base_catalog]]`
&level=`1`
]]

Вывод ссылок на языковые версии.

Вывести ссылки на языки мы можем с помощью сниппета babelLinks, таким образом:

{$_modx->runSnippet("!babelLinks", [
	"showCurrent" => 1,
	"includeUnlinked" => 1,
	"tpl" => "babelLinksTpl"
])}

Параметры showCurrent (показывать ссылку на текущую языковую версию) и includeUnlinked (показывать ссылки на не прикрепленные ресурсы) можете использовать по желанию.

Таким вот образом можно настраивать мультисайтовость и мультиязычность с помощью компонента babel. При настройке мультисайтовости, использование babel не обязательно.

Настройка мультиязычности на основе Babel

0 Число голосов: 14
3
5
1
14

Комментарии ()

  1. Андрей 08 августа 2019, 06:37 # 0
    Добрый день Артём. У меня ранее была проблема такого рода — не подтягиваются в переведенный ресурс изображения товаров из минишопа2, и при переводе любого другого ресурса, последнее изображение в контенте тоже не отображается нормально. Это когда сам пытался настроить мультиязычность компонентом Бабель.Не сталкивались случайно с такой проблемой?
    1. Петропавловский Артем 08 августа 2019, 18:12 # 0
      Зачем вам дубли изображений — они ведь одинаковы в разных контекстах? В подобных случаях я использую примерно такой код:
      [[!*context_key:eq=`web`:then=`
      	[[!msGallery? 
              	&tpl=`tpl.msGallery.custom`
      	]]
      `:else=`
      	[[!msGallery?
              	&tpl=`tpl.msGallery.custom`
              	&product=`[[!BabelTranslation? &contextKey=`web` &resourceId=`[[*id]]`]]`
      	]]
      `]]
      
      В данном куске кода мы берем картинки из галереи товара в контексте WEB.
      1. Андрей 13 августа 2019, 19:33(Комментарий был изменён) # 0
        Попробовал. С этим кодом вообще ложится сайт.У меня теперь изображения из карточки товара, не переносятся в переведенный ресурс, надо добавлять их по новой(. Контент, изображения в контенте переносятся, а из минишопа не хотят. Это у всех так, или есть выход?)
        1. Петропавловский Артем 13 августа 2019, 19:41(Комментарий был изменён) # 0
          Этот код на 100% рабочий, взят с рабочего проекта. Можете попробовать у context_key убрать символ "!". И посмотрите логи, какие ошибки там выдает? Ответ: «у меня из-за этого кода все ложится» — может сказать лишь о том, что вы недостаточно корректно адаптировали его.
          Из miniShop2 галерея не будет переноситься, так как это два совсем разных ресурса. Советую вам разобраться в принципе работы компонентов MODX и их структуре (хотя бы поверхностно) — тогда многие вопросы отпадут сами собой.
          1. Андрей 14 августа 2019, 06:10 # 0
            Сорри, неправильно выразился, не «ложится сайт», а на месте картинки выводится Array ( [files] => и куча текста, логов нет. msGallery вроде выводит картинки, но «не совсем адаптированно», по своему).
            Сам понимаю что не хватает базовых знаний, поэтому буду разбираться далее, меня чем и подкупил МОДекс, так это своей простотой и гибкостью. Надеялся что и здесь можно воспользоваться каким нибудь «хитрым кодом».
            Кстати, не разбираясь ни в html, ни в css, за небольшой промежуток времени, только благодаря вашим урокам удалось реализовать полноценный, работающий интернет-магазин, и теперь уже сделать его мультиязычным. Агонь!
            1. Петропавловский Артем 14 августа 2019, 12:36 # 0
              У вас выводится массив значений, так как нет чанков, указанных в параметре 'tpl'
              1. Андрей 14 августа 2019, 16:56 # 0
                Работает! Ура!
                Поменял названия чанков на свои.
                Спасибо огромнейшее!
        2. Андрей Дарменко 16 февраля 2021, 11:33 # 0
          Артем, спасибо большое!
      2. Андрей 15 августа 2019, 11:37 # 0
        Наверное это уже наглость с моей стороны, но если не спрошу, то не смогу спокойно спать.)
        Существует ли возможность выводить у переведённого ресурса цепочку отзывов или тикетов, которые принадлежат родительскому ресурсу? Или у каждого ресурса исключительно своя цепочка отзывов и никак это не обойти?
        1. Петропавловский Артем 16 августа 2019, 16:17(Комментарий был изменён) # 0
          Решение будет аналогично тому, которое я выше написал. Например, у комментариев Tickets есть параметр &thread, которому можно передать любое значение и формировать свои цепочки. В нашем случае нам еще поможет сниппет BabelTranslation. Только вопрос: зачем? Мы же делаем мультиязычную версию сайта, а вы хотите все комментарии и отзывы на разных языках смешать.
        2. Игорь 07 декабря 2019, 14:27 # 0
          День добрый.Где может быть косяк?В самом начале установки бабеля.При переходе на главная/ua/ выдает 404 ошибкуХотя страница есть и id указан в контексте верноДаже из админки если кликать «просмотреть» тоже 404
          1. Николай 29 января 2020, 21:09(Комментарий был изменён) # 0
            Артем, здравствуйте! Ваш способ работает отлично, но до тех пор, пока не попытаешься ввести редирект на https.
            RewriteCond %{SERVER_PORT} !^443
            RewriteRule (.*) https://mydomain.com/$1 [R=301,L]
            .Как только прописал, доступ по контексту mydomain.com/ru/ упал в цикличные редиректы. Вы не подскажите, как быть?
            1. Андрей Дарменко 16 февраля 2021, 11:19(Комментарий был изменён) # 0
              Николай, я не великий программист, но если не ошибаюсь, то Вам нужно в «Настройках контекста» указать в web (site_url — //yourdomain.com/) и соответственно в контексте UA ( //yourdomain.com/ua/ ). Здесь нельзя указывать протокол http или https. Почитайте здесь: devaka.ru/articles/moving-to-https (раздел 1)
            2. Oleg 03 августа 2020, 12:22 # 0
              Добрый день! Как с помощью babel переводить minishop в частности опции?С копированием их разобрался, то что они скидывались при создании другой версии языка… Надо просто делать копию товара в нужную категорию в другом контексте и потом привязывать вручную…А вот с переводом не совсем понятно… Есть какие то варианты?
              1. Игорь Ч 20 ноября 2020, 15:36 # 0
                Здравствуйте!А как попап стандартный (сообшение об ошибке успехе перевести?)
                1. Петропавловский Артем 21 ноября 2020, 09:39(Комментарий был изменён) # 0
                  Создать папку с переводами в самом компоненте
                  core/components/[наименование компонента]/lexicon/[ключ языка]
                  , либо использовать ключи из своего словаря. Второй способ предпочтительней, так как все будет в одном месте.
                2. Игорь Ч 21 ноября 2020, 11:57 # 0
                  нету кнопки «Ответить»
                  1. yura3d 08 июля 2021, 15:06 # 0
                    Спасибо за статью, всё работает, но у меня возникла проблема с расширением ClientConfig, которое не отображало данные ни из одного контекста, кроме контекста по-умолчанию (web). Гугление помогло найти инфу, что ClientConfig инициализируется раньше события onHandleRequest (для обработки которого в статье создаётся плагин gateway), поэтому установка контекста из плагина не работает. Смена обрабатываемого события на OnMODXInit (которое вызывается гарантированно до инициализации всех расширений) решает проблему.
                    1. Дмитрий 05 марта 2022, 10:22 # 0
                      У меня появилась проблема. Сделал всё по уроку, главная страница на разных версиях открывается без проблем, переводы работают, но теперь внутренние страницы русской версии сайта перестали работать и отдают 404 ошибку. В чем может быть проблема?
                      1. Дмитрий 05 марта 2022, 10:52 # 0
                        Извиняюсь, проверил сейчас ещё раз код, нашёл ошибку в файле .htaccess
                      2. Petar 12 октября 2022, 15:41 # 0
                        Здравствуйте. Подскажите, как быть с галереей miniShop2 и babel? Как связать, что бы каждый раз не загружать новые изображения?
                        1. Serhii 30 октября 2022, 14:37 # 0
                          У меня по контекстах не решенный вопрос это корзина. Почитал и народ говорит что если накидать товаров в корзину например на юа версии, после переключиться на ру версию то корзина будет пуста. Нету ли решения даного вопроса?

                          Наши клиенты

                          Многие компании уже доверяют нам. Будьте в их числе!

                          Хотите реализовать проект?

                          Контакты

                          Напишите нам - мы расскажем вам много интересного!


                          Пермь, шоссе Космонавтов 252, офис 218