Development

Documentation/fr_FR/book/trunk/i18n

You must first sign up to be able to contribute.

Version 4 (modified by guillaume_c, 8 years ago)
--

Cette partie de la documentation est en cours de traduction. Cela signifie qu'elle est traduite de manière soit incomplète, soit inexacte. En attendant que cette traduction soit terminée, vous pouvez consulter la version en anglais pour des informations plus fiables.

Gestion de l'internationalisation d'un projet

En bref

Symfony dispose de mécanismes natifs de gestion de l'internationalisation qui facilitent le développement d'applications web multilingues et localisées.

Introduction

L'internationalisation (i18n) d'une application comprend 3 aspects: * la gestion des standarts et des formats (date, nombres, données monétaires) * la traduction des contenus du modèle de données * la traduction de l'interface

Symfony apporte une solution à chacune de ces problématiques.

La culture de l'internaute

La classe sfUser, utilisée pour gérer les sessions utilisateur, gère nativement sa culture, c'est à dire sa langue et son pays. Voici un exemple de leur utilisation:

    [php]
    // accéder à la culture
    $culture = $this->getUser()->getCulture();
    // modifier la culture
    $this->getUser()->setCulture('en_US');

La culture est sauvegardée dans la session utilisateur et accessible pendant toute la durée de celle-ci.

Il est nécessaire de conserver à la fois la langue et le pays dans les données de culture afin de permettre d'avoir par exemple des traductions françaises différentes pour les internautes de France, de Belgique ou du canada, ou des traductions espagnoles différentes en Espagne et au Mexique.

La langue est encodée en deux caractères en minuscule, suivant la norme ISO 639-1. (par exemple en pour l'englais)

Le pays est encodé en deux caractères en majuscule, suivant la norme ISO 3166 (par exemple GB pour la Grande Bretagne)

Par défaut, la culture d'un utilisateur est celle définie par le paramètre de configuration default_culture, dans le fichier i18n.yml:

    all:
      default_culture:     fr

Tous les contenus dépendant de la culture sont affichés dans la bonne langue de manière transparente en fonction de la culture de l'utilisateur.

Gestion des standards et des formats

Une fois la culture définie, tous les helpers qui en dépendent adapteront leur affichage. Voici une liste de ces helpers:

    [php]
    // Formatage de données
    format_date($date, $format)
    format_datetime($date, $format)
    format_number($number)
    format_currency($amount, $currency)
    format_country($country_iso)
    // Champs des formulaires
    input_date_tag($name, $value, $options)
    select_country_tag($name, $value, $options)

Par exemple:

    [php]
    <?php echo format_number(12000.10) ?>
    // affichera ceci si la culture est en_US
    12,000.10
    // et cela si elle est fixée à fr_FR
    12 000,10

Pour en savoir plus sur les helpers dépendant de la culture, reportez-vous aux documentations sur les helpers de formulaires, de gestion de l'internationalisation et sur les autres helpers.

Traduction des données en base

Il est recommandé de scinder chaque table contenant des données à traduire en deux tables: l'une avec toutes les colonnes à traduire, l'autre sans données à internationaliser. Cela permet d'ajouter à volonté des traductions sans modifier le modèle. Par exemple, avec une table Produit: Commencez par créer les deux tables dans le fichier schema.yml:

    my_connection:
      my_product:
        _attributes: { phpName: Product, isI18N: true, i18nTable: my_product_i18n }
        id:          { type: integer, required: true, primaryKey: true, autoincrement: true }
        price:       { type: float }
      my_product_i18n:
        _attributes: { phpName: ProductI18n }
        id:          { type: integer, required: true, primaryKey: true, foreignTable: my_product, foreignReference: id }
        culture:     { isCulture: true, type: varchar, size: 7, required: true, primaryKey: true }
        name:        { type: varchar, size: 50 }

La première table possède deux attributs supplémentaires: isI18N et i18nTable. La seconde possède une colonne culture. Par convention, on suffixera le nom de la table de traduction par _i18n afin d'automatiser de nombreux mécanismes d'accès aux données. Il s'agit ici d'évolutions de Propel spécifiques à Symfony.

Note: Symfony offre des mécanismes d'automatisation qui permettent de définir cela plus rapidement: si le nom de la table de traduction est celui de la table principale suffixé de _i18n et que les deux tables sont liées par une colonne `id, l'exemple précédent pourra s'écrire:

     my_connection:
       my_product:
         _attributes: { phpName: Product}
         id:
         price:       float
       my_product_i18n:
         _attributes: { phpName: ProductI18n }
         name:        varchar(50)

Vous trouverez plus d'informations sur la syntaxe du fichier schema.yml et les mécanismes d'automatisation dans le chapitre consacré au modèle de données.

Une fois les classes correspondantes générées (n'oubliez pas d'appeler symfony propel-build-model et d'effacer le cache avec symfony cc après chaque modification du fichier schema.yml), vous pourrez utiliser votre classe Produit gérant l'internationalisation comme s'il n'y avait qu'une seule table:

    [php]
    $product = ProductPeer::retrieveByPk(1);
    $product->setCulture('fr');
    $product->setName('Nom du produit');
    $product->save();

    $product->setCulture('en');
    $product->setName('Product name');
    $product->save();

    echo $product->getName();           =>    'Product name'
    $product->setCulture('fr');
    echo $product->getName();           =>    'Nom du produit'

Note: Si vous ne voulez pas avoir à vous souvenir de positionner la culture à chaque fois que vous utilisez un objet internationalisé, vous pouvez surcharger la méthode hydrate de la classe. Dans l'exemple précédent, cela se fait en ajoutant ce code au fichier myproject/lib/model/Product.php:

     [php]
     public function hydrate(ResultSet $rs, $startcol = 1)
     {
       parent::hydrate($rs, $startcol);
       $this->setCulture(SF_DEFAULT_CULTURE);
     }

Cet exemple fixe la culture à sa valeur par défaut. Pour utiliser la culture de l'internaute, utilisez ce code:

     [php]
     public function hydrate(ResultSet $rs, $startcol = 1)
     {
       parent::hydrate($rs, $startcol);
       $this->setCulture(sfContext::getInstance()->getUser()->getCulture());
     }

La traduction de l'interface

Symfony stocke les traductions de l'interface utilisateur dans des fichiers XML au format XLIFF. Voici par exemple un extrait d'un fichier XLIFSS messages.fr.xml qui traduit en français un site anglais:

    [xml]
    <?xml version="1.0" ?>
    <xliff version="1.0">
      <file orginal="global" source-language="en_US" datatype="plaintext" date="2004-12-28T18:10:19Z">
        <body>
          <trans-unit id="1">
            <source>original English text</source>
            <target>Tranduction française du texte</target>
          </trans-unit>
        </body>
      </file>
    </xliff>

Les fichiers de traduction sont stockés dans le répertoire apps/myapp/i18n. Pour ajouter une nouvelle traduction, il suffit de créer un fichier messages.XX.xml`.

L'utilisation du format XLIFF vous permet d'utiliser des outils standards de traduction pour référencer tous les textes de l'interface et les traduire, sans nécessiter d'outil spécifique.

Supposons que vous vouliez un site en anglais et en français, l'anglais étant la langue par défaut. La page d'accueil doit afficher la phrase Bienvenue sur notre site. Nous sommes le , suivi par la date du jour.

Pour ce faire, vous devez:

1. Activer la traduction de l'interface dans le fichier de configuration de l'application settings.yml:

        all:
          .settings:
            i18n: on

2. Activer le helper d'internationalisation: ajoutez ,I18N dans la section standard_helpers: du fichier settings.yml. Si vous ne voulez pas internationaliser toute l'application, il faudra ajouter la ligne <?php use_helper('I18N') ?> au début de chaque fichier de template que vous voulez internationaliser.

Faites de même pour le helper Date pour formater correctement toutes les dates et heures. <?php use_helper('Date') ?>

3. Dans le template, tous les textes à traduire seront affichés en appelant la fonction __(). Par exemple, pour traduire ce template:

        [php]
        Welcome to our website. Today's date is
        <?php echo format_date(date()) ?>

modifiez-le ainsi:

        [php]
        <?php echo __('Welcome to our website.') ?>     // (ceci correspond au premier nœud du fichier de traduction)
        <?php echo __('Today's date is ') ?>            // (et ceci au second)
        <?php echo format_date(date()) ?>

4. Créez dans le répertoire app/i18n un fichier messages.fr.xml au format XLIFF, contenant pour chaque appel à la fonction __() un nœud trans-unit :

        [xml]
        <?xml version="1.0" ?>
        <xliff version="1.0">
          <file orginal="global" source-language="en_US" datatype="plaintext">
            <body>
              <trans-unit id="1">
                <source>Welcome to our website.</source>
                <target>Bienvenue sur notre site web</target>
              </trans-unit>
              <trans-unit id="2">
                <source>Today's date is </source>
                <target>Nous sommes le </target>
              </trans-unit>
            </body>
          </file>
        </xliff>

Utilisez l'attribut source-languagepour préciser le code ISO de la culture par défaut.

Ici, la culture par défaut est fixée à en_US, les textes s'afficheront donc par défaut en anglais des USA. Si, dans votre code, vous positionnez la culture à fr_BE: {{

[php] $this->getUser()->setCulture('fr_BE');

}} le texte s'affichera en wallon.

Pour ajouter une traduction en hollandais, copiez le fichier messages.fr.xml en messages.nl.xml, éditez ce fichier et traduisez en hollandais tous les textes contenus dans les balises <target>.

Notez que la traduction n'a du sens que si toutes les phrases sont traduites.

Par ailleurs, vous pouvez être tenté de découper vos phrases à traduire. Par exemple, pour traduire

    [php]
    Bienvenue aux <b>nouveaux</b> utilisateurs.<br />
    Il y a <?php echo count_logged() ?> personnes connectées.

vous pourriez coder ainsi:

    [php]
    <?php echo __('Bienvenue aux') ?><b><?php echo __('nouveaux) ?> </b><?php echo __('utilisateurs') ?> <br />
    <?php echo __('Il y a ') ?><?php echo count_logged() ?><?php echo __(' personnes connectées') ?>

En faisant ainsi, le fichier XLIFF serait difficile à traduire. Mieux vaut utiliser la syntaxe suivante:

    [php]
    <?php echo __('Bienvenue à tous les <b>nouveaux</b> utilisateurs') ?> <br />
    <?php echo __('Il y a %1% personnes connectées', array('%1%' => count_logged())) ?>

La seconde ligne montre que la fonction __()permet de faire des remplacements de texte, pour éviter d'avoir à scinder celui-ci.

Un des problèmes classiques de l'internationalisation est la traduction des pluriels. Le texte change en fonction d'un nombre de résultats, comme par exemple

    [php]
    <?php echo __('Il y a %1% personnes connectées', array('%1%' => count_logged())) ?>

S'il n'y a qu'une personne connectée, le texte devrait être:

    [php]
    <?php echo __('Il y a une personne connectée') ?>

Pour éviter les tests multiples, Symfony fournit un helper format_number_choice() dont l'usage est:

    [php]
    <?php echo format_number_choice(
      '[0]Personne n'est connecté|[1]Il y a une personne connectée|(1,+Inf] Il y a %1% personnes connectées',
      array('%1%' => count_logged()),
      count_logged()
    ) ?>

Le premier argument contient les valeurs possibles du texte, le deuxième, optionnel, est le motif de remplacement, comme pour la fonction __(), et le dernier est le nombre à tester pour déterminer quelle variante afficher. Les différentes valeurs possibles sont séparées par des barres verticales "|", suivie d'une condition de la forme:

  • [1,2]: toutes les valeurs entre 1 et 2 inclus.
  • (1,2): toutes les valeurs entre 1 et 2, sauf 1 et 2.
  • {1,2,3,4}: uniquement les valeurs de cet ensemble.
  • [-Inf,0): toutes les valeurs supérieures eou égales à moins l'infini, et strictement inférieures à 0.

Toutes les combinaisons de parenthèses et de crochets sont permises.