How To Handle i18n Db Fields With the Admin Generator Method 2
Notice: for symfony 1.2 read HowToHandlei18nDbFieldsWithAdminGenerator1.2
You can manage i18n fields in the backend generated by the admin generator another way. First method described here.
This method assumes that you'll use the final number of cultures.
This method allow you display/edit in backend application data from different cultures on single page. And it's more elegant, imho.
In this example we'll use only two cultures: en, fr. Theoreticaly, there is no limits.
Imagine you got the same schema for managing i18n categories as in method 1.
First of all add the __call method in the your object model class in lib.model directory to handle non-existing specialy named fields:
<?php ... class Category extends BaseCategory { ... public function __call($m, $a) { $data = @split('I18n', $m, 2); if( count($data) != 2 ) { // Ensure that behaviors will still work return parent::__call($m, $a); } list( $method, $culture ) = $data; if (strlen($culture)==4) { $culture=strtolower(substr($culture,0,2)).'_'.strtoupper(substr($culture,2,2)); } else { $culture=strtolower($culture); } $this->setCulture( $culture ); return call_user_func_array(array($this, $method), $a); } ...
And you'll have to use special naming convention: to display some field from certain culture you must name it like this: <field>_i18n_<culture>. For example, field title in english should look like this: title_i18n_en.
Our new generator.yml for the categories module:
generator:
class: sfPropelAdminGenerator
param:
model_class: Category
theme: default
edit:
title: "Category properties"
edit:
display:
"NONE": [name]
"Title":
- title_i18n_en
- title_i18n_fr
"Excerpt":
- excerpt_i18n_en
- excerpt_i18n_fr
"Description":
- description_i18n_en
- description_i18n_fr
fields:
title_i18n_en:
name: English
params: disabled=false
title_i18n_fr:
name: French
params: disabled=false
excerpt_i18n_en:
name: English
params: disabled=false size=100x2
excerpt_i18n_fr:
name: French
params: disabled=false size=100x2
description_i18n_en:
name: English
type: textarea_tag
params: disabled=false size=100x4
description_i18n_fr:
name: French
type: textarea_tag
params: disabled=false size=100x4
And that's all folks!!!
Update to Method2 (with Doctrine)
This method is great but at least with Doctrine causing problem with create action and throwing exception:
[Exception] Tried to call unknown method Category::getid
To overcome this, we may hand over undefined method handling to parents. Second problem to resolve is that object helpers in edit template (_edit_form.php) call for regular get method which by default is not prepared to process fields names in format like above (f.ex. name_i18n_en). Code below covers both issues:
<?php class Category extends BaseCategory { /** * Support for I18n objects * http://trac.symfony-project.com/trac/wiki/HowToHandlei18nDbFieldsWithAdminGeneratorMethod2 * */ public function __call($m, $a) { $data = @split('I18n', $m, 2); if( count($data) != 2 ) { return parent::__call($m, $a); //return call_user_func_array(array($this, 'parent::'.$m), $a); // don't work like i expected //throw new Exception('Tried to call unknown method '.get_class($this).'::'.$m); } list( $method, $culture ) = $data; return call_user_func_array(array($this->Translation[$culture], $method), $a); } /** * Getter to process culture versions * Pure __call isn't enough, because helpers calls something like Category::get('title_i18n_pl'), instead of Category::getTitleI18nPl() * */ public function get($name, $load = true) { $name_plus_culture = @split('_i18n_', $name, 2); if( count($name_plus_culture) == 2 ) { list( $name, $culture ) = $name_plus_culture; return $this->Translation[$culture]->$name; } return parent::get($name, $load); } /** * Setter to process culture versions * */ public function set($name, $value, $load = true) { $name_plus_culture = @split('_i18n_', $name, 2); if( count($name_plus_culture) == 2 ) { list( $name, $culture ) = $name_plus_culture; $this->Translation[$culture]->$name = $value; } else { parent::set($name, $value, $load); } } }
Updated to Doctrine 0.9.0. This was not yet extensively tested but for now works.
— The code and this method is cool but I needed to do this little hack for it to work with me (in the case, sqlite). Here is the “patch”. Tested in Mac OS X with Sqlite and Symfony 1.0.6
$this->setCulture( $culture );
is, for me and to work,
$this->setCulture(strtolower($culture) );
I used the strtolower function, if you didn't notice it !
this way I have this:
select * from table; 1|2007-09-01 19:22:23|2007-09-01 19:22:23
and
select * from table_i18n; 1|pt||||Primeira String| 1|en||||First String|
— This code don't work when you have two table with I18n, for Example News and Comments
And table Comment have news_id as a Foreign key
When I run backend_dev.php/comments with this code - I see
Fatal error: Call to undefined method BaseNews::call() in /lib/model/News.php on line 23
In this case instead of this part of code: return parent::call($m, $a);
write this: return parent::getId();