Лексиконы и мультиязычность
По умолчанию значения полей хранятся прямо в конфиге секции (TV mpc_config). Если включить лексиконы, переводимые значения выносятся в lexicon-файлы MODX, а в конфиге остаётся только ключ. Это даёт две вещи сразу:
- мультиязычность — один и тот же ключ резолвится в значение нужного языка по текущему контексту;
- централизованный перевод — все тексты страницы лежат в одном файле, их удобно отдавать на перевод и подменять, не трогая контент в админке.
Подсказка
Лексиконы — опциональный режим. Если сайт одноязычный и переводить контент не нужно, можно вообще не включать — компонент будет работать как раньше, со значениями в конфиге.
Включение
Режим включается системной настройкой mpc_use_lexicons (по умолчанию 0 — выключено). После включения нужно перенарезать шаблоны (mgr_tpl.php), чтобы значения переехали из конфига в лексиконы, а в плейсхолдеры добавился модификатор | lexicon.
Какой контент переводится
Не всё подряд лексиконизируется — только типы контента, перечисленные в настройке mpc_translated_content (по умолчанию text,contact). Тип определяется по тегу/назначению поля при нарезке:
| Тип | Что это | В дефолтном наборе |
|---|---|---|
text | текстовые поля, а также alt и title картинок и ссылок | да |
contact | под-поля контактов (см. ниже) | да |
image | src / srcset изображений | нет |
poster | постер видео | нет |
video | src видео | нет |
audio | src аудио | нет |
Чтобы переводить, например, и пути к картинкам (разные изображения под разные языки), добавьте 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. Дефолтный файл при апгрейде пакета перезапишется, и ваши правила потеряются.
Поддерживаются точные имена и паттерны:
$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).
Уровней — три, по возрастанию приоритета:
- global (page-types) — общий файл для всех страниц. Сюда попадают статические секции. Идентификатор — ресурс «Типы страниц» (
mpc_static_block_page_id). - type (тип страницы) — наследуется всеми страницами одного типа. Это ресурс-донор:
parent= «Типы страниц» и тот жеtemplate, что у страницы. - 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="топик:ключ":
<!-- вход -->
<span data-mpc-lexicon="blocks:promo_title">Скидки до конца месяца</span><!-- выход -->
<span>{'promo_title' | lexicon}</span>Что произошло при нарезке:
- грабер вырезал внутреннее содержимое тега (
Скидки до конца месяца) в файл лексикона топикаblocksпод ключомpromo_title; - каттер заменил содержимое тега на плейсхолдер
{'promo_title' | lexicon}. На рендере он резолвится в значение из лексикона текущего языка — поэтому переключение языка не требует перенарезки.
Вырезается именно внутренний 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> теряется ещё до санитайза:
<!-- вход -->
<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 ещё не нашёл бы ключ и вернул пусто.
Поэтому каттер эмитит отложенный тег с префиксом ##:
##'hero_title' | lexicon}На финальном проходе рендера (когда лексикон ресурса уже загружен) ## меняется на {, и модификатор отрабатывает корректно. Вручную городить {$_modx->lexicon(...)} в шаблоне не нужно — это делает нарезка.
Связанные настройки
Полный список — в разделе Справочник системных настроек. Ключевые для лексиконов:
| Настройка | По умолчанию | Назначение |
|---|---|---|
mpc_use_lexicons | 0 | включить режим лексиконов |
mpc_translated_content | text,contact | какие типы контента переводимы |
mpc_exclude_lexicons_filename | …/services/exclude_lexicons.inc.php | файл со списком исключений |
mpc_lexicon_filename_field | alias | поле ресурса для имени lexicon-файла |
mpc_lexicon_path | …/lexicon/ | папка с lexicon-файлами |
mpc_lexicons_namespace | migxpageconfigurator | namespace лексиконов MODX |
mpc_default_language | ru | язык по умолчанию |
mpc_available_languages | (пусто) | список языков (CSV) |
mpc_contact_lexicon_fields | caption | переводимые под-поля контактов; запись поле — у всех типов, тип:поле — только у этого типа (напр. address:value) |
mpc_arbitrary_lexicon_topics | (пусто) | топики для произвольных лексиконных ключей (CSV) |
mpc_allowed_tags | (пусто) | HTML-теги, не вырезаемые при записи значения в лексикон (CSV; пусто = вырезаются все) |
