Définir la locale
Le langage d’un site
Symfony est constitué de composants. Ceux-ci savent comment effectuer des appels de traductions si celle-ci est active. On sait donc facilement traiter une langue.
Par contre, comment déterminer laquelle utiliser ? Les composants ne fournissent pas d’outil pour le faire automatiquement. En effet, considérez ces exemples :
- http://tech.sensiolabs.com/
- http://tech.sensiolabs.com/fr/
- http://tech.sensiolabs.fr/
- http://fr.tech.sensiolabs.com/
- http://tech.sensiolabs.com/welcome
- http://tech.sensiolabs.com/bienvenue
Il existe beaucoup de possibilités pour définir la langue en fonction d’une URL, mais il existe d’autres indicateurs. Le
navigateur stocke un choix de langue et envoie un en-tête Accept-Language
qui est une liste de langues. Souvent les
sites proposent une liste de langues, sous forme de drapeau ou de menu déroulant, qui change la langue le temps d’une
session. L’utilisateur peut avoir un compte et avoir sélectionné une langue préférée qui sera utilisée à sa connexion.
Bref, beaucoup de combinaisons possibles en fonction des éléments de votre site. Donc impossible de faire une sorte de résolveur automatique qui fonctionnerait selon vos désirs.
Une solution
Un écouteur
Pour votre site multilingue, vous avez besoin d’un écouteur qui se chargera de résoudre la langue en fonction des possibilités de fonctionnalité de votre site.
Dans notre exemple, nous allons juste proposer aux utilisateurs trois langues et déterminer celle à utiliser en fonction du navigateur.
Préparation
Tout d’abord il faut faire un petit tour dans le fichier config.yml
afin de :
- définir une langue par défaut,
en
- définir la liste des langues qui sont disponible,
en
,fr
etes
- décommenter la ligne
translator
afin d’activer les capacités de traduction du framework
parameters:
locale: en
accepted_locales: [en, fr, es]
framework:
translator: { fallbacks: ["%locale%"] }
default_locale: "%locale%"
On ajoute, dans un template, une clé qui sera traduite :
<p>{{ 'article.welcome' | trans }}</p>
Il faut ensuite créer des fichiers de traduction. Ceux-ci peuvent être généré en ligne de commande, par exemple :
bin/console translation:update --output-format yml --force en AppBundle
Et voilà, il ne reste plus qu’à définir la langue dans la requête.
Mise en place
Voici l’écouteur :
<?php
namespace AppBundle\Locale;
// [...]
class RequestLocaleListener implements EventSubscriberInterface
{
const SESSION_LOCALE = 'locale';
/** @var string */
protected $defaultLocale;
/** @var string[] */
protected $acceptedLocales;
/**
* @param string $defaultLocale
* @param string[] $acceptedLocales
*/
public function __construct($defaultLocale, $acceptedLocales)
{
$this->defaultLocale = $defaultLocale;
$this->acceptedLocales = $acceptedLocales;
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents()
{
return [
KernelEvents::REQUEST => ['onKernelRequest', 15],
];
}
/**
* Determines and sets the locale for the request.
*
* @param GetResponseEvent $event
*/
public function onKernelRequest(GetResponseEvent $event)
{
$request = $event->getRequest();
// Avoid sub-request locale changes
if (!$event->isMasterRequest()) {
return;
}
$locale = $this->defaultLocale;
$session = $request->getSession();
if (null !== $session && $session->has(self::SESSION_LOCALE)) {
$locale = $session->get(self::SESSION_LOCALE);
} else {
// Select the best language based on browser
$localeBrowser = $request->getPreferredLanguage($this->acceptedLocales);
if (!empty($localeBrowser)) {
$locale = $localeBrowser;
}
$session->set(self::SESSION_LOCALE, $locale);
}
$request->setLocale($locale);
$request->attributes->set('_locale', $locale);
}
}
Comme indiqué ci-dessus, c’est le navigateur qui nous informe des les langues de l’utilisateur dans l’en-tête
Accept-Language
. Voici un exemple de valeur possible : fr-FR,fr;q=0.7,en;q=0.3
. Ici c’est le français qui est
prioritaire, puis l’anglais. La valeur q
est un poids représentant la priorité de la langue.
L’objet Request
possède une méthode getPreferredLanguage()
bien pratique qui vous permet, à partir d’une liste de
langues disponibles, la langue correspondant le mieux aux choix mentionnés dans l’en-tête Accept-Language
. Donc inutile
d’analyser vous même cet en-tête.
On peut constater que l’on utilise la session comme cache pour éviter de fouiller l’en-tête en permanence.
Et voilà, le tour est joué. Bien sûr, vous pouvez aller plus loin en complexifiant l’écouteur pour qu’il détermine la langue en fonction d’autres paramètres tel qu’un segment de l’URL ou un attribut de la classe de votre utilisateur.
Pour aller plus loin
Vous pouvez retrouver un autre exemple dans la documentation officielle de Symfony.
Il existe plusieurs bundles ou des extensions qui peuvent vous accompagner dans la mise en place de l’i18n de votre site :
- JMSI18nRoutingBundle
- BeSimpleI18nRoutingBundle
- Translatable des extensions Gedmo de Doctrine
Commentaires
Licence
Les publications de ce site sont mises à disposition selon les termes de la Licence Creative Commons Attribution - by-nc-nd.