Мультиязычный сайт на Yii: Элементы интерфейса и URL
По многочисленным просьбам, поступающим в обратную связь, и по повышенной потребности аудитории Yii-разработчиков хочу поделиться некоторыми моментами в реализации поддержки нескольких языков на любом проекте, написанном на этом фрэймворке.
Постановка задачи
Итак, на нашем сайте необходимо:
- Научиться извлекать текущий язык из адреса;
- Заготовить варианты всех служебных надписей для элементов интерфейса (например, заголовки портлетов,
attributeLabels
полей моделей, текста уведомлений и т. д.) на разных языках; - Научить все необходимые модели хранить несколько вариантов заголовков и текстов для каждого языка;
- Разместить ссылки для переключения на разные языки.
Использование интернационализации
Для вывода вариантов перевода надписей интерфейса достаточно ознакомиться с описанием интернационализации в руководстве.
Чтобы у нас всё работало корректно, необходимо указать приложению исходный и текущий язык:
return array( 'sourceLanguage'=>'en', 'language'=>'ru', ... )
Пусть у нас есть модель Post
с надписями:
class Post extends CActiveRecord { public function attributeLabels() { return array( 'title' => 'Заголовок', 'text' => 'Текст', 'category_id' => 'Категория', ); } }
И есть портлет для вывода последних записей:
<?php $this->widget('LatestPostsPortlet', array('title' => 'Последние записи'));
Теперь в каталоге protected/messages/ru
нужно создать файл blog.php
со списком переводов на русский язык:
return array( 'Title' => 'Заголовок', 'Text' => 'Текст', 'Category' => 'Категория', 'Latest posts' => 'Последние записи', ... );
И везде в коде перейти на использование функции Yii::t
:
class Post extends CActiveRecord { public function attributeLabels() { return array( 'title' => Yii::t('blog', 'Title'), 'text' => Yii::t('blog', 'Text'), 'category_id' => Yii::t('blog', 'Category'), ); } }
<?php $this->widget('LatestPostsWidget', array('title' => Yii::t('blog', 'Latest posts')));
Аналогично нужно к этому переводу en→ru подготовить переводы en→de, en→fr и так далее. Переводить надписи фреймворка не надо, так как у него все свои переводы уже есть.
Теперь для вывода надписей нашего сайта на другом языке нужно изменить значение параметра language
в конфигурационном файле:
return array( 'sourceLanguage'=>'en', 'language'=>'fr', ... )
Теперь переводы будут браться из аналогичного файла blog.php
из каталога protected/messages/fr
.
Если на сайте используются модули, и Вы хотите хранить переводы в папках внутри модуля блога, то вместо
Yii::t('blog', 'Title');
используйте имя класса модуля:
Yii::t('BlogModule.blog', 'Title');
Теперь можно сложить файлы
blog.php
в языковые поддиректории папкиprotected/modules/blog/messages
.
Теперь рассмотрим автоматическое переключение языка.
Указание языка в адресе
Параметры конфигурационного файла – это ни что иное, как свойства объекта CApplication
, доступного через Yii::app()
, поэтому мы в любом месте можем переключить текущий язык:
Yii::app()->language = 'fr';
Теперь нам необходимо обеспечить извлечение языка из URL текущей страницы и произвести присвоение Yii::app()->language
.
Решений, на самом деле много. Ссылки на некоторые приведены здесь. В большинстве своём они сводятся к указанию языка в виде префикса адреса. Рассматривая статью на Хабре, можно увидеть, что используется изменение маршрутов:
'rules'=>array( '<language:(ru|en|de)>' => 'site/index', '<language:(ru|en|de)>/<action:(contact|login|logout)>' => 'site/<action>', '<language:(ru|en|de)>/<controller:\w+>/<id:\d+>'=>'<controller>/view', '<language:(ru|en|de)>/<controller:\w+>/<action:\w+>/<id:\d+>'=>'<controller>/<action>', '<language:(ru|en|de)>/<controller:\w+>/<action:\w+>'=>'<controller>/<action>', ),
то есть в начале масок всех правил необходимо добавить место для языка <language:(ru|en|de)>
.
Теперь где-нибудь до выполнения наших экшенов нужно указать текущий язык приложению. Чаще всего это делают при инициализации контроллера:
class Controller extends CController { public function init() { if (!empty($_GET['language'])) Yii::app()->language = $_GET['language']; parent::init(); } }
А при генерации адресов (используя переопределённый в своём классе метод UrlManager::createUrl
) нужно форсировать добавление языка:
return array( 'components'=>array( 'urlManager'=>array( 'class'=>'UrlManager', ... ), ... ), ... );
class UrlManager extends CUrlManager { public function createUrl($route, $params=array(), $ampersand='&') { if (empty($params['language'])) { $params['language'] = Yii::app()->language; } return parent::createUrl($route, $params, $ampersand); } }
Теперь можно заходить на свой сайт с указанием языка в начале адреса:
http://site.ru/ru http://site.ru/en http://site.ru/ru/blog http://site.ru/en/blog
Здесь и проявляется первый недостаток такого решения. А именно, мы бы хотели заходить на страницы нашего сайта без указания языка «ru»:
http://site.ru http://site.ru/en http://site.ru/blog http://site.ru/en/blog
Чтобы указание языка сделать необязательным, потребуется продублировать все правила:
'rules'=>array( '<language:(ru|en|de)>' => 'site/index', '/' => 'site/index', '<language:(ru|en|de)>/<action:(contact|login|logout)>' => 'site/<action>', '<action:(contact|login|logout)>' => 'site/<action>', '<language:(ru|en|de)>/<controller:\w+>/<id:\d+>'=>'<controller>/view', '<controller:\w+>/<id:\d+>'=>'<controller>/view', '<language:(ru|en|de)>/<controller:\w+>/<action:\w+>/<id:\d+>'=>'<controller>/<action>', '<controller:\w+>/<action:\w+>/<id:\d+>'=>'<controller>/<action>', '<language:(ru|en|de)>/<controller:\w+>/<action:\w+>'=>'<controller>/<action>', '<controller:\w+>/<action:\w+>'=>'<controller>/<action>', ),
Теперь, чтобы по умолчанию использовался русский перевод, нужно указать язык явно:
class Controller extends CController { public function init() { if (empty($_GET['language'])) $_GET['language'] = 'ru'; Yii::app()->language = $_GET['language']; parent::init(); } }
И при генерации ссылок не надо дописывать язык, если он русский:
class UrlManager extends CUrlManager { public function createUrl($route, $params=array(), $ampersand='&') { if (empty($params['language']) && Yii::app()->language !== 'ru') { $params['language'] = Yii::app()->language; } return parent::createUrl($route, $params, $ampersand); } }
Согласитесь, что это не очень простой подход, так как для него требуется перерабатывать все правила маршрутизации и немного «засорять» базовый контроллер.
Прозрачная работа с языковыми адресами
Попробуем разрешить проблему языковой адресации без добавления кода в контроллер и указания языка в правилах маршрутизации. В этом нет ничего сложного.
Во-первых, оставим в покое правила маршрутизации, убрав из них язык:
'rules'=>array( '/' => 'site/index', '<action:(contact|login|logout)>' => 'site/<action>', '<controller:\w+>/<id:\d+>'=>'<controller>/view', '<controller:\w+>/<action:\w+>/<id:\d+>'=>'<controller>/<action>', '<controller:\w+>/<action:\w+>'=>'<controller>/<action>', ),
Раньше мы указывали языки прямо в коде:
'<language:(ru|en|de)>' ... $_GET['language'] = 'ru'; ... Yii::app()->language !== 'ru'
В нашем новом коде этого не будет, так как это не очень красиво и не гибко. Но теперь мы не знаем, сколько языков поддерживаем. Исправим этот недостаток:
return array( 'sourceLanguage'=>'en', 'language'=>'ru', ... 'params'=>array( 'translatedLanguages'=>array( 'ru'=>'Russian', 'en'=>'English', 'de'=>'Deutsch', ), 'defaultLanguage'=>'ru', ), );
Мы добавили два параметра для хранения списка поддерживаемых языков и для указания языка, используемого по умолчанию. Кстати, имена их не случайны и мы к этому ещё вернёмся.
Для продолжения обычной работы стандартных правил маршрутизации нам нужно в CHttpRequest::getRequestUri
перехватывать и удалять префикс языка, а при создании адреса через CUrlManager::createUrl
– добавлять префикс текущего языка снова.
Другими словами, по какому бы адресу мы ни зашли:
http://site.ru/blog http://site.ru/en/blog http://site.ru/de/blog
метод Yii::app()->request->getUrl()
должен отбрасывать язык и возвращать в любом случае:
/blog /blog /blog
А генератор адресов при вызове
Yii::app()->createUrl('blog/index');
в зависимости от текущего значения Yii::app()->language
должен генерировать ссылки вида
/blog /en/blog /de/blog
То есть, фактически, система внутри кроме доступа к параметру Yii::app()->language
вообще не должна понимать, в окружении какого языка она находится на данным момент.
Реализация обработки адресов
Напишем теперь всё, что мы имели в виду в предыдущем пункте. Переопределим стандартные классы своими наследниками.
Парсер адресов:
class DLanguageHttpRequest extends CHttpRequest { private $_requestUri; public function getRequestUri() { if ($this->_requestUri === null) $this->_requestUri = DMultilangHelper::processLangInUrl(parent::getRequestUri()); return $this->_requestUri; } public function getOriginalUrl() { return $this->getOriginalRequestUri(); } public function getOriginalRequestUri() { return DMultilangHelper::addLangToUrl($this->getRequestUri()); } }
Генератор адресов:
class DLanguageUrlManager extends CUrlManager { public function createUrl($route, $params=array(), $ampersand='&') { $url = parent::createUrl($route, $params, $ampersand); return DMultilangHelper::addLangToUrl($url); } }
Теперь эти классы нужно указать в конфигурационном файле:
return array( 'sourceLanguage'=>'en', 'language'=>'ru', 'components'=>array( 'request'=>array( 'class'=>'DLanguageHttpRequest', ... ), 'urlManager'=>array( 'class'=>'DLanguageUrlManager', ... ), ), ... 'params'=>array( 'translatedLanguages'=>array( 'ru'=>'Russian', 'en'=>'English', 'de'=>'Deutsch', ), 'defaultLanguage'=>'ru', ), );
Если в приложении уже имеются свои классы UrlManager
и HttpRequest
class UrlManager extends CUrlManager {...} class HttpRequest extends СHttpRequest {...}
то вы можете отнаследоваться прямо от них:
class DLanguageUrlManager extends UrlManager {...} class DLanguageHttpRequest extends HttpRequest {...}
Теперь, собственно, сам DMultilangHelper
:
/** * @author ElisDN <mail@elisdn.ru> * @link https://elisdn.ru */ class DMultilangHelper { public static function enabled() { return count(Yii::app()->params['translatedLanguages']) > 1; } public static function suffixList() { $list = array(); $enabled = self::enabled(); foreach (Yii::app()->params['translatedLanguages'] as $lang => $name) { if ($lang === Yii::app()->params['defaultLanguage']) { $suffix = ''; $list[$suffix] = $enabled ? $name : ''; } else { $suffix = '_' . $lang; $list[$suffix] = $name; } } return $list; } public static function processLangInUrl($url) { if (self::enabled()) { $domains = explode('/', ltrim($url, '/')); $isLangExists = in_array($domains[0], array_keys(Yii::app()->params['translatedLanguages'])); $isDefaultLang = $domains[0] == Yii::app()->params['defaultLanguage']; if ($isLangExists && !$isDefaultLang) { $lang = array_shift($domains); Yii::app()->setLanguage($lang); } $url = '/' . implode('/', $domains); } return $url; } public static function addLangToUrl($url) { if (self::enabled()) { $domains = explode('/', ltrim($url, '/')); $isHasLang = in_array($domains[0], array_keys(Yii::app()->params['translatedLanguages'])); $isDefaultLang = Yii::app()->language == Yii::app()->params['defaultLanguage']; if ($isHasLang && $isDefaultLang) array_shift($domains); if (!$isHasLang && !$isDefaultLang) array_unshift($domains, Yii::app()->language); $url = '/' . implode('/', $domains); } return $url; } }
Он и будет обрабатывать все адреса и на основе префикса адреса устанавливать язык приложения. Также мы добавили сюда два вспомогательных метода enabled()
и suffixList()
. Второй пригодится нам дальше.
Если теперь попробовать зайти по разным адресам
http://site.ru/ http://site.ru/en/ http://site.ru/de/
то строка
<?php echo Yii::app()->language;
должна правильно выводить текущий язык, а выражение
<?php echo Yii::app()->createUrl('blog/view', array('id'=>1));
должно генерировать URL с префиксом, совпадающим с текущим языком.
Также мы добавили методы для получения «настоящего» адреса текущей страницы. Это может быть полезно, например, для реализации защиты от дубликатов страниц:
public function actionView($id) { $model = $this->loadModel($id); if (Yii::app()->request->originalUrl !== $model->getUrl()) $this->redirect($model->getUrl()); $this->render('view', array('model'=>$model)); }
Виджет переключения языков
В нашем случае метод Yii::app()->request->getUrl()
возвращает адрес без префикса, так что можно собрать ссылки на другие языки простой конкатенацией префиксов:
class LanguageSwitcherWidget extends CWidget { public function run() { $currentUrl = ltrim(Yii::app()->request->url, '/'); $links = array(); foreach (DMultilangHelper::suffixList() as $suffix => $name){ $url = '/' . ($suffix ? trim($suffix, '_') . '/' : '') . $currentUrl; $links[] = CHtml::tag('li', array('class'=>$suffix), CHtml::link($name, $url)); } echo CHtml::tag('ul', array('class'=>'language'), implode("\n", $links)); } }
Теперь достаточно вывести этот виджет в шаблоне:
<?php $this->widget('LanguageSwitcherWidget');
чтобы вывести меню переключения любой страницы на все поддерживаемые языки.
Осталось теперь сделать модели мультиязычными.
Дмитрий, отличный пост. Побольше бы таких постов....Думаю сделать мультиязычность на основе вашей статьи.
Исправьте "protested" на "protected".
Исправил. Спасибо!
я так и не понял, где они нам пригодятся. Об этих методах больше ни слова не сказано.
А вообще статья хорошая - есть выбор решений и сравнительный анализ.
Во второй части.
Спасибо за подробное объяснение задачи, лучшее решение!
Применил в своем проекте dyii.ru
Немного пришлось по-разбираться, язык не менялся при разных URI.
Оказалось, что в конфиге параметры для языка по умолчанию
должны быть одинаковые.
Так не работало:
Спасибо. Сделал все так же как во втором способе, работает частично. После отправки данных через страницу "Контакты" язык сбрасывается на русский. Так же сброс происходит при авторизации.
Можно переопределить в базовом контроллере метод refresh() для использования getOriginalUrl вместо getUrl:
Аналогично можно переопределить CController::redirect() или преобразовывать адрес вручную:
Испробовал, тот же самый результат.
Может быть при генерации форм (логина или контактов) изначально в action не вставляется язык. Т.е.:
Исправьте на
Исправил. Спасибо.
Здравствуйте,
попробовал реализовать по вашему примеру и столкнулся со следующей ситуацией. Yii::app()->language устанавливается в DMultilangHelper::processLangInUrl(), а вот уже в контроллере и вьюшке ее нет
не могу понять на каком этапе может меняться это свойство
Попробуйте вывести Yii::app()->language в разных местах, например в Controller::beforeAction.
Решилось добавлением $_GET['language'] = $lang; сразу после Yii::app()->setLanguage($lang);
и все-таки кто-то смог сделать, чтоб в формах генерировался action url в соответствии правилами, описанными во 2 части?
Сейчас у меня генерируется по дефолту. Подскажите что нужно переопределить и как.
В крайнем случае можно указать action форм вручную
Это не самый лучший способ, но крайний случай.
В CActiveForm для генерации action используется CHtml::normalizeUrl()
Можно переопределить метод, дописав это
Но как потом использовать по умолчанию DLanguageCHtml вместо CHtml?
Вот так, добавив в конфиг, НЕ РАБОТАЕТ:
Для такого перекрытия можно использовать classMap в файле index.php:
При этом придётся скопировать весь класс полностью.
Как оказалось - это плохая идея. Сломаются ссылки.
Для форм пришлось проставить action, тогда заработало.
Но дальше появились проблемы еще. Любые ссылки, которые проходят через кастомные маршруты, отказались работать корректно.
Пришлось еще расширить 1 класс.
И в конфиге чуть добавить:
Продолжаем использовать и тестировать. Дополните статью, наверняка пользователи столкнутся с такими же проблемами как и я
Спасибо. Отдельные классы для правил я как-то не учёл.
Чтобы форма отправляла по правильному URL
Предлагаю заменить CActiveForm на
Все работает ))) Еще раз большое спасибо за рецепт, но вот вопрос, как зарезать мультиланг для модулей? К примеру для админки. Можно, конечно, не генерерировать линки, но можно и вручную вбить язык, и тогда ахтунг!
Можно при инициализации модуля админки вернуть язык назад:
Проблема не столько в том, что модуль будет хавать текущий язык, а в том, что можно подставить его (язык) в URL и он опять пропишется в конфиге. Тем более, если мы его поменяем таким образом, как Вы предлагаете, то он станет таким для всего приложения. И еще вопрос, если каждый пользователь будет так извращаться, то какой язык будет в итоге? Допустим я выбрал английский, а кто то через секунду русский. Я весь сайт увижу на русском? Просто не знаю как ведет себя приложение при посещении более чем одним человеком. Просветите, плиз...
Ну если какой-либо модуль нужно выводить только на одном языке, то в нём можно просто делать редирект на нужный язык:
Тогда какой бы язык в адресе пользователь ни написал, он будет перенаправлен на единственно правильный адрес. А так да, при заходе в этот модуль переведётся всё приложение.
Почему то если ставлю указанный init, выходит ошибка :
В классе SadminModule и его поведениях не найден метод или замыкание с именем "redirect".
Вот полностью класс модуля:
DMultilangHelper :
- processLangInUrl
- addLangToUrl
$domains[0] - хардкод. Не будет работать, если CUrlManager::getBaseUrl() будет возвращать что-то отличное от пустой строки.
Например, если проект находится по адресу http://localhost/yii-project/.
Предлагаю :
- Добавить метод _getLangIndex():
- Изменить processLangInUrl:
- Изменить addLangToUrl:
Да, спасибо. BaseUrl надо учесть.
BaseUrl надо учесть и в методе run - LanguageSwitcherWidget
Попробовал сделать по описанной методике (последний вариант)
у меня 2 языка - ru, en. по умолчанию - ru.
переключалка языков при переключении на en генерит адрес с префиксом: '_', т.е. на конце: '/_en/' и получаю ошибку 404.
зачем нужно это подчеркивание? если-бы не оно - все-бы работало красиво.
Исправил. Заменил $suffix на trim($suffix, '_') в переключателе.
О, теперь все хорошо. работает, благодарю:)
Отличная статья, но второй способ мне не подошел - 40 языков и возможное расширение. Поэтому сделал так:
Controller.php
UrlManager.php
main.php
Все отлично работает, но не хотелось использовать регулярки. А так как я разбираю Yii только 4й день, то очень хотелось бы услышать советы и критику по этому решению
Ну это распространённый подход как в пункте «Указание языка в адресе». А у Вас правило
точно работает?
Да. Работает. Но это одно из условий. Их больше, конечно.
Такое идет первым
Спасибо доброму модератору, который отредактировал мое сообщение и привел его в божеский вид.
Здравствуйте! Сделал мультиязычный сайт по вашей статье, основной язык русский, дополнительный английский. Теперь возникла необходимость, чтоб сайт открывался с начальной страницей на английском http://site/en, не получается это сделать. Не подскажите как можно это сделать? Спасибо!
Можно сделать редирект в .htaccess:
Обрабатывать язык приложения лучше в поведении, которое присоеденяем к событию onbeginrequest.
Отличные статьи! Спасибо Вам огромное за помощь в изучении Yii.
Только исправьте пожалуйста
на
А куда кидать классы DLanguageHttpRequest и тд...?
Можно в папку components или любую другую.
Подскажите пожалуйста, как сделать чтобы после отправки формы урл оставался /en сбрасывается
У форм указывать action вручную через $this->createUrl('').
все равно перекидывает на русский
вот форма <?php $form=$this->beginWidget('bootstrap.widgets.TbActiveForm', array(
'action'=>$this->createUrl(''),
в контроллере стоит
if(isset($_POST['LoginForm'])).......
$this->redirect(Yii::app()->homeUrl);
Чтобы форма отправляла запрос по правильному URL
можно заменить CActiveForm на
Здравствуйте Дмитрий! Спасибо за статью, отличная. Возник только один вопрос:
Что будет, если перейти из /en/... на /... (с английского на русский), в функции processLangInUrl, в условии:
Ничего. Условие не выполнится и останется тот язык, который указан в настройках.
не знаю баг это или нет, но у вас
добавляет слеш / перед урлом
А если вызвать просто $this->createAbsoluteUrl(...) ?
тогда все отлично, без слеша
для правила
после de)> слеш не нужен
ведь в других правилах его нет
а если нужны слеши в конце то можно использовать urlSuffix=>/ для всех правил
Спасибо! Исправил.
У вас допущена ошибка в классе:
Вместо getOriginalUrl() должно бить getUrl()
Это не ошибка, а дополнительный метод getOriginalUrl().
Тогда нужно добавить такой как я написал - не будет работать подстановка языка в формах
Воспользовался вашим рецептом, но проблема возникла со стилями и рисунками, теперь все стили пытаются взяться по адресу:
тогда как они лежат в
Извиняюсь, разобрался, неправильный путь указал в стилях.
Спасибо за статью.
Использовал второй метод. Но перестали подхватываться сообщения для перевода из, например для английского, protected/messages/en/front.php, выводятся ключи вместо перевода.
В конфиге компонент задан:
На вскидку не могу понять причину. Выводятся все текстовые данные через Yii::t('some_category', 'some_var'), но для русского языка значения из папки protected/messages/ru/front.php работают, для английского нет. Подскажите, пожалуйста, направление.
Спасибо
Подробнее с языками не разбирался, так что точного ответа не знаю.
Всем спасибо)
Как обычно водится, дело было не в бабине)
В конфиге было задано свойство приложения 'sourceLanguage'='en'.
Исправил значение на дефолтное 'en_us' и всё заработало как надо. Хотя ранее использовал способ, предложенный здесь, только "перепиленный" для использования кода языка в url, а не на основе поддоменов, и всё работало со значением 'en'.
Hi Dmitry,
I am trying to implement yii2-multilingual-behavior into the advanced Yii template, but I am having issues with MultilingualBehavior::className() and getting "Class 'frontend\controllers\MultilingualBehavior' not found" error.
I am not sure if I need to add change "use", "namespace" or some similar setting, please let me know what seams to be the problem.
Thanks.
Eve
Hi!
Put your class into directory common/components and set same namespace for one:
And use the behavior as:
or:
Best, Dmitry!
Hi Dmitry,
Thank you for your fast response!
Best,
Eve
А можно решить эту задачу без использования createUrl() ? Поясню. У меня мультиязычный проект, переключение языков реализовано через спец. экшн и кукис, а по умолчанию назначается язык из $_SERVER[ACCEPT_LANGUAGE]. Все урлы в проекте абсолютные, и сгенерированы без использования createUrl().
Проблема такого решения - индексация поисковыми роботами, которые не устанавливают $_SERVER[ACCEPT_LANGUAGE] и, соответсвтенно, не принимают кукис.
Можно ли как-то показать текущий язык в URL, не переписывая всю генерацию ссылок проекта?
Наверное можно.
Мне пришла идея с динамическим субдоменами языка:
ru.site.com
en.site.com
и т.д. буду пробовать внедрить её.
При таком подходе абсолютные ссылки вида /controller/action, сгенерированные без участия createUrl не поломаются
Пошел вашим путем, но решил доработать...
Вторая статья - https://elisdn.ru/blog/40/yii-based-multilanguage-site-content-translations мне не очень понравилась тем, что по мне так лучше в данном случае все держать в отдельном файле с ассоциативном массиве. Это плохо, для каждой фразы делать запрос в БД.
Но речь не о том....
Нашел недоработку в этой статье!
Дело в том что мой проект лежит НЕ в корневой директории, поэтому processLangInUrl() отрабатывал неверно...
Багу пофиксил двумя строчками в начале данного метода:
$work_dir = str_replace('index.php', '', $_SERVER['SCRIPT_NAME']);
$url = str_replace($work_dir, '', $url);
вроде заработало...
пока ещё не добрался дальше, но подозреваю что addLangToUrl() тоже криво отработает....
если честно, метод processLangInUrl() отработал криво...
Взял на себя смелость переписать его:
Да, мой вариант не учитывает showScriptName и baseUrl. В комментариях выше уже об этом пожаловались.
Добрый вечер, попробовал сделать вариант как на Хабре, в последующем постараюсь разобраться в вашей статье и применить её на практике, возникла небольшая проблемка:
В модуле админа как и положено виджет CMenu формирует ссылки в соответствии с правилами protected/config/main.php , например :
website/ru/admin/page/create
Все бы ничего, но при переходе меня скидывает на какой то action , скорее всего actionIndex , т.е. с ru/ не получается обратиться к нужному экшену. Как быть?
Уточнение:
А в такой последовательности?
ХОУ! Теперь в модулее админке ссылки правильно направляют и actions считываются, но в пользовательской части при аналогичном url -
site/ru/page/item/1 ,
Error 400
Некорректный запрос.
И с ru/ в адресной строке и без него(
Попробовал сделать как тут - создать отдельный config в модуле, только ради rules для админки, но теперь меня из админки выкидывает в клиентскую с сообщением
Здравствуйте Дмитрий, скажите пожалуйста:
1. В каком классе Вы пишете этот метод :
Я попробовал вставить в свой класс Page вместо actionView по умолчанию, но у меня exception вылез:
В классе Page и его поведениях не найден метод или замыкание с именем "getUrl".
2. И еще один момент; теперь пробую обкатать Ваш вариант решения. Вроде бы все как У Вас сделал, в частности эти правила:
или такие:
В любом случае почему - то не получается зайти в domen/module
В первой варианте страницы вида:
http://visakaz/ar/page/item/4
Выдают exception :
Как пробиться к модулям? Помогите Гуру!
Со вторым пунктом разобрался, выкинул все лишнее из SadminModule.
C первым не понял(
И еще странная штука, когда меня с модуля перекидывает в пользовательскую часть, то при введении логина и пароля вообще ничего не происходит, с чем это может быть связано? Вы выше писали про action вручную , если страничка вида
/login, то action нужно указывать в
?
Если да, то что в нем указывать?
Попробовал в виджет формы логина вписать :
firebag выдал :
Вроде нормальный action, но почему то авторизация происходит довольно странно. Она проходит, как положено перекидывает site/index, Я вывожу все данные о пользователе, но почему - то Кнопка Login остается активной как при isGuest, в чем сожет быть причина?
Еще когда я пытаюсь распечатать request:
Выходят ошибки :
В общем решил все вышеуказанные проблемы, 1 вопрос у меня. Где-то допустил ошибку. И теперь у меня формируются ссылки вида:
//de/page/item/3?language=de
Где может добавляться данный ?language=de и в каком месте могло пропасть имя домена?
C лишним куском в конце разобрался, теперь не могу одного понять, откуда лишний слэш в начале?
C этим разобрался.
Добрый день, все отлично переводится, единственный вопрос - почему языык автоматически меняется на русский при переходе по ссылкам? Имеется ввиду не флаги.
Например на главной был URL - website/news
Выбрал язык, стало - website/en/news - язык изменился
Перешел по ссылке - gallery/ , стало gallery/ и язык стал русский как по умолчанию.
Теряется выбранный язык.
Где стоит копать?
Ну так и переходите на website/en/gallery/.
Спасибо!
Т.е. мне все url прописывать вроде этого:
website/".$cur_lang."/gallery
В месте можно сей момент настроить для всех url - в конфиге или каком то классе?
Это уже автоматически происходит при использовании createUrl(...).
Отлично! Язык сохраняется, только ссылка имеет такой вид:
http://ferrum/en/service/item/id/7?language=en
В каком месте удаляется аппендицит?
А Вы так генерируете:
или ещё как-нибудь? И каким методом из статьи Вы пользуетесь?
Например в списке новостей:
В виджете меню так:
В последнем примере если без index , то language воспринимается как действие..
Попробую попробовать с вашим примером, но ведь не всегда id нужен, иногда ссылка в какую то категорию сущностей ведет или например в контакты.
Должно работать и с id, и без, если используете DLanguageUrlManager.
В вашем примере у меня выходит такая ссылка:
/en/news/item/1?language=en
Переделайте на нормальную генерацию адресов с $item->CODE и посмотрите, работает или нет. И скажите, каким методом из приведённых в статье Вы пользуетесь.
Спасибо, еще раз пересмотрю статью и маршрутизаторы, если выявлю свой косяк, опишу поконкретнее где он находится. Вообще пользовался вашим методом - Прозрачная работа... Наверное что-то упустил.
Сделал так как указано в статье. Все работает все здорово. Но возникла задача запомнить какой язык ранее выбрал пользователь, т.е положить ID языка в куку и если человек зашел на сайт на главную страницу - то отобразить ему все на выбранном языке.
Если человек заходит на site.com
то
выдаст текущий русский язык - ru, а тем временем в куке может быть, скажем, английский.
Как мне определить что человек зашел на главнюу страницу и в урле нет указания на какой-то язык, ведь Yii::app()->request->getUrl() выдает урл без языка в адресной строке даже если адресная строка язык содержит
site.com
и
site.com/en
и
site.com/ru
то Yii::app()->request->getUrl() выдаст одно и тоже = /
Решил пока что для себя таким образом:
Добрый день!
Большое спасибо за ваши статьи Дмитрий, очень помогают в освоении фреймворка :)
У меня возникла небольшая проблема, чувствую что туплю в какой-то мелочи, но не могу опнять где:
Я все делал по вашему примеру, а новые классы положил в папку "protected/components/multilanguage". И при этом мой сайт постоянно выдает ошибку:
Если положить DMultilangHelper в корень папки "components", то все хорошо работает, но как правильно сделать, чтобы все новые файлы с классами лежали в одной папке?
Добавьте этот путь в секцию import в конфиге.
Добрый день, у меня возник маленький вопрос.
В описании на официальном сайте говорится:
>>"В процессе перевода участвуют объект перевода, исходный язык и конечный язык. В Yii исходный язык по умолчанию приравнивается к исходному языку приложения, а язык — к языку приложения. Если оба языка совпадают — перевод не производится."
А если я хочу использовать более расширенные имена полей, например:
у меня есть поле
то при выборе русского языка выведет 'Показывать Альбом'.
А также есть английская версия
Но при выборе английского языка будет выводиться "show", а не 'Show Album', и это понятно, потому что язык совпадает с языком приложения, как мне вывести именно расширенную строку 'Show Album'?
Поменяйте исходный язык на какой-нибудь другой.
Тоже об этом подумал, даже сделал ключами массива полные названия, типа этого:
Но потом подумал, что длинные предложения будет неудобно писать в качестве ключа массива, так что смена языка более логична. Спасибо.
Здравствуйте, Дмитрий. Я все сделал по вашему описанию, и все отлично работает. И у меня такой вопрос. Можно ли список языков(transatedLanguages) сделать динамическим. т.е. взять его(список) из базы?
Допустим, у меня есть таблица locales для языков с полями - id, lang,langFullName.
И в моделе locales написал такую функцию для вывода списка:
а для того чтоб массив translatedLanguages был динамическим, оставил его пустым, т.е так:
и наконец, в контроллере в функции init:
список-то правильно выводится, но вот только, переключалка не работает корректно, точнее вообще не работает. Когда меняешь язык, то в адресной строке префикс не меняется, а добовляется каждый раз, при нажатии на один из языков. вот так: site.loc/en/fr/ua/
Загружайте список раньше. Например, в событии beforeRequest объекта приложения.
Добрый день!
У меня такая проблема, при использовании get переменных, ссылка строится неправильно т.е.
Должно быть так: site.ru/ru/animals?category=cats
Но ссылка в меню формируется так site.ru/animals?category=cats/language/ru
Если же генерировать через $this->createUrl('animals/index',array('category'=>'cats')) - ссылка в меню
выглядит так site.ru/en/animals?family=cats/language/en
Код url manager
Все отлично работает,
Но шалит captcha, в ней не используется createUrl:
Выдает:
domen/en/en/users/captcha/refresh/1?_=1456485384482
Ссылка на обновление картинки и сама картинка аналогично ведут себя(
Прошу прощения,
Вместо domen/en/en/users/captcha/refresh/1?_=1456485384482
domen/en/users/captcha/refresh/1?_=1456485384482
Но все равно картинка не генерируется.
Набыдлокодил в хелпере и все решилось, только не могу понять, при чем тут Captcha и createUrl(), если он не используется в прямом контексте?
Дмитрий, спустя долгое время использования этого решения заметил одну штуку. Все страницы сайта открываются как с языком так и без.
например страница
/ru/cities/ и /cities/ открываются без проблем и отображаются на дефолтном русском языке. А как бы сделать так, чтобы без /ru/ не открывалось? Все равно прописывать в rules
и т.д.?
Такое происходит часто, когда используется, например, форма, или $this->refresh() тогда язык теряется.
B в дополнении. Получается, если у сайта только 1 язык, то все урлы будут
example.com/cities/
а если добавить еще хотя бы одни то все ссылки резко станут
exmplae.com/ru/cities/
т.е дефолтный язык тоже начинает подставляться в урл
В последних примерах без приписывания language с Yii::app()->params['defaultLanguage'] как раз обрабатывается эта ситуация.
Здравствуйте, Дмитрий! Я в yii начинающий и изучаю его по вашим статьям. Вы все очень подробно и доходчиво объясняете, но вот у меня проблема второй день голову ломаю не могу понять как ее решить в DMultiLangHelper он не находит метод getLanguage() выводит ошибку:
Unknown Method – yii\base\UnknownMethodException
Calling unknown method: yii\web\Application::getLanguage()
Может вы знаете как ее устранить?
Попробовать поменять на Yii::app()->language.
Добрый день!
Дмитрий подскажите почему указание на язык приложения хранится в url, а не в cookies?
Это только вредит SEO оптимизации или еще что?
Да, ради SEO. Чтобы поисковик мог все языки проиндексировать.
Здравствуйте. Спасибо за такую хорошую статью.
Делал начиная с "Прозрачная работа с языковыми адресами". Все получилось, кроме формы входа на сайт. Примеры из комментариев не помогают. Подскажите, какой код необходимо исправить в моем:
во вьюшке прописано
в контроллере
$this->redirect(array('/site/login'));
Поясню: при выборе не дефолтного языка шастаю по сайту на выбранном языке, но при входе в личный кабинет язык становится дефолтным...
А адреса в кабинете с языком указываются?
Спасибо большое за ответ и уделенное время!
Решил проблему, добавив к виджету (в его начале) форм, во вьюхе, следующее: 'action'=>$this->createUrl('/login'), и вышло так:
У меня еще один вопрос возник, касательно LanguageSwitcherWidget.php
Можно ли его оформить, как выпадающий список, также добавив к языкам и флаги стран?
К примеру таким образом:
[флаг России] Русский
[флаг Украины] Український
Как это можно сделать?
Это уже как сверстаете.
Дмитрий, не подскажите, делаю подобное на Yii2...
Вместо СHttpRequest использую Request
В нем, вместо метода getRequestUri() подменяю getUrl - Это правильно?
Все работает, вот только Url::home() - ссылка без языка, а если переопределить метод baseUrl, то слетают пути в assets.
Не подскажите, как победить?
Здравствуйте! Вопрос о LanguageSwitcherWidget.php. Если включить переключатель на другой язык с главной страницы, то получается такой урл - sait.ru/en/. Подскажите пожалуйста как сделать чтобы слеша не было при переключении на главной странице?