• 30 heures
  • Moyenne

Ce cours est visible gratuitement en ligne.

course.header.alt.is_video

course.header.alt.is_certifying

J'ai tout compris !

Mis à jour le 13/05/2019

Traduire son site

Connectez-vous ou inscrivez-vous gratuitement pour bénéficier de toutes les fonctionnalités de ce cours !

Maintenant que votre site est opérationnel, il faut penser à monter en puissance et conquérir le reste du monde ! Pour cela, il va falloir apprendre quelques langues supplémentaires à votre site, car malheureusement tout le monde ne parle pas français. Ce chapitre a donc pour objectif de mettre en place un site multilingue. Cela passera par :

  • Traduire les messages qui apparaissent tels quels dans vos pages HTML ;

  • Placer un peu de contenu dynamique au milieu d'un texte ;

  • Afficher votre site dans différentes langues ;

  • Traduire du contenu d'entités.

Quand vous avez créé votre site, vous avez mis certains textes directement dans vos templates Twig. Seulement, quand il est question de traduire le site, la question se pose : « Comment faire pour que ce texte, actuellement en dur dans le template, change selon la langue de l'utilisateur ? » Eh bien, nous allons rendre ce texte dynamique. Comment ? Nous avons tout un chapitre pour y répondre.

Vous êtes prêts ? Allons-y !

Introduction à la traduction

Le principe

Si demain on vous demande de traduire un document du français vers une langue étrangère, de quoi auriez-vous besoin ? En plus de votre courage, il vous faut impérativement ces trois informations :

  • Ce qu'il faut traduire ;

  • La langue dans laquelle traduire ;

  • Ainsi qu'un dictionnaire si besoin.

Ensuite, la méthodologie est plutôt classique : on prend le texte original pour d'abord traduire les mots inconnus, puis on traduit les phrases en respectant la syntaxe de la langue cible.

En effet, quand nous commençons l'apprentissage d'une langue étrangère, nous cherchons le mot exact dans notre dictionnaire, sans imaginer qu'il s'agisse d'un adjectif accordé ou d'un verbe conjugué. On ne risque donc pas de trouver cette orthographe exacte. Symfony n'ayant pas d'intelligence artificielle, il va reproduire ce comportement systématiquement.

Ce qu'il en est avec Symfony

Le service de traduction ne va donc pas s'embarrasser de considérations de syntaxe ou de grammaire ; c'est dû au fait qu'il ne s'agit pas d'un traducteur sémantique. Il n'analyse pas le contenu de la phrase — ni même ne regarde si ce qu'on lui fournit en est une. Il se chargera de traduire une chaîne de caractères d'une langue à l'autre, en la comparant avec un ensemble de possibilités. Notez bien cependant que la casse, tout comme les accents, sont importants. Symfony va vraiment chercher une correspondance exacte de la chaîne à traduire. C'est pourquoi on ne parle pas de dictionnaire, mais de catalogue.

Si d'un côté c'est plutôt rassurant car il ne peut pas faire d'erreur, l'autre côté implique évidemment que cette responsabilité vous incombe, car c'est vous qui allez écrire le catalogue pour Symfony ! Donc autant vous assurer que vous connaissez bien la langue dans laquelle vous allez traduire.

La langue source, c'est celle que vous avez déjà utilisée dans vos templates jusqu'à maintenant, donc probablement le français. Comme on l'a vu juste avant, Symfony n'allant chercher que la correspondance exacte, il n'y a pas réellement besoin de la spécifier.

Quant à la langue cible, elle est en général demandée par l'utilisateur, parfois sciemment (quand il clique sur un lien qui traduit la page sur laquelle il se trouve), parfois implicitement (quand il suit un lien depuis un moteur de recherche, lien qui est dans sa langue), et parfois à son insu (les navigateurs envoient, dans l'en-tête des requêtes, la ou les locale(s) préférée(s) de l'utilisateur ; la locale utilisée sur un site est souvent stockée dans la session, liée à un cookie, qui voyage donc aussi à chaque requête).

Les locales sont composées de codes de langue, au format ISO639-1, puis éventuellement d'un sous-tiret (_) et du code du pays au format ISO3166-2 Alpha-2.

Et s'il n'y a pas de traduction dans la locale demandée ?

Symfony possède un mécanisme qui permet d'afficher quelque chose par défaut. Imaginons que vous arriviez sur un site principalement anglophone géré par des Québecois, et ceux-ci, par égard aux Français, sont en train de préparer une version spéciale en « français de France ». Cependant, tout le site n'est pas encore traduit.

Vous, en tant que visiteur, demandez la traduction pour votre localefr_FRdu texte « site.devise » :

  1. Ce qui est déjà traduit dans la localefr_FRvous sera retourné ;

  2. Ce qui n'est pas encore traduitenfr_FR, mais existe en « français général » (localefr) : c'est cette version qui sera envoyée ;

  3. Ce qui n'est pas du tout traduit en français, mais l'est en anglais, est affiché en anglais ;

  4. Ce qui ne possède aucune traduction est affiché tel quel, ici « site.devise ». Dans ce cas, quand c'est le texte original qui est affiché, c'est que vous avez oublié la traduction de ce terme.

Ainsi, il n'y aura jamais de vide là où vous avez spécifié du texte à traduire.

Prérequis

Avant de partir dans la traduction de son site, il faut vérifier que Symfony travaillera correctement avec les langues, et notamment celle qui est utilisée par défaut. Comme nous sommes sur un site francophone, je vais partir du principe que la langue par défaut de votre site est le français, et la localefr.

Configuration

Pour savoir quelle est la langue par défaut sur le site (au cas où il ne serait pas possible d'afficher celle que le client souhaite), Symfony utilise un paramètre appelélocale, comme nous l'avons vu plus haut. La locale pour le français estfr, en minuscules, et il nous faut la définir comme locale par défaut dans le fichierapp/config/config.yml. Ouvrez donc ce fichier et effectuez-y la manipulation mentionnée.

# app/config/config.yml

parameters:
    locale: fr # Mettez « fr » ici si ce n'est pas déjà le cas

On va ensuite utiliser ce paramètrelocaledans la configuration :

# app/config/config.yml

framework:
    # On définit la langue par défaut pour le service de traduction
    # Décommenter la ligne, et vérifier qu'elle est bien ainsi
    translator:      { fallbacks: ["%locale%"] }

# …

    # Vérifier cette ligne aussi, pour la langue par défaut de l'utilisateur
    # C'est celle qui sera utilisée si l'internaute ne demande rien
    default_locale: %locale%

Votre application sait maintenant que vous travaillez sur un site qui, à la base, est en français.

Mise en place d'une page de test

Pour la suite du chapitre, nous avons besoin d'une page sur laquelle réaliser nos tests. Je vous invite donc à créer la même que moi, afin qu'on s'y retrouve.

Tout d'abord voici la route, à rajouter au fichier de routes de l'application. On va la mettre dans lerouting_dev.yml, car d'une part c'est une route de test (pas destinée à nos futurs visiteurs !), et d'autre part le fichier de routes de notre bundle est préfixé par/platform qu'on ne veut pas forcément ici. Voici donc la route en question :

# app/config/routing_dev.yml

oc_platform_translation:
  path:     /traduction/{name}
  defaults:
    _controller: OCPlatformBundle:Advert:translation

Pour que la route fonctionne, il nous faut aussi créer l'action qui lui correspond, dans le contrôleur Advert. Une fois de plus, je vous mets le code, rien de bien sorcier :

<?php
// src/OC/PlatformBundle/Controller/AdvertController.php

  public function translationAction($name)
  {
    return $this->render('OCPlatformBundle:Advert:translation.html.twig', array(
      'name' => $name
    ));
  }

Et comme indiqué dans le contrôleur, il nous faut la vuetraduction.html.twig, la voici :

{# src/OC/PlatformBundle/Resources/views/Advert/translation.html.twig #}

<html>
  <body>
    Hello {{ name }}!
  </body>
</html>

C'est bon, on va pouvoir mettre la main à la pâte !

Bonjour le monde

Actuellement, quand vous accédez à/traduction/winzou, la page qui s'affiche ne contient que « Hello winzou! », et ce quelle que soit la langue. Nous allons faire en sorte qu'en français nous ayons « Bonjour winzou! », c'est-à-dire que le « Hello » soit traduit en « Bonjour ».

Dire à Symfony « Traduis-moi cela »

La traduction est possible dans Symfony à deux endroits : dans les contrôleurs (ou services) et dans la vue. Cette dernière option est la plus conseillée, car c'est dans les vues que se situe l'affichage et donc bien souvent le texte à traduire.

Le filtre Twig{{ 'string'|trans }}

Un filtre est, dans le langage Twig, une fonction destinée à formater/modifier une valeur. C'est donc tout à fait adapté à la traduction de texte, car modifier le texte pour qu'il soit dans une autre langue est une transformation comme une autre !

Plus précisément dans notre cas, c'est le filtretransque l'on va utiliser. La syntaxe est la suivante :{{ 'ma chaîne'|trans }}ou encore{{ ma_variable|trans }}. Ce filtre est prévu pour s'appliquer sur des variables ou des courtes chaînes, voici un exemple dans un contexte :

<div>
  <p>{{ message|trans }}</p>
  <button>{{ 'cancel'|trans }}</button>
  <button>{{ 'validate'|trans }}</button>
</div>
La balise de bloc Twig{% trans %}

Une autre possibilité de traduction depuis la vue consiste à encadrer tous les textes dans des blocs{% trans %} … {% endtrans %}. Ce bloc permet de traduire du texte brut, mais attention il est impossible d'y mettre autre chose que du texte. Balises HTML, code Twig, etc. sont interdits ici. Une des utilisations les plus parlantes est pour les conditions générales d'utilisation d'un site, où il y a de gros paragraphes avec du texte brut, voici un exemple :

<p>
  {% trans %}Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur
  quam nisi, sollicitudin ut rhoncus semper, viverra in augue. Suspendisse
  potenti. Fusce sit amet eros tortor. Class aptent taciti sociosqu ad litora
  torquent per conubia nostra, per inceptos himenaeos. Ut arcu justo, tempus sit
  amet condimentum vel, rhoncus et ipsum. Mauris nec dui nec purus euismod
  imperdiet. Cum sociis natoque penatibus et magnis dis parturient montes,
  nascetur ridiculus mus. Mauris ultricies euismod dolor, at hendrerit nulla
  placerat et. Aenean tincidunt enim quam. Aliquam cursus lobortis odio, et
  commodo diam pulvinar ut. Nunc a odio lorem, in euismod eros. Donec viverra
  rutrum ipsum quis consectetur. Etiam cursus aliquam sem eget gravida. Sed id
  metus nulla. Cras sit amet magna quam, sed consectetur odio. Vestibulum feugiat
  justo at orci luctus cursus.{% endtrans %}
</p>
<p>
  {% trans %}Vestibulum sollicitudin euismod tellus sed rhoncus. Pellentesque
  habitant morbi tristique senectus et netus et malesuada fames ac turpis
  egestas. Duis mattis feugiat varius. Aenean sed rutrum purus. Nam eget libero
  lorem, ut varius purus. Etiam nec nulla vitae lacus varius fermentum. Mauris
  hendrerit, enim nec posuere tempus, diam nisi porttitor lacus, at placerat
  elit nulla in urna. In id nisi sapien.{% endtrans %}
</p>

D'accord, l'exemple n'est pas vraiment bon, mais cela illustre l'utilisation. On a de gros pavés de texte, et je vous laisse regarder et réfléchir à ce que cela aurait représenté avec la solution précédente du filtre. ;)

Le servicetranslator

Parfois, vous devrez malgré tout réaliser quelques traductions depuis un contrôleur ou un service, dans le cas d'inscriptions dans un fichier de log, par exemple. Dans ce cas il faut faire appel au servicetranslator, qui est le service de traduction que la balise et le filtre Twig utilisent en réalité. Son utilisation directe est très aisée, voyez par vous-mêmes :

<?php
// Depuis un contrôleur

// On récupère le service translator
$translator = $this->get('translator');

// Pour traduire dans la locale de l'utilisateur :
$texteTraduit = $translator->trans('Mon message à inscrire dans les logs');

Notre vue

Bon, et on fait comment, finalement, pour traduire notre « Hello » en « Bonjour » ?

C'est vrai, revenons-en à nos moutons. Adaptons donc le code de notre vue en rajoutant le filtretransde Twig pour qu'il traduise notre « Hello » :

{# src/OC/PlatformBundle/Resources/views/Advert/translation.html.twig #}

<html>
  <body>
    {{ 'Hello'|trans }} {{ name }}!
  </body>
</html>

Accédez à nouveau à/traduction/winzouvia l'environnement de développement.

Eh, mais… c'est encore « Hello » qui s'affiche ! Pourtant on s'est bien mis en français, non ?

C'est exact ! Mais rappelez-vous la méthode pour faire une traduction. Il faut savoir quoi traduire, ça c'est OK, il faut savoir dans quelle langue le traduire, ça c'est OK, la locale de l'utilisateur est automatiquement définie par Symfony. Il nous manque donc… le dictionnaire !

En effet, on n'a pas encore renseigné Symfony sur comment dire « Hello » en français. Les fichiers qui vont le lui dire s'appellent des catalogues, nous y venons.

Le catalogue

Vous l'aurez compris, le catalogue est l'endroit où l'on va associer la chaîne à traduire avec sa version en langue étrangère. Si vous avez créé votre bundle grâce à la commandegenerate:bundle, alors Symfony vous a créé automatiquement un catalogue exemple, c'est le fichier enregistré dans le dossierResources/translationsdu bundle, et qui contiendra par la suite les paires de type'Ma chaîne en français' <=> 'My string in English'.

Les formats de catalogue

Le format XLIFF

Symfony recommande le XLIFF, une application du XML. C'est pourquoi, dans les bundles générés avec la ligne de commande, vous trouvez ce fichierResources/translations/messages.fr.xlfqui contient l'exemple suivant que je commente :

<!-- src/OC/PlatformBundle/Resources/translations/messages.fr.xlf -->

<?xml version="1.0"?>
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
  <file source-language="en" datatype="plaintext" original="file.ext">
    <body>
      <trans-unit id="1">
        <!-- La chaîne source, à traduire. C'est celle que l'on utilisera :
        {% trans %}Symfony is great{% endtrans %}
        ou
        {{ 'Symfony is great'|trans }}
        ou
        $translator->trans('Symfony is great')) -->
        <source>Symfony is great</source>

        <!-- La chaîne cible, traduction de la chaîne ci-dessus -->
        <target>J'aime Symfony</target>
      </trans-unit>
    </body>
  </file>
</xliff>

L'avantage du XLIFF est qu'il s'agit d'un format officiel, dont vous pouvez faire valider la structure en plus de la syntaxe. De plus, du fait du support natif de XML par PHP, il est facile de le modifier via PHP, donc de créer une interface dans votre site pour modifier les traductions. En revanche, le désavantage du XLIFF est celui du XML de façon générale : c'est très verbeux, c'est-à-dire que pour traduire une seule ligne il nous en faut vingt.

Le format YAML

Voici l'équivalent YAML du fichiermessages.fr.xlfprécédent :

# src/OC/PlatformBundle/Resources/translations/messages.fr.yml

# La syntaxe est : « la chaîne source: la chaîne cible »
Symfony is great: J'aime Symfony

Vous allez me dire que vu sa simplicité on se demande pourquoi le XLIFF existe. Disons qu'il existe des outils pour faciliter les traductions de gros catalogues pour le format XLIFF.

Pour nos exemples dans ce cours, utilisons le format YAML qui est plus lisible et plus concis. Si c'est votre choix également, pensez à supprimer le fichiermessages.fr.xlfafin qu'il n'interfère pas. 

Attention également lorsque vous avez des deux-points ( « : » ) dans votre chaîne source, vu la syntaxe utilisée dans le fichier YAML vous vous doutez qu'il ne va pas apprécier. Il faut dans ce cas encadrer la chaîne source par des guillemets, comme ceci :

"Phone number:": Numéro de téléphone :

Notez que vous pouvez ne pas encadrer la chaîne cible avec les guillemets, bien que celle-ci contienne également les deux-points. Cela est dû à la syntaxe du YAML lui-même : la fin de la chaîne cible est le retour à la ligne, donc impossible à confondre avec les deux-points. ;)

Le format PHP

Moins utilisé, ce format est néanmoins possible. La syntaxe est celle d'un simple tableau PHP, comme ceci :

<?php
// src/OC/PlatformBundle/Resources/translations/messages.fr.php

return array(
    'Symfony is great' => 'J\'aime Symfony',
);
La mise en cache du catalogue

Quelque soit le format que vous choisissez pour vos catalogues, ceux-ci seront mis en cache pour une utilisation plus rapide dans l'environnement de production. En effet, ne l'oubliez pas, si Symfony a la gentillesse de regénérer le cache du catalogue à chaque exécution dans l'environnement de développement, il ne le fait pas en production. Cela signifie que vous devez vider manuellement le cache pour que vos changements s'appliquent en production. Pensez-y avant de chercher des heures pourquoi votre modification n'apparaît pas.

Vous pouvez choisir le format qui vous convient le mieux, mais dans la suite du cours je vais continuer avec le format YAML, qui possède quelques autres avantages intéressants dont je parle plus loin.

Notre traduction

Maintenant qu'on a vu comment s'organisait le catalogue, on va l'adapter à nos besoins. Créez le fichiermessages.fr.ymlnécessaire pour traduire notre « Hello » en français. Vous devriez arriver au résultat suivant sans trop de problèmes :

# src/OC/PlatformBundle/Resources/translations/messages.fr.yml

Hello: Bonjour

Pour tester, videz votre cache et rechargez la page/traduction/winzou. Ça marche ! Vous lisez désormais « Bonjour winzou! ». Et si vous changez le paramètre dansapp/config/config.yml pour que la locale par défaut soit à nouveau l'anglais, vous avez à nouveau « Hello winzou! ».

Ajouter un nouveau message à traduire

On se doute bien que l'on n'aura pas qu'une seule chaîne à traduire sur tout un site. Il va falloir que tout ce qu'on souhaite voir s'afficher dans la langue de l'utilisateur soit renseigné dans le catalogue. Pour chaque nouvelle chaîne, on ajoute une unité de traduction :

# src/OC/PlatformBundle/Resources/translations/messages.fr.yml

Hello: Bonjour
Bye: Au revoir

Ainsi, à chaque fois que vous avez une nouvelle traduction à ajouter, vous ajoutez une ligne.

Extraire les chaînes sources d'un site existant

Mais moi j'ai déjà un site tout prêt en français, je dois trouver toutes les chaînes sources et les copier dans le catalogue à la main ? :'(

Justement, non. Vous allez devoir ajouter les balises et/ou filtres Twig de traduction dans vos vues, ça, c'est inévitable. Mais une fois que ce sera fait, les concepteurs de Symfony ont pensé aux personnes dans votre cas, et ont développé un outil permettant d'extraire toutes les chaînes entre balises{% trans %}et celles avec le filtre|trans.

Cet outil se présente sous forme d'une commande, il s'agit detranslation:update. Sa version complète est la suivante :translation:update [--prefix[="..."]] [--output-format[="..."]] [--dump-messages] [--force] locale bundle. Cette commande va lire toutes les vues du bundle spécifié, et compilera toutes les chaînes sources dans un catalogue. Vous n'aurez plus qu'à définir les chaînes cibles.

La commandetranslation:updateest du même genre quedoctrine:schema:update, dans le sens où il vous faut choisir de donner (en plus des deux paramètres obligatoires) soit--dump-messages, soit--forcepour qu'elle fasse réellement quelque chose. Cela permet de vérifier le résultat avant qu'elle n'écrive effectivement dans le catalogue.

  • La première option--dump-messagesaffiche tous les messages dans la console, plus ou moins tels qu'ils seront dans un catalogue en YAML (c'est pour cela que c'est le format de sortie par défaut). Elle tient compte des messages déjà traduits, donc pas de souci que votre précédent travail soit écrasé. Cela vous permet aussi de passer d'un format à l'autre si vous aviez commencé le travail en réutilisant le fichiermessages.fr.xlfdu bundle par exemple.

  • La seconde option--forceeffectue la mise à jour des catalogues, tout en conservant une sauvegarde des versions précédentes.

Par défaut, les extractions sont de typechaîne source: __chaîne source. En effet, Symfony ne peut deviner comment traduire la chaîne source, il la remet donc comme chaîne cible, mais en la préfixant avec__. Avec l'option--prefix="...", vous pouvez changer la partie__ par ce que vous voulez.

Il est temps de passer à la pratique, exécutons cette commande pour extraire les messages de notre bundle, et regardez ce qu'il en sort :

C:\wamp\www\Symfony> php bin/console translation:update --force fr OCPlatformBundle
Translation Messages Extractor and Dumper
=========================================

 // Generating "fr" translation files for "OCPlatformBundle"

 // Parsing templates...
 // Loading translation files...
 // Writing files...

 [OK] Translation files were successfully updated.
‌

Comme je vous l'avais mentionné, les nouvelles chaînes cibles sont identiques aux chaînes sources mais avec un préfixe. À l'aide de votre éditeur préféré, cherchez les occurrences de__ (ou du préfixe que vous avez défini vous-mêmes) dans vos catalogues pour les mettre en surbrillance. Vous ciblerez ainsi très rapidement les nouveautés, c'est-à-dire ce que vous devez traduire !

Enfin, vous noterez que les anciennes chaînes cibles ne sont pas préfixées, car nous les avions déjà traduites !

Traduire dans une nouvelle langue

Pour conquérir le monde, ce qui reste notre but à tous, c'est bien de parler anglais, mais il ne faut pas s'arrêter là ! On souhaite maintenant traduire les messages également en allemand. Il faut alors tout simplement créer le catalogue adéquat, mais on peut se simplifier la vie : dupliquez le fichiermessages.fr.ymlet nommez la copiemessages.de.yml. Ensuite, vous n'avez plus qu'à y modifier les chaînes cibles :

# src/OC/PlatformBundle/Resources/translations/messages.de.yml

Hello: Guten Tag

Vous pouvez dès à présent tester le bon fonctionnement de l'allemand en changeant le paramètre dansapp/config/config.yml, après avoir vidé votre cache bien entendu (nous avons ajouté un fichier dans le catalogue).

Récupérer la locale de l'utilisateur

Déterminer la locale

Jusqu'à présent, pour changer la locale de notre site nous avons modifié à la main le fichier de configuration. Bien entendu, ce n'est pas une solution viable, vous n'allez pas rester derrière votre PC à changer la locale dès qu'un internaute étranger arrive sur votre site !

La question se pose donc de savoir comment adapter la locale à l'utilisateur qui navigue sur votre site. Souvenez-vous, j'ai précisé un peu plus haut qu'il y avait plusieurs moyens de connaître la locale de l'utilisateur. Je vous les rappelle ici :

  1. L'utilisateur clique sur un lien qui traduit la page sur laquelle il se trouve ;

  2. L'utilisateur envoie ses préférences dans les en-têtes des requêtes ;

  3. Les paramètres par défaut.

L'ordre correspond à la priorité observée par Symfony.

La plupart des sites multilingues affichent la locale dans l'URL (exemple :http://www.site.com/fr). La locale ne dépend donc pas de l'utilisateur, mais de l'adresse à laquelle il se trouve.

Routing et locale

Vous l'avez peut-être déjà compris, pour que la locale apparaisse dans les URL, il va falloir ajouter un paramètre dans nos URL.

Le paramètre d'URL

Nous l'avons vu dans le chapitre sur le routeur : certains paramètres de route bénéficient d'un traitement spécial de la part de Symfony. Et devinez quoi ? Il y en a un prévu pour récupérer la locale ! Il s'agit du paramètre_locale, qui a déjà été mentionné dans le chapitre sur le routeur.

Mais les paramètres, on doit les traiter dans les contrôleurs, normalement, non ? Donc on va devoir modifier toutes nos actions, en plus des routes ?

Justement non, c'est en cela que le paramètre_locale(et d'autres) sont spéciaux. En effet, Symfony sait quoi faire avec ces paramètres, vous n'avez donc pas à les récupérer dans vos actions — sauf si le traitement diffère sensiblement en fonction de ce paramètre — ni le mettre vous-mêmes en session, ni quoi que ce soit.

Voici ce que cela donne sur notre route de test :

# app/config/routing_dev.yml

oc_platform_translation:
  path: /{_locale}/traduction/{name}
  defaults:
    _controller: "OCPlatformBundle:Advert:translation"

Vous pouvez déjà tester en essayant/fr/traduction/winzouou/de/traduction/winzou, le contenu de la page est bien traduit.

Attention par contre, si vous allez sur/en/traduction/winzou, vous avez toujours « Bonjour ». En effet, pour l'instant on n'a pas de catalogue pour l'anglais. Alors effectivement le « Hello » inscrit dans la vue est déjà en anglais, mais ça, Symfony ne le sait pas  ! Car on aurait pu mettre n'importe quoi à la place de « Hello », comme on le verra un peu plus loin. Bref, comme il n'y a pas de catalogue correspondant à la langue demandée, Symfony affiche la page dans la langue fallback (langue de repli en français), définie dans le fichier de configurationconfig.yml, qui est dans notre cas le français. ;)

Mais cela veut dire qu'on va éditer tous nos fichiers de routing et prendre chaque route une à une ?

Non, rassurez-vous ! Il y a au moins un moyen plus rapide de faire cela, qu'on a aussi vu dans le chapitre sur les routes… il s'agit de l'utilisation d'un préfixe ! Voyez par vous-mêmes comment rajouter le paramètre_localesur tout notre plateforme :

# app/config/routing.yml

oc_platform:
    resource: "@OCPlatformBundle/Resources/config/routing.yml"
    prefix:   /{_locale}/platform # Ici, on ajoute {_locale} au préfixe !

Vous pouvez désormais demander vos pages en différentes langues selon l'URL : /fr/platform, /en/platform, etc. Bien entendu, pour que ce soit parfaitement opérationnel, vous devez généraliser l'utilisation du filtre ou de la balise Twigtrans, et traduire les textes dans les catalogues correspondants. Pas detrans, pas de traduction !

Il manque cependant un garde-fou à notre solution : avec une locale ainsi apparente, un petit malin peut très bien changer à la main la locale dans une URL, et arriver sur un site dans une langue que vous ne pensiez pas être accessible. Veillez donc à limiter les locales disponibles en ajoutant les requirements pour ce paramètre. De fait, le routing final devrait ressembler à cela :

# app/config/routing.yml

oc_platform:
  resource: "@OCPlatformBundle/Resources/config/routing.yml"
  prefix:   /{_locale}/platform
  requirements:
    _locale: en|fr # les locales disponibles, séparées par des pipes « | »

Les paramètres par défaut

Ces paramètres sont prévus pour éviter que l'internaute ne trouve ni aucun contenu, ni un contenu incompréhensible. Mais ils sont aussi définis pour que Symfony puisse fonctionner. Nous avons vu l'ordre de priorité dans les possibilités de passer la locale au framework. En fait, Symfony ne se base que sur la session, mais la remplit selon cet ordre de priorité. Seulement, il y a toujours un moment où l'internaute arrive pour la toute première fois sur notre site (ou une nouvelle fois après avoir entièrement nettoyé son cache navigateur, ce qui, pour le serveur, revient au même). Du coup, il faut bien des paramètres par défaut.

Au début de ce chapitre, nous avons vérifié et changé quelques paramètres dansapp/config/config.yml. Reprenons le code de ce fichier un peu plus en détail, afin de mieux comprendre à quoi il sert :

# app/config/config.yml

framework:
    # On définit la langue par défaut pour le service de traduction
    # Ce qui n'est pas disponible dans la locale de l'utilisateur
    # sera affiché dans celle spécifiée ici
    translator:      { fallbacks: [%locale%] }

    # …

    # On initialise la locale de requête, celle par défaut pour
    # l'internaute arrivant pour la toute première fois sur votre site
    default_locale: %locale%

Organiser vos catalogues

Quand il y a beaucoup de traductions, les fichiers deviennent vite difficiles à manipuler. Il faut parcourir beaucoup de lignes pour retrouver là où l'on souhaite faire une modification, et c'est contraire au mode de vie des informaticiens qui veut qu'on puisse se retrouver rapidement dans du code ou dans des fichiers. Je vais donc vous proposer des solutions pour organiser vos catalogues.

Utiliser des mots-clés plutôt que du texte comme chaînes sources

Voilà une solution intéressante, à vous de choisir de l'adopter pour toutes vos traductions ou juste les chaînes très longues. L'idée est d'utiliser, au lieu du texte dans la langue source, des mots-clés.

Plutôt qu'un long discours, je vous propose un petit exemple. Prenons une page statique avec pas mal de texte, ce qui implique beaucoup de texte dans le catalogue, par exemple :

# Dans un catalogue

Le site où on apprend tout... à partir de zéro !: The website where you learn it all... from scratch!

L'entrée du catalogue est composée de deux longues phrases, ce n'est pas terrible. Et je ne vous parle pas de son utilisation dans les vues :

{# Dans une vue #}

{% trans %}Le site où on apprend tout... à partir de zéro !{% endtrans %}
{# ou #}
{{ 'Le site où on apprend tout... à partir de zéro !'|trans }}

Passons maintenant à l'utilisation d'un mot-clé, vous allez tout de suite comprendre. Voici d'abord le catalogue :

# Dans un catalogue

site.devise: The website where you learn it all... from scratch!

Vous voyez déjà à quel point c'est plus léger !

Mais l'avantage se situe surtout dans les vues, où un mot-clé est plus synthétique qu'une longue phrase, utile pour ne pas se perdre au milieu du code HTML de votre vue. Voyez vous-mêmes :

{# Dans une vue #}

{% trans %}site.devise{% endtrans %}
{# ou #}
{{ 'site.devise'|trans }}

Vous voyez : quelques mots-clés bien choisis pour résumer une phrase, séparés par des points, et vous avez déjà gagné en clarté dans vos vues et catalogues ! Cela est utilisable avec n'importe quel format de catalogue. N'hésitez pas à vous en servir copieusement.

Un des avantages également est de voir très rapidement une chaîne non traduite : au lieu du joli texte en français, vous aurez un « xxx.yyy » au milieu de votre page. Cela saute mieux aux yeux, et évite les oublis !

Enfin, un mot sur la création de deux catalogues au lieu d'un seul. C'est en réalité plutôt une bonne chose, car cela permet non seulement de séparer le texte de tout le code HTML, mais cela permet aussi de mutualiser ! En effet, si vous vous servez d'un mot ou d'une phrase de façon récurrente sur votre site (la devise par exemple), celui-ci ne sera stocké qu'à un seul endroit, dans votre catalogue. Vous pourrez alors le modifier à un unique endroit, et les répercussions s'appliqueront partout sur votre site.

Nicher les traductions

C'est une possibilité qui découle de l'utilisation des mots-clés.

Si vous optez pour l'utilisation des mots-clés, ce que je vous conseille, vous arriverez très certainement à un résultat de ce genre :

# Dans un catalogue

advert.edit.title: Édition d'une annonce
advert.edit.submit_button: Valider
advert.show.edit_button: Éditer l'annonce
advert.show.create_button: Créer une nouvelle annonce

Ce qui était très clair avec une seule ligne, le devient déjà moins lorsqu'il y en a quatre, alors imaginez avec plus !

En bons développeurs avisés, vous avez tout de suite repéré les redondances, et votre envie de les factoriser est grande. Sachez que vous n'êtes pas seuls, les développeurs du YAML ont pensé à tout, voici comment optimiser votre catalogue :

# Dans un catalogue

advert:
    edit:
        title:         Édition d'une annonce
        submit_button: Valider
    show:
        edit_button:   Éditer l'annonce
        create_button: Créer une nouvelle annonce

Quand Symfony va lire cette portion de YAML, il va remplacer chaque séquence « deux points − retour à la ligne − indentation » par un simple point, devenant ainsi l'équivalent de ce que vous aviez précédemment. Très pratique !

Côté utilisation, dans les vues ou avec le servicetranslator, rien ne change. Vous utilisez toujours{{ 'advert.edit.title'|trans }}par exemple.

Pour en revenir à l'organisation du catalogue avec ces mots-clés, je vous propose de toujours respecter une structure de ce genre :

oc:                       # Le namespace racine que vous utilisez
    platform:             # Le nom du bundle, sans la partie Bundle
        advert :          # Le nom de l'entité ou de la section
            list: liste   # Les différents messages, pages et/ou actions
            new:  nouveau # Etc.

Permettre le retour à la ligne au milieu des chaînes cibles

Certains éditeurs ne gèrent pas le retour à la ligne automatique, et du coup, ce ne sont pas les chaînes sources trop longues qui posent problème, mais les chaînes cibles. Le parseur YAML fourni avec Symfony supporte une syntaxe intéressante qui permet d'éviter d'avoir à faire défiler horizontalement le contenu des catalogues.

Difficile d'expliquer cela sans un exemple, prenons la charte du site OpenClassrooms :

# Dans un catalogue

charte:
    titre: Mentions légales
    donnee:
        # Le chevron « > » en début de chaîne indique que la chaîne cible est sur
        # plusieurs lignes, mais les retours à la ligne ne seront pas présents
        # dans le code HTML, car ils seront remplacés par des espaces.
        # L'indentation doit être faite sur tout le paragraphe.
        premier_paragraphe: >
            OpenClassrooms recueille des informations (login, e-mail) lors de
            votre enregistrement en tant que membre du site. Lors de votre
            connexion au site, un fichier "log" stocke les actions effectuées
            par votre ordinateur (via son adresse IP) au serveur.

        # La pipe « | » permet la même chose, mais les retours à la ligne seront
        # présents dans le code HTML, et non remplacés par des espaces.
        # Vous pouvez utiliser nl2br() sur une telle chaîne, cela permet
        # d'avoir le code comme présenté ci-dessous (l'indendation en moins).
        deuxieme_paragraphe: |
            Lorsque que vous vous connectez en tant que membre de OpenClassrooms et
            que vous cochez la case correspondante, un cookie est envoyé à votre
            ordinateur afin qu'il se souvienne de votre login et de votre mot de
            passe. Ceci vous est proposé uniquement afin d'automatiser la
            procédure de connexion, et n'est en aucun cas utilisé par Simple IT à
            d'autres fins.

Avec la pipe et le chevron, vous pouvez donc faire tenir votre catalogue sur 80 caractères de large, ou tout autre nombre qui vous convient.

Utiliser des listes

Encore une possibilité du language YAML qui peut s'avérer pratique dans le cas de catalogues !

Reprenons l'exemple précédent de la charte pour en faire une liste. En effet, on rencontre souvent une série de paragraphes, dont certains seront supprimés, d'autres ajoutés, et il faut pouvoir le faire assez rapidement. Si vous n'utilisez pas de liste, et que vous supprimez la partie 2 sur 3, ou que vous ajoutez un nouveau paragraphe entre deux autres… vous devez soit adapter votre vue, soit renuméroter les parties et paragraphes. Bref, ce n'est clairement pas idéal.

Heureusement, il y a un moyen d'éviter cela en YAML, et voici comment :

# Dans un catalogue

charte:
    titre: Mentions légales
    donnee:
        # les éléments de liste sont précédés d'un tiret en YAML
        - >
            OpenClassrooms recueille des informations (login, e-mail) lors de
            votre enregistrement en tant que membre du site. Lors de votre
            connexion au site, un fichier "log" stocke les actions effectuées
            par votre ordinateur (via son adresse IP) au serveur.
        - |
            Lorsque que vous vous connectez en tant que membre de OpenClassrooms et
            que vous cochez la case correspondante, un cookie est envoyé à votre
            ordinateur afin qu'il se souvienne de votre login et de votre mot de
            passe. Ceci vous est proposé uniquement afin d'automatiser la
            procédure de connexion, et n'est en aucun cas utilisé par Simple IT à
            d'autres fins.
        - Merci de votre attention.

On va pouvoir utiliser cela dans une bouclefor?

C'est justement l'idée, oui ! On peut utiliser une structure qui va générer une partie de votre page de conditions générales d'utilisation en bouclant sur les valeurs du catalogue, bien vu ! Cela va donner quelque chose comme cela :

{# Dans une vue #}

{% for i in 0..2 %}
	<p>{{ ('charte.donnee.' ~ i )|trans }}</p>
{% endfor %}

La notation0..2est une syntaxe Twig pour générer une séquence linéaire. Le nombre avant les deux points (..) est le début, celui après est la fin.

Donc quand vous ajoutez un paragraphe, vous l'insérez à la bonne place dans le catalogue, sans vous préoccuper de son numéro. Vous n'avez qu'à incrémenter la fin de la séquence dans lefor. De même si vous supprimez un paragraphe, vous n'avez qu'à décrémenter la limite de la séquence.

Utiliser les domaines

Si vous avez commencé à bien remplir votre fichiermessages.fr.yml, vous pouvez vous rendre compte qu'il grossit assez vite. Et surtout, qu'il peut y avoir des conflits entre les noms des chaînes sources si vous ne faites pas assez attention.

En fait, il est intéressant de regrouper les traductions par domaine. Le domaine par défaut estmessages, c'est pourquoi nous utilisons depuis le début le fichiermessages.XX.XXX. Un domaine correspond donc à un fichier.

Vous pouvez donc créer autant de fichiers/domaines que vous voulez, la première partie représentant le nom du domaine de traduction que vous devrez utiliser.

Mais comment définir le domaine à utiliser pour telle ou telle traduction ?

C'est un argument à donner à la balise, au filtre ou à la fonctiontrans, tout simplement :

  • Balise :{% trans from 'domaine' %}chaîne{% endtrans %}.

  • Filtre :{{ 'chaîne'|trans({}, 'domaine') }}.

  • Service :$translator->trans('chaîne', array(), 'domaine').

C'est pour cette raison qu'il faut utiliser les domaines avec parcimonie. En effet, si vous décidez d'utiliser un domaine différent de celui par défaut (messages), alors il vous faudra le préciser dans chaque utilisation detrans! Attention donc à ne pas créer 50 domaines inutilement, le choix doit avoir un intérêt.

Domaines et bundles

Quelle est la différence entre un domaine et son bundle ? Est-ce qu'on peut avoir les mêmes domaines dans des bundles différents ?

Autant de questions qui, je le sais, vous taraudent l'esprit. En fait, c'est plutôt simple : les domaines n'ont rien à voir avec les bundles. Voilà, c'est dit.

Du coup, cela veut dire que vous pouvez tout à fait avoir un domaine « A » dans un bundle, et ce même domaine « A » dans un autre bundle. Le contenu de ces deux bouts de catalogue vont s'additionner pour former le catalogue complet du domaine « A ». C'est ce que nous faisons déjà avec le domaine « messages » en fait ! Une vue du bundle « A » pourra alors utiliser une traduction définie dans le bundle « B », et inversement, à condition que le domaine soit le même.

Et si plusieurs fichiers d'un même domaine définissent la même chaîne source, alors c'est le fichier qui est chargé en dernier qui l'emporte (il écrase la valeur définie par les précédents). L'ordre de chargement des fichiers du catalogue est le même que celui de l'instanciation des bundles dans le Kernel. Il faut donc vérifier tout cela dans votre fichierapp/AppKernel.php.

Un domaine spécial : validators

Vous avez peut-être essayé de traduire les messages que vous affichez lors d'erreurs à la soumission de formulaires, et avez remarqué que vous ne pouviez pas les traduire comme tout le reste.

Pourtant, les messages d'erreur fournis par le framework étaient traduits, eux !

Oui, mais c'est parce que Symfony n'utilise pas le domaine « messages » pour traduire les messages d'erreur des formulaires. Le framework est prévu pour travailler avec le domaine « validators » dans ce contexte des messages de d'erreur de formulaires. Il vous suffit alors de placer vos traductions dans ce domaine (dans le fichiervalidators.fr.ymlpar exemple), et ce dans le bundle de votre choix comme nous venons de le voir.

Nous reviendrons sur ce domaine spécial un peu plus loin.

Traductions dépendantes de variables

La traduction d'un texte n'est pas quelque chose d'automatique. En effet, toutes les langues ne se ressemblent pas, et il peut y avoir des différences qui ont des conséquences importantes sur notre façon de gérer les traductions.

Prenons deux exemples qui vont vous faire comprendre tout de suite :

  • En français, le point d'exclamation est précédé d'un espace, alors qu'en anglais non. Du coup, le «Hello {{ name }}!» que l'on a dans notre vue n'est pas bon, car sa traduction devient «Bonjour {{ name }}! », sans espace avant le point d'exclamation. La traduction n'est pas correcte !

  • En anglais comme en français, mettre au pluriel un nom ne se limite pas toujours à rajouter un « s » à la fin. Comment faire unifdans notre vue pour prendre cela en compte ?

Le composant de traduction a tout prévu, ne vous inquiétez pas et regardons cela tout de suite. ;)

Les placeholders

Pour mettre mon espace devant le point d'exclamation français, est-ce que je dois ajouter dans le catalogue la traduction de «!» en «!» ?

Bien tenté, mais il y a heureusement une meilleure solution !

La solution apportée par Symfony est relativement simple : on va utiliser un placeholder, sorte de paramètre dans une chaîne cible. Cela va nous permettre de régler ce problème d'espacement. Rajoutez ceci dans vos catalogues français et anglais :

# src/OC/PlatformBundle/Resources/translations/messages.fr.yml

hello: Bonjour %name% !

Et

# src/OC/PlatformBundle/Resources/translations/messages.en.yml

hello: Hello %name%!

Nous avons mis un placeholder nommé%name%dans chacune des traductions anglaise et française. La valeur de ce placeholder sera spécifiée lors du rendu de la vue, ce qui permet de traduire la phrase complète. Cela évite de découper les traductions avec une partie avant la variable et une partie après la variable, et heureusement lorsque vous avez plusieurs variables dans une même phrase !

Bien entendu il faut adapter un peu notre vue, voici comment passer la valeur du placeholder de la vue au traducteur :

{# src/OC/PlatformBundle/Resources/views/Advert/translation.html.twig #}

{{ 'hello'|trans({'%name%': name}) }}

Le premier paramètre donné ici au filtretransest un tableau, dont les index sont les placeholders avec les caractères%qui le délimitent, et les valeurs, celles par lesquelles le placeholder sera remplacé dans la chaîne cible. Nous venons de dire à Symfony que « quand tu traduis la chaîne source "hello", tu vas remplacer%name%qui se trouve dans la chaîne cible par le contenu de la variablename», qui contient ici le nom de l'utilisateur.

Testez donc l'affichage de cette page en français, puis en anglais. Le point d’exclamation est bien précédé d'un espace en français, mais pas en anglais, et le nom d'utilisateur s'affiche toujours !

Parce qu'on n'utilise pas toujours le filtre, voici les syntaxes pour toutes les possibilités d'utilisation :

  • Balise :{% trans with {'%name%': name} %}hello{% endtrans %}.

  • Filtre :{{ 'hello'|trans({'%name%': name}) }}.

  • Service :$translator->trans('hello', array('%name%' => $name)).

Et dans le cas où le paramètre a une valeur fixe dans telle vue, vous pouvez bien évidemment utiliser du texte brut à la place du nom de la variablename, comme ceci :

{{ 'hello'|trans({'%name%': 'moi-même'}) }}
Les placeholders dans le domaine validators

Les messages d'erreur de formulaires, qui sont donc dans le domaine validators, peuvent contenir des nombres, principalement quand on spécifie des contraintes de longueur. Ces nombres, il faut bien les afficher à l'utilisateur. Pour cela, vous allez me dire qu'il faut utiliser les placeholders.

Raté ! Ce n'est pas du tout comme cela qu'il faut faire dans ce cas. Rassurez-vous, ce n'est que l'exception qui confirme la règle.

Donc dans le cas des messages d'erreur générés par le composantValidator, et uniquement dans ce cas, il ne faut pas utiliser les placeholders, mais une syntaxe propre à la validation. Cette syntaxe est la même que celle de Twig en fait :{{ limit }}.

Prenons le cas où vous avez utilisé la contrainteLength, vous avez envie de mentionner le nombre limite de caractères (que ce soit le maximum ou le minimum) et le nombre de caractères entrés par l'utilisateur. Ces valeurs sont fournies par le service de validation, dans les variableslimitetvaluerespectivement. Ce n'est donc pas%limit%qu'il faut utiliser dans votre traduction, mais{{ limit }}, comme ceci :

# src/OC/PlatformBundle/Resources/translations/validators.fr.yml

password:
  length:
    short: "Vous avez entré {{ value }} caractères. Or, le mot de passe ne peut en comporter moins de {{ limit }}"
    long:  "Vous avez entré {{ value }} caractères. Or, le mot de passe ne peut en comporter plus de {{ limit }}"

La raison de cette exception est que le validateur n'envoie pas les valeurs de ces variables au traducteur, il les garde pour lui et fait la substitution après le retour de la chaîne traduite par le traducteur. Pensez-y !

Gestion des pluriels

On va maintenant essayer d'afficher (et y réussir !) le nombre d'annonces correspondant à une catégorie, sous la forme « Il y a (nombre) annonces ». Il peut y en avoir une seule ou plusieurs, et comme on veut faire les choses bien, il faut que cela affiche « Il y a 1 annonce » et « Il y a (plus d'une) annonces », avec le « s » qui apparaît quand le nombre d'annonces dépasse 1.

Si vous deviez le faire tout de suite, vous feriez sûrement un petit test dans la vue pour choisir quelle chaîne traduire, dans ce style-là :

{# Dans une vue #}
{# Attention, ceci est un mauvais exemple, à ne pas utiliser ! #}

{% if nombre <= 1 %}
  {{ 'advert.nombre.singulier'|trans({'%count%': nombre}) }}
{% else %}
  {{ 'advert.nombre.pluriel'|trans({'%count%': nombre}) }}
{% endif %}

Avec le catalogue associé :

# src/OC/PlatformBundle/Resources/translations/messages.fr.yml
# Attention, ceci est un mauvais exemple, à ne pas utiliser !

advert:
    nombre:
        singulier: Il y a %count% annonce
        pluriel:   Il y a %count% annonces

Eh bien, votre intention est louable, mais une fois de plus, les concepteurs de Twig et de Symfony ont déjà réfléchi à cela et ont tout prévu ! La nouvelle balise/filtre/fonction à utiliser s'appelletranschoice, et elle s'utilise avec en argument le nombre sur lequel faire la condition, voyez par vous-mêmes :

Le filtre :

{{ 'advert.nombre'|transchoice(nombre) }}

La balise :

{% transchoice nombre %}advert.nombre{% endtranschoice %}

Le service :

<?php

$translator->transchoice('advert.nombre', $nombre);

Le catalogue, quant à lui, contient donc les deux syntaxes dans une même chaîne source. Voici la syntaxe particulière à adopter :

# src/OC/PlatformBundle/Resources/translations/messages.fr.yml

advert:
    nombre: "[0,1]Il y a %count% annonce|[2,+Inf]Il y a %count% annonces"

Avec cette syntaxe, Symfony pourra savoir que la première partie est pour 0 ou 1 annonce, et la seconde pour 2 ou plus. Je ne m'attarderai pas dessus, la documentation officielle est là si vous voulez absolument plus d'informations.

Afficher des dates au format local

J'affiche souvent des dates, et j'aimerais avoir les noms des jours/mois, mais comment les traduire ?

Pour afficher les dates sous la forme « vendredi 11 janvier 2016 », vous avez sûrement déjà utilisé le code{{ date|date('l j F Y') }}. Malheureusement, l'objetDatede PHP n'est pas très bon en langues… et quelque soit votre locale, les noms de jours et de mois sont en anglais. D'ailleurs, ils le sont même sur la page de la documentation en français.

Je vous rassure tout de suite : il est bien possible de traduire ces dates ! Dans nos vues Twig, il va falloir pour cela utiliser le filtrelocalizeddateà la place de justedate. Son utilisation est la suivante :

{{ date|localizeddate(dateFormat, timeFormat, locale) }}

Les paramètres qu'on lui passe sont les suivants :

  1. dateFormat: le format pour la date ;

  2. timeFormat: le format pour l'heure ;

  3. locale: la locale dans laquelle afficher la date formatée. Pas besoin de la spécifier, elle est fournie dans le contexte.

Mais pourquoi séparer les formats de date et d'heure ?

Voilà, c'était trop beau pour être vrai, on ne peut pas utiliser la syntaxe habituelle pour le format de date/heure (du moins, pas encore). À la place, on a le choix entre quatre formats :full,long,mediumetshort, pour l'heure comme pour la date, correspondant aux affichages donnés dans le tableau suivant. Il n'est pas possible de les modifier, mais il est en revanche possible de les combiner (donc avoir la datelonget l'heureshort, par exemple). À défaut de pouvoir faire exactement comme vous voulez, vous avez au moins les mois et les jours traduits correctement, et dans un format tout de même convenable. ;)

Format

Date

Heure

full

jeudi 15 novembre 2016

14:22:15 Heure normale de l’Europe centrale

long

15 novembre 2016

14:22:15 HNEC

medium

15 nov. 2016

14:22:15

short

15/11/16

14:22

none

(rien)

(rien)

J'ai précisé ici en dur la locale, mais dans votre code ne la mettez pas : elle est automatiquement définie à la locale courante. Votre utilisation sera ainsi aisée :

Aujourd'hui nous sommes le {{ 'now'|localizeddate('full', 'none') }} et il est {{ 'now'|localizeddate('none', 'short') }}

Et si vous l'exécutez en même temps que j'écris ces lignes (ce qui me paraît impossible…), vous obtiendrez :

Citation : Résultat

Aujourd'hui nous sommes le lundi 14 janvier 2016 et il est 20:02

Attention, si vous rencontrez l'erreur suivante : «The filter "localizeddate" does not exist in ...», c'est que vous n'avez pas encore activé l'extension Twig qui fournit ce filtre.

L'extension ne se trouve pas dans le coeur de Twig, il vous faut donc d'abord ajouter la bibliothèquetwig/extensions dans votrecomposer.json, puis faire uncomposer update :

// composer.json

"require": {
    // ...
    "twig/extensions": "~1.3"
},

Puis, il faut activer l'extension, en rajoutant simplement cette définition de service dans votre fichier de configuration, cela fonctionne exactement comme l'extension Twig qu'on a faite il y a quelques chapitres :

# app/config/config.yml

# …

# Activation de l'extension Twig intl
services:
  twig.extension.intl:
    class: Twig_Extensions_Extension_Intl
    tags:
      - { name: twig.extension }

Pour conclure

Voici pour terminer un petit récapitulatif des différentes syntaxes complètes, sachant que la plupart des arguments sont facultatifs.

Les balises :

{# Texte simple #}
{% trans with {'%placeholder%': placeholderValue} from 'domaine' into locale %}maChaîne{% endtrans %}

{# Texte avec gestion de pluriels #}
{% transchoice count with {'%placeholder%': placeholderValue} from 'domaine' into locale %}maChaîne{% endtranschoice %}

Les filtres :

{# Texte simple #}
{{ 'maChaîne'|trans({'%placeholder%': placeholderValue}, 'domaine', locale) }}

{# Texte avec gestion de pluriels #}
{{ 'maChaîne'|transchoice (count,  {'%placeholder%': placeholderValue}, 'domaine', locale) }}

Les méthodes du service :

<?php
$translator = $this->get('translator'); // depuis un contrôleur

// Texte simple
$translator->trans('maChaîne',  array('%placeholder%' => $placeholderValue) , 'domaine', $locale);

// Texte avec gestion de pluriels
$translator->transchoice($count, 'maChaîne',  array('%placeholder%' => $placeholderValue) , 'domaine', $locale)

Vous savez maintenant comment créer les traductions dans les différentes langues que vous souhaitez gérer sur votre site !

En résumé

  • La méthodologie d'une traduction est la suivante :

    • Détermination du texte à traduire : cela se fait grâce à la balise et au filtre Twig, ou directement grâce au service translator ;

    • Détermination de la langue cible : cela s'obtient avec la locale, que Symfony définit soit à partir de l'URL, soit à partir des préférences de l'internaute.

  • Traduction à l'aide d'un dictionnaire : cela correspond aux catalogues dans Symfony ;

  • Il existe plusieurs formats possibles pour les catalogues, le YAML étant le plus simple ;

  • Il existe différentes méthodes pour bien organiser ses catalogues, pensez-y !

  • Il est possible de faire varier les traductions en fonction de paramètres et/ou de pluriels.

  • Le code du cours tel qu'il doit être à ce stade est disponible sur la branche iteration-19 du dépot Github.

Exemple de certificat de réussite
Exemple de certificat de réussite