Yii 1.1: multilingual-active-record

CActiveRecord subclass for easier handling of multilingual content

NOTE (2012-03-25)

Please check fredpeak's multilingual behavior, which implements many of the ideas discussed in the Multilingual models thread into a nice behavior!

NOTE (2011-11-24)

This extension is obsolete, and not maintained anymore. I'm now using a behavior to accomplish the same thing, please see this post.


This extension simplifies the handling of multilingual content stored in the database.

It assumes that you use two tables for your content (one for the main content and a second one for the translations) with a one-to-many relationship between them.




  • Yii 1.1 or above


  • Extract the release file under protected/extensions

Usage example:

We have a table (and corresponding AR model) Post, with fields id, title, content. The title and content fields store values in the primary or default language of the application.

Then we have a table (and AR model) PostLang, with fields id, postId, lang, title, content, which stores title and content in additional languages.

First we make the Post model extend from MultilingualActiveRecord instead of CActiveRecord, and configure it (language codes ideally would come from application params rather than be hardcoded):

class Post extends MultilingualActiveRecord
  //primary language
  public function primaryLang() {
    return 'en';
  //additional languages found in the translations table
  public function languages() {
    return array('es', 'fr');
  //attributes to look for in the translations table
  public function localizedAttributes() {
    return array('title','content');

NOTE: We could also override langField() (which defaults to 'lang'), langForeignKey() and langClassName() (which in the Post example default to 'postId' and 'PostLang').

OK, now with the Post model we can use 2 new methods: localized() and multilingual().

The localized() method is meant to be used on the frontend. What it does is overwrite the localized attributes (title and content) with their translations, using the language currently set in the application.

//inside post controller, list action
$posts = Post::model()->localized()->findAll($criteria);
//inside post controller, show action
$post = Post::model()->localized()->findbyPk($id);

The multilingual() method is meant to be used on the backend, when you require access to the model together with its localized attributes in all languages:

//inside controller, edit action
$post = Post::model()->multilingual()->findbyPk($id);

It allows you to get and set the localized attributes in all languages as if they were part of the model itself, using the suffix _language, like this:

//inside view
echo $post->title_es;
echo $post->title_fr;
echo CHtml::activeTextField($post,'title_es');
echo CHtml::activeTextField($post,'title_fr');

After you call the save() method on the model, the translations will also be saved to the translations table.

Remember to include these new attributes when overriding the model's rules() method, otherwise they will not be assigned:

public function rules()
  $rules =  array(
    array('title', 'required'),
    array('title', 'length', 'max'=>128),
    array('content', 'safe'),
  foreach ($this->languages() as $l) {
    $rules[] = array('title_'.$l, 'length', 'max'=>128);
    $rules[] = array('content_'.$l, 'safe');
  return $rules;

Change Log

December 2, 2009

  • Initial release.

Total 9 comments

#6663 report it
dianakwt at 2012/01/26 12:30pm
multilingualbehaviour - update problem

Hi guillemc!

I've been using your MultilingualBehaviour, and it works fine int he translate displaying, and creating a new record, but I get an error when trying to update the records..


I wrote also the question in the forum line where you explained the behavior.

Thank you in advance!!


#5884 report it
Samir Hajiyev at 2011/11/22 02:44pm
how to

How to use this extension with related models? For example: Questions model has relation:

public function relations()
        // NOTE: you may need to adjust the relation name and the related
        // class name for the relations automatically generated below.
        return array(
            'answers' => array(self::MANY_MANY, 'Answers', 'tbl_questions_has_tbl_answers(tbl_questions_id, tbl_answers_id)')

I want to get both of them translation. Default language lang=en. In this situation it works prefect.

But when i change site language with lang=ru, it returns only Question translation! Does anybody know how to solve this problem?

Yii::app()->language = $_GET['lang'];
$model = Questions::model()->localized()->findByPk('1');
// Question
echo 'Question: ' . $model->title . '<br/>';
// Answers
foreach($model->answers as $value)
    echo $value->title . '<br/>';
#5346 report it
marcovtwout at 2011/10/06 04:51am
i18n database scheme.

There are many ways to store i18n data in your database. I personally prefer the variant with two tables per model, because the joins are simple and it's a good way to keep the model data together.

In the scheme this extension uses you get double columns for every language fields, which adds redundant work when adding or modifying language columns. A common way to solve this is to remove the language fields from the Model table, so they only exist in the ModelLanguage table. Example: Table Post (post_id, author_id, enabled), Table PostLanguage (post_id, language, title, subtitle, content).

I am modifying (and documenting) this extension to do just that, and will post the results when completed.

#5235 report it
Imri at 2011/09/25 12:52pm
Thanks! a little note

Thanks for the effort, firstly.

Isn't it preferred to save all the translations in one 'translations' table, which will be categorized by tables, and contain fields such as 'id', 'table', 'lang_id', 'key', 'value'?

Seems to me that it is a bit messy to create so many duplicate tables just for translations...

What's you opinion?

#2742 report it
cass at 2011/02/03 07:33pm
Works great

Managed to get it working quite nicely. :) Well done guillemc.

#271 report it
mech7 at 2010/07/23 04:40am
not working..

The method on line 114 should be..

public function langClassName() {
return get_class($this); }

But even then I get: Fatal error: Allowed memory size of 16777216 bytes exhausted (tried to allocate 35 bytes) in /var/www/html/flipbook/framework/collections/CList.php on line 150

#778 report it
Slavic at 2010/03/01 04:22am
Nice one

This Extension saved me mutch time.

I Combined it with a CDataProvider. Maybe you could inegrate that one in your next release. I Post the Details in the Forum.

Thanks again

#1018 report it
mikelimassol at 2010/01/08 04:10am
thanks for this great extention.

i am kinda new to yii. does this extension work with the following?


post and messages being multilingual.


#1123 report it
jerry2801 at 2009/12/02 08:35pm
great job!

thanks a lot..

Leave a comment

Please to leave your comment.

Create extension
  • Yii Version: 1.1
  • License: Other Open Source License
  • Developed by: guillemc
  • Category: Database
  • Votes: +12 / -2
  • Downloaded: 1,183 times
  • Created on: Dec 2, 2009
  • Last updated: Mar 25, 2012