Skip to content
  1. Компоненты
  2. MigxPageConfigurator
  3. Лексиконы и мультиязычность

Лексиконы и мультиязычность

По умолчанию значения полей хранятся прямо в конфиге секции (TV mpc_config). Если включить лексиконы, переводимые значения выносятся в lexicon-файлы MODX, а в конфиге остаётся только ключ. Это даёт две вещи сразу:

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

Подсказка

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

Включение

Режим включается системной настройкой mpc_use_lexicons (по умолчанию 0 — выключено). После включения нужно перенарезать шаблоны (mgr_tpl.php), чтобы значения переехали из конфига в лексиконы, а в плейсхолдеры добавился модификатор | lexicon.

Какой контент переводится

Не всё подряд лексиконизируется — только типы контента, перечисленные в настройке mpc_translated_content (по умолчанию text,contact). Тип определяется по тегу/назначению поля при нарезке:

ТипЧто этоВ дефолтном наборе
textтекстовые поля, а также alt и title картинок и ссылокда
contactпод-поля контактов (см. ниже)да
imagesrc / srcset изображенийнет
posterпостер видеонет
videosrc видеонет
audiosrc аудионет

Чтобы переводить, например, и пути к картинкам (разные изображения под разные языки), добавьте image в mpc_translated_content: text,contact,image.

Внимание

Решение «лексиконизировать ли поле» — единое для нарезки, рендера и редактора. Поэтому менять mpc_translated_content нужно с последующей перенарезкой: иначе каттер поставит | lexicon там, где грабер не завёл ключ (или наоборот), и на сайте получите пустую строку.

Исключения

Даже если тип поля переводимый, конкретное имя можно исключить. Список исключений лежит в файле, путь к которому задаёт mpc_exclude_lexicons_filename (по умолчанию components/migxpageconfigurator/services/exclude_lexicons.inc.php).

В дефолтном файле — только generic-исключения, общие для любого проекта: служебные поля MIGX/секций (MIGX_id, section_name, is_static, limit…) и нативное поле ресурса pagetitle (его MODX читает напрямую в <title>, меню, хлебных крошках — там ключ вместо значения недопустим).

Осторожно

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

Поддерживаются точные имена и паттерны:

php
$excludeLexiconFields = [
  'promo_subtitle',          // точное имя поля
  '*_alt',                   // glob: все alt-поля
  'cards_[2n+1]_title',      // числовой токен: нечётные строки списка
  '/^hero_\d+_caption$/',    // regex-литерал
];

Проверка идёт по трём формам ключа: имя поля, полный путь parentFieldName_fieldName и итоговый lex-ключ с префиксом секции.

Три уровня лексиконов

Файлы лежат в mpc_lexicon_path (по умолчанию components/migxpageconfigurator/lexicon/), в подпапке по культуре: {культура}/{идентификатор}.inc.php. Идентификатор (имя файла) берётся из поля ресурса, заданного настройкой mpc_lexicon_filename_field (по умолчанию alias, фоллбэк — id).

Уровней — три, по возрастанию приоритета:

  1. global (page-types) — общий файл для всех страниц. Сюда попадают статические секции. Идентификатор — ресурс «Типы страниц» (mpc_static_block_page_id).
  2. type (тип страницы) — наследуется всеми страницами одного типа. Это ресурс-донор: parent = «Типы страниц» и тот же template, что у страницы.
  3. resource (ресурс) — лексикон самой страницы, файл по её идентификатору.

При рендере файлы грузятся именно в этом порядке, поэтому ресурсный уровень перебивает type, а type — global. Это нужно держать в голове: значение на странице — это «самый специфичный» из найденных ключей.

Внимание

Если секцию перевели из обычной в статическую, её ключи переезжают на уровень page-types, но старые ресурсные ключи нужно вычистить — иначе они перебьют page-types. Компонент делает это сам при сохранении ресурса; подробнее — в разделе Статические секции.

Языки и синхронизация

Язык по умолчанию задаётся настройкой mpc_default_language (по умолчанию ru), список всех языков — mpc_available_languages (CSV, по умолчанию пусто = один язык).

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

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

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

Контакты

У контактов переводимость настраивается отдельно — настройкой mpc_contact_lexicon_fields (по умолчанию только caption). Записи через запятую: поле — переводить у всех типов контактов, тип:поле — только у контакта этого типа. Например, чтобы переводить значение и форматированное значение только у адреса, а подпись — у всех: caption, address:value, address:fvalue (так соцсети и телефон остаются общими для всех языков). Чтобы переводить всё у всех — caption,value,fvalue,attributes. На отдельном блоке контакта набор переопределяет атрибут data-mpc-translate. Подробнее про сами контакты — в разделе Разметка вёрстки.

Произвольный лексиконный ключ

Обычно ключ лексикона привязан к чему-то конкретному: к полю секции, к полю ресурса, к ТВ, к контакту. Но иногда переводимый текст не относится ни к чему из этого — это просто кусок текста в вёрстке, который нужно переводить: подпись в шапке, надпись на кнопке, дисклеймер в подвале. Для таких случаев есть маркер data-mpc-lexicon на не-секционном элементе: он делает содержимое тега произвольным лексиконным ключом — со своим именем и своим файлом, ни к чему не привязанным.

Доступность

Произвольные ключи появились в migxpageconfigurator 1.0.1-rc (под MODX 3) и mpc 2.5.43-rc / mpcVisualEditor 1.0.11-rc (под MODX 2). Поведение под обе версии MODX одинаково.

Маркер и формат

На элементе пишут data-mpc-lexicon="топик:ключ":

html
<!-- вход -->
<span data-mpc-lexicon="blocks:promo_title">Скидки до конца месяца</span>
html
<!-- выход -->
<span>{'promo_title' | lexicon}</span>

Что произошло при нарезке:

  • грабер вырезал внутреннее содержимое тега (Скидки до конца месяца) в файл лексикона топика blocks под ключом promo_title;
  • каттер заменил содержимое тега на плейсхолдер {'promo_title' | lexicon}. На рендере он резолвится в значение из лексикона текущего языка — поэтому переключение языка не требует перенарезки.

Вырезается именно внутренний HTML, а не только текст — инлайн-теги внутри сохраняются:

html
<!-- вход -->
<span data-mpc-lexicon="blocks:promo_title">Скидки <b>до конца</b> месяца</span>
в лексиконе blocks:   'promo_title' => 'Скидки <b>до конца</b> месяца'

Значение проходит санитайз: текст между тегами сохраняется всегда, а из тегов выживают только перечисленные в настройке mpc_allowed_tags (по умолчанию — пусто, то есть все теги вырезаются, остаётся чистый текст). style, обработчики on* и javascript: срезаются в любом случае.

Топик опционален

Топик — это имя файла лексикона ({топик}.inc.php). Его можно не указывать:

  • data-mpc-lexicon="blocks:promo_title" → топик blocks, ключ promo_title;
  • data-mpc-lexicon="promo_title" (без двоеточия) → ключ promo_title, топик по умолчанию = первый топик из настройки mpc_arbitrary_lexicon_topics.

Настройка mpc_arbitrary_lexicon_topics

Это список топиков (через запятую) — текстовая настройка в области mpc_lexicons. Она задаёт две вещи сразу:

  • в какие файлы грабер вырезает произвольные ключи (и какой топик берётся по умолчанию, когда в маркере он не указан — первый в списке);
  • какие файлы догружаются на рендере, чтобы {'ключ' | lexicon} нашёл значение.

Топик из маркера обязан быть в списке

Если указать в маркере топик, которого нет в mpc_arbitrary_lexicon_topics, грабер запишет ключ, но на сайте этот файл не загрузится — и {'ключ' | lexicon} вернёт пусто. Любой топик, который вы используете в data-mpc-lexicon, добавьте в mpc_arbitrary_lexicon_topics.

Правка из визуального редактора

Если установлен mpcVisualEditor, размеченный элемент становится редактируемым прямо на странице — клик открывает инлайн-правку (текст/HTML; через data-mpc-ftype можно переключить на textarea или richtext). Сохранение пишет в текущий язык. Как редактор находит, куда писать:

  • файл (топик): если топик задан в маркере — пишем только в него; если нет — сканируем топики из mpc_arbitrary_lexicon_topics по очереди;
  • ключ: сначала ищем в текущем языке → если нет, в языке по умолчанию (тогда ключ создаётся в текущем языке как перевод) → если нет нигде — ошибка «Ключ не найден».

Готча: блочный тег внутри строчного

Нельзя вкладывать блочные теги (<div>) внутрь строчных контейнеров (<p>, <span>). По стандарту HTML <div> не может находиться внутри <p> — браузер и HTML-парсер закрывают <p> перед <div> и выкидывают <div> наружу. Текст внутри такого <div> теряется ещё до санитайза:

html
<!-- вход -->
<p data-mpc-lexicon="common:msg">Это <b>важное</b> сообщение для <div>всех</div></p>
в лексиконе common:   'msg' => 'Это <b>важное</b> сообщение для'
                              ↑ «всех» и <div> потеряны

Это не баг и не специфика произвольных ключей — то же происходит с любой нарезкой (например с data-mpc-field), потому что причина в самом HTML-парсере. Решения:

  • использовать строчный <span> вместо <div>: …сообщение для <span>всех</span>;
  • либо вешать маркер на сам блочный элемент: <div data-mpc-lexicon="common:msg">…</div>.

Коллизия с секцией

Только на элементах без data-mpc-section

На элементе с data-mpc-section атрибут data-mpc-lexicon имеет другой, давний смысл — это префикс лексикона секции (под ним группируются переводы полей секции). Поэтому произвольный лексиконный ключ работает только на элементах без data-mpc-section.

Почему ##'ключ' | lexicon}, а не {$field | lexicon}

Это деталь реализации, но она объясняет вид плейсхолдеров в нарезанных чанках. Lexicon-файл ресурса подгружается позже первого (eager) прохода parseChunk. Если бы плейсхолдер резолвился на этом проходе ({-префикс), переопределённый модификатор lexicon ещё не нашёл бы ключ и вернул пусто.

Поэтому каттер эмитит отложенный тег с префиксом ##:

fenom
##'hero_title' | lexicon}

На финальном проходе рендера (когда лексикон ресурса уже загружен) ## меняется на {, и модификатор отрабатывает корректно. Вручную городить {$_modx->lexicon(...)} в шаблоне не нужно — это делает нарезка.

Связанные настройки

Полный список — в разделе Справочник системных настроек. Ключевые для лексиконов:

НастройкаПо умолчаниюНазначение
mpc_use_lexicons0включить режим лексиконов
mpc_translated_contenttext,contactкакие типы контента переводимы
mpc_exclude_lexicons_filename…/services/exclude_lexicons.inc.phpфайл со списком исключений
mpc_lexicon_filename_fieldaliasполе ресурса для имени lexicon-файла
mpc_lexicon_path…/lexicon/папка с lexicon-файлами
mpc_lexicons_namespacemigxpageconfiguratornamespace лексиконов MODX
mpc_default_languageruязык по умолчанию
mpc_available_languages(пусто)список языков (CSV)
mpc_contact_lexicon_fieldscaptionпереводимые под-поля контактов; запись поле — у всех типов, тип:поле — только у этого типа (напр. address:value)
mpc_arbitrary_lexicon_topics(пусто)топики для произвольных лексиконных ключей (CSV)
mpc_allowed_tags(пусто)HTML-теги, не вырезаемые при записи значения в лексикон (CSV; пусто = вырезаются все)