Несколько мультиязычных сайтов в одной админке
Вот как реализовать переключение контекстов на нескольких доменах, но когда языки находятся в папках.
Вот пример:
Я поднял этот вопрос, в результате которого мы нашли решение. Думаю это решение пригодится многим.
Вместе с Сергеем (спасибо ему большое за идеи) у нас получилось найти работающее решение (возможно есть вариант и изящнее).
Остальные настройки можно взять из статьи Василия bezumkin.ru/sections/tips_and_tricks/2439/
2. Проверки на несуществующие языки типа /de/ решаются просто — достаточно во всех контекстах сайта прописать настройку страницы 404 [error_page] для каждого контекста отдельно, даже для web. Тогда если человек пройдёт по несущесвующей ссылке site2.ru/de/, то он попадёт на 404-ю страницу, которая указана сайту site2.ru
2. Удобство управления, особенно когда есть 5 связанных между собой сайтов с 3-мя языками каждый. Очень скоро именно на таком проекте я испытаю этот плагин!
3. Если грамотно настроить права, то разные группы пользователей (менеджеров) смогут заходить в одну и ту же админку не подозревая, что на ней крутятся ещё много других сайтов. К тому же заходят они в админку на своём домене, например site1.ru/manager и site2.ru/manager
P.S.
Если вы знаете как решить эту задачу красивее — можете предложить в комментариях!
Вот пример:
- site1.ru/ru
- site1.ru/en
- site2.ru/ru
- site2.ru/en
Я поднял этот вопрос, в результате которого мы нашли решение. Думаю это решение пригодится многим.
Вместе с Сергеем (спасибо ему большое за идеи) у нас получилось найти работающее решение (возможно есть вариант и изящнее).
Плагин
Плагин на OnHandleRequest:<?php
// Работаем только на фронтенде
if ($modx->event->name != 'OnHandleRequest' || $modx->context->key == 'mgr') {return;}
$alias = $modx->getOption('request_param_alias', null, 'alias', true);
$request = &$_REQUEST[$alias];
// Определяем язык папки из URL
preg_match('|/(.*)/|', $_SERVER['REQUEST_URI'], $m);
$lang = trim($m[1]);
// Выбираем все существующие на сайте языки
$q = $modx->newQuery('modContextSetting');
$q->select('value');
$q->where(array('key' => 'cultureKey'));
$q->prepare();
$q->stmt->execute();
$langs = $q->stmt->fetchAll(PDO::FETCH_ASSOC);
$lang_arr = array();
foreach ($langs as $l) {
array_push($lang_arr, $l['value']);
}
// Если папка пуста, указываем русский язык
if (!in_array($lang,$lang_arr)) $lang = 'ru';
// Формируем нужный host_language
$host_language = $_SERVER['HTTP_HOST'].'_'.$lang;
// Выбираем контекст с настройкой host_language
$q = $modx->newQuery('modContextSetting', array('key' => 'host_language', 'value' => $host_language));
$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);
$request = preg_replace('/^'.$lang.'\//', '', $request);
}
}
}
Контексты
В настройки контекстов добавляем ключ host_language со значением site.ru_en, где «en» = cultureKey.Остальные настройки можно взять из статьи Василия bezumkin.ru/sections/tips_and_tricks/2439/
Проблема
1. Плагин не работает почему-то с ресурсами-контейнерами, поэтому пришлось обойти костылём и убрать«Суффикс контейнера» в настройках системы [container_suffix].2. Проверки на несуществующие языки типа /de/ решаются просто — достаточно во всех контекстах сайта прописать настройку страницы 404 [error_page] для каждого контекста отдельно, даже для web. Тогда если человек пройдёт по несущесвующей ссылке site2.ru/de/, то он попадёт на 404-ю страницу, которая указана сайту site2.ru
Возможные варианты применения решения:
1. Экономия места на хостинге, не нужно переплачивать за лишний хостинг.2. Удобство управления, особенно когда есть 5 связанных между собой сайтов с 3-мя языками каждый. Очень скоро именно на таком проекте я испытаю этот плагин!
3. Если грамотно настроить права, то разные группы пользователей (менеджеров) смогут заходить в одну и ту же админку не подозревая, что на ней крутятся ещё много других сайтов. К тому же заходят они в админку на своём домене, например site1.ru/manager и site2.ru/manager
P.S.
Если вы знаете как решить эту задачу красивее — можете предложить в комментариях!
Поблагодарить автора
Отправить деньги
Комментарии: 38
1)Выполнять не в цикле:
3)нет проверки на язык отсутствующий у домена
например есть набор контекстов:
P.S. я бы предложил выбрать сначала все контексты с текущим доменом, а потом уже среди них выбрать тот что с языком
$lang_arr = array_unique($lang_arr);
2)не обязательно проверяя «in_array» уникализировать значения3)нет проверки на язык отсутствующий у домена
например есть набор контекстов:
test1.com_ru
test1.com_en
test1.com_de
test2.com_ru
test2.com_en
если мы запросим для test2.com/de что получится?P.S. я бы предложил выбрать сначала все контексты с текущим доменом, а потом уже среди них выбрать тот что с языком
1) Точно, не заметил. Исправил.
2) Убрал. Логично.
3) Добавил проверку с редиректом на 404-ю страницу текущего контекста
P.S. Я пробовал так, но что-то у меня не завелось…
2) Убрал. Логично.
3) Добавил проверку с редиректом на 404-ю страницу текущего контекста
P.S. Я пробовал так, но что-то у меня не завелось…
Только что-то сомневаюсь, что моя проверка и редирект правильно работают…
Буду пробовать.
Буду пробовать.
текущий контекст это какой? разумнее сделать именно выборку домена а потом уже дефолтного контекста в рамках домена, это при условии что отсечение по домену идет по DNS, т.е. невозможно запросить домен которого нет, хотя на этот случай тоже можно сделать проверку и выдавать домен заглушку или домен по-умолчанию
Ок, давай попробуем и ваш вариант (у меня он с первого раза не завёлся)…
Хочешь чтобы я написал решение или сам?
План такой:
1)берем контексты по домену, если их нет показываем что-то по умолчанию(web например)
2)если контексты нашли проверяем язык внутри них, если наш язык не найден — берем первый контекст или контекст по-умолчанию и выбираем его
3)если найден контекст с нужным языком — берем его
План такой:
1)берем контексты по домену, если их нет показываем что-то по умолчанию(web например)
2)если контексты нашли проверяем язык внутри них, если наш язык не найден — берем первый контекст или контекст по-умолчанию и выбираем его
3)если найден контекст с нужным языком — берем его
Доработаю вначале проверку для первого плагина, потом возьмусь за твой план.
Если хочешь, можешь попробовать написать!!!
Если хочешь, можешь попробовать написать!!!
А не подскажете как выводить в wayfinder меню?
Что бы в версии en выводился чанк $enmenu а в русскоязычной $menu
Слышал, что можно методом else, them а как не знаю…
конструкции вида:
Что бы в версии en выводился чанк $enmenu а в русскоязычной $menu
Слышал, что можно методом else, them а как не знаю…
конструкции вида:
&parents=`[[*context_key:is=`web`:then=`1`:else=`[[*context_key:is=`de`:then=`5`:else=`4`]]`]]`
Спасибо
Изучай Феном, чтобы сайт из-за подобных конструкций не превратился в «неповоротливую корову».
Спасибо! а родными методами как организовать..?
Есть же стандартный метод вывода?
Как модиксу проверить если контекст en, то выводить чанк в wayfinder [[$enmenu]]
Если контекст web то выводить [[$menu]]
Спасибо
Есть же стандартный метод вывода?
Как модиксу проверить если контекст en, то выводить чанк в wayfinder [[$enmenu]]
Если контекст web то выводить [[$menu]]
Спасибо
Извини, конечно, но меня давно не возбуждают эти стандартные методы и вдаваться в подробности этих ужасов снова нет никакого желания.
Либо проверить контексты:
Либо твой пример:
Присмотрись в сторону Fenom, уверяю тебя, ни грамма не пожалеешь об этом.
{if $_modx->config.cultureKey == 'ru'}
культурКей Ru
{elseif $_modx->config.cultureKey == 'en'}
культурКей En
{/if}
Либо проверить контексты:
{if $_modx->context.key == 'web'}
контекст web
{elseif $_modx->context.key == 'en'}
контекст en
{/if}
Либо твой пример:
{if $_modx->context.key == 'web'}
{include 'menu'}
{elseif $_modx->context.key == 'en'}
{include 'enmenu'}
{/if}
Присмотрись в сторону Fenom, уверяю тебя, ни грамма не пожалеешь об этом.
Спасибо, Вы даже написали мою конструкцию, вроде выглядет красиво код.
Но не отображается, потому что пакет не поставлен..? fenom
Я просто почему стандартными средствами., потому что сам не люблю добавления ставить. кажется, что каждое дополнение будет тормозить работу скриптов в целом, ошибался.
{if $_modx->context.key == 'web'}
{include 'menu'}
{elseif $_modx->context.key == 'en'}
{include 'enmenu'}
{/if}
prntscr.com/8zlelhНо не отображается, потому что пакет не поставлен..? fenom
Я просто почему стандартными средствами., потому что сам не люблю добавления ставить. кажется, что каждое дополнение будет тормозить работу скриптов в целом, ошибался.
pdoTools надо ставить для работы с Феномом.
В работе с Феном много тонкостей. Например, скорей всего на твоём сайте, если ты поставишь pdoTools с включённым Феном, у тебя повываливаются ошибки и страница не отобразится, как надо. Это связано с тем, что Феном любой код (даже JS, даже JSON в параметрах сниппетов стандартным выводом) с фигурной скобой {, если после неё идёт какой-либо текст, а не пробел, таб или перенос строки, будет считать «за своего» и пытаться обработать. Естественно синтаксис его не устроит и он вывалит ошибку. Приведу пример:
Мы на странице вызываем код fancybox вот так:
Или есть у нас сниппет вызываемый стандартными средствами MODX с параметром, в котором указана JSON строка вот так:
И так со всем кодом на странице. Когда все подобные моменты на странице будут исправлены — Феном будет радовать нас своими возможностями и скоростью работы.
В работе с Феном много тонкостей. Например, скорей всего на твоём сайте, если ты поставишь pdoTools с включённым Феном, у тебя повываливаются ошибки и страница не отобразится, как надо. Это связано с тем, что Феном любой код (даже JS, даже JSON в параметрах сниппетов стандартным выводом) с фигурной скобой {, если после неё идёт какой-либо текст, а не пробел, таб или перенос строки, будет считать «за своего» и пытаться обработать. Естественно синтаксис его не устроит и он вывалит ошибку. Приведу пример:
Мы на странице вызываем код fancybox вот так:
$("a[rel='ajaxModal']").fancybox({'type':'iframe', 'arrows':false});
Феном видит это безобразие и в логе ошибок в бекенде сообщает нам, что он не понимает синтаксис, который мы указали для него на странице. Что нам надо сделать, чтобы удовлетворить потребность Фенома в корректном синтаксисе? Да просто после фигурной скобы поставить пробел, вот так:$("a[rel='ajaxModal']").fancybox({ 'type':'iframe', 'arrows':false});
Или есть у нас сниппет вызываемый стандартными средствами MODX с параметром, в котором указана JSON строка вот так:
[[!pdoResources? &sortby=`{"id":"DESC"}`]]
Феном не устроит подобное отношение и он, как и в прошлом примере скажет нам, что синтаксис не уместен. Что мы делаем? Правильно! Просто ставим пробел после скобы:[[!pdoResources? &sortby=`{ "id":"DESC"}`]]
И так со всем кодом на странице. Когда все подобные моменты на странице будут исправлены — Феном будет радовать нас своими возможностями и скоростью работы.
Меня это не пугает, буду изучать fenom) pdotools поставил, но всеравно конструкция в шаблоне не отображает меню.
а сейчас решил наплодить темплейты — в Главной [[$menu]] в копия главной [[$enmenu]] ппц как топор))
а сейчас решил наплодить темплейты — в Главной [[$menu]] в копия главной [[$enmenu]] ппц как топор))
Думаю есть какой то код типа ignore, что бы fenom не обрабатывал не корректный для него синтаксис
Есть, но он не работает… — modx.pro/help/6326/
А что мешает переименовать чанки в соответствии с языком и его подставлять?
Например чанки [[$enmenu]] и [[$rumenu]], а потом просто подставлять системный параметр:
Например чанки [[$enmenu]] и [[$rumenu]], а потом просто подставлять системный параметр:
[[$[[++cultureKey]]menu]]
Можно и по другим параметрам, но принцип ясен.
Да, спасибо! я так сделал:
а чанки menu_ru и menu_en
[[$menu_[[++cultureKey]]]]
а чанки menu_ru и menu_en
дополнительная проверка по контексту интересна, но наверное надо сделать что-то одно чтобы управлять было удобнее и прозрачнее
$context=$host.'-'.$lang;
if ($modx->getCount('modContext', $context)) {
$modx->switchContext($context);
$request = preg_replace('/^'.$lang.'\//', '', $request);
}
Выкинул лишние проверки, спасибо! Теперь код стал чище и прозрачнее.
Но проверку для несуществующего языка (/de/) ещё не дописал…
Но проверку для несуществующего языка (/de/) ещё не дописал…
Ну, справедливости ради, хотел бы заметить, что я предлагал другое решение, попроще и с минимальным количеством запросов к БД.
Создаем контексты для все языков с ключами host-language — site1-ru (для site1.ru), site1-en (для site1.ru/en/), site2-ru (для site2.ru), site2-en (для site2.ru/en/). Контекст web не рабочий. В нем одна страница 404.
Вот плагин.
Создаем контексты для все языков с ключами host-language — site1-ru (для site1.ru), site1-en (для site1.ru/en/), site2-ru (для site2.ru), site2-en (для site2.ru/en/). Контекст web не рабочий. В нем одна страница 404.
Вот плагин.
<?php
// Работаем только на фронтенде
if ($modx->event->name != 'OnHandleRequest' || $modx->context->key == 'mgr') {return;}
$alias = $modx->getOption('request_param_alias', null, 'alias', true);
$request = &$_REQUEST[$alias];
// Определяем язык папки из URL
preg_match('|/(.*)/|', $_SERVER['REQUEST_URI'], $m);
$lang = trim($m[1]);
//Сравниваем с языками из массива. Языки указывает админз, он знает какие, незачем делать запрос к БД
if (! in_array($lang,array('en','de','sp'))) $lang = 'ru';
// Определяем домен
preg_match('/(.+)(\.com|ru)/', $_SERVER['HTTP_HOST'], $m);
$host = trim($m[1]);
//Формируем ключ контекста
$context = $host.'-'.$lang;
//Проверяем наличие контекста, если есть, переключаем
if ($modx->getCount('modContext', $context)) {
$modx->switchContext($context);
$request = preg_replace('/^'.$lang.'\//', '', $request);
}
//Если контекст не найден, то остаемся в контексте web, у которого страница 404 (заглушка).
если будет 2 домена site1.ru и site1.eu к примеру, надо будет немного подправить вариант ;)
Ну не пользователи же добавляют домены и языки. Это работа админа :)
//Прописываем языки в массиве. Админ знает какие, незачем делать запрос к БД
if (! in_array($lang,array('en','de','sp'))) $lang = 'ru';
Да, я решил добавить запрос в БД, чтобы решение было универсальнее.// Определяем домен
preg_match('/(.+)(\.com|ru)/', $_SERVER['HTTP_HOST'], $m);
А если все домены в разных доменных зонах? И новые домены будут добавляться непонятно каких зон? Как автоматизировать этот момент?//Если контекст не найден, то остаемся в контексте web, у которого страница 404 (заглушка).
Не стал развивать этот момент, т.к. тогда пришлось бы переносить весь контекст web в другой контекст, а потом настраивать всё это. К тому же синхронизация ресурсов в Babel-е может слететь. Особенно неудобно это будет, если в контексте web настроенный рабочий магазин с тысячами товаров. А если все домены в разных доменных зонах? И новые домены будут добавляться непонятно каких зон? Как автоматизировать этот момент?Админ для начала должен будет создать контекст со всеми настройками и это никак не автоматизируешь. А уж добавить за 2 секунды еще одну зону я думаю он будет в состоянии.
В конце концов можно регулярку сделать более универсальной — убрать зоны.
Как-то Василий вспомнил одну интересную поговорку «лучше день потерять потом за пять минут долететь». Я тоже уже кучу времени потерял с этим решением в ущерб работе с мыслю о инвестиции в будущую экономию времени на настройку — гораздо проще раз поставить плагин и забыть о нём.
В моём варианте я тоже вначале срезал у регулярки зоны, правда не знаю как она себя поведёт в ситуации с поддоменами типа sub.site.ru — я не силён в регулярках. Поэтому я не использую её для определения хоста, а запрашиваю контекст из БД через $host_language, который у меня указан вместе с доменной зоной (site.ru_en)
В моём варианте я тоже вначале срезал у регулярки зоны, правда не знаю как она себя поведёт в ситуации с поддоменами типа sub.site.ru — я не силён в регулярках. Поэтому я не использую её для определения хоста, а запрашиваю контекст из БД через $host_language, который у меня указан вместе с доменной зоной (site.ru_en)
Да я без претензий, поэтому и написал «справедливости ради».
Я тебя упомянул в статье, потому что без твоих идей я бы не справился, спасибо тебе!
Проверки на несуществующие языки типа /de/ оказывается и не нужно было писать в плагине. Достаточно было во всех контекстах сайта прописать настройку страницы 404 [error_page] для каждого контекста отдельно, даже для web.
Все мои задачи уже решены!
Все мои задачи уже решены!
ну если по одному домену выдается информация с другого — странно это выглядит как минимум
2. Проверки на несуществующие языки типа /de/ решаются просто — достаточно во всех контекстах сайта прописать настройку страницы 404 [error_page] для каждого контекста отдельно, даже для web. Тогда если человек пройдёт по несущесвующей ссылке site2.ru/de/, то он попадёт на 404-ю страницу, которая указана сайту site2.ruсудя по твоему коду, будет не найден ключ вида host_language и по умолчанию показан контекст web
Не понял что ты имеешь ввиду? У меня задача была чтобы языки были не на отдельных доменах, а в подпапках определённого домена. И если указывается несуществующая подпапка языка, то соответственно это несуществующая ошибка главного языка, и это нормально.
переключение контекста в какой момент идет? и по каким критериям? и по каким критериям он может не переключится, а лучше конечно взять да протестировать на домене который не в контексте web
Если считаешь что правильно все, то ладно)
Если считаешь что правильно все, то ладно)
Уже тестировал на сайтах:
Оба этих сайта в одной админке. У первого языки ru (web) и en, а у второго ru, en, ro. Можешь погонять, пока они ещё в тестовом режиме.
Оба этих сайта в одной админке. У первого языки ru (web) и en, а у второго ru, en, ro. Можешь погонять, пока они ещё в тестовом режиме.
домен на контексте web какой?
bazstudio.com/ — web
bazstudio.com/en/ — en
macinatorul.com/ — macinatorul-ru
macinatorul.com/en/ — macinatorul-en
macinatorul.com/ro/ — macinatorul-ro
bazstudio.com/en/ — en
macinatorul.com/ — macinatorul-ru
macinatorul.com/en/ — macinatorul-en
macinatorul.com/ro/ — macinatorul-ro
надо чтобы на первом домене где web было больше языков чем на втором, пример
bazstudio.com/ — web
bazstudio.com/en/ — en
bazstudio.com/ro/ — ro
macinatorul.com/ — macinatorul-ru
macinatorul.com/en/ — macinatorul-en
при обращении к macinatorul.com/ro/
будет показываться 404 с bazstudio.com, а не с macinatorul.com
bazstudio.com/ — web
bazstudio.com/en/ — en
bazstudio.com/ro/ — ro
macinatorul.com/ — macinatorul-ru
macinatorul.com/en/ — macinatorul-en
при обращении к macinatorul.com/ro/
будет показываться 404 с bazstudio.com, а не с macinatorul.com
По ссылке:
macinatorul.com/bg/
Выдает:
host_language: bazstudio.com_ru
а должен macinatorul-ru
особенно если разные дизайны будут и наполнение это сильно будет влиять
хотя чего я доказываю, если устраивает как работает, пожалуйста ;)
macinatorul.com/bg/
Выдает:
host_language: bazstudio.com_ru
а должен macinatorul-ru
особенно если разные дизайны будут и наполнение это сильно будет влиять
хотя чего я доказываю, если устраивает как работает, пожалуйста ;)
Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.