• avatar 1px
  • 0
Ну если тема именно в том, чтобы не использовать evoBabel, то для связи было бы проще и логичнее использовать Selector.
Там тебе и дерево нужное, и выпадающий список удобный.
  • avatar paic
  • 0
Ну что ж вы меня все пытаетесь убедить в том, с чем я и так согласен))

Еще раз — обратите внимание на Название темы:
Простая мультиязычность для простого сайта.

Я же никого не заставляю это повторять, если сайт сложный, со многими неизвестными в ТЗ со всякими если и возможно по расширяемости и прочему, если много языков, или если просто не нравится.

Но я считаю, что выбор средств и способов реализации должен осуществляться исходя из поставленных задач. И если задача простая, то и реализация должна быть простой. Зачем самолет с вертикальным взлетом, если достаточно велосипеда.

Да и на практике огромное количество сайтов работают как их сделали на 2-х языках, так и работает годами.

А по объему работы сделать копии шаблонов и отредактировать их никак не больше, чем с применением специальных компонентов. Опять же — если сайт простой.

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

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

И еще же вам надо поставить, запустить и настроить сам компонент с модулем-плагинами-сниппетами-таблицами.

По моему плагину.
Без плагина сайт тоже будет работать.
Потому что этот плагин — это в дополнение, а не вместо. Сделан на случай какого-либо статейника, блога и подобного активного сайта. Но можно, конечно, и сразу ставить и пользоваться.
А что будет, если нужно добавить третий язык, допустим, французский — третий комплект шаблонов делать?
И в каждом соответственно уже будет по две ссылки на другие языки, то есть их в сумме шесть штук потребуется разметить: ru-> en, ru->fr, fr->en, fr->ru, en->ru, en->fr.

А потом мы друг решим довнести какую-то функциональность в сайт — и нужно будет править уже не два шаблона, а три.

ИМХО, избыточное дублирование кода проблем больше породит, чем кажущаяся сложность работы спец. компонентов. Опять-таки, у вас тоже без плагина не обошлось, и это тоже некий элемент «скрытности физики работы» вносит.

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

Еще важный момент — тот же evoBabel можно доавтоматизировать до такого состояния, что администратор сайта сможет добавить сам дополнительный язык, создав ресурс с соответствующим шаблоном и дозаполнив модуль evobabellexicon соответствующими значениями. Конечно, соображаловка некоторая понадобится, чтобы заполнить поля, в которых внесены идентификаторы всяких родительских/служебныхх ресурсов, но не придется лезть в шаблоны и чанки и что-то там править. В вашем варианте пока без правки шаблонов и чанков добавление дополнительного языка не обойдется, что существенно усложняет задачу «легкого» применения.

Обдумайте :)
  • avatar paic
  • 0
Спасибо за отзыв.
Я в курсе, как работает evoBabel и другие, начиная с e-KAO и YAMS ))
Но задача не стояла сделать еще один evoBabel.

Задача стояла показать как максимально просто сделать мультиязычный сайт, не прибегая к специальным компонентам, с кучей плагинов, сниппетов, дополнительными таблицами в базе. Это один момент.

А второй заключается в том, что в таком варианте ничем не скрыта физика работы подобного сайта — имеется ввиду, что если под кнопкой переключателя языка ссылка, то ссылка и пишется, а не плейсхолдер или еще что-то.
Тому кто делает мультиязычный сайт впервые это в любом случае полезно знать, вне зависимости на чем он будет делать.

Касательно разрастания сайта — для этого и сделал плагин miniLang — клепайте на здоровье хоть 1000, хоть сколько захотите страниц и категорий, все сопряжения устанавливаются автоматически:
— сопряжение страниц
— сопряжение родителей
— сопряжение шаблонов

А со временем может и добавлю лексиконы и уберу дублирующие шаблоны, подумаю. Но добавлять дополнительные таблицы в базу пока не хочется, пока смотрю в сторону ClientSettings.
Дублировать шаблоны только ради того, чтобы вписать в них другие идентификаторы ресурсов для вызовов DlMenu, DlCrumbs и прочих сниппетов, и другие названия чанков — это очень избыточно.

Тут как минимум следующие решения напрашиваются:

1 — Аналогично тому, как это сделано в evoBabel, можно задавать свои плейсхолдеры вида [+menu_parent_id+]. Заполнить которые можно хоть в сниппете, хоть в плагине.
И вызывать уже

[[DLMenu?
&parents=`[+menu_parent_id+]`
&outerTpl=`[+menu_outer_tpl+]`
&maxDepth=`2`

Где хранить эти значения плейсхолдеров и как их выдергивать сниппетом или плагином, это уже дело техники.

2. Можно в prepare-сниппетах в зависимости от языковой версии подменять tpl и ownerTPL на соответствующий чанк.
Но это уже какие-то мега-костели.

Мне доводилось забирать сайты на modx, которые были вроде и маленькими, но там было наделано по 40-50 шаблонов на все случаи жизни. После «прореживания» и удаления дублирующего кода удавалось уменьшить их на 5-10 штук ;)

И еще, любой маленький сайт из 10-20 страниц может неожиданно разрастись страниц до 1000, если заказчик проявит неожиданно энтузиазм. И если там будет куча одинаковых по сути шаблонов, то проще будет свихнуться, чем поддерживать это.

В целом, установка и настройка evoBabel даже на последних версиях Evo и PHP (у меня — работает) не выглядит такой уж сложной по сравнению с дублированием шаблонов, чанков и всего подобного.
  • avatar paic
  • 0
Про дублирование я сразу написал, что это минус. В дальнейшем можно будет подумать на этот счет.
Зато эта штука точно запустится сразу и будет работать без всяких-яких, и не поломается ни при каких обстоятельствах — там ломаться нечему.
И сделать можно за один вечер. У меня на писанину больше ушло ((
Про лучше и изящнее — просто не успел, ниже выложил, там тоже найдется что покритиковать, но все же))
  • avatar paic
  • 0
11. Плагин miniLang

Сразу код
/**
 * miniLang
 * 
 * Мультиязычность - создание альтернативных страниц
 * 
 * @category    plugin
 * @version     1.0.0
 * @internal    @events OnDocFormSave
**/

switch ($modx->Event->name) {
    case 'OnDocFormSave': {
    	if ($mode == "new") {
        	include_once(MODX_BASE_PATH.'assets/lib/MODxAPI/modResource.php');
        	$doc = new modResource($modx);
        	$doc->edit($id);
        	$template = $doc->get('template');
			$pagetitle = $doc->get('pagetitle');
			$image = $doc->get('image');
			
			// Определение родителя альтернативной версии
			$parent = $doc->get('parent');
			if ($parent != 0) {
				$value = $doc->get('parent');
				$tmplvarid = '9';
				$out = $modx->db->getValue('Select contentid from '.$modx->getFullTableName('site_tmplvar_contentvalues').' where value="'.$value.'" and tmplvarid="'.$tmplvarid.'"');
			}else{ 
				$out = '11';
			}
			$newparent = $out;
			
			// Определение шаблона альтернативной версии
			$table1 = $modx->getFullTableName( 'site_templates' );
			$res = $modx->db->select("description", $table1, "id='".$template."'");
			while ($row=$modx->db->getRow($res)) {
				$description = $row['description'];
			}
			$category = '2';
			$newtemplate = $modx->db->getValue('Select id from '.$table1.' where description="'.$description.'" and category="'.$category.'"');
			
			// Создание страницы альтернативной версии
        	        if ($template != $newtemplate) {
				$doc->create(array(
    				'pagetitle' => $pagetitle,
    				'template' => $newtemplate,
    				'parent' => $newparent,
					'published' => 0,
					'altRu' => $id,
					'image' => $image
				));
				
		// Запись id созданной альтернативной страницы в ТВ основной страницы
		    $newid = $doc->save(true, false);
		    $table = $modx->getFullTableName('site_tmplvar_contentvalues');
		    $fields = array(
                    	'value' => $newid,  
                    	'contentid'  => $id,  
                    	'tmplvarid' => 10
                    );   
    		    $modx->db->insert( $fields, $table);  
		}
			
    	    }
	}
}

Плагин составлялся исходя из того, что основной язык сайта русский, и добавления страниц на сайт так же будет сначала на русском языке, а потом на альтернативном (английском).
Что делает плагин:
1. При создании страницы на русском языке в любой категории или в корне сайта, при ее сохранении дополнительно создается страница в англоязычной версии в такой же категории или тоже в ее корне.
2. Альтернативная страница создается не опубликованной (в плагине можно изменить).
3. На альтернативную страницу передается заголовок (для последующей идентификации) и изображение (чтобы меньше работы было, но можно и поменять).
4. Выполняется двухстороннее сопряжения — в оба ТВ altEn и altRu вписываются нужные id страниц, т.е. вручную их больше прописывать не надо.

Основная страница



Альтернативная страница



Таким образом после создания русскоязычной версии остается только перейти на англоязычную, поменять заголовок и текст, опубликовать.

Комментарии по коду прописаны, что еще осталось пояснить:
tmplvarid = 9 — это altRu
tmplvarid = 10 — это altEn
category = 2 — категория, в которой находятся шаблоны англоязычной версии
description — это описание шаблона, в данном случае оно пошло «в дело», с помощью него сделано сопряжение шаблонов, чтобы шаблон создаваемой альтернативной страницы использовал аналогичный шаблон, какой выбран у основной страницы.
Поэтому, если посмотрите скриншот, он одинаков для каждой пары шаблонов, например у шаблонов и Блог, и Блог En — blog.



12. TV параметры altEn и altRu

Они теперь вроде как и не у дел, можно даже скрыть, чтобы «не путались под ногами».
А можно и оставить на случай форс-мажора.

Я оставил, но «приукрасил». В качестве украшательства рассматривались несколько вариантов, в итоге остановился на ddTree (DropDown Tree) (да, он еще живой, на v 7.3 полет нормальный).
Выглядит теперь так



Это все, что я хотел написать в рамках этого топика. Дополнения и критика, как всегда приветствуется.
Еще раз хочу подчеркнуть, что это для простых сайтов, когда проще и быстрее продублировать несколько шаблонов, чем ставить и настраивать какой-то сложный и объемный компонент.
Эту задачу было бы интереснее решить в 3.x, ну или через EvoTwig (заодно можно было бы написать расширение для твига для лексиконов).
Если я берусь за задачу, которую уже решали до меня, то мне хочется решить ее лучше, чем предшественники. Ну или хотя бы изящнее (: Здесь, увы, я этого не наблюдаю — одно дублирование шаблонов чего стоит. Нужно признать очевидное — из коробки ево не способна ни в мультиязычность, ни в мультисайтовость.

За старания, конечно, плюс.
  • avatar paic
  • 0
  • avatar paic
  • 0
14. Контроллеры в EvoTwig, исправления и дополнения к п.13.

Заключительный алгоритм при работе с контроллерами, начиная с установки:

1. Создаем в папке assets файл composer.json, внутри файла помещаем
{
	"require":{
		"pathologic/evo-twig-lib": "*"
	}
}

2. Запускаем Composer и переходим в папку assets (вариант для Open Server)
cd domains/site.ru/assets
где site.ru — это ваш домен.

3. Запускаем команду и ждем ее выполнения
composer require pathologic/evo-twig-lib

4. Устанавливаем плагин EvoTwig2 из Extras (или вручную), включаем его в админке (по умолчанию он выключен.

5. Создаем нужные шаблоны в папке tpl и подключаем их в админке к нужным шаблонам Evo. Проверяем работоспособность.

6. Для работы с контроллерами создаем свои контроллеры (глобальный и дочерние для шаблонов) и помещаем их в отдельную папку вне папки Vendor. Я их разместил в папке по адресу assets/plugins/evotwig/addons/controllers/

Глобальный контроллер — расширяет базовый контроллер, а дочерние контроллеры для шаблонов должны расширять глобальный контроллер. Т.е. получается такая цепочка: базовый контроллер — глобальный контроллер — дочерний контроллер.
Дочерний контроллер подключается к шаблону, например @FILE:service@ServiceController, где service — название шаблона, а ServiceController — дочерний контроллер.
Котроллер GlobalController никуда подключать не надо.

В конфигурации плагина EvoTwig в поле Base controller class вместо прописанного там по умолчанию Pathologic\EvoTwig\BaseController указываем класс своего контроллера. У меня это Controllers\GlobalController

Пример Глобального контроллера, который расширяет штатный Базовый контроллер EvoTwig
<?php

namespace Controllers;

use Pathologic\EvoTwig\BaseController;

class GlobalController extends BaseController 
{
    public function __construct(\DocumentParser $modx, array $params = [])
    {
        parent::__construct($modx, $params);
	$this->docid = $this->modx->documentIdentifier;
        $this->setTemplateData([
            'mymenu' => json_decode($this->modx->runSnippet('DLMenu', ['parents' => 0, 'maxDepth' => 2, 'api' => 1]), true)[0]
	    // здесь можно добавлять и другие переменные, через запятую
        ]);
    } 
}

Пример дочерних контроллеров, которые расширяют Глобальный контроллер
<?php

namespace Controllers;

class ServiceController extends GlobalController
{
    public function __construct(\DocumentParser $modx, array $params = [])
    {
        parent::__construct($modx, $params);
        $this->setTemplateData([
	    'service' => json_decode($this->modx->runSnippet('DocLister', ['tvPrefix' => '', 'parents' => $this->docid, 'depth' => 0, 'tvList' => 'image', 'api' => 1]), true)
	    // здесь можно добавлять и другие переменные, через запятую
        ]);
    }
}


Как вариант, можно и так:
Глобальный контроллер
<?php

namespace Controllers;

use Pathologic\EvoTwig\BaseController;

class GlobalController extends BaseController 
{
    public function __construct(\DocumentParser $modx, array $params = [])
    {
        parent::__construct($modx, $params);
	$this->docid = $this->modx->documentIdentifier;
	$this->setGlobalData();
        $this->setPageData();
    } 
    public function setGlobalData()
    {
        $this->data['mymenu'] = json_decode($this->modx->runSnippet('DLMenu', ['parents' => 0, 'maxDepth' => 2, 'api' => 1]), true)[0];
        // сюда же добавляются и другие глобальные переменные
    }

    protected function setPageData()
    {
        // оставлять пустым, используется для дочерних контроллеров
    }
}

Дочерний контроллер
<?php

namespace Controllers;

class ServiceController extends GlobalController
{
    protected function setPageData()
    {
        $this->data['service'] = json_decode($this->modx->runSnippet('DocLister', ['tvPrefix' => '', 'parents' => $this->docid, 'depth' => 0, 'tvList' => 'image', 'api' => 1]), true);
	// сюда же добавляются и другие дочерние переменные
    }
}

7. В файл composer.json помещаем код автозагрузки для созданных контроллеров
{
	"require":{
		"pathologic/evo-twig-lib": "*"
	},
	"autoload":{
		"psr-4": {
            "Controllers\\": "plugins/evotwig/addons/controllers/"
        }
    }
}


8. В Composer запускаем команду
composer update

9. Подключаем дочерние контроллеры к нужным шаблонам и проверяем работоспособность сайта.

10. Обновление
Все что выше в п.1-9 — это для текущей версии EvoTwig 2.2.0, которая вышла 31.03.21.
В предыдущей версии все то же самое, за исключением п.6 в части конфигурации плагина EvoTwig.
Ранее в конфигурации не было поля Base controller class и нужно было в поле Controllers namespace прописывать namespace своего глобального контроллера, у меня это Controllers.

Теперь порядок обновления:
— Обновляем плагин EvoTwig2.
— В конфигурации плагина удаляем из поля Controllers namespace неймспейс своего контроллера. А в поле Base controller class вместо прописанного там по умолчанию Pathologic\EvoTwig\BaseController прописываем класс своего контроллера, например, у меня это Controllers\GlobalController.
— выполняем пункты 7 и 8, при этом обновятся и evo-twig-lib — иначе обновления плагина не вступят в силу, и автогазрузка своего контроллера.

Это все, что планировал написать в этом топике. Любые дополнения приветствуются.

Автору спасибо и за помощь, и за EvoTwig2.
я пока с Дитто работаю, версию ПХП понизил))
В таком случае умываю руки
НЕ ПОМОГЛО!(((((
Ну, как бы на эту же версию я такой же сайт подымал, и ничего. А тут вдруг такое(((
Скорее не к автору сниппета а к изменчивости php. От версии к версии очень многие вещи становятся неактуальными, выпиливаются старые функции, или меняется еще что нибудь, на обратную совместимость можно вообще не надеяться.
Блин, так это ведь тогда больше вопрос к автору сниппета))
Точно не вспомню, но на 7.3 и предыдущих версиях константы определяются по разному, это легко гуглится, у меня так отвалился ajax submit, переопределял константы в соответствии с новой версией.
А подробнее? У меня 7.3, вроде как везде.
Была похожая проблема, ругалось на константы, на разных версиях php они по разному объявляются, смотрел код и менял объявление констант в соответствии со своей версие php, мне помогло