Мультиязычный сайт на 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
<?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
<?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
<?php echo Yii::app()->language; ?>

должна правильно выводить текущий язык, а выражение

<?php
<?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
<?php $this->widget('LanguageSwitcherWidget'); ?>

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

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

Комментарии

 

lordius

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

 

Александр – alexanderschilling.ru

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

 

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

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

 

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

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

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

 

Test – dyii.ru

Спасибо за подробное объяснение задачи, лучшее решение!
Применил в своем проекте 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">
.....

 

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

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

 

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 – slovonline.ru

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

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

 

Одиночка Айс – www.daemonhk.kz

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

 

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

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

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

 

Одиночка Айс – www.daemonhk.kz

Проблема не столько в том, что модуль будет хавать текущий язык, а в том, что можно подставить его (язык) в 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 – avm.dp.ua

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

 

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

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

 

Александр shaggy – avm.dp.ua

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

 

Роман

Отличная статья, но второй способ мне не подошел - 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 – slovonline.ru

Чтобы форма отправляла запрос по правильному 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 – lomsoft.com

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

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

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

 

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

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

 

omlk – lomsoft.com

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

 

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

 

Максим – ventures-club.net

А можно решить эту задачу без использования createUrl() ? Поясню. У меня мультиязычный проект, переключение языков реализовано через спец. экшн и кукис, а по умолчанию назначается язык из $_SERVER[ACCEPT_LANGUAGE]. Все урлы в проекте абсолютные, и сгенерированы без использования createUrl().
Проблема такого решения - индексация поисковыми роботами, которые не устанавливают $_SERVER[ACCEPT_LANGUAGE] и, соответсвтенно, не принимают кукис.
Можно ли как-то показать текущий язык в URL, не переписывая всю генерацию ссылок проекта?

 

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

Наверное можно.

 

Максим – ventures-club.net

Мне пришла идея с динамическим субдоменами языка:
ru.site.com
en.site.com
и т.д. буду пробовать внедрить её.

При таком подходе абсолютные ссылки вида /controller/action, сгенерированные без участия createUrl не поломаются

 

LAVRIK – lavrik-v.ru

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

 

LAVRIK – lavrik-v.ru

если честно, метод 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

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

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

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

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

 

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

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

 

Andrey

Спасибо!

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

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

 

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

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

 

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

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

$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

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

 

Виктор – guides.by

Сделал так как указано в статье. Все работает все здорово. Но возникла задача запомнить какой язык ранее выбрал пользователь, т.е положить 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() выдаст одно и тоже = /

 

Виктор – guides.by

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

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(), если он не используется в прямом контексте?

 

Виктор – guides.by

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

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

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

<language: >

и т.д.?

Такое происходит часто, когда используется, например, форма, или $this->refresh() тогда язык теряется.

 

Виктор – guides.by

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. Чтобы поисковик мог все языки проиндексировать.

 

Михаил

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

во вьюшке прописано

<?php echo CHtml::submitButton('Вход', array('class'=>'btn btn-template-main')); ?>

в контроллере

public function actionIndex(){
    if (is_null(Yii::app()->user->id))
        $this->redirect('/index.php/site/login');
...

 

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

$this->redirect(array('/site/login'));

 

Михаил

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

 

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

А адреса в кабинете с языком указываются?

 

Михаил

Спасибо большое за ответ и уделенное время!
Решил проблему, добавив к виджету (в его начале) форм, во вьюхе, следующее: 'action'=>$this->createUrl('/login'), и вышло так:

$form=$this->beginWidget('DLanguageActiveForm', array(
	'id'=>'login-form',
	'action'=>$this->createUrl('/login'),
	'enableClientValidation'=>true,
	'clientOptions'=>array(
		'validateOnSubmit'=>true,
	),
));

 

Михаил

У меня еще один вопрос возник, касательно LanguageSwitcherWidget.php
Можно ли его оформить, как выпадающий список, также добавив к языкам и флаги стран?
К примеру таким образом:
[флаг России] Русский
[флаг Украины] Український
Как это можно сделать?

 

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

Это уже как сверстаете.

 

Error202

Дмитрий, не подскажите, делаю подобное на Yii2...

Вместо СHttpRequest использую Request
В нем, вместо метода getRequestUri() подменяю getUrl - Это правильно?

Все работает, вот только Url::home() - ссылка без языка, а если переопределить метод baseUrl, то слетают пути в assets.

Не подскажите, как победить?

 

Сергей

Здравствуйте! Вопрос о LanguageSwitcherWidget.php. Если включить переключатель на другой язык с главной страницы, то получается такой урл - sait.ru/en/. Подскажите пожалуйста как сделать чтобы слеша не было при переключении на главной странице?

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

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


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





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