пятница, 21 октября 2011 г.

Интернационализация (сокращённо I18N). Мультиязычность. Сообщения на нужных языках из базы данных

Ну вот и закончил я с интернационализаций! Языки переключаются, сообщения сайта переводятся, данняе из базы данных берутся в соответствии с выбраным пользователем языком. Осталось только рассказать вам, мои читатели, как же это у меня получилось :)

Я, конечно зря не предусмотрел мультиязычности заранее, на стадии проектирования базы данных. Учти я это, избавил бы себя от лишней работы, но что сделано, то сделано - я же только учусь. Переделывать пришлось многое: все модели и вьюхи, но если понять что надо сделать на одной модели, то исправить стальные – дело техники.
Покажу на товарах.

Итак: сейчас имеется модель, assortiment. Она описана и работает с таблицей tbl_assortiment. В таблице есть поля:

Id
Name
Description
Rating
Category_id
Shown

Поля Name и Description надо интернационализировать, остальные показывать как есть вне зависимости от выбранного языка. Перевод для этих полей для меня легче всего хранить в этой же таблице, для чего я создал дополнительные поля. Получилось так:

Id
Name – это поле можно удалить, но если удалить сразу, то сайт перестанет работать
Name_ru – название на русском
Name_et – название на эстонском
Name_en – название на английском
Description – это поле можно удалить, но если удалить сразу, то сайт перестанет работать
Description_ru – описание на русском
Description_et – описание на эстонском
Description_en – описание на английском
Rating
Category_id
Shown

Теперь эти новые поля надо прописать в модели так, как если бы их сгенерировал gii. То есть в файле Z:\home\catalog.loc\www\protected\models\Assortiment.php
public function rules()
{
// NOTE: you should only define rules for those attributes that
// will receive user inputs.
return array(
array('name_ru, name_et, name_en, description_ru, 
description_et,description_en , category_id, rating', 'required'),
array('del_img', 'boolean'),
array('category_id, rating', 'numerical', 'integerOnly'=>true),
array('shown_ru,shown_et, prise_ru, prise_old_ru, 
prise_et, prise_old_et', 'safe'),
array('icon', 'file',
'types'=>'jpg, gif, png',
'maxSize'=>1024 * 1024 * 5, // 5MB
'allowEmpty'=>'true',
'tooLarge'=>'The file was larger than 5MB. 
Please upload a smaller file.',
),
// The following rule is used by search().
// Please remove those attributes that should not be searched.
array('id, name, description, category_id, rating, 
shown', 'safe', 'on'=>'search'),
);
}


public function attributeLabels()
{
return array(
'id' => Yii::t('main-ui', 'Id'),
'name_ru' => Yii::t('main-ui', 'name_ru'),
'name_et' => Yii::t('main-ui', 'name_et'),
'name_en' => Yii::t('main-ui', 'name_en'),
'description' => Yii::t('main-ui', 'Description'),
'category_id' => Yii::t('main-ui', 'Category'),
'rating' => Yii::t('main-ui', 'Rating'),
'shown_ru' => Yii::t('main-ui', 'Show in Russian catalog?'),
'shown_et' => Yii::t('main-ui', 'Show in Estonian catalog?'),
'icon' => Yii::t('main-ui', 'Image'),
'del_img'=>Yii::t('main-ui', 'Delete image?'),
);
}


public function search()
{
// Warning: Please modify the following code to remove attributes that
// should not be searched.
$criteria=new CDbCriteria;
$criteria->compare('id',$this->id);
$criteria->compare('name_ru',$this->name_ru,true);
$criteria->compare('name_et',$this->name_et,true);
$criteria->compare('name_en',$this->name_en,true);
$criteria->compare('description_ru',$this->description_ru,true);
$criteria->compare('description_et',$this->description_et,true);
$criteria->compare('description_en',$this->description_en,true);
$criteria->compare('category_id',$this->category_id);
$criteria->compare('rating',$this->rating);
$criteria->compare('shown_ru',$this->shown_ru,true);
$criteria->compare('shown_et',$this->shown_et,true);
return new CActiveDataProvider(get_class($this), array(
'criteria'=>$criteria,
));
}
И в представлениях используем полученные новые атрибуты модели в зависомоисти от выбранного языка. Файл Z:\home\catalog.loc\www\protected\views\assortiment\_form.php
Понятно, надо добавить поля ввода для новых колонок таблицы (ну или для новых атрибутов модели, если говорить правильно).
<div class="row">
<?php echo $form->labelEx($model,'name_ru'); ?>
<? echo CHtml:: image (
 Yii ::app()->urlManager->baseUrl.'/images/flags/ru.gif',
'по-русски'); ?>
<?php echo $form->textArea($model,'name_ru'
,array('rows'=>1, 'cols'=>50)); ?>
<?php echo $form->error($model,'name_ru'); ?>
</div>
Тут после названия поля идет картика флага, который уже использовался в меню для переключения языков. Точто так же добавяются и дугие поля: тот же код, но вместо _ru будет _et _en. Это названия товаров, описания на других языках добавляется также.
Немного сложнее с другими представлениями, папример с отображением списка товара:
Файл Z:\home\catalog.loc\www\protected\views\assortiment\ _view.php
Чтобы обратиться к полю модели имя в выбраном язке надо использовать такой код:
<b><?php echo Yii::t('main-ui', 'Name'); ?>:</b>
<?php echo CHtml::link(
CHtml::encode($data->{name_.Yii::app()->language}), 
array('view', 'id'=>$data->id)); ?>
<br />
Название сразу является ссылкой на просмотр этого товара. Само поле стало доступным так:
$data->{name_.Yii::app()->language}
Вместо Yii::app()->language подставляестся выбранный пользователем на сайте язык, выражение в фигурных скобках расчитывается перым, и получается, например
$data->name_ru
Значение поля имя на русском языке. Все очень просто! Теперь в представлениях, где надо выводить именно поле на выбраном языке менямем просто
$data->name
на
$data->{name_.Yii::app()->language}
В файле Z:\home\catalog.loc\www\protected\views\assortiment\ admin.php чтобы вывести колонку на нужном языке использую
array(
'name'=>'id',
'headerHtmlOptions'=>array('width'=>'50px')),           
'name_'.Yii::app()->language,
Теперь процесс вам должен быть ясен, осталось акуратненько пройтись по всему коду и внести эти изменения во все модели и представления, после это можно удалить ставшие ненужными поля без префексов языков.
Если делать сайт сразу с нужными полями, то буде все гораздо проще.

Прололжение следует.

2 комментария:

  1. На $data->{name_.Yii::app()->language} лучше вообще нигде не менять. Вместо этого лучше оставить $data->name и добавить в модель геттер getName() и сеттер setName(). Я упомянул этот подход у себя в http://www.elisdn.ru/blog/40

    ОтветитьУдалить