Multilingual models

The behavior has been published in the Yii extension repository as multilingual-behavior.

hi. I use this extension. Excellent extension but when use afterFind() function in model class it give error "Property "Category.title_en" is not defined."

I forget


parent::afterFind();

=)

Hi Edgar,

I’ve tried to answer your question in the comments of the extension page so that everyone can read it. Thanks for using the extension

Hello,

Thanks for this very useful and very well thought out extension.

I’ve just started with it, and I have run into a problem while checking non regression.

referencing the newly internationalized model (but without actually anything of the new behaviour), this exception occurs:

in english: the table " {{newslang}} " … cannot be found in the database.

the "{" looked suspicious. I went and looked at the source and line 328 of multilingualBehaviour.php (in createLangClass) I found




return '{{{$this->langTableName}}}';



Removing the two extraneous pairs of accolades seems to fix it. Am I correct in assuming this was an error? Or was it actually intended and will I run into a problem when trying to use the multilingual behaviour?

Sorry I couldn’t post directly to the extension’s page, not allowed to, I am too recently arrived to these parts.

Thanks again for everything.

Hello,

In activeRecord, you can use table name like this : {{table_name}}, in order to use the table prefix you’ve declared (see here).

So there is no error on the line you’ve mentioned. I don’t see why it doesn’t works for you, maybe it’s related to the version of Yii you use or the db system (see here).

If you don’t use table prefix you can remove the two pairs of curly braces safely.

By the way you seem to be french and I am too so if you have any other question or don’t understand my (bad) english, feel free to PM me in french ;).

Hi,

first of all, thanks to guillemc, fredpeaks and everyone else that has contributed to create and improve this behaviour, it is really helpful.

I have one question about it. I have seen that in line 303 all the validations are being added to the translated attributes except the "required" ones:




if(in_array($attr, $rule_attributes) && ($rule[1] !== 'required' || $this->forceOverwrite)) {

    $validators->add(CValidator::createValidator($rule[1], $owner, $attr . '_' . $l, array_slice($rule, 2)));

}

Maybe I am missing something about the "forceoverwrite" attribute, but I was dealing with some translated attributes that were not being assigned until I noticed that the fact of removing all the required validations was leaving some of my attributes "unsafe", and therefore, not assignable. I guess this is not the way it should work by default?

@Proctophantasmist: I solved this database naming problem by adding this to the config/main.php:


'db'=>array(

   ...

   'tablePrefix'=>'',

),

So there’s no need to touch the behaviour code :)

Thank you very much

Hi,

Thanks for your post, it’s revealing something I’ve not thought about before : translation of an attribute that has only a “required” rule… In this case, translated attributes will not have any rule defined if the forceOverwrite option is set to true. So they will be ‘unsafe’.

The behavior should add a “safe” rule for those attributes. But it’s not simple to code because we have to do that for every different scenario defined in the model…

I will look at this problem when I’ll have time but for now I think you should add an extra rule for the attributes to be translated. Maybe a “type” rule with type defined to “string” (or something else) or a “length” rule.

If you have any solution to the problem, please feel free to share ! ;)

Hope this will help.

Hi,

I don’t think we must deal with the scenarios, because they are already been added to the validation (last parameter of createValidation, array_slice($rule, 2)).

I think something like this should work:




foreach ($this->languages as $l) {

	foreach($this->localizedAttributes as $attr) {

		foreach($rules as $rule) {

			$rule_attributes = array_map('trim', explode(',', $rule[0]));

			if(in_array($attr, $rule_attributes)) {

				if ($rule[1] !== 'required' || $this->forceOverwrite) {

					$validators->add(CValidator::createValidator($rule[1], $owner, $attr . '_' . $l, array_slice($rule, 2)));

				}

				else if($rule[1] === 'required') {

					//We add a safe rule in case the attribute has only a 'required' validation rule assigned

					//and forceOverWrite == false

					$validators->add(CValidator::createValidator('safe', $owner, $attr . '_' . $l, array_slice($rule, 2)));

				}

			}

		}

	}

}



The only thing that doesn’t seem ok is that we are adding the ‘safe’ rule even if the attribute has another validation rules (in those cases there’s no need to, because the other rules make the attribute safe). Maybe we should check that the attribute has only one ‘required’ rule?

Hello everyone.

I’m also trying to integrate this excellent extension to the projects that I’m working on but I have problem with inserting translation contents to the PostLang table. It inserts new entries to the table and the translations of title field but not the translations of the content field. Any idea how to fix it?

Thanks in advance.

Hello.

Would anyone, please, help me to fix the problem inserting to postLang table?

Best Regards

Hi,

it could be related with the issue we were talking about above, when a field in a model has just the ‘required’ rule.

To fix it, add forceoverwrite = true to the behaviour variable list


public function behaviors() {

    return array(

        'ml' => array(

            ...

            'forceOverwrite' => true,

            ...

        ),

    );

}

or try adding another validation rule to the content field, for example:




public function rules()

    {

        return array(

            ...

            array('content', 'safe'),

        );

    }



Thank you very much, two2wyes!

It fixed the problem! I’m grateful! I have been looking at the codes, trying to find where can be the problem.

Best Regards

Hi,

Great job, I think you’re right, I’ve tested it quickly and doesn’t find any problem.

Sorry for the time it took me to answer… I’m going to integrate your code in the behavior.

Thanks for the work.

About your question, I don’t think these extra “safe” rules are really a problem.

Maybe a little more memory usage… but if we have to test if each attribute already has a rule in addition to the “required” one, we’ll have to deal with the scenario and this will be more complex.

Best regards,

Fred

I’ve some kind of problem here:

My website does not have a default language. Users can submit machine data in english or turkish. when they do submit data on english pages, i assume the data to be english, and when they do submit data on turkish pages, i want the data to be stored in turkish.

The problem comes, when they want to translate the data to another language. Let’s say a user submitted data in turkish and wants to translate it in english. if i set my defaultlanguage to english, and the user goes to turkish pages, turkish data is actually the main data and the english should be the translated one.

so, how should i setup my models, so that every language can be translated to each other?

Ok, i was able to solve this by having a language field on my main table so that i know which language the main entry is.

Right now, my problem is with search.

when i tried to change the model to:




	return new CActiveDataProvider($this, array(

			'criteria'=>$this->ml->modifySearchCriteria($criteria),

		));



and have this on my view:




$this->widget('zii.widgets.CListView', array(

	'dataProvider'=>$model->multilang()->search(),

	'itemView'=>'_search_list2',

	'enablePagination'=>true,

	'template'=>"{items} ",

)); 



i get an sql error.




Column 'l_machine' in where clause is ambiguous. The SQL statement executed

was: SELECT COUNT(DISTINCT `t`.`id`) FROM `tbl_machines` `t` LEFT OUTER JOIN 

`tbl_machinesLang` `i18nMachines` ON (`i18nMachines`.`machine_id`=`t`.`id`) AND (i18nMachines.lang_id='tr_TR') LEFT OUTER JOIN `tbl_machinesLang` 

`multilangMachines` ON (`multilangMachines`.`machine_id`=`t`.`id`) WHERE 

(l_machine LIKE :ycp0) 



the generated sql should be clearly multilangMachines.l_machine

where can i change it?

thanks.

"Missing argument 1 for CActiveRecordBehavior::afterSave()"… Any ideas?

Merci pour la réponse, et pardon, je suis parti pour un moment, puis ai du m’occuper d’un autre projet…

I use Sqlite on this project, I remember at the time I stepped through the yii code but can’t remember if the bug was specific to the sqlite component, probably is, since no one else mentioned it. Anyway, adding the following to my db config fixed it for me:


 'tablePrefix' =>'',

Thanks again.

Hi,

I don’t understand if I can have translated relations.

I have a GalleryFile model which has a belongs_to relation to a File model


 'file' => array(self::BELONGS_TO, 'File', 'file_id') 

The file must be localized in italian and english so I put this in the behavior config array


'localizedAttributes' => array('file_id','other_fields' .. )

Usually I can get the File model using the relation,


$galleryFileModel->file

the nicest thing ever would be to be able to do something like


 $model->file_en 

and get the File which id is the localized english file_id. Is it possibile as now to do something like that ?

Or I have to define a relation for each language?

Regards and thanks to the authors of this excellent extension!

Hello and thanks for sharing this amazing behavior.

However please notice an issue I found using the behavior. I was trying to add i18n at the post table. Post table has required fields the title and content attributes. The blog should support two languages English (en) and Greek (el).

So i created a form as defined on your example which creates title_en, title_el, content_en and content_el fields. When submitting the form the application returned an error stating that title and content are required!

By examining the behavior I found out that these attributes (title_en, title_el, content_en and content_el) not only they had’nt the “required” validator assigned but the original attributes (title and content) continued to have their own “required” validators.

So I modified the code to remove the "required" validator from the original attribute and add "required" validator the new ones:




	else if($rule[1] === 'required') {

		//We add a safe rule in case the attribute has only a 'required' validation rule assigned

		//and forceOverWrite == false

		//$validators->add(CValidator::createValidator('safe', $owner, $attr . '_' . $l, array_slice($rule, 2)));

		// we do need to add the required property for the new attributes and remove the old required property for 

		$validators->add(CValidator::createValidator('required', $owner, $attr . '_' . $l, array_slice($rule, 2)));

		$validatorList = $owner->getValidatorList();

		

		$i = 0;

		while($i < $validatorList->count) {

			$validator = $validatorList->itemAt($i);

			if( $validator instanceof CRequiredValidator ) {

				$index = array_search($attr, $validator->attributes);

				if ($index !== false) {

                                	unset($validator->attributes[$index]);

                                        $validator->attributes = array_values($validator->attributes);

                                        break;

				}

			}

			$i++;

		}

	}

The above works fine, it may help somone else also.

However, if you think that I missed something, or that something is wrong, let me know.

Thanks!