Yii 1.1: yii-language-behavior

A behavior for handling multiple languages in Yii.
15 followers

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.

Resources

Total 7 comments

#18209 report it
Backslider at 2014/09/26 12:14am
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)); ?>
#18208 report it
Backslider at 2014/09/25 11:57pm
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 />';
}
#18196 report it
Andres Lee at 2014/09/24 10:19pm
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.

#13882 report it
tehmaestro at 2013/07/04 03:57am
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?

#13559 report it
Backslider at 2013/06/05 11:43pm
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"

#12742 report it
marcovtwout at 2013/04/09 09:22am
Code consistency

Why are you using both camelCase and under_score for variable naming? Only the first complies with Yii coding style..

#12318 report it
Frostmaind at 2013/03/13 01:54pm
Good job

Thanks for the extension, this is very good implementation of multilanguage model.

Leave a comment

Please to leave your comment.

Create extension