Модуль мультиязычности WordPress (ml-switcher) – Полная документация

Введение

Модуль мультиязычности для WordPress[1] предназначен для создания многоязычного сайта на единой установке WordPress без использования сторонних плагинов. Он позволяет хранить переводы записей и страниц внутри одной базы данных, используя префиксы языков в URL. Архитектура модуля обеспечивает высокую производительность и простое управление переводами: все языковые версии контента связаны между собой и обслуживаются одним набором файлов. Важной особенностью является интеграция с искусственным интеллектом (ИИ)[2]: модуль может работать в связке с сервисами машинного перевода, получая автоматические переводы через REST API[3]. Это дает преимущество разработчикам и администраторам — они могут автоматически получать черновые переводы контента от ИИ и затем редактировать их, что ускоряет процесс локализации сайта.

Основной принцип работы модуля заключается в добавлении к URL адресам страниц префикса языка (например, /en/ для английской версии, /ru/ для русской и т.д.), а также в фильтрации контента по текущему языку. При заходе пользователя на сайт модуль определяет предпочтительный язык (на основе URL, cookie или настроек браузера) и загружает соответствующую языковую версию страницы. Пользователь может легко переключаться между языками с помощью специального переключателя языков на сайте. Модуль автоматически сохраняет выбор языка пользователя и обеспечивает корректное отображение содержимого на выбранном языке при последующих визитах.

Данная документация адресована разработчикам и администраторам WordPress. В ней подробно описаны назначение и принцип работы модуля, его архитектура, алгоритмы функционирования, а также преимущества использования. Приведены инструкции по установке, настройке интерфейса переключения языков и интеграции с ИИ для получения переводов. В документацию включены диаграмма архитектуры решения и блок-схема процесса переключения языка для лучшего понимания, описан алгоритм работы шаг за шагом. Также вы найдете примеры кода для интеграции, советы по обслуживанию модуля, раздел по возможным ошибкам и их отладке, а в конце – глоссарий терминов с пояснениями[4].

ШагКлючевой код / крючок WPЧто происходит
Загрузка плагинаplugins_loadedPlugin::instance()Создаётся singleton‑объект, читаются настройки языков, регистрируются таксономия ml_language, REST‑meta, хуки фронта и админки
Определение текущего языкаcurrentLang()Последовательность: URL‑префикс → cookie ml_lang → Accept‑Language → язык по умолчанию
Принудительный префикс + редиректtemplate_redirectmaybeRedirect()Если в URL нет корректного префикса, плагин формирует /{lang}/…, проверяет, существует ли страница, задаёт cookie и делает 302‑редирект
Фильтрация запросовpre_get_postsГлавный WP‑запрос ограничивается постами с термином таксономии ml_language = currentLang()
Сохранение постаsave_postonSavePost()Посту ставится текущий язык и общий group_id. При включённой опции создаются черновики‑заглушки для остальных языков
Фронт‑энд переключательprintFlagSwitcher() + CSS + JSВ <body> выводится фиксированная кнопка‑флаг c выпадающим списком. Кнопка стилизована в ml-switcher.css; раскрытие/закрытие реализовано в ml-switcher.js
SEO‑тегиwp_headinjectHreflang()На страницах‑записях выводятся ссылки <link rel="alternate" hreflang="…"> для всех переводов

Архитектура модуля

Модуль состоит из клиентской и серверной частей, которые совместно обеспечивают мультиязычность. На стороне клиента – это плавающий переключатель языков в интерфейсе сайта (кнопка с выпадающим списком флагов стран и названий языков), стилизованный CSS и управляющий JavaScript-код. На стороне сервера – PHP-движок MultilingualEngine.php, который интегрируется в WordPress и выполняет всю логику переключения языка: перенаправление на нужный URL с языковым префиксом, фильтрацию запросов к базе данных по языку, создание связанных записей-переводов и т.д. Ниже представлена общая схема архитектуры модуля и его взаимодействия с окружением WordPress.

flowchart LR
    subgraph WordPress
        CoreWP["WP ядро<br/>router & loop"]
        Tax["Taxonomy<br/>ml_language"]
        Meta["Meta:<br/>_ml_group_id / _ml_language"]
    end

    subgraph Plugin["MultilingualEngine (Plugin.php)"]
        Detect["Language&nbsp;Detector<br/>(URL,Cookie,Header)"]
        Redirect["Redirect&nbsp;Manager"]
        QueryFlt["pre_get_posts<br/>filter"]
        Saver["onSavePost<br/>handler"]
        Switcher["Flag&nbsp;Switcher<br/>HTML builder"]
        SEO["hreflang<br/>injector"]
    end

    Assets["Frontend assets<br/>ml-switcher.css + .js"]:::asset
    Browser["Браузер<br/>(клики пользователя)"]:::user

    CoreWP -->|hooks| Plugin
    Detect --> Redirect
    Detect --> QueryFlt
    Saver --> Tax
    Saver --> Meta
    Switcher --> Assets
    Assets -.-> Browser
    Browser -.клик flag.-> Redirect
    SEO --> CoreWP

    classDef asset fill:#e0f8ff,stroke:#0aa;
    classDef user fill:#fff2cc,stroke:#c90;


Рис. 1. Общая архитектура решения: модуль встроен в среду WordPress и взаимодействует с браузером пользователя, базой данных и внешним AI-сервисом перевода.

Как видно из диаграммы (рис. 1), в центре находится WordPress с установленным плагином Multilingual Engine. Модуль регистрирует собственные компоненты в системе WordPress:

  • Таксономия ml_language – специальная невидимая таксономия WordPress, с помощью которой каждому посту или странице присваивается метка языка (напр. en, ru и т.д.)[5]. Все записи на определенном языке объединены этой меткой.
  • Группа переводов – у всех связанных переводов одного материала есть общий идентификатор группы (хранится в метаполе _ml_group_id). При сохранении оригинальной записи модуль генерирует уникальный ID группы и присваивает его оригиналу и всем черновикам переводов, что позволяет связать записи между собой.
  • Shortcode [language_switcher] – шорткод, который выводит выпадающий список для переключения языка (альтернативный интерфейс вместо плавающей кнопки)[6]. Также регистрируется виджет “Language Switcher”, который можно добавить в области виджетов темы (например, в боковую колонку) с тем же функционалом.
  • Опции настроек – в консоли WordPress (раздел “Настройки – Общие”) модуль добавляет поля для указания доступных языков (список кодов), выбора языка по умолчанию и включения/отключения автоматического создания черновиков переводов. Эти параметры сохраняются в базе данных и влияют на поведение модуля.

На схеме видно, что браузер пользователя посылает HTTP-запросы на сервер WordPress, которые обрабатываются плагином. Алгоритм определения языка работает следующим образом: модуль проверяет, указан ли языковой префикс в запрошенном URL. Если да – этот префикс воспринимается как текущий язык страницы. Если нет – плагин пытается определить предпочтительный язык пользователя: сперва из сохраненного cookie[7] с выбором языка (если такое cookie было ранее установлено), иначе – анализирует заголовок браузера Accept-Language[8] (берет первые две буквы – код языка, если он входит в список поддерживаемых). Если ни cookie, ни заголовок не дали приемлемого результата, используется язык по умолчанию, заданный в настройках плагина.

После определения языка плагин осуществляет перенаправление, если необходимо. Например, если пользователь зашел на сайт по адресу без префикса (например, https://example.com/about) и модуль решил, что должен быть язык en, произойдет перенаправление (HTTP 302) на https://example.com/en/about. Модуль проверяет, существует ли такой URL (есть ли опубликованная страница “about” с меткой языка en); если да – посетитель попадет на страницу на английском. Если запрошен URL с неправильным или отсутствующим префиксом, но при этом не найден соответствующий контент, модуль не перенаправляет, чтобы не вызвать лишних ошибок: в этом случае сработает стандартная обработка (например, будет показана страница 404, если действительно такой страницы нет ни на одном языке).

Когда загружается страница с правильным языковым префиксом, модуль сохраняет текущий язык в cookie ml_lang (сроком на 1 год) – это нужно, чтобы запомнить выбор пользователя. Далее происходит загрузка контента: модуль “подмешивает” в основной запрос WordPress условие фильтрации по таксономии языка, чтобы выводились только записи нужного языка. Таким образом, на многоязычном сайте одна и та же ссылка с разными префиксами (/en/..., /fr/...) отобразит разные записи из одной группы переводов.

Кроме этого, серверная часть модуля выполняет ряд вспомогательных функций:

  • Автоматическое создание “пустых” черновиков для всех языков при сохранении новой записи на сайте (если включена соответствующая опция). Например, если сайт поддерживает английский, испанский и русский, и администратор публикует новую запись на английском, модуль сам создаст черновики той же записи (с тем же заголовком) для испанского и русского языков. Эти черновики связаны общей группой переводов и изначально скрыты от посетителей (не опубликованы).
  • Добавление hreflang-ссылок в HTML-код страницы[9]: на каждой странице модуль генерирует теги <link rel="alternate" hreflang="..." href="..."> для всех языковых версий данной страницы. Это улучшает SEO, подсказывая поисковым системам доступные языковые версии контента.
  • Вывод плавающего переключателя языков (HTML-код кнопки и списка ссылок) на каждую страницу сайта. Этот код подключается в начале и в конце шаблона страницы (события wp_body_open и wp_footer темы), чтобы гарантировать появление переключателя независимо от структуры темы.
  • Загрузка статических ресурсов: модуль автоматически подключает файл стилей ml-switcher.css и скрипт ml-switcher.js на стороне клиента (для управления выпадающим списком языков). Эти файлы хранятся в каталоге плагина assets и регистрируются через стандартные функции wp_enqueue_style и wp_enqueue_script.

Наконец, модуль взаимодействует с внешними сервисами перевода (ИИ) через REST API WordPress. Он не содержит встроенного механизма вызова стороннего API, но за счет открытых REST-эндпойнтов позволяет внешней программе получить список записей, требующих перевода, и загрузить переводы обратно. Структура базы данных (с таксономией языков и метой группы) упрощает интеграцию: зная ID группы переводов, можно программно собрать все связанные версии записи и обновить их содержимое. Подробнее об этом – в разделах по интеграции с ИИ.

Алгоритм работы модуля

Ниже приведена блок-схема, иллюстрирующая процесс определения и переключения языка при работе модуля (рис. 2), а также пошаговое описание алгоритма.

Рис. 2. Упрощенная блок-схема процесса определения и переключения языка модулем.

  1. Запрос страницы: Пользователь запрашивает страницу сайта (впервые или при переключении языка). Браузер отправляет HTTP-запрос на сервер WordPress, включая сохраненное cookie языка (если оно было) и заголовок Accept-Language с предпочтениями локали.
  2. Определение языка: Плагин анализирует запрошенный URL. Если URL содержит префикс языка (например, /ru/), этот язык принимается как текущий. Если префикса нет, модуль последовательно проверяет: сохранен ли cookie ml_lang с языком у пользователя; если да – выбирается язык из cookie. Если cookie отсутствует, берется код языка из заголовка браузера (первые 2 буквы, например en из en-US) при условии, что этот язык поддерживается сайтом. Если не удалось определить – подставляется язык по умолчанию, настроенный администратором.
  3. Перенаправление (если нужно): Если определенный на предыдущем шаге язык (например, ru) не совпадает с префиксом в запрошенном URL (или префикса не было), модуль сгенерирует новый URL с корректным префиксом и выполнит перенаправление (HTTP 302) на него. Например, запрос https://site.com/about перенаправится на https://site.com/en/about, если выбранный язык en. Перед редиректом модуль проверяет, существует ли такая страница – чтобы не перенаправлять на несуществующий адрес. Если проверка не прошла (целевая страница не найдена), перенаправление не выполняется.
  4. Фильтрация контента: После возможного редиректа WordPress загружает страницу. Модуль встраивается в процесс выборки контента: он добавляет в главный запрос условие (таксономию языка), которое ограничивает записи текущим языком. Это гарантирует, что на странице будут только посты и меню нужного языка. Одновременно записывается cookie ml_lang с выбранным языком (чтобы в будущем пользователь сразу попадал на правильную локаль).
  5. Вывод страницы: Пользователю отдается страница на нужном языке. В код страницы модуль добавляет блок переключателя языков (HTML-код кнопки/списка) и теги hreflang для поисковых систем. Браузер пользователя отображает страницу. Если пользователь меняет язык через переключатель (выбирает другой язык), цикл повторяется с шагa 1 для нового языка: происходит переход по соответствующей локализованной ссылке, и алгоритм определения языка запускается снова.

Установка и подключение

Модуль мультиязычности поставляется в виде плагина WordPress. Для установки необходимо скопировать файлы плагина (MultilingualEngine.php, ml-switcher.css, ml-switcher.js и др.) в каталог wp-content/plugins/MultilingualEngine на вашем сайте. После этого активируйте плагин через панель администратора WordPress (раздел “Плагины”). Требования для работы модуля: WordPress версии 6.8 или выше и PHP версии не ниже 8.3.

После активации плагина перейдите в админ-панель WordPress в раздел “Настройки – Общие”. Пролистайте страницу вниз – вы увидите новые настройки модуля мультиязычности:

  • Доступные языки: текстовое поле для ввода кодов языков, поддерживаемых сайтом (через запятую). Укажите здесь 2-буквенные коды всех языков, которые будут использоваться (например: en, ru, fi).
  • Язык по умолчанию: выпадающий список, где можно выбрать один из указанных выше языков – тот, который будет использоваться по умолчанию для новых посетителей (если их язык не определен).
  • Автоматически создавать черновики переводов: флажок, включающий автосоздание пустых переводов при добавлении нового поста. Если опция активна (по умолчанию активна), модуль будет генерировать черновики для всех остальных языков при каждом создании записи. Если отключить – администратор должен будет вручную добавлять переводы для новых записей.

Важно: После первой активации плагина рекомендуется назначить языки всем существующим записям. Для этого на странице настроек присутствует кнопка “Assign default language for all posts/records” (в русскоязычном интерфейсе может называться “Назначить язык по умолчанию для всех записей”). Нажмите эту кнопку один раз – плагин пометит все старые записи и страницы как принадлежащие языку по умолчанию. Это необходимо, чтобы старый контент начал отображаться (иначе он будет фильтроваться из-за отсутствия метки языка).

Если ваш сайт изначально был одностоязычным, после выполнения вышеуказанной операции все существующие записи получат метку языка (например, “en”). Вы можете по необходимости создать переводы: откройте запись в админке и переключите язык (в выпадающем списке “Язык” в метабоксе или другом UI, предусмотренном плагином). Там вы увидите черновики для других языков (если автосоздание было включено) – можете заполнить их содержимым и опубликовать. Если черновики не создавались автоматически, добавьте переводы вручную через кнопку “Добавить перевод” (механизм зависит от интерфейса плагина, например, это может быть кнопка или плюс рядом с переключателем языка).

Таким образом, порядок запуска модуля:

  1. Установить и активировать плагин.
  2. Задать список поддерживаемых языков и язык по умолчанию в настройках.
  3. Назначить языки всем старым записям (кнопкой “Assign default language…”).
  4. При необходимости – заполнить или отредактировать переводы существующего контента (через админку WordPress).
  5. Проверить работу переключения языков на сайте во фронтенде.

Настройка интерфейса переключения языка

Модуль автоматически выводит на страницах сайта элемент интерфейса для выбора языка. По умолчанию используется плавающий переключатель в правом верхнем углу экрана: это кнопка с изображением флага текущего языка, при нажатии на которую раскрывается список всех доступных языков с флажками. Данный блок подключается модулем без дополнительной настройки – сразу после активации плагина вы увидите переключатель на сайте (если тема WordPress корректно вызывает хуки wp_body_open или wp_footer).

Переключатель стилизован файлами ml-switcher.css и ml-switcher.js, которые плагин автоматически подключает на фронтенде. CSS-файл определяет внешний вид блока (позицию, фон, рамки) и стилит элементы списка языков. JavaScript-файл отвечает за работу выпадающего списка: он отлавливает клик по кнопке переключения и показывает/скрывает список, а также закрывает список при клике в любом другом месте страницы. Ниже приведен упрощенный пример HTML-кода, который генерирует модуль для переключателя:

<div class="ml-switcher-box">
    <button class="ml-switcher-button" type="button">
        <img src="/wp-content/plugins/MultilingualEngine/assets/flags/us.svg" alt="" width="50" height="30">
    </button>
    <div class="ml-switcher-dropdown">
        <a href="https://site.com/en/page">
            <img src="/wp-content/plugins/MultilingualEngine/assets/flags/us.svg" alt="" width="25" height="15"> EN
        </a>
        <a href="https://site.com/ru/page">
            <img src="/wp-content/plugins/MultilingualEngine/assets/flags/ru.svg" alt="" width="25" height="15"> RU
        </a>
        <!-- ... ссылки на другие языки ... -->
    </div>
</div>

В примере выше button.ml-switcher-button содержит флаг текущего языка (50×30 пикселей), а выпадающий div.ml-switcher-dropdown – скрыт по умолчанию и при активации отображает список языков. Каждый пункт – это ссылка на ту же страницу, но с другим языковым префиксом, включающая мини-флаг (25×15 пикселей) и код языка (EN, RU и т.д.). При клике на такой пункт происходит переход на соответствующую локализованную страницу (либо черновик перевода, либо опубликованный перевод). Скрипт ml-switcher.js гарантирует, что список закрывается при повторном нажатии на кнопку или при клике вне списка.

Вы можете изменить внешний вид переключателя языков с помощью CSS. Например, чтобы изменить расположение или цвета, вы можете переопределить стили классов .ml-switcher-box, .ml-switcher-dropdown и т.д. через файл стилей вашей темы или кастомный CSS. По умолчанию:

  • .ml-switcher-box позиционирован position: fixed; и расположен в правом верхнем углу страницы (top: 12px; right: 12px;). Вы можете изменить эти значения для перемещения блока в другое место экрана.
  • .ml-switcher-button – это сама кнопка-флаг. У нее убраны стандартные границы и фон, заданы фиксированные размеры (50x30px) и скруглены углы. Внутри кнопки находится тег <img> с флагом.
  • .ml-switcher-dropdown – выпадающий список. Изначально display: none; (спрятан). Скрипт меняет стиль на display: block; при раскрытии. Стили задают белый фон, серую рамку и небольшую тень для выпадающего меню, а также позиционируют его сразу под кнопкой.
  • .ml-switcher-dropdown a – элементы ссылок на языки. Им задан внутренний отступ, выравнивание по центру по вертикали (align-items: center;) и иконка (флаг) слева от текста языка. При наведении (:hover) фон пункта слегка затемняется.

Если вы хотите полностью отключить плавающий переключатель (например, чтобы встроить переключатель в меню сайта), вы можете воспользоваться альтернативными способами:

  • Виджет “Language Switcher”: Плагин регистрирует виджет, который вы можете добавить через “Внешний вид – Виджеты”. Виджет по умолчанию выводит простой выпадающий список языков (HTML-элемент <select>). Вы можете разместить его, например, в боковой колонке или подвале сайта. Этот виджет – альтернативный способ, он отключает плавающий блок, когда активен, и может быть стилизован отдельно.
  • Шорткод [language_switcher]: Вы можете вставить шорткод [language_switcher] в любое место контента или шаблон (через do_shortcode в PHP) – он выведет выпадающий список языков (такой же, как виджет). Например, добавив [language_switcher] в шапку страницы или меню, вы получите переключение языков встроенное в дизайн сайта.

При использовании виджета или шорткода встроенный плавающий переключатель (кнопка с флагом) по-прежнему выводится (так как он жёстко подключается через wp_body_open/wp_footer). Если он вам не нужен, вы можете отключить его через небольшой фрагмент кода: например, добавить в файл functions.php вашей темы действие, которое удалит хук вывода переключателя. В коде плагина видно, что он привязан к wp_body_open и wp_footer:

add_action('wp_body_open', [$plugin, 'printFlagSwitcher']);
add_action('wp_footer',    [$plugin, 'printFlagSwitcher']);

Чтобы отменить это, вы можете вызвать:

remove_action('wp_body_open', [$plugin_instance, 'printFlagSwitcher']);
remove_action('wp_footer',    [$plugin_instance, 'printFlagSwitcher']);

где $plugin_instance = MultilingualEngine\Plugin::instance(); – singleton-объект плагина. После удаления этих хуков плавающий переключатель не будет выводиться, и вы сможете использовать только виджет/шорткод для переключения языков (например, интегрировать его в свое меню). Этот шаг рекомендуется делать разработчикам, уверенным в своих действиях, и после сохранения резервной копии файлов.

Флаги для языков хранятся в папке assets/flags плагина в виде SVG-файлов. По умолчанию используются флаги стран для каждого языка (плагин сопоставляет коды языков с кодами стран ISO – например, en -> us.svg, de -> de.svg, fr -> fr.svg и т.д.)[10]. Если для какого-то языка файл не найден, на кнопке вместо флага отображается символ “” (эмодзи глобуса) как запасной вариант. Вы можете добавить собственные SVG-иконки для дополнительных языков – достаточно сохранить файл с названием по коду страны (ISO 3166-1 alpha-2) в эту папку. Например, для финского языка (fi) можно добавить fi.svg. Размеры флагов можно регулировать через CSS (свойства width и height в стилях .ml-switcher-button img и .ml-switcher-dropdown img).

Серверная обработка переключения языка (PHP)

В серверной части модуль реализован в файле MultilingualEngine.php, который содержит класс плагина и логику обработки языков. Основные аспекты работы серверной части:

  • Определение языка запроса: Метод currentLang() осуществляет определение текущего языка на основе URL, cookie и заголовков. Он возвращает код языка (строку, например «en» или «ru»). Если URL содержит префикс, он приорететен. Если нет, проверяется cookie ml_lang, затем HTTP_ACCEPT_LANGUAGE. Если ничего не сработало – возвращается язык по умолчанию. Таким образом, на каждом запросе сервер «знает», какой язык должен быть активен.
  • Перенаправление на URL с префиксом: В хуке template_redirect модуль регистрирует функцию maybeRedirect(). Эта функция выполняется перед генерацией страницы. Она проверяет: если текущий URL не начинается с нужного языкового префикса (который определен currentLang()), и при этом существует страница по адресу с таким префиксом – производится редирект (HTTP 302) на этот URL. После перенаправления устанавливается cookie языка (чтобы запомнить выбор). Данная логика предотвращает ситуации, когда пользователь оказывается на несоответствующей локали (например, заходит на «/» – получает редирект на «/en/»).
  • Фильтрация основного запроса: В хуке pre_get_posts модуль подключает функцию preGetPosts(), которая вмешивается в главный WP_Query на фронтенде. Она добавляет параметр tax_query с условием ml_language = [текущий_язык] для основных запросов на сайте (если это не админка и не специальный запрос). Проще говоря, WordPress при выборке записей на блоге, в категориях, в меню – получит только записи на текущем языке. Это происходит автоматически и прозрачно для темы: вы увидите только нужные посты без дополнительных усилий.
  • Привязка языков к записям: В хуке save_post модуль регистрирует метод onSavePost(), который выполняется при каждом сохранении записи (поста или страницы) в админке. Если запись новая (еще не имеет меток языка), функция: 1) присваивает ей текущий язык (таксономия ml_language) и уникальный идентификатор группы переводов (мета-ключ _ml_group_id), 2) если включена опция автосоздания черновиков – пробегается по списку доступных языков и для каждого другого языка создает пустой черновик (через wp_insert_post), помечает его соответствующей меткой языка и тем же group_id, тем самым формируя пакет связанных записей. Эти черновики можно найти в админке (будут в статусе “Черновик” на своем языке). Логика также учитывает механизмы автосохранения и ревизий, чтобы лишний раз не создавать дублей.
  • Вывод тегов hreflang: В хуке wp_head модуль регистрирует метод injectHreflang(). Он выполняется на отдельных страницах (is_singular()) и генерирует для каждой другой языковой версии текущей записи тег <link rel="alternate" hreflang="xx" href="URL">. Для этого код получает все записи с таким же _ml_group_id, что и у текущей, фильтрует опубликованные и выводит их permalink с соответствующим кодом языка. Эти метатеги помогают поисковикам понимать, на каких URL лежит тот же контент на других языках.
  • Прочие интеграции: Плагин регистрирует шорткод [language_switcher] и виджет (класс LanguageWidget), о которых упоминалось ранее. Также плагин добавляет в админ-меню для удобства отдельные пункты “Posts (EN)” / “Posts (RU)” и т.п. под разделом “Записи” и “Страницы” – они фильтруют список записей в админке по языку (параметр ml_language в URL). Это реализовано через метод addAdminLanguageMenus() и фильтр pre_get_posts для админки (adminLanguageFilter()). Таким образом, администратор может легко переключаться между списками записей разных языков прямо в меню.

Весь обмен данных между клиентом и сервером сводится к тому, что при запросах сервер опирается на URL (префикс) и cookie для понимания, какой язык требуется. Сервер не использует сессии или другие механизмы – только стандартные HTTP-инструменты. Это упрощает масштабируемость: например, кеширование страниц может учитываться на уровне URL (разный URL – разные версии кеша).

Если рассмотреть пример: администратор создал запись “Hello World” на английском. Плагин присвоил ей group_id mlg_abc123 и таксономию ml_language = en, затем создал черновики этой записи для say, «ru» и «fi». Когда посетитель заходит на /en/hello-world, WordPress на уровне запросов: 1) перенаправит на себя (префикс уже есть, редирект не нужен), 2) отфильтрует основной запрос, чтобы он вывел только пост с ml_language = en. Страница отобразит английский текст. При переходе на русский (нажав переключатель – перейдя на /ru/hello-world), сервер: 1) увидит префикс “ru”, 2) если черновик “Hello World” на русском еще не опубликован, WordPress все равно выведет его (пусть пустой или с пометкой “черновик”, в зависимости от реализации шаблона), потому что запрос фильтруется по ml_language = ru и group_id тот же. Администратор может заполнить этот черновик русским переводом и опубликовать – тогда страница начнет показывать контент.

Серверная обработка также включает некоторые утилитарные вещи:

  • Метод translateUrl($url, $lang) – статическая функция, которая берет произвольный URL (на текущем сайте) и заменяет/добавляет в нем языковой префикс $lang. Она используется для генерации ссылок на языковые версии (например, для переключателя и hreflang).
  • Работа с cookie через maybeSetCookie($lang) – устанавливает cookie ml_lang на год вперед, если оно не установлено или отличается от $lang. Вызывается при редиректах и других случаях, чтобы сохранить выбор пользователя.
  • Точка входа плагина plugins_loaded – инициализирует класс плагина (Singleton), читаются настройки (список языков, язык по умолчанию) из базы через get_option, регистрируются таксономии и метаполя (в registerTaxonomy() и registerRestMeta()), подключается перевод (textdomain) и развешиваются все необходимые хуки (redirect, filter, save_post, wp_head и пр.).

Из всего вышеперечисленного важно помнить, что серверная часть занимается управлением данными и потоками навигации. Она не выполняет никаких переводов сама по себе – то есть не вызывает API переводчиков и не меняет текст контента “на лету”. Переводы контента должны быть либо введены вручную администратором, либо загружены через интеграцию с ИИ (о чем ниже). Сервер хранит оригиналы и переводы как отдельные записи, связанные группами.

Интеграция с ИИ через REST API

Одним из преимуществ данного модуля является возможность интеграции с системами машинного перевода или другими внешними сервисами для автоматизации перевода контента. Плагин не содержит встроенного механизма обращения к API перевода (например, к OpenAI или Yandex.Translate), однако его архитектура облегчает реализацию такого взаимодействия внешними скриптами.

В основе интеграции лежит REST API WordPress[11], который позволяет получать и обновлять записи, включая их метаданные, с помощью HTTP-запросов. Плагин регистрирует метаполя _ml_group_id и _ml_language как доступные через REST (параметр show_in_rest => true при register_post_meta), а также обеспечивает, что язык записи виден через таксономию. Это значит, что внешнее приложение может:

  • Получить список всех записей на сайте, включая указание их языка и группы переводов.
  • Искать записи, принадлежащие определенной группе переводов (например, все языковые версии конкретной статьи).
  • Создавать новые записи (черновики переводов) или обновлять содержимое существующих записей через REST API, сохраняя правильные метаполя языка и группы.

Например, возможен следующий сценарий интеграции с внешним сервисом перевода:

  1. Внешний скрипт (например, на Python) запрашивает через REST API список черновиков переводов, которые еще не переведены. Для этого он может выполнить GET-запрос: /wp-json/wp/v2/posts?status=draft&meta_key=_ml_group_id&meta_value=mlg_... – фильтруя по группе конкретной записи или перебирая все draft записи другого языка.
  2. Скрипт обнаруживает, что, например, для group_id mlg_abc123 есть запись на языке “ru” со статусом draft и пустым содержимым. Он также получает через REST оригинал (опубликованную запись, скажем, на “en”) с тем же group_id, чтобы взять из нее исходный текст (поля title, content).
  3. Скрипт отправляет текст оригинала на внешний API перевода (например, OpenAI GPT-4, DeepL или другой провайдер) и получает переведенный текст на русский язык.
  4. Далее скрипт делает PUT-запрос через REST API к черновику “ru” (уже зная его ID): передает в теле JSON обновленные поля title и content (переведенные). Благодаря тому, что _ml_language и таксономия уже привязаны, ему не нужно это менять – достаточно обновить содержание.
  5. Опционально, скрипт может сразу опубликовать запись, изменив ее статус на publish через REST, или оставить в черновиках для ручной проверки редактором.

При такой схеме плагин обеспечивает структуру данных, а внешняя программа – только перевод текста и взаимодействие через API. Важный момент – авторизация: REST API требует, чтобы запросы на создание/обновление записей были аутентифицированы (например, с помощью JWT-токена, Basic Auth или Application Passwords WordPress). В конфигурации плагина указано 'auth_callback' => fn() => current_user_can('edit_posts') для метаполей – это значит, что редактировать языковые поля можно только аутентифицированному пользователю с правами редактора. Поэтому внешнему скрипту нужно либо использовать ключ авторизации, либо работать в контексте учётной записи администратора.

Через REST API доступны не только записи, но и таксономия ml_language. Однако плагин установил show_in_rest = false для таксономии языка, поэтому проще оперировать метаполем _ml_language или фильтром ?ml_language=xx (в REST-запросах). Например, можно получить все записи на русском GET-запросом: /wp-json/wp/v2/posts?ml_language=ru – благодаря фильтру adminLanguageFilter такой запрос будет понят и исполнен (хотя это скорее вспомогательный эффект).

Следует отметить, что интеграцию с ИИ можно организовать и внутри WordPress, создав отдельный плагин или WP-CLI команду. Например, можно написать WP-CLI команду wp ml_engine translate missing --to=ru, которая найдет все незаполненные русские черновики и заполнит их переводом через API. Но это выходит за рамки данного модуля – такой функционал нужно реализовать отдельно, используя возможности, которые предоставляет модуль (метки языка, group_id и REST API).

Подытожим: модуль мультиязычности совместно с REST API WordPress дает вам “каркас” для интеграции:

  • Структурированные данные (группы переводов, указание языка у записей).
  • Возможность внешнего доступа и управления этими данными через стандартные REST эндпойнты.
  • Отсутствие жесткой привязки к конкретному сервису перевода – вы свободны выбрать любой и интегрировать его.

Используя это, вы можете связать сайт, например, с облачным ИИ-сервисом, который будет “подтягивать” новые записи, переводить их и возвращать переводы на сайт. Такая архитектура более гибкая и надежная: основной сайт остается легковесным, а все тяжелые операции (как вызов ИИ) вынесены наружу.

Загрузка переводов через WP REST API

Данный раздел дополняет предыдущий и описывает более прикладной пример получения и загрузки переводов с помощью HTTP-запросов к WordPress REST API. Предположим, у нас есть сайт на WordPress с установленным модулем, и мы хотим написать скрипт на Python, который переведет все записи на новый язык (например, добавили поддержку испанского es) с помощью сервиса OpenAI.

1. Получение списка записей для перевода. Сначала через REST API получим все записи, требующие перевода на испанский. Скорее всего, после добавления языка “es” и сохранения настроек, для всех существующих записей модуль уже создал черновики с ml_language = es. Нам нужно найти их. Один из способов:

curl -u admin:password \
  "https://example.com/wp-json/wp/v2/posts?status=draft&ml_language=es"

Этот запрос вернет JSON-массив постов со статусом draft и таксономией ml_language = es. В каждом объекте поста будут метаполя _ml_group_id и _ml_language (WordPress выдает их обычно внутри meta). Нас интересует содержимое оригинала для перевода – как его получить? У каждого черновика есть поле meta._ml_group_id. Используя его, мы можем найти связанный опубликованный пост (например, который на языке по умолчанию). Выполним второй запрос для первой записи в списке (подставив ее group_id):

curl -u admin:password \
  "https://example.com/wp-json/wp/v2/posts?meta_key=_ml_group_id&meta_value=mlg_abc123&status=publish"

Этот запрос вернет опубликованные записи, у которых group_id = mlg_abc123. Как правило, это будет оригинал (например, английская версия). Из ответа забираем title.rendered и content.rendered – это исходный заголовок и текст.

2. Отправка текста на перевод. Далее с помощью библиотеки для OpenAI (или HTTP запросом) отправляем полученный текст на перевод. Например, используя OpenAI API:

response = openai.ChatCompletion.create(
  model="gpt-4",
  messages=[
    {"role": "system", "content": "Translate the following text to Spanish."},
    {"role": "user", "content": original_text}
  ]
)
translated_text = response["choices"][0]["message"]["content"]

Предположим, OpenAI вернул перевод. Теперь у нас есть переменные translated_title и translated_content.

3. Загрузка перевода на сайт. Нам нужно обновить черновик поста (с ID, скажем, 42) на испанском. Сделаем PUT запрос к REST API:

curl -X PUT -u admin:password \
  -H "Content-Type: application/json" \
  -d '{"title":"Título traducido", "content":"

Contenido en español...

", "status":"publish"}' \ "https://example.com/wp-json/wp/v2/posts/42"

Это обновит заголовок, контент и опубликует пост (из draft в publish). В теле JSON мы можем указать либо сырой HTML (например, если контент содержит теги абзацев, заголовков и т.д.), либо чистый текст – WordPress сам обернет его абзацами. Здесь важно, что мы не трогаем метаполя: _ml_group_id и _ml_language уже привязаны, а таксономия ml_language для поста 42 = “es” уже задана. Таким образом, наш PUT-запрос просто дополнил запись содержимым и опубликовал.

4. Результат на сайте: После такого обновления посетители смогут перейти на /es/slug и увидеть испанский перевод записи. Плагин благодаря group_id и hreflang автоматически привяжет эту версию к остальным языкам. Если на сайте включено кэширование страниц, возможно, нужно сбросить кеш для данной URL (в зависимости от используемого плагина кеша).

Повторив эти шаги для всех записей (например, скриптом в цикле по всем черновикам “es”), мы быстро наполним сайт машинными переводами. Конечно, качество перевода зависит от ИИ, и стоит предусмотреть проверку и редактуру. Можно опубликовать переводы сразу (как выше) или оставить их черновиками и уведомить редактора.

Совет: При интеграции с внешними API старайтесь не превышать допустимые лимиты запросов (rate limits). Например, OpenAI GPT-4 может быть медленным или дорогим для большого объема текста. Переводите пакетами или используйте очереди. Модуль мультиязычности не накладывает ограничений на скорость публикации записей, но внешние сервисы – могут.

Безопасность: REST API WordPress – мощный инструмент, но требует защиты. Рекомендуется создавать отдельного технического пользователя с правами редактора, генерировать для него Application Password и использовать его для аутентификации API-запросов (вместо передачи явного пароля администратора). Application Password позволяет легко отозвать доступ, не раскрывает основной пароль и логируется в WordPress.

Подытоживая, загрузка переводов через WP REST API – это вопрос написания скриптов, использующих стандартные REST эндпойнты. Модуль же обеспечивает консистентность данных: все переводы будут правильно связаны, помечены и отображены, как только вы их загрузите и опубликуете.

Создание и хранение языковых данных

В традиционных мультиязычных решениях для WordPress нередко используются отдельные таблицы или файлы (например, PO/MO-файлы) для перевода строк. В данном модуле подход другой: весь переводимый контент хранится в базе данных WordPress как обычные записи (посты и страницы). При этом:

  • Не создаются дополнительные таблицы – используются стандартные механизмы: таксономия для языков и метаполя для группировки переводов.
  • Каждая языковая версия – это полноценная запись. Например, “О компании (RU)” – отдельная запись с собственным ID, заголовком и содержимым, связанная с “About us (EN)” через group_id.
  • Переводами управляет WordPress – они редактируются в админке как обычно (просто переключаясь на нужный язык), сохраняются ревизии, можно использовать встроенные поля (категории, теги) для каждой версии независимо, если нужно.
  • Статический контент темы (строки шаблонов, кнопок) – не переводится этим модулем. Он предназначен именно для перевода записей/страниц. Строки темы по-прежнему следует локализовать средствами WordPress (через __() и PO-файлы).

Модуль хранит некоторые служебные данные:

  • wp_options.ml_available_languages: опция (сериализованный массив или строка) с доступными кодами языков.
  • wp_options.ml_default_language: язык по умолчанию (код).
  • wp_options.ml_auto_create_drafts: флаг (0/1) автосоздания черновиков.
  • wp_postmeta._ml_group_id: у каждой записи, участвующей в мультиязычных группах, есть этот мета-ключ (например, mlg_641f94840eb47). Записи с одинаковым значением принадлежат одной группе перевода (т.е. переводы друг друга).
  • wp_postmeta._ml_language: у каждой записи (кроме случаев, когда опция _ml_language использовалась в более ранних версиях) хранится код языка. Хотя сейчас предпочтительно использовать таксономию ml_language, модуль все равно сохраняет этот мета-ключ для обратной совместимости и удобства.
  • wp_term_taxonomy / wp_terms: таксономия ml_language и термины для каждого языка. При добавлении нового кода языка плагин создает термин с соответствующим slug (например, slug = «ru»). Эти термины привязываются к записям (таблица wp_term_relationships). Администратор обычно не взаимодействует с ними напрямую (они скрыты в UI), но они используются WP_Query.

Отсюда следует, что никаких отдельных “языковых файлов” модуль не формирует – все хранится в стандартных таблицах WordPress. Это упрощает резервное копирование (достаточно дампа базы), перенос сайта (все включено) и консистентность данных.

При добавлении нового языка:

  • Обновляется опция ml_available_languages (например, добавили “es”).
  • Создается новый термин таксономии ml_language (slug “es”).
  • При сохранении любой записи после этого, если автодрафты включены, для всех записей с новыми языками начнут создаваться черновики “es”. Для уже существующих записей мгновенно черновики не генерируются (кроме случая, когда вы отредактируете и пересохраните запись). Поэтому администратору можно либо “тронуть” каждую запись (что неудобно), либо использовать REST API или скрипт для массового создания недостающих переводов. Альтернативно – временно отключить фильтр save_post и самостоятельно проставить group_id и таксономию новым записям через код.

Для удаления языка:

  • Убирается код из ml_available_languages. Термин таксономии остается в базе (но можно удалить его через wp_delete_term).
  • Записи данного языка не отображаются на сайте (фильтр WP_Query будет отсеивать их, так как язык больше не в списке). По сути, они “выпадают” из групп переводов.
  • Если нужно полностью удалить переводы на этом языке – администратору нужно вручную удалить записи или написать скрипт (например, по всем постам с taxonomy ml_language = XX – удалить).

Стоит упомянуть, как модуль ведет себя при удалении записей:

Если удалить перевод (например, запись на языке FR) – другие языки продолжат существовать, группа останется, просто без FR. Можно позже снова добавить перевод (будет новая запись с тем же group_id, если копировать ID или плагин создаст новый group_id). Если удалить оригинал (предположим, запись на языке по умолчанию) – переводные записи останутся в базе, но потеряют контекст смысла. Однако для плагина это не проблема: они по-прежнему объединены group_id, могут быть отображены (если напрямую зайти по URL). Вероятно, администратору следует также удалить переводы, если удаляется оригинал, чтобы не было “осиротевших” переводов.

В общем, хранение данных построено на следующих принципах:

  • Максимум использования встроенных средств WordPress (посты, таксономии, мета).
  • Не дублировать содержимое: каждый текст хранится ровно один раз, в своей записи.
  • Связи между переводами хранятся легко и понятно (групповой ID). Это лучше, чем, скажем, хранить ID перевода в каждом посте (так было бы избыточно). Один group_id на группу – и все посты его знают.
  • Восстановление/перенос сайта не требует специальных действий: достаточно восстановить базу и файлы плагина – мультиязычные связи восстановятся автоматически.

Cookie и предпочтения пользователя

Модуль использует cookie-файл ml_lang для хранения выбранного пользователем языка[7]. Этот cookie – единственный клиентский “памятный” элемент, который плагин задействует. Рассмотрим его работу подробнее:

  • Cookie устанавливается сервером, когда он «уверен» в языке пользователя. Обычно это случается при редиректе: после определения языка для пользователя, перед отправкой 302 Redirect, модуль вызывает setcookie на языке X. В результате браузер после редиректа хранит ml_lang = X.
  • Также cookie может устанавливаться, даже если редиректа не было: например, если пользователь сразу пришел на страницу с нужным префиксом. В функции maybeRedirect() есть ветка: если префикс был правильный с самого начала, все равно вызвать maybeSetCookie (чтобы обновить cookie, вдруг оно было другим или отсутствовало).
  • Cookie ml_lang имеет срок жизни 1 год (опция 'expires' => time() + YEAR_IN_SECONDS). Так что предпочтение пользователя будет “помниться” долго. Ключ cookie – ml_lang, значение – код языка (например, «ru»). Доступно по домену сайта (path = «/»), не имеет флага HttpOnly (т.е. теоретически JS на странице может его прочитать, но это не критично).
  • При последующих визитах (если пользователь заходит на / или любой URL без префикса) сервер считает этот cookie. Метод currentLang() проверяет $_COOKIE['ml_lang'] и, если там поддерживаемый язык, возьмет его. Это позволяет возвращающимся посетителям сразу попадать на предпочитаемый язык. Например, вчера посетитель переключился на испанский, ушел, сегодня набирает снова домен – модуль увидит ml_lang=es и сразу отправит его на /es/ версию сайта.
  • Cookie обновляется при каждом переключении языка. То есть, когда пользователь вручную выбирает другой язык на сайте, происходит переход по ссылке с новым префиксом. Сервер обработает запрос, определит новый язык и вызовет maybeSetCookie – тем самым перепишет cookie на новое значение.
  • Cookie ставится не для админки (в административной панели логика определения языка не применяется, да и cookie не нужен). Он нужен только для фронтенда, когда пользователь ходит по публичной части сайта.

Стоит учитывать, что cookie ml_lang используется и при наличии кэширования страниц. Если вы используете плагин кеша (WP Super Cache, W3TC и др.), они обычно игнорируют cookie, и могут закешировать страницу не учитывая язык. Это проблема: двуязычный сайт, / без префикса – может кешироваться один раз с языком EN и отдаваться всем. Чтобы этого избежать, нужно настроить кэш-плагин: например, WP Super Cache имеет опцию “Rejected Cookies” – добавить туда ml_lang, чтобы кеш разделялся по языковому cookie. Другой вариант – кэшировать по URI, но не кешировать / вовсе или сразу редиректить / -> /xx/ при заходе.

Примечание: Cookie ml_lang хранит предпочтение, но не является «истиной в последней инстанции». Если пользователь руками введет URL с другим языком, сервер всегда отдаст преимущество URL. Cookie служит лишь для случаев, когда язык не указан (нет префикса) – тогда он подсказывает, куда редиректить.

Кроме cookie и Accept-Language, плагин не хранит ничего на клиенте. Он не использует локальное хранилище браузера, не отправляет Ajax-запросы. Весь механизм предпочтений сводится к cookie + redirect.

Для разработчиков: если нужно задать язык принудительно (например, настраивается A/B тест, в котором хотите перевести пользователя на определенный язык), можно установить cookie ml_lang через JS или серверно, затем отправить пользователя на / – модуль тут же определит язык из cookie и перенаправит. Но обычно достаточно просто направить на /xx/ ссылку.

Обслуживание и обновление

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

  • Добавление нового языка: как отмечалось, добавление кода в настройки приведет к появлению нового языка. Следует помнить, что существующий контент автоматически не переведется – его нужно будет перевести (вручную или через интеграцию). Если авто-драфты включены, новые черновики под этот язык будут создаваться при следующих сохранениях записей. Рекомендуется после добавления языка пройтись по основным страницам и заполнить переводы.
  • Удаление языка: при исключении языка из списка в настройках убедитесь, что на сайте не остались “висячие” ссылки на этот язык (например, в меню могли быть хардкод-ссылки). Контент на удаленном языке станет недоступен, но останется в базе. Если нужно, можно удалить эти записи (по фильтру языка). Термин таксономии можно удалить командой WP-CLI wp term delete ml_language <term-id>, но это не обязательный шаг.
  • Обновление плагина: если вы получили новую версию Multilingual Engine, обновление сводится к замене файлов. Структура данных (в базе) при этом обычно остается прежней, либо мигрируется автоматически в коде. Например, если в новой версии появился новый мета-ключ, он будет заполнен на лету. Рекомендуется перед обновлением сделать бэкап базы, а после обновления – проверить настройку “Legacy content – Assign default language” (вдруг она нужна или появились другие admin-нотификации, сообщающие о миграциях).
  • Совместимость с другими плагинами: модуль не конфликтует с большинством плагинов, однако есть нюансы: плагины кеширования нужно настраивать (см. раздел о cookie), SEO-плагины могут захотеть свой hreflang – желательно отключить их встроенные функции hreflang, чтобы не дублировать. Если используете плагины, изменяющие URL (например, кастомные структуры), убедитесь, что они учитывают префикс. Как правило, все работает, т.к. WordPress воспринимает префикс как часть request URI и модуль обрабатывает это прежде, чем дело доходит до других.
  • Диагностика: модуль имеет режим отладки. Если определить константу MULTILINGUALENGINE_DEBUG = true в wp-config.php или включен WP_DEBUG_LOG, то плагин будет писать логи (функцией error_log) о своих операциях – например, запись о редиректе [ML] Redirect (cookie) / -> /en/... или ошибки при создании драфтов [ML] Failed to create draft for ... появятся в debug.log. Это полезно при расследовании проблем.

Модуль практически не требует обслуживания в плане «очистки данных». Он не накапливает ничего лишнего (разве что трансient ml_assign_notice хранит число обработанных записей для админ-нотификации, но он авто-удаляется через 30 секунд).

Если вы желаете переименовать языковой код (например, вместо «uk» использовать «ua») – нужно сделать это вручную аккуратно: в опциях, в term slug и поправить cookie, т.к. пользователи с cookie старого кода будут редиректиться в никуда. Лучше заранее выбрать верные коды (ориентируйтесь на стандарты ISO 639-1 или ваши предпочтения).

Модуль не предусматривает каких-то автоматических обновлений контента, поэтому основная часть обслуживания – поддерживать переводы актуальными. Тут могут помочь интеграции с ИИ: например, при создании новой записи сразу дергать скрипт, который наполнит черновики переводами. В любом случае, важно настроить редакторский процесс под многоязычность, чтобы переводчики/редакторы знали, где искать нужные версии (в админке интерфейс, благодаря виджету, меню и group связи, облегчает навигацию).

Еще один аспект: SEO и карты сайта. Если вы используете плагин генерации sitemap (например, Yoast SEO), убедитесь, что в sitemap попадают все языковые версии URL. Если нет, возможно, потребуется настроить их или использовать отдельные sitemap для каждого языка. Модуль сам sitemap не генерирует, но теги hreflang он дал, и поисковики их учтут.

Примечание: Всегда тестируйте функциональность мультиязычности после значимых изменений на сайте (например, смены темы, установки нового плагина). Убедитесь, что переключение языков работает, страницы всех языков доступны, hreflang-теги на месте. Лучше выявить возможный конфликт сразу, чем обнаружить позже, что, например, новая тема не вызывает wp_body_open и переключатель не появляется.

В заключение, можно сказать, что модуль мультиязычности несложен в сопровождении: основные действия – добавление/удаление языков и перевод контента – выполняются через знакомый интерфейс. Благодаря отладочным логам и прозрачной структуре, при проблемах вы всегда можете заглянуть в debug.log или в базу (термины, метаполя) чтобы понять, где что может быть не так.

Возможные ошибки и отладка

При работе модуля могут возникать ситуации, которые выглядят как ошибки. Ниже перечислены распространенные проблемы и способы их решения:

  • После активации пропал весь контент с главной страницы / блога. Скорее всего, вы забыли нажать “Assign default language for all posts”. В результате все существующие записи не имеют метки языка, а фильтр pre_get_posts скрывает их. Решение: зайдите в “Настройки – Общие” и нажмите кнопку назначения языка для старого контента. Это вернет записи на место (им будет присвоен язык по умолчанию).
  • Переключатель языков не отображается на сайте. Возможные причины: тема не вызывает wp_body_open или wp_footer. Попробуйте активировать стандартную тему WordPress (например, Twenty Twenty-One) и проверить появится ли. Если да – проблема в вашей теме. Решение: Добавьте вызов <?php wp_body_open(); ?> сразу после тега <body> в файле header.php вашей темы (для современных тем). Если это невозможно, плагин также дублирует вывод в wp_footer – убедитесь, что <?php wp_footer(); ?> присутствует перед закрывающим </body>. Без этих хуков переключатель не выводится.
  • Происходит бесконечный редирект при заходе на сайт. Например, / редиректит на /en/, а /en/ снова редиректит на /en/ и так далее. Такое возможно, если неправильно определился currentLang. Убедитесь, что в ml_available_languages есть язык по умолчанию. Также причиной может быть, что URL /en/ не соответствует никакой странице (например, нет front-page на en, и home_url(‘/en’) вернуло сам /en, что может вызвать self-redirect). В коде maybeRedirect есть проверки на предотвращение саморедиректа, но все же. Решение: Проверьте, есть ли опубликованная запись/страница на данном языке, являющаяся главной (или включена настройка “Отображать последнии записи” для блога). Возможно, нужно назначить языки старому контенту (см. первый пункт).
  • Некорректно определился язык браузера (Accept-Language). Например, у пользователя стоит французский, а его редиректит на английский. Модуль берет только первые 2 буквы (fr из fr-CA например). Если fr не в списке языков, он не учтет fr-CA. Решение: Добавьте fr в список или, если не планируете поддерживать, то ожидаемое поведение – редирект на дефолтный язык. Другой нюанс: если cookie уже стоит, Accept-Language игнорируется. Возможно, у пользователя остался cookie от прошлой сессии. Очистка cookie решит (или можно ориентировать пользователей на явный выбор языка переключателем).
  • Флаги не отображаются, видны только значки “*”. Это означает, что не найден SVG-файл флага. Например, для китайского кода zh ожидается cn.svg (китайский – флаг Китая). Если файла нет, ставится звездочка. Решение: Добавьте нужный SVG в /assets/flags или проверьте права доступа к файлам (вдруг сервер не отдает SVG). В качестве временного решения можно прописать CSS, чтобы вместо span * показывать текст названия языка.
  • Пользователей с разными языками кеш обслуживает неправильный контент. Проблема: кеш не разделяется по языку. Например, первый посетитель EN получил HTML, сохранился кеш страницы, второй посетитель RU получает тот же HTML (английский). Решение: настроить плагин кеширования. В WP Rocket, например, добавить динамический cookie ml_lang для вариации кеша. В других – как минимум исключить / из кеша, или писать свой drop-in. Это важно: мультиязычный сайт должен либо иметь отдельный поддомен/домен на язык (тут не актуально, у нас префиксы), либо умный кеш по cookie/URL.
  • REST API не возвращает/не принимает поля ml_language или group_id. Убедитесь, что вы аутентифицируетесь. Без авторизации вы увидите только опубликованные записи и не увидите метаполей (они требуют edit_posts права). При PUT/POST запросах без авторизации будет ошибка. Решение: Используйте Application Password или OAuth/JWT. Также, convicing to ensure auth_callback настроен как в плагине (он есть) и у пользователя достаточно прав.
  • Ссылки hreflang дублируются или конфликтуют с SEO-плагином. Если используете Yoast SEO или RankMath, они могут генерировать свои hreflang. Рекомендуется отключить их функцию мультиязычности (в Yoast, возможно, придется установить WPML integration или фильтровать их вывод). В идеале должен быть один набор hreflang тэгов – от нашего модуля, так как он точно знает group связки.
  • При создании записи появляется ошибка или в логах сообщения [ML] Failed to create draft for XX. Это значит, что модуль не смог создать черновик для одного из языков. Обычно причина – у пользователя не хватает прав на запись определенного типа (маловероятно, если админ), либо конфликт с другим плагином при insert_post. Если такое случилось, перевод для языка не создан. Решение: Попробуйте вручную добавить недостающую запись (в админке, переключившись на язык и нажать “Добавить перевод”). Либо повторно сохраните запись – модуль попытается снова создать. Также включите WP_DEBUG_LOG – возможно, там поймаете описание ошибки (например, “запись не создана: duplicate slug” – тогда это нужно решать, исправив slug).

Для отладки включайте ML_DEBUG (константа) или WP_DEBUG_LOG – плагин достаточно подробно логирует свои шаги. Логи пишутся в wp-content/debug.log (если включен лог WP).

Если нужно проанализировать структуру данных, можете в админке открыть запись и посмотреть в метабокс “Переводы” (плагин, вероятно, добавляет UI список переводов, если нет – то воспользуйтесь phpMyAdmin или WP-CLI для просмотра postmeta). Групповой ID удобен тем, что его можно искать и сразу видеть все связанные записи.

В случае конфликтов с другими плагинами, стратегия стандартная: отключите другие плагины и тестируйте мультиязычность. Если проблема исчезла – включайте по одному, выискивая виновника. Например, были кейсы несовместимости с плагинами “псевдо-папок” (которые тоже лезут в URL). Решение там – либо отказаться от конфликтующего плагина, либо писать исключения.

Наконец, помните, что все URL с языковым префиксом – фактически новые адреса для вашего сайта. Убедитесь, что у сервера нет жестких редиректов, мешающих им (например, раньше стоял редирект /something -> /something-else, а теперь /en/something -> 404, т.к. не учтен). Такие вещи исправляются правилами веб-сервера или плагинами-редиректорами.

Примеры использования (HTML/JS)

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

  1. Встраивание переключателя языков в меню сайта. Допустим, у вас есть меню в шапке, и вы хотите добавить туда пункты переключения языков (вместо плавающей кнопки). Самый простой способ – использовать шорткод [language_switcher] прямо в объекте меню (можно попытаться добавить как HTML, но WordPress меню не позволит, лучше воспользоваться виджетом в область меню, если поддерживается, либо хардкодить). Альтернативно – в шаблоне header.php вызвать echo do_shortcode('[language_switcher]'); внутри структуры меню. Это выведет <select> выпадающий. Вы можете стилизовать его (например, скрыть стандартный селект и оформить как список).
  2. Ссылка “Перейти на русский” отдельно на странице. Вы можете получить URL другой языковой версии програмно. Например, внутри цикла, зная group_id, можно найти пост нужного языка: $translations = get_posts([... 'meta_key' => '_ml_group_id', 'meta_value' => $group, 'tax_query' => [['taxonomy' => 'ml_language', 'terms' => 'ru']] ...]); – или использовать метод плагина: $url = MultilingualEngine\Plugin::translateUrl(get_permalink(), 'ru', $plugin->langs). Это даст URL текущей страницы на русском. Можно вывести: <a href="...">Перейти на русский</a>. Такой код можно использовать в шаблоне, если нужен кастомный вывод ссылок переключения (например, флажки плюс текст).
  3. Пример JavaScript: автоматический редирект по языку браузера. Допустим, вы хотите, чтобы нового посетителя, даже до загрузки страницы, перенаправило на определенный язык (в обход серверного редиректа, который и так есть, но ради примера). Можно добавить в <head> скрипт:
    if(!document.cookie.match(/ml_lang/) && navigator.language.startsWith('fi')) { window.location = '/fi/'; }. Это проверит, нет ли cookie языка и если браузер финский – перекинет на финскую версию. Однако, это дублирует функционал сервера, поэтому не нужно в реальности.
  4. Вывод флага текущего языка где-то в дизайне. Флаг текущего языка – SVG-картинка, путь к которой можно получить: $flagUrl = plugin_dir_url($file).'assets/flags/'. $iso . '.svg';, где $iso – код страны из ML_FLAG_MAP[$lang]. Проще – взять готовый HTML, который модуль рендерит (кнопка переключателя). Но если нужно, можете вывести <img src="..." alt="..."> для текущего языка.
  5. Пример WP-CLI: массовое добавление перевода. С помощью WP-CLI можно написать команду: foreach post where ml_language = en create draft for ru. Код будет аналогичен onSavePost. Но лучше воспользоваться уже созданными черновиками. WP-CLI можно использовать для отладки: wp post meta get ID _ml_group_id – покажет группу; wp post list --meta_key=_ml_group_id --meta_value=mlg_abc123 – покажет все записи группы. Полезно для скриптов.

В целом, хоть плагин и автоматизирует многое, разработчик имеет возможность вручную вмешиваться: либо через PHP (используя API WP для терминов и мета), либо через JS (например, считывать cookie ml_lang для чего-то), либо через REST API как выше.

Еще один пример – создание страницы “Выбор языка” (Language switcher page). Некоторые сайты при первом заходе показывают страницу с выбором языка. Можно реализовать: сделайте страницу WP, скажем “Select Language”, на ней разместите ссылки или кнопки языков, а при выборе – скрипт ставит cookie и редиректит. Но проще задействовать уже имеющийся функционал: если пользователь попал на «/» и вы хотите “промежуточный экран” – можно модифицировать maybeRedirect функцию плагина через hook или свой код, но это сложнее. Обычно такие страницы не используют, вместо них – автопереключение или всплывающий диалог (который опять же работает с cookie).

Следует также понимать, что модуль работает не только с типом post и page, но теоретически может применяться и к произвольным типам записей (CPT). В коде registerTaxonomy('ml_language', ['post','page'], ...) – т.е. прикреплен только к постам и страницам. Если вы хотите переводы для CPT, можно вручную зарегистрировать таксономию ml_language к ним (register_taxonomy('ml_language', ['your_cpt'], ...)) – тогда система сможет их тоже фильтровать. Однако автосоздание черновиков работает только для пост/страниц (в onSavePost есть проверка на тип). Можно расширить, но нужно лезть в код.

Наконец, пример SQL-запроса: если хотите получить список групп переводов и языков, можно выполнить:

SELECT meta_value as group_id, GROUP_CONCAT(DISTINCT pm2.meta_value) as languages, 
       COUNT(*) as count
FROM wp_postmeta pm 
JOIN wp_postmeta pm2 ON pm.post_id = pm2.post_id AND pm2.meta_key = '_ml_language'
WHERE pm.meta_key = '_ml_group_id'
GROUP BY pm.meta_value;

Это выведет все group_id, какие языки в группе присутствуют и сколько записей. По этому можно, например, найти группы, где не хватает какого-то языка (сравнив со списком). Но это, конечно, более сложный путь, обычно достаточно WP_Query.

Обобщая, интеграция элементов модуля в HTML/JS идет по стандартным канонам WP: шорткоды, виджеты, WP API. Модуль не заставляет вас писать собственный JavaScript (все работает без кодирования), но вы при желании можете доработать UX – например, заменить выпадающий список на кастомный UI, используя API плагина. Благо, все необходимое (URL других языков, текущий язык, cookie) доступно.

Мы рассмотрели несколько практических примеров – их можно использовать как основу для кастомизации поведения на вашем многоязычном сайте.

Глоссарий терминов

  1. WordPress: Популярная система управления контентом (CMS) с открытым кодом, используемая для создания сайтов и блогов. WordPress предоставляет базовую функциональность сайта, а за счет плагинов (как модуль мультиязычности) ее возможности расширяются.
  2. ИИ (Искусственный интеллект): В контексте перевода – технология, позволяющая машине (алгоритму) выполнять задачи, требующие интеллекта, например, перевод текста с одного языка на другой. Популярные примеры – нейросети вроде GPT-4 от OpenAI, которые способны генерировать высококачественные переводы.
  3. REST API: Архитектурный стиль API, при котором взаимодействие происходит через стандартные HTTP-запросы (GET, POST, PUT, DELETE) к определенным URL, возвращающие данные обычно в формате JSON. WordPress имеет встроенный REST API[11], позволяющий получать и изменять данные сайта программно.
  4. Сноски (footnotes): Примечания или пояснения, выносимые в конец документа или страницы, на которые ссылаются в тексте цифрами (напр., как этот глоссарий). Используются для пояснения терминов или ссылок, не прерывая основного изложения.
  5. Таксономия: В WordPress – механизм группировки контента по терминам. Стандартные таксономии – рубрики (categories), метки (tags). В контексте модуля мультиязычности создается своя таксономия (ml_language), где термины – это языки, а посты привязаны к этим терминам по своему языку.
  6. Shortcode (шорткод): Короткий код в квадратных скобках, распознаваемый WordPress в тексте записи и заменяемый динамическим содержимым. Например, [language_switcher] – шорткод, который плагин мультиязычности заменяет на HTML-код выпадающего списка языков.
  7. Cookie: Небольшой файл (ключ-значение), сохраняемый браузером по запросу сервера. Cookie отправляются серверу при каждом запросе к соответствующему домену. В нашем случае используется cookie ml_lang для хранения выбранного языка пользователя. Благодаря cookie сайт «помнит» предпочтение языка между посещениями.
  8. Заголовок Accept-Language: HTTP-заголовок, который браузер отправляет на сервер, указывая предпочтительные языки пользователя (и регион). Формат – например: Accept-Language: en-US,en;q=0.9,ru;q=0.8, где браузер сообщает, что предпочитает английский (США), затем английский (общий), затем русский, с указанными приоритетами. Сервер может использовать эту информацию для локализации контента.
  9. Атрибут hreflang: HTML-атрибут в элементе <link rel="alternate">, указывающий язык и регион (опционально) альтернативного представления страницы. Например, hreflang="ru" для русской версии. Поисковые системы используют hreflang-теги, чтобы не считать переведенные страницы дублированным контентом и показывать пользователю в выдаче страницу на его языке.
  10. ISO-коды (языковые и стран): Стандартизированные коды для обозначения языков и стран. ISO 639-1 – двубуквенные коды языков (например, en – английский, ru – русский). ISO 3166-1 alpha-2 – двубуквенные коды стран (например, US – США, RU – Россия). Модуль использует языковые коды для меток языка, а для флажков – соответствующие им коды стран (через карту ML_FLAG_MAP, например, 'en' => 'us').
  11. WP REST API: Конкретная реализация REST API в WordPress, доступная по URL вида /wp-json/wp/v2/.... Позволяет получать записи (/posts), таксономии, пользователей и прочие сущности WordPress в формате JSON, а также изменять их, создавая, обновляя или удаляя, с соответствующими правами. Используется для интеграций с внешними приложениями и AJAX-функционала внутри самого WordPress (например, Gutenberg редактор общается с WP через REST API).
Разработка и продвижение сайтов webseed.ru
Прокрутить вверх