Переключение контекстов мультиязычного сайта

Написал 2 инструкции для простого переключения контекстов в классической ситуации нескольких языковых версий.

Директории

Плагин для ситуации, когда домен один, а языковые версии отличаются префиксом в адресе: mysite.com/ru/page.html и mysite.com/en/page.html.
То есть, условия такие: - Все контексты на одном домене, и у них отличается base_url
  • Используются friendly urls
  • base_url может не совпадать с именем контекста
Идея заключается в выборке всех контекстов и сравнения их базового адреса с запросом. Создавать директории и копировать index.php не нужно.
Итак, у нас два контекста: web и en c такими настройками:
Создаём новый плагин, добавляем для него событие OnHandleRequest и копируем этот код:
<?php
// Работаем только на фронтенде и только с friendly urls
if ($modx->event->name != 'OnHandleRequest' || $modx->context->key == 'mgr' || !$modx->getOption('friendly_urls')) {return;}

// Получаем запрашиваемый url
$alias = $modx->getOption('request_param_alias', null, 'alias', true);
$request = &$_REQUEST[$alias];

// Выбираем контексты с настройкой base_url
$q = $modx->newQuery('modContextSetting', array('key' => 'base_url', 'value:!=' => ''));
$q->select('context_key,value');

$contexts = array();
$tstart = microtime(true);
if ($q->prepare() && $q->stmt->execute()) {
    // Учитываем наш запрос в БД
    $modx->queryTime += microtime(true) - $tstart;
    $modx->executedQueries++;
    // Разбираем результаты
    while ($row = $q->stmt->fetch(PDO::FETCH_ASSOC)) {
        $base_url = trim($row['value'], '/');
        $context = $row['context_key'];
        // Если запрос начинается с base_url какого-то контекста
        if (preg_match('/^('.$base_url.')\//i', $request)) {
            // То переключаемся на этот контекст
            // Web инициализируется в index.php - на него переключаться не нужно
            if ($context != 'web') {
                $modx->switchContext($context);
            }
            // Вырезаем base_url из запроса, чтобы MODX нашел ресурс по uri
            $request = preg_replace('/^'.$base_url.'\//', '', $request);
            // Дело сделано - выходим из цикла
            break;
        }
    }
}
Кстати говоря, еще можно у основного контекста web убрать base_url, чтобы он открывался без /ru/, а второй контекст был по-прежнему с /en/. Например, вот сайт документации - русская версия и английская.

Поддомены, или разные домены

При конфигурации поддоменами адреса у нас будут такие: ru.mysite.com/page.html и en.mysite.com/page.html.
Этот случай проще, так как нужно только сравнить запрашиваемый хост со значениями в БД и получить соответствующий контекст. Здесь не нужно менять запрос и результат выборки может быть только один.
Настройки контекстов такие:
Точно также создаём плагин, и отмечаем событие OnHandleRequest.
<?php
// Работаем только на фронтенде
if ($modx->event->name != 'OnHandleRequest' || $modx->context->key == 'mgr') {return;}

// Определяем запрашиваемый хост
$host = $_SERVER['HTTP_HOST'];

// Выбираем контекст с настройкой base_url
$q = $modx->newQuery('modContextSetting', array('key' => 'http_host', 'value' => $host));
$q->select('context_key');

$tstart = microtime(true);
if ($q->prepare() && $q->stmt->execute()) {
    // Учитываем наш запрос в БД
    $modx->queryTime += microtime(true) - $tstart;
    $modx->executedQueries++;
    // Получаем ключ контекста
    if ($context = $q->stmt->fetch(PDO::FETCH_COLUMN)) {
        // Web инициализируется в index.php - на него переключаться не нужно
        if ($context != 'web') {
            $modx->switchContext($context);
        }
    }
}
Как вы понимаете, в настройках контекстов должны быть уникальные http_host.

Заключение

Вот так несложно мы полностью автоматизировали переключение языков на мультиязычных сайтах. Можно добавлять сколько угодно контекстов - плагин менять не придётся.

Обновлено 03.11.2014г.

Пример сниппета для вывода ссылок на документ в другом контексте для первого случая - директорий.
<?php
$tplRu = '<a href="[[+link]]">ru</a>';
$tplEn = '<a href="[[+link]]">en</a>';
$tplRuActive = '<span>ru</span>';
$tplEnActive = '<span>en</span>';

$output = '';
if ($modx->context->key == 'web') {
    $output .= $tplRuActive . ' | ' . $tplEn;
    $link = 'en/';
    if ($modx->getOption('site_start') != $modx->resource->id && $modx->getCount('modResource', array('uri' => $modx->resource->uri, 'context_key' => 'en'))) {
        $link .= $modx->resource->uri;
    }
}
else {
    $output .= $tplRu . ' | ' . $tplEnActive;
    $link = '/';
    if ($modx->getOption('site_start') != $modx->resource->id && $modx->getCount('modResource', array('uri' => $modx->resource->uri, 'context_key' => 'web'))) {
        $link .= $modx->resource->uri;
    }
}

return str_replace('[[+link]]', $link, $output);
Здесь web - русский контекст, а en - английский. Обязательное условие - одинаковые uri ресурсов в разных контекстах. Если нет соответствия, то ссылка будет в корень контекста.