Internationalisation du site avec Symfony

Dans ce chapitre, nous allons voir comment rendre le site international afin d'afficher les textes en Français et Anglais.




La culture des utilisateurs


Chaque visiteur sur un site Symfony créé une session sur le site. Cette session contient un objet utilisateur qui est accessible à n'importe quel moment avec la commande:

Code php:
$this->getUser();


Chaque utilisateur dispose de sa culture propre, la langue qu'il parle.
Comme par exemple: en_US ou fr ou encore fr_BE.
Pour connaître la culture d'un utilisateur ou lui assigner une nouvelle culture, il suffit d'utiliser l'accesseur et mutateur suivants:

Code php:
$this->getUser()->setCulture('fr_FR');
$culture = $this->getUser()->getCulture();


La culture par défaut est quant à elle définie dans le fichier de configuration apps/frontend/config/settings.yml:

Code:
all:
  .settings:
    default_culture:        fr



Le module i18n dans Symfony


Pour activer le module i18n dans Symfony, il suffit d'ajouter les paramètres suivants dans le fichier de configuration apps/frontend/config/settings.yml

Code:
    i18n:                   on
    standard_helpers:       [Partial, Cache, I18N]


Le premier paramètre va activer l'i18n et le deuxième permettra de charger les helpers de templates "Partial", "Cache" et "I18N" par défaut.
Vous n'aurez pas à déclarer ces helpers en haut de vos templates pour les utiliser.

Enregistrez, quittez puis supprimez le cache symfony

symfony cc


Le module i18n va nous permettre de créer des dictionnaire multi lingue pour traduire nos textes et afficher les bons en fonction de la culture de l'utilisateur.






Mise en place du changement de langue


Nous allons maintenant mettre en place une fonctionnalité en bas de page du site permettant de choisir sa langue. Le tutorial Jobeet explique parfaitement ce point, je vais donc effectuer qu'une simple traduction.

Un plugin "sfFormExtraPlugin" permet de mettre en place un select pour choisir sa langue.
Nous allons donc l'installer:

symfony plugin:install sfFormExtraPlugin


Une fois installé, supprimez le cache:

symfony cc


Maintenant que le plugin est fonctionnel, nous allons ajouté notre "select" en bas de page, dans le layout du site (nous serons amené à le bouger plus tard).

Ouvrez le layout: apps/frontend/templates/layout.php
Nous aurions pu le modifier ainsi:

Code html:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
  <head>
    <?php include_http_metas() ?>
    <?php include_metas() ?>
    <?php include_title() ?>
    <link rel="shortcut icon" href="/favicon.ico" />
  </head>
  <body>
    <?php echo $sf_content ?>
    <?php $form = new sfFormLanguage(
      $sf_user,
      array('languages' => array('en', 'fr'))
      )
    ?>
    <form action="<?php echo url_for('@change_language') ?>">
      <?php echo $form ?><input type="submit" value="ok" />
    </form>
  </body>
</html>


MAIS, comme nous l'avons vu précédemment, il faut limiter au maximum le PHP directement dans les templates, d'autant plus que nous pourrions avoir à l'afficher ailleurs.
La meilleure solution: Un composant (component).

Un composant est rattaché à un module, comme nous n'avons aucun module dédié aux langues, nous allons créer un nouveau module "language":

symfony generate:module frontend language


Un composant comporte un fichier action spécial: actions/components.class.php
Nous allons donc créer le fichier apps/frontend/modules/language/actions/components.class.php:

Code php:
<?php
class languageComponents extends sfComponents
{
  public function executeLanguage(sfWebRequest $request)
  {
    $this->form = new sfFormLanguage(
      $this->getUser(),
      array('languages' => array('en', 'fr'))
    );
  }
}
?>


Comme pour les fichiers actions, les fichiers composants doivent étendre une classe spéciale: sfComponents.

Et comme je vous l'ai expliqué dans la partie sur les templates, un composant dispose d'un template précédé par un underscore "_".
Nous allons créer le fichier apps/frontend/modules/language/templates/_language.php

Code html:
<form action="<?php echo url_for('@change_language') ?>">
  <?php echo $form ?><input type="submit" value="ok" />
</form>


Comme vous pouvez le constater vous-même, nous avons utilisé une nouvelle route dans le formulaire: @change_language. Il faut donc l'ajouter dans le fichier routing.yml (apps/frontend/config/routing.yml):

Code:
change_language:
  url:   /change_language
  param: { module: language, action: changeLanguage }


Cette route va appeler l'action changeLanguage du module "language".
Créons cette action, ouvrez le fichier apps/frontend/modules/language/actions/actions.class.php, supprimez la méthode executeIndex, inutile, puis ajoutez l'action:

Code php:
<?php
class languageActions extends sfActions
{
  public function executeChangeLanguage(sfWebRequest $request)
  {
    $form = new sfFormLanguage(
      $this->getUser(),
      array('languages' => array('en', 'fr'))
    );
 
    $form->process($request);
 
    return $this->redirect('@localized_homepage');
  }
}
?>


Là encore, nous allons utiliser une nouvelle route "@localized_homepage" lors de la redirection du choix d'une langue.
Cette route va permettre d'ajouter la langue dans l'URL.

Ouvrez donc le fichier routing.yml (apps/frontend/config/routing.yml):

Code:
localized_homepage:
  url:   /:sf_culture/
  param: { module: main, action: index }
  requirements:
    sf_culture: (?:fr|en)


La route se complique un peu, :sf_culture est une variable qui contient la culture de notre utilisateur. L'URL sera donc /fr/ ou /en/ suivant la langue choisie.
Pour que la route s'applique, il faut que les langues soient "fr" ou "en".

Il ne nous reste plus qu'à rajouter le select dans notre layout pour tester. (apps/frontend/templates/layout.php)

Code html:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.o$
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
  <head>
    <?php include_http_metas() ?>
    <?php include_metas() ?>
    <?php include_title() ?>
    <link rel="shortcut icon" href="/favicon.ico" />
  </head>
  <body>
    <?php echo $sf_content ?>
    <?php include_component('language', 'language') ?>
  </body>
</html>


Et voilà le résultat:



Les librairies de traduction Symfony


Passons maintenant aux choses sérieuses.

Nous allons utiliser un helper i18n sur tous nos textes statiques afin qu'ils puissent être traduit dynamiquement suivant la langue du visiteur.

Code php:
<?php echo __("Mon texte à traduire"); ?>


Il faudra systématiquement utiliser cet helper sur vos textes statiques. C'est la raison pour laquelle je vous parle de l'internationalisation dès le début du tutorial, afin que nous n'ayons pas à repasser sur toutes les chaînes plus tard.

Tous ces textes mis dans le helper __() seront automatiquement chargés dans un fichier de langue grâce à une commande d'extraction:

symfony i18n:extract frontend fr --auto-save


En utilisant cette commande et en spécifiant "fr", cela signifie que nous allons créer un dictionnaire de phrases à traduire en français.
Ce qui signifie que l'on devra écrire tous nos textes statiques en anglais.
Il est bien sûr possible de faire le contraire !

Faisons un petit test !
Modifiez le template index du module main (apps/frontend/modules/main/templates/indexSuccess.php):

Code php:
<h2><?php echo $helloWorld; ?></h2>
<p><?php echo __('How are you doing today ?'); ?></p>


Rafraichissez votre page, vous devriez obtenir quelque chose comme ceci:


Quelque soit la langue, le même texte est affiché. C'est normal, nous n'avons pas encore fait la traduction. Si Symfony ne trouve pas une correspondance dans la langue, il affiche celle par défaut.

symfony i18n:extract frontend fr --auto-save

Code:
>> i18n      extracting i18n strings for the "frontend" application
>> i18n      found "1" new i18n strings
>> i18n      found "0" old i18n strings
>> i18n      saving new i18n strings


1 nouvelle chaine à traduire a été détectée. Nous pouvons à présent la traduire.
Les fichiers langues se trouvent dans le répertoire apps/frontend/i18n/LANGUE

Dans notre cas, nous allons modifier le fichier apps/frontend/i18n/fr/messages.xml:
Voici le texte que vous devriez avoir pour le moment:

Code xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE xliff PUBLIC "-//XLIFF//DTD XLIFF//EN" "http://www.oasis-open.org/committees/xliff/documents/xliff.dtd">
<xliff version="1.0">
  <file source-language="EN" target-language="fr" datatype="plaintext" original="messages" date="2009-08-11T20:03:44Z" product-name="messages">
    <header/>
    <body>
      <trans-unit id="1">
        <source>How are you doing today ?</source>
        <target/>
      </trans-unit>
    </body>
  </file>
</xliff>


Dans les balises source se trouvent le texte à traduire, il faut ensuite mettre la correspondance dans la balise target actuellement fermée.

Code xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE xliff PUBLIC "-//XLIFF//DTD XLIFF//EN" "http://www.oasis-open.org/committees/xliff/documents/xliff.dtd">
<xliff version="1.0">
  <file source-language="EN" target-language="fr" datatype="plaintext" original="messages" date="2009-08-11T20:03:44Z" product-name="messages">
    <header/>
    <body>
      <trans-unit id="1">
        <source>How are you doing today ?</source>
        <target>Comment vas-tu aujourd'hui ?</target>
      </trans-unit>
    </body>
  </file>
</xliff>


Sauvegardez puis quittez le fichier.
Comme lorsque l'on modifie un fichier de configuration, nous devons supprimer le cache:

symfony cc


Et maintenant testez:


Le texte en français s'affiche correctement lorsque l'on sélectionne la langue française !

Vous voyez, ce n'est pas si compliqué finalement !

Migration sous Symfony 1.4


Si vous utilisez les version 1.3 ou 1.4 de Symfony, vous aurez sans doute une erreur du type:

500 | Internal Server Error | InvalidArgumentException
sfValidatorI18nChoiceLanguage does not support the following options: 'culture'.


Pour la corriger, il faut modifier le plugin sfFormExtraPlugin ainsi:

Modifiez plugins/sfFormExtraPlugin/lib/form/sfFormLanguage.class.php:

Code php:
public function configure()
  {
    $this->setValidators(array(
          'language' => new sfValidatorI18nChoiceLanguage(array
    ('languages' => $this->options['languages'])),
        ));
  /*
    $this->setValidators(array(
      'language' => new sfValidatorI18nChoiceLanguage(array('culture' => $this->user->getCulture(), 'languages' => $this->options['languages'])),
    ));
    */
    $this->setWidgets(array(
      'language' => new sfWidgetFormI18nChoiceLanguage(array('culture' => $this->user->getCulture(), 'languages' => $this->options['languages'])),
    ));
  }








Rechercher sur la Ferme du web