Мультиязычный сайт на 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 http://www.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'); ?>

чтобы вывести меню переключения любой страницы на все поддерживаемые языки.

Осталось теперь сделать модели мультиязычными.

Комментарии

 

lordius

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

Ответить

 

Александр

Исправьте "protested" на "protected".

Ответить

 

Дмитрий Елисеев

Исправил. Спасибо!

Ответить

 

Роман Глебушкин
Также мы добавили сюда два вспомогательных метода enabled() и suffixList(). Они пригодятся нам дальше.

я так и не понял, где они нам пригодятся. Об этих методах больше ни слова не сказано.

А вообще статья хорошая - есть выбор решений и сравнительный анализ.

Ответить

 

Test

Спасибо за подробное объяснение задачи, лучшее решение!
Применил в своем проекте dyii.ru

Немного пришлось по-разбираться, язык не менялся при разных URI.

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

Так не работало:

    'language'=>'ru',

    'params'=>array(
        'defaultLanguage'=>'en',
Ответить

 

Тимур

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

Ответить

 

Дмитрий Елисеев

Можно переопределить в базовом контроллере метод refresh() для использования getOriginalUrl вместо getUrl:

class Controller extends Ccontroller
{
    public function refresh($terminate=true, $anchor='') {
        $this->redirect(Yii::app()->getRequest()->getOriginalUrl() . $anchor, $terminate);
    }
}

Аналогично можно переопределить CController::redirect() или преобразовывать адрес вручную:

$this->redirect(DMultilangHelper::addLangToUrl($returnUrl));
Ответить

 

Тимур

Испробовал, тот же самый результат.
Может быть при генерации форм (логина или контактов) изначально в action не вставляется язык. Т.е.:

<form id="login-form" action="/site/login" method="post">
.....
Ответить

 

Sherzod

Исправьте на

parent::init();
Ответить

 

Дмитрий Елисеев

Исправил. Спасибо.

Ответить

 

Sasha

Здравствуйте,
попробовал реализовать по вашему примеру и столкнулся со следующей ситуацией. Yii::app()->language устанавливается в DMultilangHelper::processLangInUrl(), а вот уже в контроллере и вьюшке ее нет
не могу понять на каком этапе может меняться это свойство

Ответить

 

Дмитрий Елисеев

Попробуйте вывести Yii::app()->language в разных местах, например в Controller::beforeAction.

Ответить

 

Sasha

Решилось добавлением $_GET['language'] = $lang; сразу после Yii::app()->setLanguage($lang);

Ответить

 

Владимир

и все-таки кто-то смог сделать, чтоб в формах генерировался action url в соответствии правилами, описанными во 2 части?

Сейчас у меня генерируется по дефолту. Подскажите что нужно переопределить и как.

Ответить

 

Дмитрий Елисеев

В крайнем случае можно указать action форм вручную

'action'=>$this->createUrl(''),
Ответить

 

Владимир

Это не самый лучший способ, но крайний случай.

В CActiveForm для генерации action используется CHtml::normalizeUrl()

Можно переопределить метод, дописав это

class DLanguageCHtml extends CHtml {
    public static function normalizeUrl($url) {
        $url = parent::normalizeUrl($url);
        return DMultilangHelper::addLangToUrl($url);
    }
}

Но как потом использовать по умолчанию DLanguageCHtml вместо CHtml?

Вот так, добавив в конфиг, НЕ РАБОТАЕТ:

'CHtml' => array(
   'class' => 'DLanguageCHtml',
)
Ответить

 

Дмитрий Елисеев

Для такого перекрытия можно использовать classMap в файле index.php:

require_once($yii);

Yii::$classMap=array(
        'CHtml'=>'path/to/my/CHtml.php',
);

Yii::createWebApplication($config)->run();

При этом придётся скопировать весь класс полностью.

Ответить

 

Владимир

Как оказалось - это плохая идея. Сломаются ссылки.
Для форм пришлось проставить action, тогда заработало.

Но дальше появились проблемы еще. Любые ссылки, которые проходят через кастомные маршруты, отказались работать корректно.

Пришлось еще расширить 1 класс.

class DLanguageUrlRule extends CUrlRule {

    public function createUrl($manager,$route,$params,$ampersand) {
        $url = parent::createUrl($manager,$route,$params,$ampersand);
        if (false !== $url) {
            return DMultilangHelper::addLangToUrl($url);
        }
        return false;
    }
}

И в конфиге чуть добавить:

'urlManager'=>array(
    'class' => 'DLanguageUrlManager',
    'urlRuleClass' => 'DLanguageUrlRule',
),

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

Ответить

 

Дмитрий Елисеев

Спасибо. Отдельные классы для правил я как-то не учёл.

Ответить

 

Nikita

Чтобы форма отправляла по правильному URL
Предлагаю заменить CActiveForm на

class DLanguageActiveForm extends CActiveForm
{
    public function init()
    {
        $this->action = DMultilangHelper::addLangToUrl(CHtml::normalizeUrl($this->action));
        parent::init();
    }
}
Ответить

 

Одиночка Айс

Все работает ))) Еще раз большое спасибо за рецепт, но вот вопрос, как зарезать мультиланг для модулей? К примеру для админки. Можно, конечно, не генерерировать линки, но можно и вручную вбить язык, и тогда ахтунг!

Ответить

 

Дмитрий Елисеев

Можно при инициализации модуля админки вернуть язык назад:

class AdminModule extends CWebModule {
    public function init() {
        Yii::app()->language = Yii::app()->params['defaultLanguage'];
        parent::init();
    }
}
Ответить

 

Одиночка Айс

Проблема не столько в том, что модуль будет хавать текущий язык, а в том, что можно подставить его (язык) в URL и он опять пропишется в конфиге. Тем более, если мы его поменяем таким образом, как Вы предлагаете, то он станет таким для всего приложения. И еще вопрос, если каждый пользователь будет так извращаться, то какой язык будет в итоге? Допустим я выбрал английский, а кто то через секунду русский. Я весь сайт увижу на русском? Просто не знаю как ведет себя приложение при посещении более чем одним человеком. Просветите, плиз...

Ответить

 

Дмитрий Елисеев

Ну если какой-либо модуль нужно выводить только на одном языке, то в нём можно просто делать редирект на нужный язык:

class AdminModule extends CWebModule {
    public function init() {
        if (Yii::app()->request->getUrl() != Yii::app()->request->getOriginalUrl()) {
            $this->redirect(Yii::app()->request->getUrl());
        }
    }
}

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

Ответить

 

Andrey

Почему то если ставлю указанный init, выходит ошибка :

SadminModule и его поведениях не найден метод или замыкание с именем "redirect".

В классе SadminModule и его поведениях не найден метод или замыкание с именем "redirect".

Вот полностью класс модуля:

class SadminModule extends CWebModule
{

	public function init()
	{
                    if (Yii::app()->request->getUrl() != Yii::app()->request->getOriginalUrl()) {
                              $this->redirect(Yii::app()->request->getUrl());
                   }

                     $this->setImport(array(
                            'sadmin.models.*',
                            'sadmin.components.*',
                     ));
	}

	public function beforeControllerAction($controller, $action)
	{
		if(parent::beforeControllerAction($controller, $action))
		{
			return true;
		}
		else
			return false;
	}
}
Ответить

 

Azzz

DMultilangHelper :
- processLangInUrl
- addLangToUrl

$domains[0] - хардкод. Не будет работать, если CUrlManager::getBaseUrl() будет возвращать что-то отличное от пустой строки.
Например, если проект находится по адресу http://localhost/yii-project/.

Предлагаю :
- Добавить метод _getLangIndex():

	private static function _getLangIndex()
	{
		$index = 0;

		$baseUrl = ltrim(Yii::app()->getBaseUrl(), '/');
		if (strlen($baseUrl))
		{
			$baseUrlChunks = explode('/', $baseUrl);
			if (count($baseUrlChunks) > 0)
				$index = count($baseUrlChunks);
		}

		return $index;
	}

- Изменить processLangInUrl:

	public static function processLangInUrl($url) {
		if (self::enabled()) {
			
			$index = self::_getLangIndex();
			$domains = explode('/', ltrim($url, '/'));

			$isLangExists = in_array($domains[$index], array_keys(Yii::app()->params['translatedLanguages']));
			$isDefaultLang = $domains[$index] == Yii::app()->params['defaultLanguage'];

			if ($isLangExists && !$isDefaultLang) {
				Yii::app()->setLanguage($domains[$index]);
				array_splice($domains, $index, 1);
			}

			$url = '/' . implode('/', $domains);
		}

		return $url;
	}

- Изменить addLangToUrl:

	public static function addLangToUrl($url) {
		if (self::enabled()) {

			$index = self::_getLangIndex();
			$domains = explode('/', ltrim($url, '/'));

			$isHasLang = in_array($domains[$index], array_keys(Yii::app()->params['translatedLanguages']));
			$isDefaultLang = Yii::app()->getLanguage() == Yii::app()->params['defaultLanguage'];

			if ($isHasLang && $isDefaultLang)
				array_splice($domains, $index, 1);

			if (!$isHasLang && !$isDefaultLang)
				array_splice($domains, $index, 0, Yii::app()->getLanguage());

			$url = '/' . implode('/', $domains);
		}

		return $url;
	}
Ответить

 

Дмитрий Елисеев

Да, спасибо. BaseUrl надо учесть.

Ответить

 

Andrei

BaseUrl надо учесть и в методе run - LanguageSwitcherWidget

Ответить

 

Александр shaggy

Попробовал сделать по описанной методике (последний вариант)
у меня 2 языка - ru, en. по умолчанию - ru.
переключалка языков при переключении на en генерит адрес с префиксом: '_', т.е. на конце: '/_en/' и получаю ошибку 404.
зачем нужно это подчеркивание? если-бы не оно - все-бы работало красиво.

Ответить

 

Дмитрий Елисеев

Исправил. Заменил $suffix на trim($suffix, '_') в переключателе.

Ответить

 

Александр shaggy

О, теперь все хорошо. работает, благодарю:)

Ответить

 

Роман

Отличная статья, но второй способ мне не подошел - 40 языков и возможное расширение. Поэтому сделал так:

Controller.php

    public function init()
    {
        $this->sLangCode = $this->getLangCode();
        parent::init();
    }

    public function getLangCode()
    {
        //  find in path languageCode
        preg_match('~^(\w{2})(?:/|$)~i', Yii::app()->request->getPathInfo(), $aLangs);
        if ( !empty($aLangs[1]) ) $sLangCode = $aLangs[1];
        else $sLangCode = 'en';
        Yii::app()->setLanguage($sLangCode);
        return $sLangCode;
    }

UrlManager.php

class UrlManager extends CUrlManager
{
    public function createUrl($sRoute, $aParams = array(), $ampersand = '&')
    {
        if ( !empty($aParams['lsLang']) )
        {
            $lsLang = 1;
            unset($aParams['lsLang']);
        }
        $sUrl = parent::createUrl($sRoute, $aParams, $ampersand);
        if ( Yii::app()->sourceLanguage != Yii::app()->language && empty($lsLang) )
        {
            $sUrl = '/' . Yii::app()->language . $sUrl;
        }
        return $sUrl;
    }
}

main.php

.....
'sourceLanguage'=> 'en',
'language'      => 'en',
....
'(<language:(\w{2})>/?)?user/<action:\w+>.html'
....

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

Ответить

 

Дмитрий Елисеев

Ну это распространённый подход как в пункте «Указание языка в адресе». А у Вас правило

'(<language:(\w{2})>/?)?user/<action:\w+>.html' => ...

точно работает?

Ответить

 

Роман

Да. Работает. Но это одно из условий. Их больше, конечно.

(<language:(\w{2})>/?)?


Такое идет первым

Ответить

 

Роман

Спасибо доброму модератору, который отредактировал мое сообщение и привел его в божеский вид.

Ответить

 

Константин

Здравствуйте! Сделал мультиязычный сайт по вашей статье, основной язык русский, дополнительный английский. Теперь возникла необходимость, чтоб сайт открывался с начальной страницей на английском http://site/en, не получается это сделать. Не подскажите как можно это сделать? Спасибо!

Ответить

 

Дмитрий Елисеев

Можно сделать редирект в .htaccess:

RewriteRule ^$ /en [R=301,L]
Ответить

 

Александр

Обрабатывать язык приложения лучше в поведении, которое присоеденяем к событию onbeginrequest.

Ответить

 

Вячеслав

Отличные статьи! Спасибо Вам огромное за помощь в изучении Yii.
Только исправьте пожалуйста

<?php echo $this->widget('LanguageSwitcherWidget'); ?>


на

<?php $this->widget('LanguageSwitcherWidget'); ?>
Ответить

 

volofya

А куда кидать классы DLanguageHttpRequest и тд...?

Ответить

 

Дмитрий Елисеев

Можно в папку components или любую другую.

Ответить

 

Akulenok

Подскажите пожалуйста, как сделать чтобы после отправки формы урл оставался /en сбрасывается

Ответить

 

Дмитрий Елисеев

У форм указывать action вручную через $this->createUrl('').

Ответить

 

Akulenok

все равно перекидывает на русский

вот форма <?php $form=$this->beginWidget('bootstrap.widgets.TbActiveForm', array(
'action'=>$this->createUrl(''),

в контроллере стоит
if(isset($_POST['LoginForm'])).......
$this->redirect(Yii::app()->homeUrl);

Ответить

 

Дмитрий Елисеев
$this->redirect(DMultilangHelper::addLangToUrl(Yii::app()->homeUrl));
Ответить

 

Nikita

Чтобы форма отправляла запрос по правильному URL
можно заменить CActiveForm на

class DLanguageActiveForm extends CActiveForm
{
    public function init()
    {
        $this->action = DMultilangHelper::addLangToUrl(CHtml::normalizeUrl($this->action));
        parent::init();
    }
}
Ответить

 

Абай

Здравствуйте Дмитрий! Спасибо за статью, отличная. Возник только один вопрос:

Что будет, если перейти из /en/... на /... (с английского на русский), в функции processLangInUrl, в условии:

if ($isLangExists && !$isDefaultLang)
{
    $lang = array_shift($domains);
    Yii::app()->setLanguage($lang);
}
Ответить

 

Дмитрий Елисеев

Ничего. Условие не выполнится и останется тот язык, который указан в настройках.

Ответить

 

Akulenok

не знаю баг это или нет, но у вас

DMultilangHelper::addLangToUrl($this->createAbsoluteUrl(...));

добавляет слеш / перед урлом

Ответить

 

Дмитрий Елисеев

А если вызвать просто $this->createAbsoluteUrl(...) ?

Ответить

 

Akulenok

тогда все отлично, без слеша

Ответить

 

Евгений

для правила

'<language:(ru|en|de)>/' => 'site/index',

после de)> слеш не нужен
ведь в других правилах его нет
а если нужны слеши в конце то можно использовать urlSuffix=>/ для всех правил

Ответить

 

Дмитрий Елисеев

Спасибо! Исправил.

Ответить

 

omlk

У вас допущена ошибка в классе:

class DLanguageHttpRequest extends CHttpRequest {
    ...
    public function getOriginalUrl() {
        return $this->getOriginalRequestUri();
    }
    
    ... 
}

Вместо getOriginalUrl() должно бить getUrl()

Ответить

 

Дмитрий Елисеев

Это не ошибка, а дополнительный метод getOriginalUrl().

Ответить

 

omlk

Тогда нужно добавить такой как я написал - не будет работать подстановка языка в формах

Ответить

 

Rinat

Воспользовался вашим рецептом, но проблема возникла со стилями и рисунками, теперь все стили пытаются взяться по адресу:

http://sd.dev/en/css/style.css

тогда как они лежат в

http://sd.dev/css/style.css
Ответить

 

Rinat

Извиняюсь, разобрался, неправильный путь указал в стилях.

Ответить

 

Vadeam

Спасибо за статью.
Использовал второй метод. Но перестали подхватываться сообщения для перевода из, например для английского, protected/messages/en/front.php, выводятся ключи вместо перевода.
В конфиге компонент задан:

'messages' => array(
    'class' => 'CPhpMessageSource',
),

На вскидку не могу понять причину. Выводятся все текстовые данные через Yii::t('some_category', 'some_var'), но для русского языка значения из папки protected/messages/ru/front.php работают, для английского нет. Подскажите, пожалуйста, направление.
Спасибо

Ответить

 

Дмитрий Елисеев

Подробнее с языками не разбирался, так что точного ответа не знаю.

Ответить

 

Vadeam

Всем спасибо)
Как обычно водится, дело было не в бабине)
В конфиге было задано свойство приложения 'sourceLanguage'='en'.
Исправил значение на дефолтное 'en_us' и всё заработало как надо. Хотя ранее использовал способ, предложенный здесь, только "перепиленный" для использования кода языка в url, а не на основе поддоменов, и всё работало со значением 'en'.

Ответить

 

Eve

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:

namespace common\components;

use yii\base\Behavior;

class MultilingualBehavior extends Behavior
{
    ...
}

And use the behavior as:

'class' => \common\components\MultilingualBehavior::className(),

or:

use common\components\MultilingualBehavior;

...

'class' => MultilingualBehavior::className(),

Best, Dmitry!

Ответить

 

Eve

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 не поломаются

Ответить

 

LAVRIK

Пошел вашим путем, но решил доработать...
Вторая статья - http://www.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() тоже криво отработает....

Ответить

 

LAVRIK

если честно, метод processLangInUrl() отработал криво...
Взял на себя смелость переписать его:

    public static function processLangInUrl($url)
    {		
        if (self::enabled())
        {
            $domains = explode('/', ltrim($url, '/'));
			
			$lang = Yii::app()->params['defaultLanguage'];
			for ($i=0; $i<count($domains); $i++) {
				if ( in_array($domains[$i], array_keys(Yii::app()->params['translatedLanguages'])) ) {
					$lang = $domains[$i];
				}
			}			
			Yii::app()->setLanguage($lang);
			$url = str_replace($lang.'/', '', $url);	// просто уберу его из ссылки, если он там есть
        }
 
        return $url;
    }
Ответить

 

Дмитрий Елисеев

Да, мой вариант не учитывает showScriptName и baseUrl. В комментариях выше уже об этом пожаловались.

Ответить

 

Andrey

Добрый вечер, попробовал сделать вариант как на Хабре, в последующем постараюсь разобраться в вашей статье и применить её на практике, возникла небольшая проблемка:

В модуле админа как и положено виджет CMenu формирует ссылки в соответствии с правилами protected/config/main.php , например :

website/ru/admin/page/create

Все бы ничего, но при переходе меня скидывает на какой то action , скорее всего actionIndex , т.е. с ru/ не получается обратиться к нужному экшену. Как быть?

Ответить

 

Andrey

Уточнение:

'urlManager'=>array(
    'class'=>'application.components.UrlManager',
    'urlFormat'=>'path',
    'showScriptName'=>false,
    'rules'=>array(
        '<language:(ru|en|de|ch|tu|ar)>/' => 'site/index',
        '<language:(ru|en|de|ch|tu|ar)>/<action:(contact|login|logout)>/*' => 'site/<action>',
        '<language:(ru|en|de|ch|tu|ar)>/<controller:\w+>/<id:\d+>'=>'<controller>/view',
        '<language:(ru|en|de|ch|tu|ar)>/<controller:\w+>/<action:\w+>/<id:\d+>'=>'<controller>/<action>',
        '<language:(ru|en|de|ch|tu|ar)>/<controller:\w+>/<action:\w+>/*'=>'<controller>/<action>',
        '<language:(ru|en|de|ch|tu|ar)>/<module:\w+>/<controller:\w+>/<action:\w+>/<id:\d+>' => '<module>/<controller>/<action>/<id>',
        '<language:(ru|en|de|ch|tu|ar)>/<module:\w+>/<controller:\w+>/<action:\w+>' => '<module>/<controller>/<action>',
    ),
),
Ответить

 

Дмитрий Елисеев

А в такой последовательности?

'<language:(ru|en|de|ch|tu|ar)>/' => 'site/index',
'<language:(ru|en|de|ch|tu|ar)>/<action:(contact|login|logout)>/*' => 'site/<action>',
'<language:(ru|en|de|ch|tu|ar)>/<module:\w+>/<controller:\w+>/<action:\w+>/<id:\d+>' => '<module>/<controller>/<action>/<id>',
'<language:(ru|en|de|ch|tu|ar)>/<module:\w+>/<controller:\w+>/<action:\w+>' => '<module>/<controller>/<action>',
'<language:(ru|en|de|ch|tu|ar)>/<controller:\w+>/<id:\d+>'=>'<controller>/view',
'<language:(ru|en|de|ch|tu|ar)>/<controller:\w+>/<action:\w+>/<id:\d+>'=>'<controller>/<action>',
'<language:(ru|en|de|ch|tu|ar)>/<controller:\w+>/<action:\w+>/*'=>'<controller>/<action>',
Ответить

 

Aтвкун

ХОУ! Теперь в модулее админке ссылки правильно направляют и actions считываются, но в пользовательской части при аналогичном url -

site/ru/page/item/1 ,

Error 400
Некорректный запрос.

И с ru/ в адресной строке и без него(

Ответить

 

Andrey

Попробовал сделать как тут - создать отдельный config в модуле, только ради rules для админки, но теперь меня из админки выкидывает в клиентскую с сообщением

Невозможно обработать запрос "module"
Ответить

 

Andrey

Здравствуйте Дмитрий, скажите пожалуйста:

1. В каком классе Вы пишете этот метод :

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));
}

Я попробовал вставить в свой класс Page вместо actionView по умолчанию, но у меня exception вылез:

В классе Page и его поведениях не найден метод или замыкание с именем "getUrl".

2. И еще один момент; теперь пробую обкатать Ваш вариант решения. Вроде бы все как У Вас сделал, в частности эти правила:

'/' => 'site/index',
 '<action:(contact|login|logout)>' => 'site/<action>',
'<module:\w+>/<controller:\w+>/<action:\w+>/<id:\d+>' => '<module>/<controller>/<action>/<id>',
 '<module:\w+>/<controller:\w+>/<action:\w+>' => '<module>/<controller>/<action>',
'<controller:\w+>/<id:\d+>'=>'<controller>/view',
'<controller:\w+>/<action:\w+>/<id:\d+>'=>'<controller>/<action>',
'<controller:\w+>/<action:\w+>'=>'<controller>/<action>',

или такие:

'/' => '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>',
'<module:\w+>/<controller:\w+>/<action:\w+>/<id:\d+>' => '<module>/<controller>/<action>/<id>', '<module:\w+>/<controller:\w+>/<action:\w+>' => '<module>/<controller>/<action>',

В любом случае почему - то не получается зайти в domen/module

В первой варианте страницы вида:

http://visakaz/ar/page/item/4

Выдают exception :

Error 400
Некорректный запрос.

Как пробиться к модулям? Помогите Гуру!

Ответить

 

Andrey

Со вторым пунктом разобрался, выкинул все лишнее из SadminModule.
C первым не понял(

И еще странная штука, когда меня с модуля перекидывает в пользовательскую часть, то при введении логина и пароля вообще ничего не происходит, с чем это может быть связано? Вы выше писали про action вручную , если страничка вида
/login, то action нужно указывать в

$form=$this->beginWidget('CActiveForm', array( ... 'action' => ...

?

Если да, то что в нем указывать?

Ответить

 

Andrey

Попробовал в виджет формы логина вписать :

'action'=>$this->createUrl(''),

firebag выдал :

<form id="login-form" action="/login" method="post">

Вроде нормальный action, но почему то авторизация происходит довольно странно. Она проходит, как положено перекидывает site/index, Я вывожу все данные о пользователе, но почему - то Кнопка Login остается активной как при isGuest, в чем сожет быть причина?

Ответить

 

Andrey

Еще когда я пытаюсь распечатать request:

My::printArr(Yii::app()->request->getParam['LoginForm']['username']);
My::printArr(Yii::app()->request->getPost['LoginForm']['password']);

Выходят ошибки :

Не определено свойство "DLanguageHttpRequest.getParam".
Не определено свойство "DLanguageHttpRequest.postParam".
Ответить

 

Andrey

В общем решил все вышеуказанные проблемы, 1 вопрос у меня. Где-то допустил ошибку. И теперь у меня формируются ссылки вида:

//de/page/item/3?language=de

Где может добавляться данный ?language=de и в каком месте могло пропасть имя домена?

Ответить

 

Andrey

C лишним куском в конце разобрался, теперь не могу одного понять, откуда лишний слэш в начале?

Ответить

 

Andrey

C этим разобрался.

Ответить

 

Andrey

Добрый день, все отлично переводится, единственный вопрос - почему языык автоматически меняется на русский при переходе по ссылкам? Имеется ввиду не флаги.

Например на главной был URL - website/news
Выбрал язык, стало - website/en/news - язык изменился
Перешел по ссылке - gallery/ , стало gallery/ и язык стал русский как по умолчанию.

Теряется выбранный язык.

Где стоит копать?

Ответить

 

Дмитрий Елисеев

Ну так и переходите на website/en/gallery/.

Ответить

 

Andrey

Спасибо!

Т.е. мне все url прописывать вроде этого:
website/".$cur_lang."/gallery

В месте можно сей момент настроить для всех url - в конфиге или каком то классе?

Ответить

 

Дмитрий Елисеев

Это уже автоматически происходит при использовании createUrl(...).

Ответить

 

Andrey

Отлично! Язык сохраняется, только ссылка имеет такой вид:

http://ferrum/en/service/item/id/7?language=en

В каком месте удаляется аппендицит?

Ответить

 

Дмитрий Елисеев

А Вы так генерируете:

$this->createUrl('/service/item', array('id' => $id));

или ещё как-нибудь? И каким методом из статьи Вы пользуетесь?

Ответить

 

Andrey

Например в списке новостей:

echo CHtml::link($item->getName(), Yii::app()->createUrl('news/item/id/'.$item->CODE));

В виджете меню так:

...
'url' => Yii::app()->createUrl('/news/index')
...

В последнем примере если без index , то language воспринимается как действие..

Попробую попробовать с вашим примером, но ведь не всегда id нужен, иногда ссылка в какую то категорию сущностей ведет или например в контакты.

Ответить

 

Дмитрий Елисеев

Должно работать и с id, и без, если используете DLanguageUrlManager.

Ответить

 

Andrey

В вашем примере у меня выходит такая ссылка:

/en/news/item/1?language=en

Ответить

 

Дмитрий Елисеев

Переделайте на нормальную генерацию адресов с $item->CODE и посмотрите, работает или нет. И скажите, каким методом из приведённых в статье Вы пользуетесь.

Ответить

 

Andrey

Спасибо, еще раз пересмотрю статью и маршрутизаторы, если выявлю свой косяк, опишу поконкретнее где он находится. Вообще пользовался вашим методом - Прозрачная работа... Наверное что-то упустил.

Ответить

 

Виктор

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

Если человек заходит на site.com

то

public function init()
{
 print Yii::app()->language; exit;
}

выдаст текущий русский язык - ru, а тем временем в куке может быть, скажем, английский.

Как мне определить что человек зашел на главнюу страницу и в урле нет указания на какой-то язык, ведь Yii::app()->request->getUrl() выдает урл без языка в адресной строке даже если адресная строка язык содержит

site.com
и
site.com/en
и
site.com/ru

то Yii::app()->request->getUrl() выдаст одно и тоже = /

Ответить

 

Виктор

Решил пока что для себя таким образом:

public function init()
    {
        if($_SERVER['REQUEST_URI'] == '/')
        {
            if(!empty(Yii::app()->request->cookies['language']) && Yii::app()->request->cookies['language']!=Yii::app()->language)
            {
                Yii::app()->language =  Yii::app()->request->cookies['language'];
            }
        }

        Yii::app()->language = (empty(Yii::app()->language) ? Yii::app()->params['defaultLanguage'] : trim(Yii::app()->language));

        Yii::app()->request->cookies['language'] = new CHttpCookie('language', Yii::app()->language);     

        parent::init();
    }

Ответить

 

Corus

Добрый день!
Большое спасибо за ваши статьи Дмитрий, очень помогают в освоении фреймворка :)

У меня возникла небольшая проблема, чувствую что туплю в какой-то мелочи, но не могу опнять где:

Я все делал по вашему примеру, а новые классы положил в папку "protected/components/multilanguage". И при этом мой сайт постоянно выдает ошибку:

"Fatal error: Class 'DMultilangHelper' not found in .../protected/components/multilanguage/DLanguageUrlManager.php on line 8"

Если положить DMultilangHelper в корень папки "components", то все хорошо работает, но как правильно сделать, чтобы все новые файлы с классами лежали в одной папке?

Ответить

 

Дмитрий Елисеев

Добавьте этот путь в секцию import в конфиге.

Ответить

 

Сергей

Добрый день, у меня возник маленький вопрос.
В описании на официальном сайте говорится:
>>"В процессе перевода участвуют объект перевода, исходный язык и конечный язык. В Yii исходный язык по умолчанию приравнивается к исходному языку приложения, а язык — к языку приложения. Если оба языка совпадают — перевод не производится."
А если я хочу использовать более расширенные имена полей, например:
у меня есть поле

...
'show' => 'Показывать Альбом',
...


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

...
'show' => 'Show Album',
...


Но при выборе английского языка будет выводиться "show", а не 'Show Album', и это понятно, потому что язык совпадает с языком приложения, как мне вывести именно расширенную строку 'Show Album'?

Ответить

 

Дмитрий Елисеев

Поменяйте исходный язык на какой-нибудь другой.

Ответить

 

Сергей

Тоже об этом подумал, даже сделал ключами массива полные названия, типа этого:

...
'Show Album' => 'Показывать Альбом',
...


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

Ответить

 

Name Last

Здравствуйте, Дмитрий. Я все сделал по вашему описанию, и все отлично работает. И у меня такой вопрос. Можно ли список языков(transatedLanguages) сделать динамическим. т.е. взять его(список) из базы?
Допустим, у меня есть таблица locales для языков с полями - id, lang,langFullName.
И в моделе locales написал такую функцию для вывода списка:

public function langsList() {
  $model  = self::model()->findAll(array('select'=>'lang,langFullName'));
  $arr = array();
  foreach($model as $key) {
    $arr[$key->lang] = $key->langFullName;
  }
  return $arr;
}

а для того чтоб массив translatedLanguages был динамическим, оставил его пустым, т.е так:

...
'translatedLanguages' => array(),
...

и наконец, в контроллере в функции init:

Yii::app()->params['translatedLanguages'] = Locales::model()->langsList();

список-то правильно выводится, но вот только, переключалка не работает корректно, точнее вообще не работает. Когда меняешь язык, то в адресной строке префикс не меняется, а добовляется каждый раз, при нажатии на один из языков. вот так: 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

'<language:(ru|en)>/animals/?' => 'animals/index',
Ответить

 

Andrey

Все отлично работает,

Но шалит captcha, в ней не используется createUrl:

$this->widget('CCaptcha',
                                [
                                    'captchaAction' => '/users/captcha',
                                ]
                            );

Выдает:

domen/en/en/users/captcha/refresh/1?_=1456485384482

Ответить

 

Andrey

Ссылка на обновление картинки и сама картинка аналогично ведут себя(

Ответить

 

Andrey

Прошу прощения,

Вместо domen/en/en/users/captcha/refresh/1?_=1456485384482

domen/en/users/captcha/refresh/1?_=1456485384482

Но все равно картинка не генерируется.

Ответить

 

Andrey

Набыдлокодил в хелпере и все решилось, только не могу понять, при чем тут Captcha и createUrl(), если он не используется в прямом контексте?

Ответить

 

Виктор

Дмитрий, спустя долгое время использования этого решения заметил одну штуку. Все страницы сайта открываются как с языком так и без.

например страница

/ru/cities/ и /cities/ открываются без проблем и отображаются на дефолтном русском языке. А как бы сделать так, чтобы без /ru/ не открывалось? Все равно прописывать в rules

<language: >

и т.д.?

Такое происходит часто, когда используется, например, форма, или $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.

Ответить

Оставить комментарий

Войти | Завести аккаунт


(никто не увидит)



Можно использовать теги <p> <ul> <li> <b> <i> <a> <pre>