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

Во время работы над modx-test.com (который потом вырос в https://modhost.pro) придумал и реализовал прикольную мультиязычность.
Никаких копирований файлов, поддоменов и прочих заморочек. 1 простой плагин, который делает всю работу.
Нам нужно 2 контекста, по умолчанию в основном контексте ставим русский язык. В настройках контекстов указаны следующие параметры:
web- cultureKey - ru
  • site_start - 1
  • error_page - 1
en- cultureKey - en
  • site_start - 3
  • error_page - 3
В системных настройках отключаем проверку дупликатов url во всех контекстах. Url соответствующих страниц должны быть одинаковыми - так гораздо проще. Иначе, надо будет наворачивать сниппет, выдающий ссылку на другую языковую версию.
А вот и сам плагин, реагирует на событие OnHandleRequest:
<?php
if ($modx->event->name != "OnHandleRequest" || $modx->context->key == 'mgr') {return;}

if (isset($_GET['lang']) && $_GET['lang'] != $_SESSION['lang']) {
    if ($_GET['lang'] == 'en') {
        $_SESSION['lang'] = 'en';
    }
    else {
        $_SESSION['lang'] = 'ru';
    }
}

if ($_SESSION['lang'] == 'en') {
    $modx->switchContext('en');
}
То есть, при указании параметра ?lang=en или ?lang=ru любой ссылке - контекст сайта переключается, а вместе с ним и язык - и это запоминается в сессию, то есть ссылка больше не нужна.
Прощай геморрой с настройками!
Бонус в виде сниппета для вывода ссылки на переключение:
<?php
$link = $modx->makeUrl($modx->resource->id, '', '', 'full');

if ($modx->getOption('cultureKey') == 'en') {
    return "<a href=\"$link?lang=ru\">Русский</a>";
}
else {
    return "<a href=\"$link?lang=en\">English</a>";
}
Поисковики такой сайт индексируют нормально - ведь на каждой странице есть ссылка на другую языковую версию, и урл отличается. То есть в индексе страницы с разным ?lang=(en|ru) и с разным содержимым.

69 комментариев

Владимир
Всем доброго дня!
Кто подскажет, где посмотреть все возможные значения (двусимвольные сокращения языка) для cultureKey.
В настоящий момент делаю сайт с мультиязычностю Finnish, English, Russian, French, Spanish, Arabic, соответственно, двусимвольные сокращения fi,web,ru,fr,es,ar как для cultureKey так и для настроек плагина babel. Планируется добавления языков которые в babel отсутствуют, и что бы их добавить туда нужно знать общепринятое сокращение. (например один из таких языков иврит)
Константин Хетагуров
Добрый день.
Делаю всё как тут написано. Отображается ссылочка на английскую версию сайта, при нажатии на неё в адрес, добавляется ?lang=en и ничего не происходит. Подскажите в чем проблема, пожалуйста. Я так понимаю, что просто не переключаются контексты, правильно?
Всем доброго времени суток!
В данной реализации, на нерусской версии сайта даты будут выводиться на русском языке. Поэтому в последнее условие необходимо добавить принудительную установку локали:
if ($_SESSION['lang'] == 'en') {
    $modx->switchContext('en');
    setlocale(LC_TIME, 'en_US.UTF8');
}
В зависимости от языка или кодировки строчку setlocale(LC_TIME, 'en_US.UTF8'); надо изменить. Точно так же можно написать и для нескольких языков.
Василий Наумкин
Да, верно.
Еще можно выводить даты через сниппет dateAgo - он берёт названия месяцев из лексикона.
Здравствуйте! Подскажите пожалуйста, как переделать ваше решение мультиязычности без контекстов? Т.е.: Создаются ТВ для каждого языка, и у каждого документа прописываются названия, контент и тд на каждом языке. Пробовала ваш вариант плагина, только вместо $modx->switchContext('en'); пробовала $modx->setPlaceholder('cultureKey','en'); и $modx->setOption('cultureKey', 'en'); не помогло.
Я даже просто пробовала показать $_SESSION['lang'], переходя по ссылкам http://shop.test1.ru/?lang=en и http://shop.test1.ru/?lang=ru - ничего не выводит, т.е. даже сессию не устанавливает.
Включила кеширование настроек контекстов (до этого было отключено), сессия устанавливается. Теперь пробую выводить нужные ТВ в зависимости от сессии, работает через раз - при каждом обновлении страницы, меняются с русских на англ. и наоборот) Видимо дело в кешировании! Подскажите, кто сталкивался, как с этим бороться? Спасибо.
Нет) Простите за флуд, Я так понимаю сессия устанавливается при первом заходе, и уже выводятся нужные ТВ при следующем обновлении страницы. Наверное я туплю, пошла разбираться)
Все получилось, всем спасибо!)
Василий Наумкин
На здоровье! =)
Alexander Legan
Привет. Bond Nataly, как я понял, Вы пытались создать отдельные TV под каждый язык для каждого ресурса, и в итоге все получилось? Буду благодарен если у Вас будет возможность расписать решение поподробнее.
Ох, это ж было аж год назад, почти ничего не помню уже) Ну приблизительно могу описать так: При переключении языка у меня в сессию записывался язык (ru,en,ua). И потом исходя из этого, у меня подставлялись нужные TV, которые были названы с префиксом своего языка. Например, en_pagetitle, ua_pagetitle и тд. И сниппет для вызова нужной тв, например так pagetitle вызывала:
[[!cK?id=`1`&tv=`pagetitle`&field=`Главная страница`]]
И сам сниппет cK:
$cK = (isset($lang)) ? $lang : $_SESSION['lang'];
if($cK != '' && $cK != 'ru') {
  $tv_name = $cK.'_'.$tv;
  $tvv = $modx->getObject('modTemplateVar', array('name' =>$tv_name));
  $out = $tvv->getValue($id);
  echo trim($out)!='' ? $out : $field;
} else {
  echo $field;
}
Alexander Legan
Спасибо. Уже ближе к полуночи сам к такому решению пришёл, а теперь убедился в правильности хода мыслей. Данное решение позволяет избавиться от дублирования ресурсов, что в некоторых случаях (сайтах) очень даже правильно, и это мне подходит. ) Спасибо за оперативность.
Пожалуйста )
Мордынский Николай
Ты однако круче чем Чак Норис. Помог человек силой мысли)) во как!!! ))
Василий Наумкин
Не поминай имя Чака всуе, а то - накажет!
Kondakov Dmitriy
Спасибище огромное! Как нельзя кстати вовремя) только вот не получается сделать с тремя языками, наведите на мысль пожалуйста, в php не особо силен
Kondakov Dmitriy
Разобрался, правда незнаю насколько это правильно. Воспользовался elseif: код плагина
<?php
if ($modx->event->name != "OnHandleRequest" || $modx->context->key == 'mgr') {return;}

if (isset($_GET['lang']) && $_GET['lang'] != $_SESSION['lang']) {
    if ($_GET['lang'] == 'en') {
        $_SESSION['lang'] = 'en';
    }
    elseif ($_GET['lang'] == 'kz') {
        $_SESSION['lang'] = 'kz';
    }
    else {
        $_SESSION['lang'] = 'ru';
    }
}

if ($_SESSION['lang'] == 'en') {
    $modx->switchContext('en');
}
if ($_SESSION['lang'] == 'kz') {
    $modx->switchContext('kz');
}
Василий Наумкин
Все верно.
Только в последнем блоке вместо 2х if лучше тоже else if.
Kondakov Dmitriy
Точно, поторопился что-то. еще пришлось сделать небольшой сниппет для вывода своего меню в каждом контексте, чтобы не делать несколько шаблонов. Если надо кому выложу
Андрей Минаков
Было бы здорово.
Константин Баранов
делаю для заказчика и важен только сайт, а не гугловская выдача, но интересно, как гугл на такие сессионные вещи реагирует, спасибо за простое и красивое решение.
Василий Наумкин
Пока не было проблем.
Константин Баранов
Как думаете Василий, лучше делать копии шаблонов, например для английских ресурсов или проверку контекста/сессии для вывода соответствующих чанков/сниппетов в общих шаблонах?
Василий Наумкин
Проверка удобнее, а шаблоны быстрее.
Ну а выбирает каждый сам, зависит от размера сайта и личных предпочтений.
А зачем делать копии чанков или шаблонов? Для каждой фразы создается свой ключ в настройках контекста, а в шаблоне подставляется [[++ключ_фразы]]. Параметры в сниппеты тоже можно передавать таким образом, например [[getresources? &parents=[[++ID\_родителя\_для\_текущего\_языка]]]]. После завершения основной версии, делается копия контекста и в настройках копии просто переводим фразы на соответствующий язык. По такой схеме, пока не приходилось использовать копии чанков и шаблонов.
Василий Наумкин
Зачем использовать системные параметры, если есть лексиконы?
Тег [[%name]] специально для этого. Вот дока.
Спасибо, Василий, так еще лучше, почему-то не обращал раньше внимания на управление словарями.
Самое удобное решение из тех, что встречал, но не без недостатка - url каждой страницы на разных языках должны быть одинаковыми, столкнулся с необходимостью вручную устанавливать урлы во всех контекстах для большого количества ресурсов, что очень неудобно.
Поправил немного сниппет для вывода ссылки на переключение языка:
<?php
if ($modx->getOption('cultureKey') == 'en') {
    $translateLink = $modx->runSnippet('BabelTranslation',array('contextKey' => 'web'));
    $link = $modx->makeUrl($translateLink,'',array('lang' => 'ru'));
    return "<a href=\"$link\">Русский</a>";
}
else {
    $translateLink = $modx->runSnippet('BabelTranslation',array('contextKey' => 'en'));
    $link = $modx->makeUrl($translateLink,'',array('lang' => 'en'));
    return "<a href=\"$link\">English</a>";
}
Теперь нет необходимости указания одинаковых url для соответствующих страниц. Естественно, все соответствующие страницы должны быть связаны через Babel.
Артур Костюнин
Здравствуйте. Подскажите в чем может быть проблема (все делал по пунктам). При переходе на английскую версию сайт выдает ошибку
503 Error Page not found The page you requested was not found. Спасибо.
Андрей Минаков
Та же ересь. Никак не найду почему. :(
Мордынский Николай
а страничка в английском контексте у тебя стем же урл что и в русском? должны быть одинаковые
Андрей Минаков
Абсолютно идентичные и урл и алиасы - на болванке сайта тестирую, по странице в каждом контексте. Более того - такую же ошибку выдает в недефолтном (en) контексте при простом просмотре страницы. Предположений много, но дело, кажется, в каких-то настройках - сам скрипт потестить пока не выходит. Есть идеи?
Андрей Минаков
Повторил по инструкции на тестовом сайте: заработало! Причин отказа так и не понял. Предполагаемое решение см. чуть ниже.
Андрей Минаков
Решение: Предположительно, надо быть внимательным к адресу контекста в плагине:
$modx->switchContext('адрес_контекста');
Результат: http://s2955.modx-test.com
Gabaraev Akhsar
Здравствуйте! А как можно при входе на сайт автоматический выбирать язык в зависимости от страны?
Мордынский Николай
Узнаешь язык клиента и инициализируешь соответсвующий контекст в сесси
Gabaraev Akhsar
Да, я видел эту страницу. Просто думал по ip как-то надежнее, а тут http://mitasych.com/kak-opredelit-geograficheskoe-polozhenie-po-ip-i-svyazat-ego-s-google-maps не нашел где качать файлы geoip.inc, geoipcity.inc, geoipregionvars.php.
Мордынский Николай
а если человек с ноутом заходит на твой сайт из макдональда в Англии и хочет русский а получает en
Gabaraev Akhsar
То он переключит язык на русский, и выбранный язык хранится в сессии. Какой вариант лучше - однозначного мнения нет, как я понял :)
Мордынский Николай
ну вот видишь сам ответ дал)
Дмитрий Нижник
Подскажите пожалуйста, в чём может быть проблема!
Всё прекрасно работало, но в один момент ошибка стала вылетать 404ая, копал и накопал вот что когда отключаешь событие OnHandleRequest в плагине:
<?php
if ($modx->event->name != "OnHandleRequest" || $modx->context->key == 'mgr') {return;}

if (isset($_GET['lang']) && $_GET['lang'] != $_SESSION['lang']) {
    if ($_GET['lang'] == 'en') {
        $_SESSION['lang'] = 'en';
    }
    else {
        $_SESSION['lang'] = 'ru';
    }
}

if ($_SESSION['lang'] == 'en') {
    $modx->switchContext('en');
}
то сайт открывается, а язык не переключается..
Помогите пожалуйста, 2 дня уже убил, кэш чистил, переустанавливал систему, ничего не помогает :(
Андрей Минаков
Фазы луны, вероятно. Тоже лечил, но была 503-я: здесьУ меня все работает с настройками, приведенными в топике. Проверьте имена контекстов в коде (второй контекст точно зовется en?) и их настройки.
Дмитрий Нижник
Всё работало, я только установил Quip и что то пошло не так, естественно я удалил Quip... Точно фаза луны, блин..
Дмитрий Нижник
Удалил [[!Loginza?action=getProfile]] всё заработало, фаза луны пришла в норму, что думаете по этому поводу?
Андрей Минаков
Увы, у меня нет версий. Я не очень в php шарю. :( Может Логинза что-то в запросе свое дописывала, что скрипт мультиязычности понять не мог?
Сергей Шлоков
Василий, если есть время, кивни правильно или нет. Воюю с контекстами. Есть домен site.ru и поддомен sub.site.ru. Делаю согласно официальному руководству и твоим комментам на habre Как я воевал с контекстами. Не работает через http_host, хоть ты тресни (в контекстах прописал все настройки согласно твоей картинке из коммента). Вот плагин на OnHandleRequest
<?php
/* don't execute if in the Manager */
if ($modx->event->name != "OnHandleRequest" || $modx->context->key == 'mgr') {return;}

switch ($modx->getOption('http_host')) {
    case 'sub.site.ru':
        // if the http_host is of a specific domain, switch the context
        $modx->switchContext('sub');
        break;
    default:
        // by default, don't do anything
        break;
}
Если заменить $modx->getOption('http_host') на $_SERVER['HTTP_HOST'], то все работает, но хотелось бы узнать мнение грамотных специалистов - правильно так?
Василий Наумкин
Если работает - это правильно.
Сергей Шлоков
Спасибо. Успокоил.
Дмитрий Нижник
При переключении языков Wayfinderы частично отключаются т.е. главное меню работает а доп.меню слева не работает, а в английской версии и главное пропадает, после обновления страницы всё появляется
З.Ы точно так же форма ввода пароля не отображается, только после повторного обновления страницы. Пакет Login. Помогите пожалуйста кто чем может?!
Мордынский Николай
С таким не сталкивался. Все работало как надо.
Я делал для каждого языка категорию и в нее копировал чанки снипеты и шаблоны отдельно копия каждого элемента для каждого языка нудно зато потом путаницы нет что где менять и скешированием тоже нету проблемм.
Дмитрий Нижник
Заметил что контекст (а именно [[*context_key]]) срабатывает правильно, а вот айди из словаря подставляет от другого контекста. Что за магия, не могу понять
Дмитрий Нижник
С cultureKey проблемы, хотя в контекстах настроено правильно
Дмитрий Нижник
Help!!!
Дмитрий Нижник
Заманался, cultureKey путается при переключении на другие языки.. короче спасибо за помощь
Василий Наумкин
На здоровье!
Homchenko Kostya
Установил чистый MODx 2.2.8 сделал все по пунктам, не работает ни русская версия ни английская (((( Че за БРЕД ?!?!?!
Homchenko Kostya
Все, разобрался. Оказывается в этой части
if ($_SESSION['lang'] == 'en') {
    $modx->switchContext('en');
}
там где
switchContext('en');
надо вместо "en" явно указать имя своего контекста для англоязычных материалов.
На демосайте в Firefox 22 под Mac OS после достаточно быстрой смены языков 3-4 раза на странице каталога(да и вообще любой кроме товара) туда-сюда возникает такое при открытии конкретного товараостаются часть чанков случайным образом от прошлого языка. решается сменой языка туда-обратно на странице товара. на скрине, например, от текущего русского есть только цвет. наверное, такое в реальности не произойдет, но все же баг
Василий Наумкин
На этом сайте вообще один контекст и все тексты переключаются через лексиконы, динамически. То есть, это одна и таже страница, и некоторые плейсхолдеры на ней кэшируются.
Если же делать 2 контекста, как расписано в заметке, то такой проблемы быть не может.
Я тоже собирался через лексиконы делать, потому что получается наполнение на всех языках одно, нужно только подписывать в зависимости от языка. Судя по всему, возникает проблема только при быстром переключении подряд, если же это рабочий режим и все делается достаточно медленно, то все ок.
Павел Левин
Я вот делал мультиязычность вида your.domain/en/ и т.п. скок душе угодно по этой инструкции.
Правда никаких сессий там не используют и соответственно $_GET параметров.
Николай
Я уже которые сутки пытаюсь догуглиться до истины. Для этого плагина нужен же этот сниппет? На http://minishop2.com/ Я его не видел, там так же не переводятся товары. Можно настроить это все так чтобы весь сайт был полностью на англ \ рус языке? Я так понял babel создает во втором контексте копию документа, который меняется контентом, шаблонами, и на сайте потом подменяется исходный ресурс на копию. Верно? А как же cart.html, он на сайте должен же быть один.
Василий Наумкин
На minishop2.com это сделано довольно коряво.
Нормальный мультиязычный магазин лично я ни разу не делал, но со всех сторон кажется, что дублировать товары для перевода на другой язык - плохо.
Илья Никитин
Подскажите, кто в курсе. Я всё насчёт поисковиков и индексации. Может вместо использования clean-param, который прикажет игнорировать все ?lang= лучше использовать Disallow /*?lang=ru? Иначе как поисковики попадут на страницы переводов, если будут шарить по страницам без параметров? С другой стороны, тогда останутся дубли у иностранных страниц...
Чикин Артур
Robots.txt вам в помощь.
Василий Наумкин
Эта заметка устарела, лучше делать вот так - http://bezumkin.ru/sections/tips\_and\_tricks/2439/
Илья Никитин
О, спасибо. Может стоит это указать в начале заметки? А то в гугле эту первым делом выдаёт.
Василий Наумкин
Лучше я просто редирект сделаю.
bezumkin.ru
Личный сайт Василия Наумкина
Прямой эфир
Василий Наумкин
03.12.2024, 13:13:34
Генерация - это создание статичный файлов, для их работы потом pm2 не нужен, только правильная настр...
Василий Наумкин
22.11.2024, 03:33:54
Спасибо!
inna
06.11.2024, 15:47:13
Да. Все работает. Спасибо.
Василий Наумкин
01.07.2024, 11:56:41
Да, верно, именно так. А в контроллере, скорее всего, ловить данные методом post.
Василий Наумкин
26.06.2024, 09:38:15
О, точно, вылезает если не залогинен. Спасибо, исправил!
Василий Наумкин
09.04.2024, 04:45:01
> Ошибка 500 Это не похоже на ошибку Nginx, это скорее всего ошибка PHP - надо смотреть его логи. ...
Василий Наумкин
20.03.2024, 21:21:52
Volledig!
Андрей
14.03.2024, 13:47:10
Василий! Как всегда очень круто! Моё почтение!
russel gal
09.03.2024, 20:17:18
> А этот стоило написать хотя бы затем, чтобы получить комментарий от юзера, который ничего не писал...
Александр Наумов
27.01.2024, 03:06:18
Василий, спасибо! Извини, тупанул.