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

1. Первое, что стоит сделать, это разобраться с маршрутизацией урлов.
Мы рассмотрим пример, когда текущий язык указывается в начале урла, например для русской версии новостей урл будет такой: /ru/news/

Для реализации такого поведения необходимо в конфиге переопределить дефолтный класс CUrlManager:

      'urlManager'=>array(
          'class'=>'application.components.PrUrlManager',
          'languages'=>array('ru','en'),
          'langParam'=>'lang',
      ),      

Наш новый UrlManager будет базироваться на расширении LangUrlManager:

Yii::import('application.extensions.urlManager.LangUrlManager', true);

class PrUrlManager extends LangUrlManager  {
  protected function createUrlRule($route,$pattern) {    
    $language = $this->languages;         
    return parent::createUrlRule($route, "<".$this->langParam.":(".implode('|',$language).")>/".$pattern);
  }
}

Здесь мы у всех зарегистрированных правил меняем роут - добавляем в начале новый параметр из языка. Т.о. все сгенерированные урлы будут идти с префиксом /en/ или /ru/
Всю остальную логику реализует расширение LangUrlManager.

2. Теперь разберемся с моделями. Для начала определяем в каких моделях необходимо переводить данные на другой язык. Например это могут быть Новости (поля Заголовок, Краткое содержание, Основной контент) и Меню (Заголовок, Название, Контент).

Для каждой модели необходимо создать новые поля. Например, для Новостей это будут title_en, short_en, content_en. "_en" на конце должно быть обязательно. И если в приложении используются и другие языки, например немецкий, то и для него надо создать соответствующие поля. Напомним, что в ygin поля автоматически создаются при создании свойств объекта.

3. Далее необходимо переопределить классы моделей. Рассмотрим в качестве примера Новости:

class PrNews extends News {
  private function getAttributeByLanguage($attr)  {
    if (Yii::app()->language != 'ru') $attr .= '_'.Yii::app()->language;
    return $attr;
  }
  
  public function getTitle()  {
    return $this->getAttributeByLanguage('title');
  }
  public function getShort()  {
    return $this->getAttributeByLanguage('short');
  }
  public function getContent()  { 
    return $this->getAttributeByLanguage('content');
  }
}

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

4. Теперь разберемся с админкой, чтоб для неё не применялась обработка маршурутов из п. 1.

По умолчанию админка использует настройки из проектного конфига project.php, и по необходимости его можно переопределить создав файл конфига backend.php:

$projectConfig = include dirname(__FILE__).'/project.php';
unset($projectConfig['components']['urlManager']); // приводим urlManager к значению по умолчанию
return $projectConfig;

5. На этом внедрение мультиязычности на сайт можно считать законченным. При обращении к модели $news->title автоматически будет браться информация по текущему языку.

6 сентября 2013

Автор: Михаил Абрамов

Комментарии (6)

Добавить комментарий
  • Александр
    08.04.2014, 17:17:22

    Переопределение гетеров не работает. Нужно через магию переопределять.
  • Александр
    10.06.2014, 11:54:26

    Получается вариант с переопределением методов вызова свойств модели будет работать при прямом вызове этого метода. Если же вызывать напрямую свойство то обращение будет к магическому методу __get() который никак не переопределен и все будет работать по стандартной схеме
  • Александр
    10.06.2014, 11:55:33

    Таким образом вызов $news->getTitle() обработается нормально, а вот $news->title уже нет
  • Александр
    10.06.2014, 11:56:07

    Вариант решения

    private $data = array(
    'name' => array(
    'en' => 'name_en'
    ),
    'caption' => array(
    'en' => 'caption_en'
    ),
    'content' => array(
    'en' => 'content_en'
    )
    );
  • Александр
    10.06.2014, 11:56:20

    public function __get($name)
    {
    if (Yii::app()->language != 'ru') {
    if (in_array($name, array_keys($this->data))) {
    return $this->{$this->data[$name][Yii::app()->language]};
    }
    }
    return parent::__get($name);
    }
    • Михаил
      15.06.2014, 16:13:37

      Нет, всё будет работать.
      Вызов $news->title будет всегда заруливать на getTitle().
      Так будет происходить потому что нет у модели атрибута title. Ведь поля переименуются в title_ru