This extension is designed for back end creation and update of multiple languages and the display of a single, selected language on the front end. I will extend this as I continue to work on it for my own applications. It differs from similar extensions in that all translatable data is contained within a single language specific table for each class that has translatable data.
Requirements ¶
Yii 1.1 or above
Usage ¶
I choose to put the behavior within /protected/behaviors/
.
Configure your primary model class by loading the behavior. For example, I have two models: Category
(primary) and CategoryLang
(translation class) - see example SQL in the download package. Thus I put the following within my Category
class:
public function behaviors() {
return array(
'LanguageBehavior' => array(
'class' => 'application.behaviors.LanguageBehavior',
'translationClass' => 'CategoryLang',
'translationForeignKey' => 'category_id',
'languageColumn' => 'language_id',
'languageRelation' => 'categoryLang', // choose a name for the relation
'translationColumns' => array('category_name','category_description'),
'languages' => Yii::app()->params['languages'],
),
);
}
*Note - Although I have declared a relation in the above configuration: 'languageRelation' => 'categoryLang'
, the relation in fact does not exist in my model. This property is used when generating a dynamic relation within the behavior.
You also need to declare your translation attributes as public properties within the primary model class, plus a property for handling translation errors:
public $categoryName;
public $categoryDescription;
public $translationError;
This extension was designed using a separate language
table (SQL included in the download). Languages should be set in the application configuration using a suitable configuration behavior. I use the following code within beginRequest()
:
if(!isset(Yii::app()->params['languages'])) {
$languages = Language::model()->findAll();
$language_array = array();
foreach($languages as $val) {
$language_array[] = array('id' => $val->id,
'code' => $val->code,
'name' => $val->name,
'image' => $val->image);
}
Yii::app()->params['languages'] = $language_array;
}
Translations are loaded automatically in the back end with your model as an array which can be looped through like this:
<?php foreach (Yii::app()->params['languages'] as $val) : ?>
<b><?php echo $val['name']; ?>:</b><br />
<b><?php echo CHtml::encode($data->getAttributeLabel('category_name')); ?>:</b>
<?php echo CHtml::encode($data->category_name[$val['id']]); ?>
<br />
<?php endforeach; ?>
You should use a foreign key constraint CASCADE to delete records in your language tables (see example SQL in download).
The extension handles validation of translatable attributes, however Yii's addError()
does not allow anything other than a string. This means that to display errors in the "Yii way" requires some creative hacking of your create/update form, plus the use of our $translation_error
property. Below is a simple example:
<?php foreach (Yii::app()->params['languages'] as $val) : ?>
<fieldset>
<legend><?php echo $val['name']; ?></legend>
<div class="row">
<?php (isset($model->translationError['category_name'][$val['id']]) ? $class='error': $class=''); ?>
<?php echo $form->labelEx($model,'category_name_', array('class' => $class)); ?>
<?php echo $form->textField($model,'category_name[' . $val['id'] . ']',array('size'=>60,'maxlength'=>255, 'class' => $class)); ?>
<?php echo (isset($model->translationError['category_name'][$val['id']]) ? '<div class="errorMessage">' . $model->translationError['category_name'][$val['id']] . '</div>':''); ?>
</div>
</fieldset>
<?php endforeach; ?>
The behavior outputs the error messages at the top of your form from our translation class validation rules using the standard $form->errorSummary($model)
For your front end simply set Yii::app()->session['language_id']
to the ID of the selected/detected language or whatever you want your default language to be.
*Please Note - Yii::app()->session['language_id']
must always be set for the front end and unset for the back end.
You can access model attributes in the front end like this:
$category = Category::model()->findByPk($id);
// 'categoryLang' is our relation name declared in the model behavior configuration
echo $category->categoryLang->category_name;
History ¶
27-12-2012 - Version 1.0.0 First release, just the basics.
27-12-2012 - Version 1.0.1 Updates for better error handling and refactoring for when new languages are added.
28-12-2012 - Version 1.0.2 Removed redundant $translation_table property.
28-12-2012 - Version 1.0.3 Support for front end single (selected) language.
29-12-2012 - Version 1.0.4 Dynamic relation for front end single (selected) language.
04-04-2013 - Version 1.0.5 Update for PHP 5.4
05-06-2013 - Version 1.0.6 Minor update
Credits ¶
Thanks to guillemc and fredpeaks for their work on Yii languages.
Good job
Thanks for the extension, this is very good implementation of multilanguage model.
Code consistency
Why are you using both camelCase and under_score for variable naming?
Only the first complies with Yii coding style..
Re: Code consistency
The Core Framework Coding Standard only specifies camelCase for class methods and properties, not all variables.
To me its a readability thing to use underscore. The standard states: "We aren't forcing you to use this code style for your application"
Help for _view.php
Hey, so, I'm trying to create the link in _view.php :
<?php echo CHtml::link(CHtml::encode($data->id), array('view', 'slug'=>$data->categoryLang->slug)); ?>
Slug being just a translation column in categoryLang, like name. This doesn't work, says : Trying to get property of non-object.
That means that the relation hasn't been built right? Does it mean that in actionIndex, using a CActiveDataProvider, the behavior that creates the relation doesn't get triggered ? How can I fix this?
Fetch all data from table.
Thanks for this implementation, its very logic and easy to integrate!
One question. How can I fetch all values from a table?
For example I have a list of categories and I want to show all of them on the sidebar.
My code is working but just for one record:
$categories = FaqCategories::model()->findByPk(3);
echo $categories->faqCategoriesTranslations->name;
I want to show all Categories in my sidebar.
Thanks.
Re. Fetch all data from table.
It's done exactly the same way:
$categories = FaqCategories::model()->findAll(); foreach($categories as $val) { echo $val->faqCategoriesTranslations->name . '<br />'; }
Re. Help for _view.php
"Does it mean that in actionIndex, using a CActiveDataProvider, the behavior that creates the relation doesn't get triggered ?"
No - check your work, you have made an error. Try:
<?php echo CHtml::link(CHtml::encode($data->categoryLang->slug), array('view', 'id'=>$data->id)); ?>
If you have any questions, please ask in the forum instead.
Signup or Login in order to comment.