Live Coding : Créer un site multilingue avec Symfony 4

Par Nouvelle-Techno.fr le 31 janvier 2020 - Catégories : Tutoriel Symfony Live-Coding

Lire l'article sur le site d'origine

Il arrive parfois d'avoir à mettre en place un site dans plusieurs langues, au niveau de son interface utilisateur.

Symfony nous propose des outils pour mettre en place cette "localisation", également appelée "Internationalization" ou "i18n" (en référence au nombre de caractères entre i et n).

Nous allons voir comment utiliser ces outils.

ATTENTION : nous n'allons pas traiter de la traduction du contenu de la base de données, traîtée par l'extension Doctrine Translatable.

Symfony utilisera le service de traduction (Translation service) pour rechercher une traduction pour les chaînes identifiées. Si il trouve une traduction, il remplacera la chaîne par sa traduction.

Information : il sera toujours préférable d'écrire le site en langue anglaise et de le traduire vers les autres langues.

Installer le service

Le service de traduction s'installera au moyen de Composer. Il est déjà installé par défaut dans les configurations basées sur "website-skeleton".

La commande ci-dessous permettra de l'installer

composer require symfony/translation

Une fois installé, le service sera configuré par le fichier "config/packages/translation.yaml" qui contient le code ci-dessous par défaut

# config/packages/translation.yaml
framework:
    default_locale: 'en'
    translator:
        default_path: '%kernel.project_dir%/translations'

L'option "default_locale" permet de définir la langue par défaut du site. Nous allons configurer le Français en indiquant "fr".

L'option "default_path" indique le chemin vers le dossier contenant les traductions.

Utiliser le service

Une fois le service de traduction installé, nous allons pouvoir l'utiliser. Il est possible de traduire des chaînes directement dans les contrôleurs, mais également dans les vues.

Dans les contrôleurs

Si vous devez envoyer une chaîne à traduire depuis un contrôleur, comme dans un message flash par exemple, il est possible de le faire en utilisant les instructions suivantes dans le contrôleur

use Symfony\Contracts\Translation\TranslatorInterface;

public function index(TranslatorInterface $translator)
{
    $message = $translator->trans('Your comment is pending approval');

    // ...
}

Dans les vues

Nous devrons parfois traduire des chaînes directement dans les vues (fichiers Twig) afin de personnaliser l'interface en fonction de la langue de l'utilisateur.

Par exemple, si nous souhaitons traduire la section suivante de nos vues

<h2>Commentaires ({{ article.commentaires|length }})</h2>
{% for commentaire in article.commentaires %}
    <p>Publié par {{commentaire.pseudo }}</p>
    <div>{{ commentaire.contenu }}</div>
{% else %}
    <p>Il n'y a pas encore de commentaire</p>
{% endfor %}

Nous aurons à traduire les expressions "Commentaires", "Publié par" et "Il n'y a pas encore de commentaire"

Nous allons déclarer des chaînes traductibles dans les vues, comme ci-dessous

{% trans %}Chaîne à traduire{% endtrans %}

{# ou #}

{{ 'Chaîne à traduire'|trans }}

Nous allons utiliser les deux dans notre exemple. Ce qui donnera

<h2>{% trans %}Comments{% endtrans %} ({{ article.commentaires|length }})</h2>
{% for commentaire in article.commentaires %}
    <p>{% trans %}Published by{% endtrans %} {{commentaire.pseudo }}</p>
    <div>{{ commentaire.contenu }}</div>
{% else %}
    <p>{% trans %}There's no comment yet{% endtrans %}</p>
{% endfor %}

Créer le fichiers de traduction

Nous allons enfin créer le fichier de traduction qui contiendra les chaînes en anglais et leur traduction dans la langue de notre choix.

Création manuelle

Le fichier sera à créer en utilisant le code de la langue sur 2 caractères (à l'emplacement de xx), comme fr pour le français.

On créera donc autant de fichiers que de langues, sous le format "translation/messages.xx.xlf"

Pour traduire notre message flash vu précédemment, nous inscrirons donc ceci dans le fichier

<?xml version="1.0" encoding="utf-8"?>
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
  <file source-language="en" target-language="fr" datatype="plaintext" original="file.ext">
    <header>
      <tool tool-id="symfony" tool-name="Symfony"/>
    </header>
    <body>
      <trans-unit id="2lTOrMA" resname="Article published successfully">
        <source>Article published successfully</source>
        <target>Article publié avec succès</target>
      </trans-unit>
    </body>
  </file>
</xliff>

Ce fichier n'est pas simple, la partie ci-dessous explique comment l'automatiser.

Création automatique

Il existe une commande permettant de créer automatiquement le fichier de traduction pour la langue de notre choix.

Cette commande va parcourir tous les fichiers et intégrer toutes les chaines dans le fichier correspondant à la langue choisie.

ATTENTION : cette commande ne traduit pas le contenu

php bin/console translation:update --force fr

Le fichier xlf sera créé automatiquement.

Mettre en place le choix de la langue

Pour permettre aux utilisateurs de choisir la langue dans laquelle le site doit s'afficher, nous allons devoir stocker la langue choisie dans la session.

Pour commencer, nous allons créer une variable d'environnement qui contiendra la liste des langues souhaitée.

Cette variable sera déclarée dans "config/services.yaml" de cette façon

parameters:
    # ici vos autres variables
    app.locales: [en, fr]

Nous allons également déclarer cette variable globalement dans les fichiers Twig afin d'y accéder depuis tous les fichiers. Nous l'appellerons "locales" et l'intégrons dans "config/packages/twig.yaml"

twig:
    default_path: '%kernel.project_dir%/templates'
    debug: '%kernel.debug%'
    strict_variables: '%kernel.debug%'
    # On déclare ci-dessous le nom de la variable globale
    globals:
        locales: '%app.locales%'

On crée maintenant une méthode dans un contrôleur qui permettra de changer la langue.

Cette méthode contiendra le code suivant

/**
 * @Route("/change_locale/{locale}", name="change_locale")
 */
public function changeLocale($locale, Request $request)
{
    // On stocke la langue dans la session
    $request->getSession()->set('_locale', $locale);

    // On revient sur la page précédente
    return $this->redirect($request->headers->get('referer'));
}

Nous appellerons cette méthode depuis notre menu ou tout fichier Twig dans lequel nous donnerons le choix de la langue aux utilisateurs.

{% for locale in locales if locale != app.request.locale %}
    <a href="{{ path('change_locale', {'locale': locale}) }}">{{ locale }}</a>         
{% endfor %}

Et voilà, la traduction est en place.

Intercepter les requêtes

Afin de gérer les changements de langues dans le coeur de Symfony (Kernel), nous allons nous attacher à chacune des requêtes en créant un souscripteur d'évènements (EventSubscriber).

Pour ce faire, nous allons entrer la commande suivante

php bin/console make:subscriber LocaleSubscriber

A la question de l'évènement à utiliser, entrer

Symfony\Component\HttpKernel\Event\RequestEvent

Le fichier "LocaleSubscriber.php" est créé dans le dossier "src/EventSubscriber"

Nous allons remplacer son code par celui-ci

<?php

namespace App\EventSubscriber;

use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpKernel\KernelEvents;

class LocaleSubscriber implements EventSubscriberInterface
{
    // Langue par défaut
    private $defaultLocale;

    public function __construct($defaultLocale = 'en')
    {
        $this->defaultLocale = $defaultLocale;
    }

    public function onKernelRequest(RequestEvent $event)
    {
        $request = $event->getRequest();
        if (!$request->hasPreviousSession()) {
            return;
        }

        // On vérifie si la langue est passée en paramètre de l'URL
        if ($locale = $request->query->get('_locale')) {
            $request->setLocale($locale);
        } else {
            // Sinon on utilise celle de la session
            $request->setLocale($request->getSession()->get('_locale', $this->defaultLocale));
        }
    }

    public static function getSubscribedEvents()
    {
        return [
            // On doit définir une priorité élevée
            KernelEvents::REQUEST => [['onKernelRequest', 20]],
        ];
    }
}

Voilà qui termine ce tutoriel.

Obtenir de l'aide

Pour obtenir de l'aide, vous pouvez accéder au serveur Discord pour une entraide par chat

#Tutoriel #Controllers #MVC #Views #Symfony #Live-Coding