[EVO] Мультиязычность, ещё 1 вариант:)

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

Исходные данные
Мое решение основанно на вот этих Статьях:
e-kao.ru/multilingual
e-kao.ru/multilingual-2
спасибо за них автору:)

Довольна долго пользовался первым вариантом но тут стала задача что б клиент сам мог добавить язык или даже 3-4 то есть бесконечное кол во.
в итоге я понял что несколько шаблонов не выход.

Итого получаем что есть структура документов:
ru
-главная
-новости
-…
en
-home
-news
-…


Шаблоны всего в 1 екземпляре.

Далее берем плагин переключения языков отсюда:
e-kao.ru/multilingual-2 он нам подходит (правда не в случае когда у нас много языков с возможностью клиенту добавить)
но вот вариант с решением через плейсхолдеры мне не понравился так как пришлось бы плодить очень много чанков и тут написал вот такой снипет, взяв за основую UltimateParent

<?php
$top= isset ($top) && intval($top) ? $top : 0;
$id= isset ($id) && intval($id) ? intval($id) : $modx->documentIdentifier;
$topLevel= isset ($topLevel) && intval($topLevel) ? intval($topLevel) : 0;
if ($id && $id != $top) {
    $pid= $id;
    if (!$topLevel || count($modx->getParentIds($id)) >= $topLevel) {
        while ($parentIds= $modx->getParentIds($id, 1)) {
            $pid= array_pop($parentIds);
            if ($pid == $top) {
                break;
            }
            $id= $pid;
            if ($topLevel && count($modx->getParentIds($id)) < $topLevel) {
                break;
            }
        }
    }
}
//получаем псевданим документа 
$txt = $modx->getDocument($id,'alias');

//разбираем чанк и создаем масив
$godChunk = $modx->getChunk($txt['alias']);
$paramLines = explode("\r\n",$godChunk);
$paramArr = array();
foreach($paramLines as $line) {
    list($param,$ppid) = explode('==',$line);
    $paramArr[$param] = $ppid;
}

//получаем параметр
$ppid = $paramArr[$name];

//выдаем параметр
return $ppid;
?>


Снипет получает самый верхний документ в дереве документов
смотрит его allias и ищет такой же чанк :) ну а в чанке у нас уже хранятся параметры.
Все в 1 в таком виде:

язык==2102
главная==1
название==Компания
производители==Производители
выберите==выберите
бренды==68
категории товаров==Категории товаров
каталог==168
поиск по==Поиск по №
поиск==881
Статьи==Статьи
cтатьи==76
copyright==2012 © Название компании


ну а в нужном месте теперь выводим снипет вида:
[[lang?name=`главная`]]


Все :) вот и весь снипет :)

при первой загрузке конечно дает немного лишних запросов. но в итоге все падает в кеш и нет проблем :)

ну а для создания ещё 1 языка просто копируем ветку языка + создаем соответствующий чанк:) с чем справиться любой менеджер если ему показать как:)

Да и хочу заметить что у нас почти не будет лишних элементов что очень радует
и при правках не будет нужно их вносить в каждый язык

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

avatar
Ленг в чанке удобно но зачастую все 2 языка и хочется все писать прям в Шаблоне
немного перекроил снипет

<?php
// снипет засисит от allias самого верхнего документа нужно использовать ru и en 
// можно использовать и другое но тогда изменить последние 2 строки в снипете
// так же можно добавлять еще другие языки если что 
// пример  [[swith? &ru=`Русский` &en=`English`]]
$top= isset ($top) && intval($top) ? $top : 0;
$id= isset ($id) && intval($id) ? intval($id) : $modx->documentIdentifier;
$topLevel= isset ($topLevel) && intval($topLevel) ? intval($topLevel) : 0;
if ($id && $id != $top) {
    $pid= $id;
    if (!$topLevel || count($modx->getParentIds($id)) >= $topLevel) {
        while ($parentIds= $modx->getParentIds($id, 1)) {
            $pid= array_pop($parentIds);
            if ($pid == $top) {
                break;
            }
            $id= $pid;
            if ($topLevel && count($modx->getParentIds($id)) < $topLevel) {
                break;
            }
        }
    }
}
//получаем псевданим документа 
$txt = $modx->getDocument($id,'alias');

if ($txt['alias'] == 'ru') {return $ru;}
if ($txt['alias'] == 'en') {return $en;}
?>
avatar
Вы уж простите если не понимаю очевидного, но как-то все отрывками описано и возможно я где-то ошибся. Вроде все как описанное сделал, вот прям пошагово список действий:

1. Создаем структуру папок следующего вида:
ru (1)
-главная
-новости
-…
en (2)
-home
-news
-…
2. Создаем новый плагин с кодом:

<code>
$e = &$modx->Event;
if ($e->name == 'OnParseDocument') {
	$langPrefix = array(0=>"", 1=>"ru-", 2=>"en-");
	if ($modx->documentObject['parent']=='0') {
  		$lang=0;
	} else {
  		$lang=$modx->documentIdentifier;
  		do {
    		foreach ($modx->documentMap as $mapEntry) {
      			$parentId=array_search($lang, $mapEntry);
      			if ($parentId) break;
    		}
    		if ($parentId) $lang=$parentId;
  		} while ($parentId);
	}
if ( preg_match_all("~\[\+lang\.(.*)\+\]~U",
    $modx->documentOutput, $matches)) {
	  	foreach ($matches[1] as $placeholder) {
	    	$modx->setPlaceholder('lang.'.$placeholder,
	        $modx->getChunk(
	        $langPrefix[$lang].$placeholder));
	  	}
	}
}

</code>
указываем там ID своих родительских папок для языков:
$langPrefix = array(0=>"", 1=>«ru-», 2=>«en-»);
и вешаем его на событие onParseDocument.

3. Создаем новый сниппет, например с названием switch и пишем в него следующий код

<code>
<?php
// снипет засисит от allias самого верхнего документа нужно использовать ru и en 
// можно использовать и другое но тогда изменить последние 2 строки в снипете
// так же можно добавлять еще другие языки если что 
// пример  [[switсh? &ru=`Русский` &en=`English`]]
$top= isset ($top) && intval($top) ? $top : 0;
$id= isset ($id) && intval($id) ? intval($id) : $modx->documentIdentifier;
$topLevel= isset ($topLevel) && intval($topLevel) ? intval($topLevel) : 0;
if ($id && $id != $top) {
    $pid= $id;
    if (!$topLevel || count($modx->getParentIds($id)) >= $topLevel) {
        while ($parentIds= $modx->getParentIds($id, 1)) {
            $pid= array_pop($parentIds);
            if ($pid == $top) {
                break;
            }
            $id= $pid;
            if ($topLevel && count($modx->getParentIds($id)) < $topLevel) {
                break;
            }
        }
    }
}
//получаем псевданим документа 
$txt = $modx->getDocument($id,'alias');
if ($txt['alias'] == 'ru') {return $ru;}
if ($txt['alias'] == 'en') {return $en;}
?>
</code>
4. Создаем чанки en и ru в которых храним параметры (например так)
язык==2102
главная==1
название==Компания
производители==Производители
выберите==выберите
бренды==68
категории товаров==Категории товаров
каталог==168
поиск по==Поиск по №
поиск==881
Статьи==Статьи
cтатьи==76
copyright==2012 © Название компании
5. Вызываем там где нужно сниппет для переключения языков в таком виде [[switсh? &ru=`Русский` &en=`English`]]

Но не работает, подскажите в каком месте я ошибся?

Сайт висит на локалхосте, но если нужно могу перенести на сервер.
Комментарий отредактирован 2013-04-09 20:12:02 пользователем ablik
avatar
Вы действительно что-то перепутали :) Сниппет switch просто возвращает в зависимости от алиаса главного родителя то или иное слово, т.е. [[switсh? &ru=`Русский` &en=`English`]] вернет вам слово Русский на алиасах /ru/../../ или слово English на алиасах /en/../../ и служит для вставки прямо в шаблоне (так сказать прямой перевод для каждого места где это нужно) вроде [[switсh? &ru=`Все новости` &en=`All news`]] или [[switсh? &ru=`Читать дальше` &en=`Read more`]]

Если же вы хотите использовать для переводов чанки (как в сниппете lang), то вам не нужен сниппет switch, вам нужен сниппет lang и два чанка с переводами ru и en (названия должны совпадать с названиями алиасов). И вот тогда вы уже вызываете в нужных местах шаблона не [[switсh? &ru=`Главная` &en=`Main page`]] а [[lang?name=`главная`]], а в чанках ru у вас значится главная==Главная, а в чанке en у вас значится главная==main page
avatar
Да и плагин в вашем случае не совсем нужен. Это получается уже как-бы третий вариант решения той же задачи :) Вполне можно обойтись условиями [[if]] или phx для вывода нужных меню в зависимости от определенного сниппетом языка документа.
Комментарий отредактирован 2013-04-10 12:09:41 пользователем webber
avatar
Я ту немножко отвлекся от обсуждения, но впереди выходные и можно продолжить эксперименты:)

Вроде стало чуть понятней, но вопросы все еще остались. Я думал [!switch!] нужен в качестве переключателя, [!lang!] сниппет переводчик который нужно вставлять в теле шаблона. Но не совсем понятен механизм работы например с Ditto или Wayfinder, что хранить в чанках с переводом?

Я раньше вообще переводы делал с помощью TV-парамтеров, самый быстрый и простой способ, но есть очень существенный недостаток, URL не меняется. Поэтому решил разобраться как сделать по человечески, из всего что нашел в сети метод описанный в статье показался самым простым, но есть пока много неясных моментов.
avatar
Для переключения языков используется снипет с сайта E-KAO
так как там работает верно если нет документа то переключает на раздел а не на главную это важно когда разные новости к примеру или ошиблись с написанием alias

по поводу swith и lang то снипеты выполняют одну и туже цель
только в lang — все переводы вынесенны в 1 файлик (это удобно когда языков больше 2-х)

swith вставляем по месту сразу же так же его используем и в ditto
avatar
Для Ditto и Wayfinder вам нужно вернуть в первую очередь startId для соответствующего языка. Для этого в сниппете lang у вас и есть переменные вроде каталог==168 или бренды==68. Аналогично (но с другими числами id эти же параметры будут и в чанках других языков). Т.к. вам нужно получить [[lang? &name=`каталог`]] и взять это в качестве startID для ditto или wayfinder (там startId — вечная путаница где как писать)).
Ну а в самих чанках ditto те параметры вроде pagetitle или id уже будут те, что нужно исходя из стартового id для языка. И там нужно с помощью того же lang получать только общие фразы вроде «подробнее», «просмотреть фото» и т.п.

Раз уж собрались экспериментировать, я бы на вашем месте использовал для получения нужных startID сниппет switch (возможно, добавив туда параметр id и выводя в виде плейсхолдеров [+id_startID+] в вызовах дитто или wf в качестве параметра startId, а для переводов в самих чанках — сниппет lang.
Комментарий отредактирован 2013-04-13 11:45:00 пользователем webber
avatar
Всем спасибо, вроде разобрался, действительно, этот способ самый простой и быстрый в реализации из всех которые я попробовал на EVO. Есть правда свои нюансы, но с этим уже буду разбираться в ходе экспериментов:)
avatar
Кстати, почему бы вместо хождения по родителям просто не разобрать в сниппете строку REQUEST_URI и обойтись парой строчек:

if(strpos($_SERVER['REQUEST_URI'],'/en/')){return $en;}
else {return $ru;}
avatar
Кстати да здравая мысль и быстрее и проще
и минусов не вижу
avatar
А можно подробней? И как для 3 языков?
avatar
Зачем вы раскопали все эти древности, если на данный момент поддерживается два решения evoBabel и bLang по мультиязычности? :)
avatar
Некрофилия, некрофилия… :)
avatar
товарищи, а как перевести меню на wayfinder*e? башка совсем не варит)
avatar
как-то вот так
[[Wayfinder? &startId=`[[switch? &ru=`1` &en=`101`]]`]]

где 1 — это например ID родителя русскоязычного меню
а 101 — это ID родителя англоязычного меню
avatar
спасибо! Исчерпывающий ответ :)

после использования yams, осталось послевкусие.
наведите на толковую мысль, бо запутался уже.

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

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

все ровно modx не брошу — потому что он хороший! :)
Комментарий отредактирован 2013-06-27 14:27:49 пользователем Fr3ddy
avatar
мыши кололись, плакали но продолжали грызть кактус ©=))
сам такой же))
avatar
допустим, перевод меню сработал, а как бы с главной страницей? отдельный вывод меню?
avatar
не совсем понял о чем речь, сайт в сети, можно глянуть?
avatar
сайт на локалке)

есть главная страница, потом папки ru\en
при переходе на версию сайта нужного языка, меню работает, а на главной нет, предполагаю что нужен отдельный шаблон для главной и чанки. так?
avatar
А может стоит продублировать главную страницу на обоих языках внутри соответствующих папок? Или это у вас что-то типа страницы приветствия?
Тут ведь такая штука, плагин редиректит на одну из указанных главных страниц и они по любому должны иметь вид типа site.ru/ru или site.ru/en
Это во всяком случае то как я понимаю. С отдельным шаблоном можете опробовать, но не уверен что будет работать.
avatar
задача у меня простая, сделать 1-2 страницы на англ.
но! Меню нужно переведенное, к чему с вашей помощью я и пришел, да и разделять языки по папкам в таком случае не вижу смысла. Может есть решение попроще, не пришло пока в голову как
avatar
задача решилась созданием нужного дерева и переключателем.

ссыль на переключатель
avatar
Приветствую!
Никак не разберусь как сделать переключатель между тремя языками. Что-то подсказывает, что должно быть просто, а решения не нахожу…
Подскажите — люди добрые!
avatar
оказывается, выход есть. редко очень, когда на эво его нет)

&parents=`0` &filter=`alias,[*alias*],7`

конечно, если для языков отдельные ветки и алиасы
avatar
И вот передо мною также встала задача реализации мультиязычности. В рассмотренных решениях мне не нравится
1. создание отдельной ветки документов под каждый язык (вряд ли это будет удобно и наглядно клиенту, и сложно проконтролировать наличие документов для второго языка)
2. работа через чанки (доступ клиента к чанкам вообще не вариант)
Возможно, я что-то не уловил в представленных решениях, я их рассматривал относительно поверхностно.
А что мне нужно:
1. Удобство для клиента, минимум кликов, простота публикации, создание в одном документе сразу двух языков.
2. Заполнение другого языка в обязательном порядке, дабы ВСЕ страницы гарантированно дублировались в обоих языках.

Решение придумал такое:

1. Создаем в документе вкладку с нужным языком. Создаем в ней нужные параметры (заголовок, контент и т.д.)
2. Создаем форму переключателя языков, которая отправляет на сервер соответствующий параметр
3. Ловим этот параметр из SESSION сниппетом, и подставляем его в класс у body (rus/eng)
4. В шаблоне все прописываем в виде
<span class="ru">[*pagetitle*]</span><span class="en">[*pagetitle-en*]</span>

5. в стилях прописываем
.rus > .en, .eng > .ru {display:none;}
6. В идале еще проверять по IP откуда пришел посетитель, и в зависимости от страны показывать по умолчанию тот или иной вариант языка.

В общем, хотел бы услышать мнение сообщества или рекомендации.
avatar
1. Интересное решение тем что нет копий страниц что хорошо для поисковиков + переключение языка в 1 клик на js без перезагрузки

2. Создание отдельной ветки не всегда плохо тут уже зависит от задачи так как иногда бывает что к примеру в разных языках разные новости (тут ориентируемся от задачи)

3. Выносить переводы оформления все равно кудато нужно как вариант можно настроить через cfgTV и хранить в настройках или даль на редактирования только несколько нужных чанков опять же отобразив их в админке явно гдетто а не в стандартном месте. тут не вижу проблемы собственно
— как вариант мультиТВ с 3 полями: ИмяПараметра; РУ; ЕН а дальше уже делаем свой снипет где преобразуем JSON в масив и вытаскиваем по ключам то что нам надо

По поводу авто установки языка лучше ловить не по IP а по установенному языку в браузере ну или ловить несколько переменных и расставить приоритеты что важнее

В добавок еще можно сделать плагин который будет автоматом переводить контент при заполнении скажем Пишем новость на русском сохраняем и оно само через Яндекс его переводить (1 потом легче перевести уже имея хоть что то, если не совсем хорошо с языком, 2 исключим возможность незаполненных страниц )
avatar
За идеи спасибо. Переключение языка в 1 клик на js без перезагрузки — вот тут пока завис. Сам переключатель, думаю, смогу сделать, видел аналогичные скрипты, а вот запомнить переключение и передать на другую страницу…
avatar
JS умеет писать в куки или в сессию :)
можно подсмотреть реализацию вот тут:
cheats.evolution-cms.com/

там по такому принцыпу работает показывать или не показывать коменты
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.