Development

HowToHandlei18nDbFieldsWithAdminGeneratorMethod2

You must first sign up to be able to contribute.

Version 14 (modified by Mozzy, 8 years ago)
I removed the exception line and replaced it with a call to the parent's call method to ensure that registered behaviors on a class will still work. It took me a while to find out how to fix this after I used this method of using i18n in the admin generator. I hope that this change will help a lot of other people in the future.

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();