symfony Forms in Action

Chapitre 8 - L'Internationalisation et la Localisation

You are currently browsing
the website for symfony 1

Visit the Symfony2 website


About

You are currently reading "symfony Forms in Action" which is licensed under the Creative Commons Attribution-Share Alike 3.0 Unported License license.

Master symfony

Be trained by SensioLabs experts (2 to 6 day sessions -- French or English).
trainings.sensiolabs.com

Books on symfony

Learn more about symfony with the official guides.
books.sensiolabs.com

L'audit Qualité par SensioLabs

200 points de contrôle de votre applicatif web.
audit.sensiolabs.com

Chapter Content

L'Internationalisation des Formulaires

Spécifier le fichier dictionnaire à utiliser pour stocker les traductions

Internationaliser les messages d'erreur

Définition d'un Objet de Traduction Personnalisé

Paramètres Supplémentaires acceptés par l'Objet de Traduction

Intégration des Objets Propel Internationalisés

Widgets localisés

Sélecteurs de dates

Sélecteur de pays

Sélecteur de langue

symfony training
Be trained by symfony experts
Feb 21: Köln (Getting Started with Symfony2 - English)
Feb 27: Köln (Mastering Symfony2 - English)
Mar 05: Köln (Web Development with Symfony2 - Deutsch)
Mar 05: Montreal (Web Development with Symfony2 - English)
Mar 05: Montreal (Getting Started with Symfony2 - English)
and more...

Search


powered by google
You are currently browsing "symfony Forms in Action" in French for the 1.2 version - Switch to version: - Switch to language:
Creative Commons License This work is licensed under a Creative Commons Attribution-Share Alike 3.0 Unported License.
This version of symfony is not maintained anymore.
If some of your projects still use this version, consider upgrading as soon as possible.

La plupart des applications web populaires sont consultables dans différentes langues et sont même parfois localisées en fonction du pays de l'internaute. Symfony apporte une solution efficace pour gérer ces problématiques à travers un sous-framework dédié (voir le chapitre "I18n And L10n" du livre symfony).

Le framework de formulaires permet également la gestion native de la traduction des interfaces et des objets internationalisés.

L'Internationalisation des Formulaires

Dans un projet symfony, les formulaires sont internationalisables par défaut. On peut donc traduire les labels des champs de formulaire, les textes d'aide et les messages d'erreurs, en éditant les fichiers de traduction au format XLIFF, gettext ou tout autre format supporté par symfony.

Le Listing 8-1 reprend le code du formulaire de contact que nous avons développé dans les premiers chapitres.

Listing 8-1 - Formulaire de Contact

class ContactForm extends sfForm
{
  public function configure()
  {
    $this->setWidgets(array(
      'name'  => new sfWidgetFormInput(),    // le label sera "Name" par défaut
      'email' => new sfWidgetFormInput(),    // le label sera "Email" par défaut
      'body'  => new sfWidgetFormTextarea(), // le label sera "Body" par défaut
    ));
 
    // Changeons par exemple le label du champ email
    $this->widgetSchema->setLabel('email', 'Email address');
  }
}

Nous pouvons définir directement la traduction des noms de champs dans le fichier XLIFF correspondant. Le Listing 8-2 montre le fichier de traduction pour le français.

Listing 8-2 - Fichier de traduction XLIFF

// apps/frontend/i18n/messages.fr.xml
<?xml version="1.0" ?>
<xliff version="1.0">
  <file original="global" source-language="en" datatype="plaintext">
    <body>
      <trans-unit>
        <source>Name</source>
        <target>Nom</target>
      </trans-unit>
      <trans-unit>
        <source>Email address</source>
        <target>Adresse email</target>
      </trans-unit>
      <trans-unit>
        <source>Body</source>
        <target>Message</target>
      </trans-unit>
    </body>
  </file>
</xliff>

Spécifier le fichier dictionnaire à utiliser pour stocker les traductions

Si vous utilisez les dictionnaires de stockage des chaînes de traduction de symfony (http://www.symfony-project.org/book/1_2/13-I18n-and-L10n#chapter_13_sub_managing_dictionaries), vous pouvez spécifier à quel dictionnaire les éléments textuels de l'interface du formulaire courant vont être associés. Dans le Listing 8-3, les éléments textuels de l'interface du formulaire ContactForm traduits en français seront stockés dans le fichier contact_form.fr.xml.

Listing 8-3 - Personnalisation du Dictionnaire de Traduction

class ContactForm extends sfForm
{
  public function configure()
  {
    // ...
 
    $this->widgetSchema->getFormFormatter()->setTranslationCatalogue('contact_form');
  }
}

L'utilisation de dictionnaires dédiés aux formulaires est très pratique pour spécialiser et identifier rapidement les fichiers contenant les traductions.

Internationaliser les messages d'erreur

Il peut s'avérer intéressant de générer des messages d'erreurs reprenant la valeur soumise par l'utilisateur dans la description de l'erreur (par exemple, "L'adresse email user@domain n'est pas valide."). Cela peut se faire depuis la classe de définition du formulaire en définissant des messages d'erreur personnalisés utilisant des références aux noms des paramètres de configuration du validateur associé, sous la forme %paramètre% comme nous l'avons déjà vu.

Le Listing 8-4 applique ce principe au champ name du formulaire de contact.

Listing 8-4 - Internationalisation des Messages d'Erreurs

class ContactForm extends sfForm
{
  public function configure()
  {
    // ...
 
    $this->validatorSchema['name'] = new sfValidatorEmail(
      array('min_length' => 2, 'max_length' => 45),
      array('min_length' => 'Name "%value%" must be at least %min_length% characters.',
            'max_length' => 'Name "%value%" must not exceed %max_length% characters.',
      ),
    );
  }
}

Nous pouvons maintenant référencer ces messages directement dans le fichier de traduction XLIFF francophone comme dans le Listing 8-5.

Listing 8-5 - Fichier XLIFF de traduction des Messages d'Erreurs

<trans-unit>
  <source>Name "%value%" must be at least %min_length% characters</source>
  <target>Le nom "%value%" doit comporter un minimum de %min_length% caractères</target>
</trans-unit>
<trans-unit>
  <source>Name "%value%" must not exceed %max_length% characters</source>
  <target>Le nom "%value%" ne peut comporter plus de %max_length% caractères</target>
</trans-unit>

Définition d'un Objet de Traduction Personnalisé

Il se peut cependant que vous désiriez utiliser un autre mécanisme de traduction de l'interface que celui proposé par symfony, par exemple dans le cas d'une utilisation avec un autre framework. Dans ce cas, vous devez définir un objet de traduction personnalisé.

Un objet de traduction se présente sous la forme d'un callable PHP. Plus spécifiquement, ces références peuvent être de trois types :

En PHP, un callable est une référence à une fonction ou méthode de classe existante pouvant être appelés par le langage. On peut aussi définir un callable comme étant tout objet PHP renvoyant true si passé à la fonction is_callable().

Par exemple, imaginons une classe d'internationalisation (typiquement le genre de chose dont on hérite en récupérant un projet existant), volontairement simple comme le montre le Listing 8-6.

Listing 8-6 - Classe I18N

class myI18n
{
  static protected $default_culture = 'en';
  static protected $messages = array('fr' => array(
    'Name'    => 'Nom',
    'Email'   => 'Courrier électronique',
    'Subject' => 'Sujet',
    'Body'    => 'Message',
  )); 
 
  static public function translateText($text)
  {
    $culture = isset($_SESSION['culture']) ? $_SESSION['culture'] : self::$default_culture; 
    if (array_key_exists($culture, self::$messages)
        && array_key_exists($text, self::$messages[$culture]))
    {
      return self::$messages[$_SESSION['culture']][$text];
    }
    return $text;
  }
}
 
// Utilisation type de la classe exemple
$myI18n = new myI18n();
 
$_SESSION['culture'] = 'en';
echo $myI18n->translateText('Subject'); // => affiche "Subject"
 
$_SESSION['culture'] = 'fr';
echo $myI18n->translateText('Subject'); // => affiche "Sujet"

Chaque formulaire peut définir son propre callable de gestion de l'internationalisation comme montré dans le Listing 8-7.

Listing 8-7 - Définition d'un Filtre d'Internationalisation de Formulaire

class ContactForm extends sfForm
{
  public function configure()
  {
    // ...
    $this->widgetSchema->getFormFormatter()->setTranslationCallable(array(new myI18n(), 'translateText'));
  }
}

Paramètres Supplémentaires acceptés par l'Objet de Traduction

La fonction de traduction de formulaires peut prendre jusqu'à trois arguments :

Pour y voir plus clair, regardons de plus près la valeur retournée par la méthode sfFormWidgetSchemaFormatter::translate() :

return call_user_func(self::$translationCallable, $subject, $parameters, $catalogue);

La propriété self::$translationCallable étant une référence au callable PHP, ce dernier sera donc appelé de la manière suivante :

$myI18n->translateText($subject, $parameters, $catalogue);

Si nous devions réécrire la classe MyI18n afin de tirer parti de ces paramètres supplémentaires reçus en arguments, elle pourrait devenir :

class myI18n
{
  static protected $default_culture = 'en';
  static protected $messages = array('fr' => array(
    'messages' => array(
      'Name'    => 'Nom',
      'Email'   => 'Courrier électronique',
      'Subject' => 'Sujet',
      'Body'    => 'Message',
    ),
  ));
 
  static public function translateText($text, $arguments = array(), $catalogue = 'messages')
  {
    $culture = isset($_SESSION['culture']) ? $_SESSION['culture'] : self::$default_culture; 
    if (array_key_exists($culture, self::$messages) &&
        array_key_exists($messages, self::$messages[$culture] &&
        array_key_exists($text, self::$messages[$culture][$messages]))
    {   
      $text = self::$messages[$_SESSION['culture']][$messages][$text];
      $text = strtr($text, $arguments);
    }   
    return $text;
  }
}

Intégration des Objets Propel Internationalisés

Si vous utilisez des objets Propel internationalisés, le framework de formulaires les prend en charge nativement. Prenons un exemple très simple de modèle de données internationalisé :

propel:
  article:
    id:
    author:     varchar(255)
    created_at:
  article_i18n:
    title:      varchar(255)
    content:    longvarchar

Générons les classes Propel et les classes de formulaires correspondantes :

$ php symfony build:model
$ php symfony build:forms

Vous noterez que les fichiers suivants ont été générés dans votre projet symfony :

lib/
  form/
    ArticleForm.class.php
    ArticleI18nForm.class.php
    BaseFormPropel.class.php
  model/
    Article.php
    ArticlePeer.php
    ArticleI18n.php
    ArticleI18nPeer.php

Éditons la classe ArticleForm afin de configurer les langues que nous proposerons à la saisie (par défaut, le formulaire de base n'en inclut aucune). Ici, proposons le français et l'anglais au sein du même formulaire comme montré dans le Listing 8-8.

Listing 8-8 - Formulaires I18n pour un objet Propel internationalisé

class ArticleForm extends BaseArticleForm
{
  public function configure()
  {
    $this->embedI18n(array('en', 'fr'));
  }
}

Si vous désirez personnaliser les labels de langues dans le formulaire, vous pouvez le faire dans la méthode configure() de la classe de formulaire comme décrit dans le Listing 8-9 :

Listing 8-9 - Personnalisation des Labels de langue

$this->widgetSchema->setLabel('en', 'English');
$this->widgetSchema->setLabel('fr', 'French');

Figure 8-1 - Formulaire Propel Internationalisé

Formulaire Propel Internationalisé

L'immense intérêt du framework de formulaire est ici qu'un appel à la méthode save() de l'objet de formulaire sauvera non seulement notre article mais la ou les traductions que nous lui aurons associées depuis le formulaire.

Widgets localisés

Pour proposer des champs de saisie localisés, c'est à dire formattés en fonction d'une certaine localisation géographique de l'utilisateur, au sein de formulaire, vous pouvez utiliser des widgets dédiés :

Sélecteurs de dates

Pour proposer un champ de saisie de date et heure localisé dans le format lié aux usages de la langue courante de l'utilisateur, ces différents widgets sont disponibles :

Sélecteur de pays

Le widget sfWidgetFormI18nSelectCountry permet de présenter à l'utilisateur une liste déroulante contenant les noms de pays traduits dans une langue donnée :

$this->widgetSchema['country'] = new sfWidgetFormI18nSelectCountry(array('culture' => 'fr'));

Vous pouvez au besoin restreindre les noms de pays listés au moyen de l'option countries :

$countries = array('fr', 'en', 'es', 'de', 'nl');
$this->widgetSchema['country'] = new sfWidgetFormI18nSelectCountry(array('culture'   => 'fr',
                                                                         'countries' => $countries));

Sélecteur de langue

Le widget sfWidgetFormI18nSelectLanguage permet de présenter à l'utilisateur une liste déroulante contenant les noms des langues traduites dans une langue donnée. Exemple en français :

$this->widgetSchema['language'] = new sfWidgetFormI18nSelectLanguage(array('culture' => 'fr'));

Vous pouvez au besoin restreindre les noms de langues listées au moyen de l'option languages :

$languages = array('fr', 'en', 'es', 'de', 'nl');
$this->widgetSchema['language'] = new sfWidgetFormI18nSelectLanguage(array('culture'   => 'fr',
                                                                           'languages' => $languages));

Questions & Feedback

If you find a typo or an error, please register and open a ticket.

If you need support or have a technical question, please post to the official user mailing-list.