Doctrine 2 & i18n

Posted on 14/10/2011 · Posted in Doctrine, Zend

Doctrine 2 est dépourvu – en natif – de système de traduction.
Grâce au système d’extension, des contributeurs dont certains membres de l’équipe de développement Doctrine ont développés Gedmo, un pack d’extension pour Doctrine.

Cette extension inclue :

  • Tree
  • Translatable
  • Sluggable
  • Timestampable
  • Loggable
  • Sortable

Nous nous intéresserons à Translatable qui nous permettra de traduire nos données.

Travaillant avec le Framework Zend, les exemples seront orientés pour cette architecture, il est à noter que cela n’est pas un pré-requit et que cela fonctionnera avec ou sans Zend Framework.

Fonctionnement

Le module i18n de Gedmo fonctionne grâce à l’utilisation d’un table annexe contenant la traduction de certaines propriétés de vos Entités.
La table de traduction contient donc le nom du champs traduite, la langue, la valeur, le type d’objet ainsi qu’une reference vers cet objet.

Par défaut, l’extension aura besoin de la table : ext_translations
Les champs suivant seront ainsi necessaire :

  • id
  • content
  • field
  • locale
  • foreign_key
  • object_class

Voici un jeu de donnée qui vous permettra de mieux comprendre l’utilisation de ce type de structure :

  • content : Un día me voy a comprar el mundo
  • field : title
  • locale : es_es
  • foreign_key : 3
  • object_class : EntityArticle
  • content : Un jour j’achèterai le monde
  • field : title
  • locale : fr_fr
  • foreign_key : 3
  • object_class : EntityArticle
  • content : Le monde semble être à l’agonie, que pouvons nous faire pour le sauver.
  • field : description
  • locale : fr_fr
  • foreign_key : 3
  • object_class : EntityArticle

A noter, que les données relatives à la langue par défaut de votre application sont stockés au sein de la table gerant directement votre entité.
Par défaut, nous utilisons la langue “en_US“, ce qui signifie que la table ext_translations ne contiendra pas de traduction pour “en_US”.
Dans mon exemple, ces données seront stockés dans la table “articles” de l’entité EntityArticle

Ainsi, l’extension stockera l’ensemble des traductions de votre application au sein d’une même table.
Attention, le champs “forien_key” ne correspond pas à une clé étrangère pour votre base de donnée relationnelle.
Une des possibilités de l’extension consiste à pouvoir définir une table spécifique à la traduction d’une Entités, ainsi vous pourrez définir une vrai clé étrangère au sein de votre RDBMS.

Exploitation

Au niveau de l’exploitation des données,
Vous définirez la langue utilisée au sein de votre application lors de l’execution, cela correspondra normalement à la langue de l’utilisateur.
Il ne restera plus qu’a utiliser votre application comme vous en avez l’habitude.

Nous partons du principe que votre environnement Doctrine est correctement configuré et fonctionnel.

Voici la configuration minimum a définir :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
$classLoader = new DoctrineCommonClassLoader( 'Gedmo', APPLICATION_PATH . '/../library/');
$classLoader->register();

$config     = new DoctrineORMConfiguration();

$driverChain        = new DoctrineORMMappingDriverDriverChain();
$entityDriver       = new DoctrineORMMappingDriverXmlDriver ();

$translatableDriver = $config->newDefaultAnnotationDriver( APPLICATION_PATH . '/../library/DoctrineExtensions/Gedmo/Translatable/Entity');

$driverChain->addDriver ($entityDriver,     'EntityArticle');
$driverChain->addDriver ($translatableDriver,   'GedmoTranslatable');

$config->setMetadataDriverImpl($driverChain);

$eventManager       = new DoctrineCommonEventManager();
$translatableListener   = new GedmoTranslatableTranslationListener();
       
$translatableListener->setTranslationFallback( true );
$eventManager->addEventSubscriber($translatableListener);

$entityManager = DoctrineORMEntityManager::create($conn, $config, $eventManager);

Ensuite, il faut configurer vos entités.
J’utilise le XML Mapping, vous trouverez facilement l’équivalence pour YAML / Annotation.

1
 

Nous avons simplement ajouté le noeud XML gedmo:translatable à notre champs afin de spécifier que celui-ci peut-être traduit.
A noter qu’il faut inclure l’espace de nom xmlns:gedmo.

Voici un exemple d’utilisation avec Doctrine

1
2
3
4
5
6
7
8
9
10
//En utilisant la langue par défaut qui est l'anglais,
$article    = $entityManager->find('EntityArticle', 3);
$article->getTitle ();
//One day I'll buy the world

//En spécifiant la langue espagnole,
$translatableListener->setTranslatableLocale("es_es");
$article    = $entityManager->find('EntityArticle', 3);
$article->getTitle ();
//Un día me voy a comprar el mundo

Entity de traduction

Nous avons vu précédemment que les traductions sont par défaut stockées dans la table ext_translations.
Voyons comment utiliser une table dédiée à la traduction d’une entité. Cela permettra en plus d’avoir une structure de base de donnée plus propre, et d’avoir des clés étrangère au sein de votre RDBMS.

1
        ..

Créons la class ArticleTranslation qui n’a rien de bien extraordinaire, sauf qu’elle doit étendre AbstractTranslation qui va définir les differentes propriétés de votre table de traduction.

1
2
3
4
5
6
7
8
9
namespace Entity

use GedmoTranslatableEntityAbstractTranslation;
use DoctrineORMMapping as ORM;

class ArticleTranslation extends AbstractTranslation
{
   
}

A cela, ajoutons le fichier de mapping.

1
 

Il ne vous reste plus qu’a créer la table article_translation avec les champs énuméré en début d’article.

Conclusion

Comparer à Doctrine 1, la mise en place de l’internationalisation pour votre application peut sembler plus difficile. Par contre, l’exploitation de cette implementation est tout simplement user-friendly.