Как настроить Babel, чтобы не было мучительно больно

Один день проведённый на modx.pro показал, что у людей очень много проблем с дополнением Babel. Люди боятся его за сложность настройки. На самом деле всё не так страшно. Эта статья, надеюсь, поможет при создании мультиязычных сайтов. Целью этой статьи не является разбор, чем одно дополнение лучше другого, это именно инструкция: как сделать.
Итак, у вас есть или разрабатывается сайт, который должен стать мультиязычным. С чего начать? Самый важный момент – проверить, что у вас в системных настройках в разделе «Дружественные URL» включены «friendly_urls» (Использовать дружественные URL). Затем создаем дополнительные контексты (то же меню раздел «Контексты»). Например, были у нас web и mgr (да, менеджер тоже находится в отдельном контексте), в дополнение к нему создаём контексты с ключами en и de. Ключи после создания (обычным способом) поменять нельзя, поэтому вызывайте всех святых, чтобы не ошибиться. Во время создания контекстам лучше сразу дать красивые имена, например, English и German (имя можно исправить позже). Контекст web также лучше переименовать для удобства.

Теперь можно скачивать дополнение Babel, как обычно, меню «Приложения» — «Установщик» При установке он сразу подхватит ваши текущие контексты, при желании их можно подправить (Context Keys). Если по какой-то причине позже появится дополнительный контекст, не беда, в «Системным настройках», в разделе Babel можно внести изменения. Иногда случается, что после правок системных настроек Вавилон не отображает значок перевода на фронтенде, хотя у вас связка ресурсов есть. Это из-за сессии. Поэтому после изменения настроек принудительно завершаем все сеансы.
Теперь переходим к редактированию контекстов. Правой кнопкой мыши жмём над нужным нам контекстом, например, Russian, выбираем «Редактировать контекст» и создаём следующие параметры (порядок не важен):
  • Ключ: «base_url», значение: "/ru/".
  • Ключ: «cultureKey», значение: «ru»
  • Ключ: «site_start», значение: «1» (ID номер первой страницы контекста)
  • Ключ: «site_url», значение: «example.ru/ru/» (меняем на свой домен, если надо, указываем протокол https).

Там же переключаемся на «Права доступа» и проверяем есть ли для группы anonymous политика доступа «Load Only» с рангом 9999. Если нет, то создаем.
Далее, все остальные контексты редактируем по такому же принципу.
Следующий шаг – создание плагина, который будет отвечать за переключение контекстов. Идём в «Элементы» и создаём новый плагин, для удобства обзовём его «OnHandleRequest» и включаем для него в разделе «Системные события» OnHandleRequest. Код плагина:
<?php
if($modx->context->get('key') != "mgr"){
    /* grab the current langauge from the cultureKey request var */
    switch ($_REQUEST['cultureKey']) {
        case 'de': 
            $modx->switchContext('de'); 
            setlocale(LC_ALL, 'de_DE.UTF8');
            break;
        case 'en': 
            $modx->switchContext('en'); 
	setlocale(LC_ALL, 'en_US.UTF8');
            break;
        default:
            // Set the default language/context here
            $modx->switchContext('web');
            setlocale(LC_ALL, 'ru_RU.UTF8');
             break;
    }
	unset($_GET['cultureKey']);
}
Кроме переключения на нужный контекст мы также меняем локаль для системы. Теперь PHP скрипты будут разговаривать на правильном языке для каждого контекста. Очень удобно.
Осталось совсем чуть-чуть, поправить .htaccess и в путь. Это при условии, что у вас стоит Апач. Если нет, то переделываете правила под свою систему. Начальный htaccess есть в инсталляции MODX’a, я объясню, что надо поменять. Итак, старый кусок кода:
# The Friendly URLs part
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php?q=$1 [L,QSA]
Меняем на это:
# Force language when requesting the root (/) . Смена языка при запросе корня сайта
RewriteCond %{HTTP:Accept-Language} ^ru [NC]
RewriteRule ^$ /ru/ [R=301,L]

RewriteCond %{HTTP:Accept-Language} !^ru [NC]
RewriteRule ^$ /ru/ [R=301,L]

# Языки поменяли, теперь сменим виртуальные пути на реальные
 
# redirect all requests to /en/favicon.ico  to /favicon.ico
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(en|ru|de)/favicon.ico$ favicon.ico [L,QSA]
 
# redirect all requests to /en/assets* to /assets*
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(en|ru|de)/assets(.*)$ assets$2 [L,QSA]
 
# redirect all other requests to /en/* 
# to index.php and set the cultureKey parameter
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(en|ru|de)?/?(.*)$ index.php?cultureKey=$1&q=$2 [L,QSA]
Да, не забываем, что в темплейтах всех ваших страниц должен быть указан :
<head>
    ...
    <base href="[[++site_url]]" />
    ...
</head>
Сайт уже должен работать и адресная строка, при запросе корня сайта (example.ru) должна поменяться на example.ru/ru/. Осталось настроить меню языков. Самый простой вариант, чтобы работало и понятно было:
<ul>
  [[BabelLinks? &tpl=`BabelLinksTpl`]]
</ul>
BabelLinksTpl:
<li><a href="[[+url]]" class="[[+cultureKey]][[+active:notempty=` [[+active]]`]]">[[+cultureKey]]</a></li>
Теперь на главной странице будет так:
<ul>
<li><a href="http://example.ru/en/" class="en">en</a></li>
<li><a href="http://example.ru/de/" class="de">de</a></li>
</ul>
Чтобы было видно текущий язык, надо вызов оформить так:
[[BabelLinks? &tpl=`BabelLinksTpl` &showCurrent=`1`]]
Теперь можно переводить материалы.

На фронтенде, на странице у которой есть перевод появится меню выбора языка. Причем вести ссылка будет на конкретный документ, что связан с оригиналом.
Какие проблемы могут возникнуть во время использования? Поскольку у нас URL стал виртуальным, то могут возникнуть проблемы со ссылками, которые генерятся другими дополнениями. Например, Gallery или AjaxForm. В первом случае нам надо изменить настройки «Источников файлов» (меню «Медиа»). Заходим, как обычно жмём праву кнопку над источником «Filesystem» и выбираем «Редактировать». Меняем два параметра:
  • baseUrl изменить на «/»;
  • baseUrlRelative изменить на «Нет»;

А в случае с AjaxForm в «Системных настройках», раздел «Сессии и куки», параметр «session_cookie_path» поменять на «/».

Что еще почитать? Обязательно официальную документацию на английском, надеюсь теперь она вам будет понятна:
MODX docs – Babel
а это дамашняя страница Babel'a, отсюда я черпал вдохновение:
SEO Friendly Multilingual Websites with MODx and Babel
Ну и конечно же, при создании статьи не обошлось без «Stack Overflow»
Янис
12 сентября 2018, 10:03
modx.pro
22
13 039
+12

Комментарии: 35

SEQUEL.ONE
12 сентября 2018, 11:58
+1
Спасибо за труд. Для новичков самое то. Не хватает только примеров как в шаблонах например переводить pagetitle и другие плейсхолдеры, там же можно в том числе через модификаторы. Вот на Fenom бы примеры написать, было бы супер.
    Янис
    12 сентября 2018, 12:15
    +1
    Принцип работы с Вавилоном в том и заключается, что вы получаете новый документ в другом контексте и все поля можно заполнять на другом языке. Если поля были чем-то заполнены до перевода, то данные автоматом сохранятся в новом документе. Нет нужды что-то править в темплейте. Там вообще голого текста быть не должно, т.е. темпплейт может выглядеть как-то так:
    ...
    <main>
    <h1>[[*pagetitle]]</h1>
    <p>[[*longtitle]]</p>
    [[*content]]
    <a href="[[~[[*link_url]]]]">[[*link_text]]</a> <!-- кастомные ТВшки -->
    </main>
    ...
      SEQUEL.ONE
      12 сентября 2018, 12:37
      0
      Неудачный коммент, сейчас посмотрел как настроенно у меня. Вот скажем нужно в подвале копирайт перевести на несколько языков статично. Вот так мы будем переводить статично:

      [[++cultureKey:is=`ru`:then=`<p>Все права защищены.</p>`]]
      								    [[++cultureKey:is=`en`:then=`<p>All rights reserved.</p>`]]
      								    [[++cultureKey:is=`de`:then=`
          								    [[pdoMenu?
          								        &parents=`113`
          								        &outerClass=`about`
          								        &firstClass=``
          								    ]]
      								    `]]
      Как мы видим в немецкой версии сайта выводим только ссылки из родителя, т.к. в Германии все сайты должны иметь набор из 3ёх страниц Impressum, AGB и Datenschutzt. А вот ещё пример вывода pdoPage с Babel:

      {'!pdoMenu' | snippet : [
                                          'parents' => '4,85,86', 
                                          'level' => 1,
                                          'displayStart' => 1,
                                          'firstClass' => '',
                                          'tplStart' => '@INLINE <li class="first"><a href="[[+link]]" [[+attributes]]>[[+menutitle]]</a></li>[[+wrapper]]',
                                          'context' => $_modx->context.key
                                      ]}
      В parents указываем родителей желаемых для вывода разделов из разных контекстов.

      P.S. Я вот про такие примеры писал)
        Баха Волков
        12 сентября 2018, 12:54
        0
        … сейчас посмотрел как настроенно у меня. Вот скажем нужно в подвале копирайт перевести на несколько языков статично. Вот так мы будем переводить статично:
        Вот про это я и говорю Янис, тут ведь можно было использовать запись собственного словаря и не проверять все это. Ну в данном случае только проверить если контекст de то вывести меню

        А вот ещё пример вывода pdoPage с Babel:
        Тут тоже согласен (хоть и напрямую к Babel не относиться) но все же. Тут же можно было выйти из ситуации так: создать настройку для контекстов в которой хранились бы нужные id.

        Пример:
        Создать настройку context_menu_id
        Задать его контекстам, web => 4, en => 85, de => 86

        И уже нужно было бы выводить меню так:

        {'!pdoMenu' | snippet : [
            'parents' => $_modx->context.context_menu_id,
            'level' => 1,
             'displayStart' => 1,
            'firstClass' => '',
            'tplStart' => '@INLINE <li class="first"><a href="[[+link]]" [[+attributes]]>[[+menutitle]]</a></li>[[+wrapper]]',
        'context' => $_modx->context.key
        ]}
          Янис
          12 сентября 2018, 12:55
          0
          Всё просто.
          В первом случае, если менеджеру запрещено редактировать эти записи, используем словарь MODX'a:
          <p>[%copy_info]]</p>
          Если разрешено, то на первой странице сайта создаём ТВ'шки со всякой служебной инфой и потом выводим нужные поля, например через fastField:
          <p>[[#[[++site_start]].copy_info]]</p>
          А во втором случае (относится ко всем сниппетам из pdoTools) можно ограничивать выборку по контекстам через опцию &context. Получить текущий контекст через Fenom вообще плёвое дело:
          {$_modx->context.key}
            Янис
            12 сентября 2018, 13:18
            0
            Оу, я невнимательно прочитал вторую часть вашего коммента, у вас там всё уже правильно расписано. Смотрю ещё одной статьёй, не отделаться. На самом деле – это огромный пласт «Как сделать мультиязычный сайт?» Тема для следующей статьи уже есть.
      Баха Волков
      12 сентября 2018, 12:18
      0
      Отличная работа. Советую добавить работу с собственным словарем, на странице кроме данных которые вводятся есть еще много чего, что разработчик при разработке обычного (не мультиязычного) сайта прописывает прямо в тело шаблона.

      Примеров много:

      • Плейсхолдеры полей ввода
      • Заголовки блоков
      • Тексты кнопок
      • И многое другое
      Для таких задач я создаю отдельный словарь, в MODX это удобно. Но вдруг люди/читатели не знают как это делать. Пример из жизни: Попался сайт интернет магазин который я так и не вернул в жизнь, там разработчик в каждом месте где нужно было вывести текст проверял контекст и выводил нужный текст или вообще чанки нагородил под каждый язык.
        Янис
        12 сентября 2018, 12:31
        0
        Это уже отдельная инструкция по работе со словорями :) К работе с Вавилоном она не отностится. Как я уже писал, в темплейте текста быть не должно, также как и в сниппете не должно быть эха. Конечно же, когда я начинал работать на MODX'е, у меня было и то и другое, но с опытом и чтением форумов приходит понятие – как можно сделать красиво и универсально.
          Баха Волков
          12 сентября 2018, 12:36
          0
          Согласен!) Тогда с тебя не только этот урок, а цикл уроков «How сделать multiязычный webсайт» где описать все это дело)
            Янис
            12 сентября 2018, 12:58
            +2
            Да, вполне, но уже на следующей неделе. Деньги зарабатывать тоже надо :)
        Denis Den
        13 февраля 2019, 14:40
        +1
        Спасибо за статью, но я столкнулся с проблемой. Не выводились переключатели языков.
        После долгих попыток, выяснилось, что дело в пробеле в строке вывода сниппета BabelLinks.
        Лишний пробел перед названием чанка в строке вызова.
        То есть, это
        [[BabelLinks? &tpl=` BabelLinksTpl` &showCurrent=`1`]]
        Должно быть так
        [[BabelLinks? &tpl=`BabelLinksTpl` &showCurrent=`1`]]
        После этого заработало.
          Янис
          14 февраля 2019, 11:09
          0
          Спасибо, поправил.
          Denis Den
          14 февраля 2019, 13:07
          0
          Подскажите, есть ли вариант более красивой и короткой записи, чем эта?
          [[++cultureKey:is=`ru`:then=`
                    [[pdoResources?
                          &parents=`3`
                          &depth=`0`
                          &tpl=`image_preview_industry`
                          &includeTVs=`image_preview, image_preview_alt`
                          &sortdir=`ASC`
                      ]]
                      `:else=`[[pdoResources?
                          &parents=`21`
                          &depth=`0`
                          &tpl=`image_preview_industry`
                          &includeTVs=`image_preview, image_preview_alt`
                          &sortdir=`ASC`
                      ]]`]]
          Видел выше, что можно как-то проверять по контексту.
          З.Ы. Fenom у меня почему-то не работает. Вызовы выводятся, как строка.
            Андрей
            14 февраля 2019, 13:24
            0
            Вобще проще создать настройку в контекстах, в которой будет записаны нужные parents id, и выводить без проверок:
            [[pdoResources?
                &parents=`[[++category_id]]`
                &depth=`0`
                &tpl=`image_preview_industry`
                &includeTVs=`image_preview, image_preview_alt`
                &sortdir=`ASC`
            ]]

            З.Ы. Fenom у меня почему-то не работает. Вызовы выводятся, как строка.
            В системных настройках раздела pdoTools включить — pdotools_fenom_parser.
              Denis Den
              14 февраля 2019, 14:05
              0
              То есть, надо создать в каждом контексте ключ «category_id» и перечислить все ID документов родителя?
              Например: «3,4,5,10» — для контекста ru и «21,22,23,24» — для контекста en. Но тогда возникает вопрос: каким образом pdoResources определит, какой родитель должен быть выведен и на каком языке? Спасибо.
                Андрей
                14 февраля 2019, 14:40
                0
                То есть, надо создать в каждом контексте ключ «category_id» и перечислить все ID документов родителя?
                Нет, перечислять не нужно, под каждый id пишем свой ключ.

                Но тогда возникает вопрос: каким образом pdoResources определит, какой родитель должен быть выведен и на каком языке? Спасибо.
                И зачем в один вызов подставлять всех родителей? Логично предположить, что один ключ с id = один отдельный вызов pdoResources.
              Янис
              14 февраля 2019, 13:28
              0
              Да, самый простой вариант — ограничение по контексту через ключ
              &context=``
              Осталось только получить текущий контекст :) Вот здесь приведён код сниппета, который выведет контекст как для текущего документа, так и для заданного:
              [SOLVED] Return the current context key

              А с Fenom'ом скорее всего проблема из-за какого-нибудь скрипта или инлайн стиля (если он правильно поключен). Вот ссылка на документацию с описанием конфликта фигурных скобок и что с этим делать: Тег {ignore}

              P.S. У Андрея вариант даже более быстрый.
              Evgeniy
              04 марта 2019, 14:22
              0
              Огромное спасибо за мануал.
              Вывожу [[BabelLinks]]. Подскажите, пожалуйста где можно заменить:
              <ul>
                  <li>Russian</li>
                  <li>Ukrainian</li>
                  <li>English</li>
              </ul>
              на:
              <ul>
                  <li>По-русски</li>
                  <li>Українською</li>
                  <li>In English</li>
              </ul>
                Янис
                05 марта 2019, 17:28
                +1
                Через темплейт для BabelLinks. В статье был пример вызова с &tpl. Начиная с третьей версии Babel изменился темплейт по умолчанию. Старый выглядел так:
                <li><a href="[[+url]]" class="[[+cultureKey]][[+active:notempty=` [[+active]]`]]">[[%babel.language_[[+cultureKey]]? &topic=`default` &namespace=`babel`]]</a></li>
                Где
                [[%babel.language_[[+cultureKey]]? &topic=`default` &namespace=`babel`]]
                и есть вывод записи из Лексикона. Остается только поменять перевод на свой.
                Константин
                04 декабря 2019, 22:38
                0
                Добрый день!
                Есть два контекста — по умолчанию (украинский) и русский: web и ru соответственно.
                В конфиге Babel контексты указаны.
                  [[BabelLinks? &tpl=`BabelLinksTpl`]]
                и чанк
                [[+cultureKey]]
                Такая конструкция выводит только ru.

                В Вашем примере тоже изначально при трех языках выводиться только два
                Подскажите, пожалуйста, как при двух языках выводить две ссылки (переключатели) ru | ua?
                Показывать текущий не вариант, так как при смене на русский будет ru | ru
                  Янис
                  04 декабря 2019, 23:58
                  0
                  В статье, в самом конце я указал:
                  Чтобы было видно текущий язык, надо вызов оформить так:
                  [[BabelLinks? &tpl=`BabelLinksTpl` &showCurrent=`1`]]
                    Константин
                    05 декабря 2019, 00:13
                    0
                    Показывать текущий не вариант, так как при смене на русский будет не ru | ua,
                    а — ru | ru
                    По крайней мере, у меня так, пробовал
                      Янис
                      05 декабря 2019, 09:09
                      0
                      В чанке только один плейсхолдер [[+cultureKey]]? Сессии пользователей пробовал сбросить?
                      Иногда случается, что после правок системных настроек Вавилон не отображает значок перевода на фронтенде, хотя у вас связка ресурсов есть. Это из-за сессии. Поэтому после изменения настроек принудительно завершаем все сеансы.
                        Константин
                        06 декабря 2019, 01:28
                        0
                        Сори, не верно оформил предыдущий пост, вырезался html.
                        Продублирую
                        Переключатель
                        <ul class="switch-lang">
                          [[BabelLinks? &tpl=`BabelLinksTpl` &showCurrent=`1`]]
                        </ul>
                        Сейчас я вывожу с текущим. При загрузке украинской версии вроде все норм: «ru | ua»
                        но после перехода на русский, получается «ru | ru»

                        чанк BabelLinksTpl
                        <li><a href="[[+url]]" class="[[+cultureKey]][[+active:notempty=` [[+active]]`]]">[[+cultureKey]]</a></li>
                        Попробовал сбросить сессии пользователей, к сожалению, не помогло.
                          Янис
                          07 декабря 2019, 00:16
                          0
                          Тут только могу посоветовать проверить все настройки с самого начала. Если не получится, можешь прислать доступ в личку, я посмотрю, в чём может быть проблема.
                            Константин
                            07 декабря 2019, 19:47
                            0
                            Уточните, пожалуйста, в Вашем примере есть три языка: английский, немецкий, русский.
                            Такой код выведет английский, немецкий.
                            <ul>
                            <li><a href="http://example.ru/en/" class="en">en</a></li>
                            <li><a href="http://example.ru/de/" class="de">de</a></li>
                            </ul>
                            Русский выводиться только через текущий? [[BabelLinks? &tpl=`BabelLinksTpl` &showCurrent=`1`]]
                            Если текущим пользователь выберет английский, то разве не получиться что-то вроде:
                            «английский, немецкий, английский», из которых последний — текущий.
                            То есть анкор «русский» пропадет, хотя ссылка будет идти на русскую версию.
                            ?
                    Ruslan
                    29 января 2020, 03:33
                    0
                    Здравствуйте, столкнулся с аналогичной проблемой. Только ru выводит (текущую), не могу перевестись на uk.
                    Вам удалось решить проблему?
                      Константин
                      14 февраля 2020, 00:51
                      0
                      Добрый день! Быстрого решения не нашел. Пока пришлось приостановить.
                      Можно по конкретнее?
                      Только ru выводит (текущую), не могу перевестись на uk.
                    Николай
                    15 января 2020, 15:26
                    0
                    Добрый день. Столкнулся с проблемой, которая описана в обсуждении выше, в урле сайта «ru | ru». Сайтом этим до меня занимался другой человек, да и опыт работы и MODX у меня не очень большой. Подскажите, пожалуйста, как убрать 2-е ru? По предыдущим комментария не до конца всё понял. С какого боку подойти, с чего начать.? Спасибо.
                      Андрей
                      15 июня 2022, 11:42
                      0
                      Первый опыт работы с MODX. Сделал все по инструкции. После перевода, менял Псевдонимы у переведенных Ресурсов (для красоты Permalinks). На данный момент ни одна из переведенных страниц не открывается, везде выдает 404 Page not found. Главная страница (Index) вообще не открывается (Сайт novadsail.com выполнил переадресацию слишком много раз).

                      Друзья, пожалуйста отзовитесь и помогите. Проект лежит мертвым грузом, начальство просит срочно разобраться.
                      Андрей
                      15 июня 2022, 14:20
                      0
                      На сайте используются 3 языка: Русский / Английский / Румынский. Нужен Чанк, который будет подставлять нужную ссылку в зависимости от выбранного языка с условием заданного ID default версии.

                      Пример Чанка: [[*context_key:is=`web`:then=`[[+ru_id]]`:else=`[[BabelTranslation:default=`[[+ru_id]]`? &resourceId=`[[+ru_id]]` &contextKey=`en`]]`]]

                      Пример вызова: Ссылка на страницу

                      Проблема в том, что в данном Чанке есть проверка между 2мя языками, а мне необходимо проверять среди 3х языков (web = ru / en / ro). Пожалуйста, можете подсказать как изменить код?
                        Петр
                        05 апреля 2023, 09:42
                        0
                        Столкнулся с проблемой, не работает пагинация на других языковых версиях сайта, на базовой версии работает. использую [[!pdoPage]]. При переходе на стр 2, стр 3 и т.д. идет редирект обратно на стартовую страницу.
                        Где копать?
                          Vladimir
                          05 апреля 2023, 13:34
                          0
                          При полной настройки должно работать, без самого кода, и настроек, из чата не помочь, если что можете стучаться @invictusmaneobart
                          Ольга
                          15 марта 2024, 11:04
                          0
                          здравствуйте! а есть еще варианты, в чем косяк, если с AjaxForm в «Системных настройках», раздел «Сессии и куки», параметр «session_cookie_path» поменяли на «/», а сайт все равно пишет 2 сессии. с одной страницы письмо улетает, при переводе уже не работает
                            Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.
                            35