i18n et admin-generator Jeudi, décembre 20th, 2007
Voici ce qu’il faut ajouter pour que l’i18n fonctionne de manière transparente avec les modules genérer via l’admin-generator de symfony. Pour illustrer l’exemple et s’extasier une nouvelle fois sur le temps qu’on gagne avec l’admin-generator, nous allons mettres en place la partie administration d’un système de news catégorisées et multilingue.
Soit le MLD suivant:

Note qui vaut des points:
folklore symfoniste, bug de la commande symfony qui convertit le xml en yml ou bug du batch-db-18n, j’en sais rien, mais le fait est qu’il faut toujours avoir la clé étrangère en premier et le champ culture en second (ainsi que clé primaire tous les deux (penser à virer l’auto incrémente sur le champs culture dans DBDesigner aussi (il se met automatiquement)))dans les tables *_i18n pour avoir un shema.yml correct!!
une fois stocké le fichier généré par DBdesigner 4 dans le répertoire data/schema.xml, il est temps de faire tourner le petit batch qui change la vie afin d’obtenir le fichier config/shema.yml que symfony utilisera pour générer les classes et le SQL adéquat.
php batch/convert_db.php
Le fait que les news soit multilingue nécessite d’exécuter la commande de patch pour l’internationalisation afin que symfony puisse faire le lien entre les objets et leurs attributs traduits:
symfony patch-db-i18n myapp
Ensuite on laisse la magie opérée
symfony propel-build-all
Les classes sont crées et les tables sql aussi, on peut donc générer les parties administrations des objets news et news_category
symfony propel-init-admin myapp new News
symfony propel-init-admin myapp newCategory NewsCategory
un petit vidage de cache
symfony cc
et on peut admirer le résultat sur http://monsite.com/news et http://monsite.com/newsCategory
Mais, enfer et damnation, si on tente de créer une nouvelle news, on va s’apercevoir que les champs internationalisés ne sont pas affichés … voici ce qu’il faut modifier pour que ça marche:
Dans le modèle de l’objet news dans lib/model/News.php
<?php
/**
* Subclass for representing a row from the ‘news’ table.
*
*
*
* @package lib.model
*/
class News extends BaseNews
{
public function hydrate(ResultSet $rs, $startcol = 1)
{
parent::hydrate($rs, $startcol);
$this->setCulture(sfContext::getInstance()->getUser()->getCulture());
$lang = array_keys(sfConfig::get(’app_lang_available’));
$lg = array_pop($lang);
while($this->getTitle()==” && $lg )
{
$this->setCulture($lg);
$lg = array_pop($lang);
}
}
}
Cette surcharge de la méthode hydrate va permettre de charger l’objet dans la langue cournate du user. Si la traduction n’est pas disponible on essaie la langue suivante, et sinon la langue suivante.
N.B. le app.yml contient
all:
lang:
available:
fr: francais
en: anglais
zh: chinois
Donc dans ce cas précis le while à un sens mais la logique est à adapter selon les cas. Typiquement, dans le cas présent, il existe forcément une version française, et la version anglaise sera affichée si la version chinoise n’existe pas (c’est la traduction la plus rare dans ce cas).
De manière symétrique modifier le modèle de l’objet newsCategory dans lib/model/NewsCategory.php
<?php
/**
* Subclass for representing a row from the ‘news_category’ table.
*
*
*
* @package lib.model
*/
public function hydrate(ResultSet $rs, $startcol = 1)
{
parent::hydrate($rs, $startcol);
$this->setCulture(sfContext::getInstance()->getUser()->getCulture());
$lang = array_keys(sfConfig::get(’app_lang_available’));
$lg = array_pop($lang);
while($this->getTitle()==” && $lg )
{
$this->setCulture($lg);
$lg = array_pop($lang);
}
}
Voilà pour l’affichage. Maintenant Il serait bon qu’en modification la logique suivie soit différente, à savoir si la traduction existe dans la langue courante du user elle s’affiche et si ce n’est pas le cas les champs s’affichent vides afin qu’ils puissent être traduits.
Pour se faire il faut surcharger le controleur de l’objet News dans apps/myapp/modules/news/actions/actions.class.php
<?php
/**
* news actions.
*
* @package u-clermont1
* @subpackage news
* @author Your name here
* @version SVN: $Id: actions.class.php 2288 2006-10-02 15:22:13Z fabien $
*/
class newsActions extends autonewsActions
{
//http://trac.symfony-project.com/trac/wiki/HowToHandlei18nDbFieldsWithAdminGenerator
//enable i18n in admin generator
protected function getNewsOrCreate ($id = ‘id’)
{
if (!$this->getRequestParameter(’id’, 0))
{
$news = new News();
}
else
{
$news = NewsPeer::retrieveByPk($this->getRequestParameter($id));
$this->forward404Unless($news);
}
$news->setCulture($this->getUser()->getCulture());
return $news;
}
}
et de manière symétrique celui de newsCategory dans apps/myapp/modules/newsCategory/actions/actions.class.php.
<?php
/**
* newsCategory actions.
*
* @package u-clermont1
* @subpackage newsCategory
* @author Your name here
* @version SVN: $Id: actions.class.php 2288 2006-10-02 15:22:13Z fabien $
*/
class newsCategoryActions extends autonewsCategoryActions
{
protected function getNewsCategoryOrCreate ($id = ‘id’)
{
if (!$this->getRequestParameter(’id’, 0))
{
$newsCategory = new NewsCategory();
}
else
{
$newsCategory = NewsCategoryPeer::retrieveByPk($this->getRequestParameter($id));
$this->forward404Unless($newsCategory);
}
$newsCategory->setCulture($this->getUser()->getCulture());
return $newsCategory;
}
}
Maintenant il faut manipuler un peu le generator.yml de chaque objet afin de forcer l’affichage des champs internationalisés
pour les news apps/myapp/modules/news/config/generator.yml
generator:
class: sfPropelAdminGenerator
param:
model_class: News
theme: default
list:
display: [id, title]
edit:
display:
NONE: [id, title, hook, detail]
fields:
title:
name: titre
type: input_tag
params: size=80 disabled=false
help: le titre est obligatoire
hook:
name: accroche
type: textarea_tag
params: tool=Basic rich=fck disabled=false
help: donnez un bref résumé cette actualité
detail:
name: detail
type: textarea_tag
params: tool=Basic rich=fck disabled=false
help: donnez le détail cette actualité
pour les newsCategory apps/myapp/modules/news/config/generatorCategory.yml
generator:
class: sfPropelAdminGenerator
param:
model_class: NewsCategory
theme: default
list:
display: [id, title, rank]
edit:
display:
NONE: [id, title, rank]
fields:
title:
name: titre
type: input_tag
params: size=80 disabled=false
help: le titre est obligatoire
rank:
name: ordre
params: size=2 disabled=false
help: détermine l’ordre d’affichage, notamment sur la page d’accueil
Voilà l’administration du système de news est désormais multilingue