Internationalisation du site avec Symfony
Dans ce chapitre, nous allons voir comment rendre le site international afin d'afficher les textes en Français et Anglais.
La culture des utilisateurs
Chaque visiteur sur un site Symfony créé une session sur le site. Cette session contient un objet utilisateur qui est accessible à n'importe quel moment avec la commande:
Chaque utilisateur dispose de sa culture propre, la langue qu'il parle.
Comme par exemple: en_US ou fr ou encore fr_BE.
Pour connaître la culture d'un utilisateur ou lui assigner une nouvelle culture, il suffit d'utiliser l'accesseur et mutateur suivants:
|
$this->getUser()->setCulture('fr_FR');
$culture = $this->getUser()->getCulture(); |
La culture par défaut est quant à elle définie dans le fichier de configuration
apps/frontend/config/settings.yml:
|
all:
.settings:
default_culture: fr |
Le module i18n dans Symfony
Pour activer le module i18n dans Symfony, il suffit d'ajouter les paramètres suivants dans le fichier de configuration
apps/frontend/config/settings.yml
|
i18n: on
standard_helpers: [Partial, Cache, I18N] |
Le premier paramètre va activer l'i18n et le deuxième permettra de charger les helpers de templates "Partial", "Cache" et "I18N" par défaut.
Vous n'aurez pas à déclarer ces helpers en haut de vos templates pour les utiliser.
Enregistrez, quittez puis supprimez le cache symfony
Le module i18n va nous permettre de créer des dictionnaire multi lingue pour traduire nos textes et afficher les bons en fonction de la culture de l'utilisateur.
Mise en place du changement de langue
Nous allons maintenant mettre en place une fonctionnalité en bas de page du site permettant de choisir sa langue. Le tutorial Jobeet explique parfaitement ce point, je vais donc effectuer qu'une simple traduction.
Un plugin "sfFormExtraPlugin" permet de mettre en place un select pour choisir sa langue.
Nous allons donc l'installer:
symfony plugin:install sfFormExtraPlugin |
Une fois installé, supprimez le cache:
Maintenant que le plugin est fonctionnel, nous allons ajouté notre "select" en bas de page, dans le layout du site (nous serons amené à le bouger plus tard).
Ouvrez le layout:
apps/frontend/templates/layout.php
Nous aurions pu le modifier ainsi:
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<?php include_http_metas() ?>
<?php include_metas() ?>
<?php include_title() ?>
<link rel="shortcut icon" href="/favicon.ico" />
</head>
<body>
<?php echo $sf_content ?>
<?php $form = new sfFormLanguage(
$sf_user,
array('languages' => array('en', 'fr'))
)
?>
<form action="<?php echo url_for('@change_language') ?>">
<?php echo $form ?><input type="submit" value="ok" />
</form>
</body>
</html> |
MAIS, comme nous l'avons vu précédemment, il faut limiter au maximum le PHP directement dans les templates, d'autant plus que nous pourrions avoir à l'afficher ailleurs.
La meilleure solution: Un composant (component).
Un composant est rattaché à un module, comme nous n'avons aucun module dédié aux langues, nous allons créer un nouveau module "language":
symfony generate:module frontend language |
Un composant comporte un fichier action spécial:
actions/components.class.php
Nous allons donc créer le fichier
apps/frontend/modules/language/actions/components.class.php:
|
<?php
class languageComponents extends sfComponents
{
public function executeLanguage(sfWebRequest $request)
{
$this->form = new sfFormLanguage(
$this->getUser(),
array('languages' => array('en', 'fr'))
);
}
}
?> |
Comme pour les fichiers actions, les fichiers composants doivent étendre une classe spéciale: sfComponents.
Et comme je vous l'ai expliqué dans la partie sur les templates, un composant dispose d'un template précédé par un underscore "_".
Nous allons créer le fichier
apps/frontend/modules/language/templates/_language.php
|
<form action="<?php echo url_for('@change_language') ?>">
<?php echo $form ?><input type="submit" value="ok" />
</form> |
Comme vous pouvez le constater vous-même, nous avons utilisé une nouvelle route dans le formulaire:
@change_language. Il faut donc l'ajouter dans le fichier routing.yml (
apps/frontend/config/routing.yml):
|
change_language:
url: /change_language
param: { module: language, action: changeLanguage } |
Cette route va appeler l'action changeLanguage du module "language".
Créons cette action, ouvrez le fichier
apps/frontend/modules/language/actions/actions.class.php, supprimez la méthode executeIndex, inutile, puis ajoutez l'action:
|
<?php
class languageActions extends sfActions
{
public function executeChangeLanguage(sfWebRequest $request)
{
$form = new sfFormLanguage(
$this->getUser(),
array('languages' => array('en', 'fr'))
);
$form->process($request);
return $this->redirect('@localized_homepage');
}
}
?> |
Là encore, nous allons utiliser une nouvelle route "@localized_homepage" lors de la redirection du choix d'une langue.
Cette route va permettre d'ajouter la langue dans l'URL.
Ouvrez donc le fichier routing.yml (
apps/frontend/config/routing.yml):
|
localized_homepage:
url: /:sf_culture/
param: { module: main, action: index }
requirements:
sf_culture: (?:fr|en) |
La route se complique un peu, :sf_culture est une variable qui contient la culture de notre utilisateur. L'URL sera donc /fr/ ou /en/ suivant la langue choisie.
Pour que la route s'applique, il faut que les langues soient "fr" ou "en".
Il ne nous reste plus qu'à rajouter le select dans notre layout pour tester. (
apps/frontend/templates/layout.php)
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.o$
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<?php include_http_metas() ?>
<?php include_metas() ?>
<?php include_title() ?>
<link rel="shortcut icon" href="/favicon.ico" />
</head>
<body>
<?php echo $sf_content ?>
<?php include_component('language', 'language') ?>
</body>
</html> |
Et voilà le résultat:
Les librairies de traduction Symfony
Passons maintenant aux choses sérieuses.
Nous allons utiliser un helper i18n sur tous nos textes statiques afin qu'ils puissent être traduit dynamiquement suivant la langue du visiteur.
|
<?php echo __("Mon texte à traduire"); ?> |
Il faudra systématiquement utiliser cet helper sur vos textes statiques. C'est la raison pour laquelle je vous parle de l'internationalisation dès le début du tutorial, afin que nous n'ayons pas à repasser sur toutes les chaînes plus tard.
Tous ces textes mis dans le helper
__() seront automatiquement chargés dans un fichier de langue grâce à une commande d'extraction:
symfony i18n:extract frontend fr --auto-save |
En utilisant cette commande et en spécifiant "fr", cela signifie que nous allons créer un dictionnaire de phrases à traduire en français.
Ce qui signifie que l'on devra écrire tous nos textes statiques en anglais.
Il est bien sûr possible de faire le contraire !
Faisons un petit test !
Modifiez le template index du module main (apps/frontend/modules/main/templates/indexSuccess.php):
|
<h2><?php echo $helloWorld; ?></h2>
<p><?php echo __('How are you doing today ?'); ?></p> |
Rafraichissez votre page, vous devriez obtenir quelque chose comme ceci:
Quelque soit la langue, le même texte est affiché. C'est normal, nous n'avons pas encore fait la traduction. Si Symfony ne trouve pas une correspondance dans la langue, il affiche celle par défaut.
symfony i18n:extract frontend fr --auto-save |
|
>> i18n extracting i18n strings for the "frontend" application
>> i18n found "1" new i18n strings
>> i18n found "0" old i18n strings
>> i18n saving new i18n strings |
1 nouvelle chaine à traduire a été détectée. Nous pouvons à présent la traduire.
Les fichiers langues se trouvent dans le répertoire
apps/frontend/i18n/LANGUE
Dans notre cas, nous allons modifier le fichier apps/frontend/i18n/fr/messages.xml:
Voici le texte que vous devriez avoir pour le moment:
|
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE xliff PUBLIC "-//XLIFF//DTD XLIFF//EN" "http://www.oasis-open.org/committees/xliff/documents/xliff.dtd">
<xliff version="1.0">
<file source-language="EN" target-language="fr" datatype="plaintext" original="messages" date="2009-08-11T20:03:44Z" product-name="messages">
<header/>
<body>
<trans-unit id="1">
<source>How are you doing today ?</source>
<target/>
</trans-unit>
</body>
</file>
</xliff> |
Dans les balises
source se trouvent le texte à traduire, il faut ensuite mettre la correspondance dans la balise
target actuellement fermée.
|
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE xliff PUBLIC "-//XLIFF//DTD XLIFF//EN" "http://www.oasis-open.org/committees/xliff/documents/xliff.dtd">
<xliff version="1.0">
<file source-language="EN" target-language="fr" datatype="plaintext" original="messages" date="2009-08-11T20:03:44Z" product-name="messages">
<header/>
<body>
<trans-unit id="1">
<source>How are you doing today ?</source>
<target>Comment vas-tu aujourd'hui ?</target>
</trans-unit>
</body>
</file>
</xliff> |
Sauvegardez puis quittez le fichier.
Comme lorsque l'on modifie un fichier de configuration, nous devons supprimer le cache:
Et maintenant testez:
Le texte en français s'affiche correctement lorsque l'on sélectionne la langue française !
Vous voyez, ce n'est pas si compliqué finalement !
Migration sous Symfony 1.4
Si vous utilisez les version 1.3 ou 1.4 de Symfony, vous aurez sans doute une erreur du type:
500 | Internal Server Error | InvalidArgumentException
sfValidatorI18nChoiceLanguage does not support the following options: 'culture'.
Pour la corriger, il faut modifier le plugin sfFormExtraPlugin ainsi:
Modifiez
plugins/sfFormExtraPlugin/lib/form/sfFormLanguage.class.php:
|
public function configure()
{
$this->setValidators(array(
'language' => new sfValidatorI18nChoiceLanguage(array
('languages' => $this->options['languages'])),
));
/*
$this->setValidators(array(
'language' => new sfValidatorI18nChoiceLanguage(array('culture' => $this->user->getCulture(), 'languages' => $this->options['languages'])),
));
*/
$this->setWidgets(array(
'language' => new sfWidgetFormI18nChoiceLanguage(array('culture' => $this->user->getCulture(), 'languages' => $this->options['languages'])),
));
} |