SALT > Manuel > Tutoriel > Affichage >
La classe I18n permet d'afficher les textes d'une application dans la langue de l'utilisateur.

Cette classe est inspirée du projet php-i18n.
Les textes sont écrits dans un fichier au format YAML puis ce fichier est transformé automatiquement en classe où chaque entrée est une constante.

Lors de l'utilisation, dans le meilleur des cas, l'affichage d'un texte ne nécessitera que le temps d'accès à une constante de classe.

Concepts

Ce qu'on appelle "locale" dans la suite est en réalité un "language-tag" tel que décrit ici : https://www.w3.org/International/articles/language-tags/.
C'est une chaîne de caractères identifiant la langue de l'utilisateur. Elle est en général de la forme : langue[-region].

La constante salt\I18N_DEFAULT_LOCALE contient la locale par défaut et vaut en.
alertLe contenu de cette constante est fixe et ne peut pas être surchargé pour une application.
La locale par défaut détermine quelle classe sera la classe mère de toutes les classes générées par I18n, on ne peut donc pas permettre à une application utilisant le framework de la surcharger car on pourrait avoir deux applications avec des valeurs différentes, ce qui pourrait engendrer une classe A ayant pour classe parente B, et une classe B ayant pour classe parente A, ce qui est impossible.

Si on souhaite tout de même modifier la locale par défaut, il faut la changer dans le fichier conf/config.php du framework SALT, et elle s'appliquera à toutes les applications.

Les fichiers de langues correspondants (de SALT et de votre application) doivent contenir toutes les entrées référencées dans SALT ou l'application.
Il y a une instance de classe I18n par "application" ou par "module" si votre application est modulaire.
Par exemple, une application utilisant SALT a en général une instance I18n pour le framework SALT et une instance pour elle-même.

Chaque instance permet de définir : Pour chaque locale, on va générer une classe de même nom que la locale. Pour une application, on aura l'héritage des classes générées suivants :
Locale initialisée Héritage
A A extends I18N_DEFAULT_LOCALE
A-B B extends A
A extends I18N_DEFAULT_LOCALE
A-B-C C extends B
B extends A
A extends I18N_DEFAULT_LOCALE
En clair cela veux dire qu'on peut définir toutes les entrées de en.yml et ne redéfinir dans en-gb.yml que les entrées spécifiques à la locale en-gb, les autres entrées étant héritées de la classe mère.
On peut également traduire partiellement une application, les textes manquants seront récupérés depuis la classe mère qui est la locale par défaut.

Configuration

La méthode init() permet d'initialiser UNE SEULE locale. Dans le cas où l'on passe plusieurs locales avec un tableau, on initialisera la 1ère locale disponible uniquement.
A noter que si aucune locale indiquée dans init() n'est disponible, on essayera également d'initialiser la locale par défaut avant d'échouer avec une exception.
Une fois la locale initialisée, on peut :

Utilisation basique

Avec les fichiers suivants :
lang/fr.yml lang/en.yml
text: Test de texte
jours:
  lundi: Lundi
  mardi: Mardi
a:
  b:
   c: D
escape: "Texte avec 2 points :"
francais: FR
text: Text test
jours:
  lundi: Monday
  mardi: Tuesday
a:
  b:
   c: D
escape: "Text with colon :"
english: EN
Pour l'entrée "escape", on doit entourer le texte avec des guillemets ou des apostrophes s'il contient un caractère spécial, comme ":"
Dans les fichiers il y a une erreur (pour l'exemple) : la clé "francais" n'existe pas dans le fichier en.yml qui est le langage par défaut.
Le fait qu'une clé du langage par défaut ("english") n'existe pas dans fr.yml est toléré, mais le contraire non.
<?php
// initialisation
define('salt\I18N_LOCALE''en');
$i18n I18n::getInstance('APP'RELATIVE);
$i18n->init('fr')->alias('T'); // la classe générée sera donc "fr", elle étendra de la classe "en"
                                // et sera accessible via la classe "T"
// utilisation
echo T::text// affiche "Test de texte"
echo T::jours_lundi// affiche "Lundi"
echo T::a_b_c// affiche "D"
echo T::escape// affiche "Texte avec 2 points :"

$en $i18n->init('en'); // récupération ponctuelle d'une autre locale
echo $en::jours_lundi// affiche "Monday"

echo T::francais;     // affiche "FR"
echo T::english;     // affiche "EN" car T correspond à la classe générée à partir de fr.yml,
                    //     mais elle hérite aussi de la classe générée à partir de en.yml qui définit bien cette clé.
echo $en::english;     // affiche "EN"
echo $en::francais// affiche une erreur indiquant que la constante de classe "francais" n'est pas définie
Ici on voit que les clés générées dans la classe utilisent le caractère underscore comme séparateur : La clé "jours_lundi" n'existe pas directement dans le fichier source

Textes avec remplacement

Lorsqu'un texte contient une partie variable, on peut le définir avec un format : text_avec_remplacement: variable1 : %s, variable2 : %05d

<?php
echo T::text_avec_remplacement('var1'3); // affiche : "variable1 : var1, variable2 : 00003"
Les formats sont ceux utilisés par sprintf.

Listes

Lorsque le fichier contient des listes, comme la clé "jours" dans l'exemple au dessus, on peut récupérer directement la clé pour avoir l'ensemble des sous valeurs : <?php
var_dump
(T::jours); // affiche un tableau associatif : ['lundi' => 'Lundi', 'mardi' => 'Mardi']

En cas d'erreur

Si on demande une clé qui n'existe pas, PHP levera une FATAL ERROR de type "Undefined class constant"
Il n'est pas possible avec les versions actuelles de PHP d'intercepter proprement cette erreur dans le framework. Il est donc de la responsabilité de l'utilisateur du framework de faire attention à ce que toutes les clés utilisées dans le code existent bien, au moins dans la locale par défaut.

Performance

Par défaut, à chaque initialisation de locale, le framework vérifie la date de dernière modification du fichier YAML pour déterminer s'il doit ou non regénérer la classe.
Cela se traduit par des appels aux méthodes PHP file_exists et filemtime à chaque page.

Il est possible de s'en passer en modifiant la manière dont l'instance I18n est initialisée : <?php
$i18n 
I18n::getInstance('APP'RELATIVEI18n::MODE_USE_GENERATED);
En faisant cela, on n'appelle plus file_exists et filemtime, on utilise directement les classes générées précédemment sans aucune vérification.
Cependant, à chaque modification d'un fichier YAML, il faudra regénérer manuellement les classes.

Génération

Lorsqu'on utilise le mode I18n::MODE_USE_GENERATED, on doit générer manuellement les classes.
Pour cela on doit exécuter le code suivant : <?php
$i18n 
I18n::getInstance('APP'RELATIVEI18n::MODE_USE_GENERATED);
$i18n->generate(); // va regénérer toutes les classes
alertIl ne faut pas utiliser la méthode generate() sur une page accessible aux utilisateurs normaux.
Idéalement seul l'administrateur du site doit appeler cette méthode, lors d'une mise à jour par exemple.
Dans le cas contraire, la méthode n'étant pas thread-safe, on s'expose à des erreurs en cas d'appels concurrents.
Cette opération étant à faire pour chaque instance I18n, il est également possible de le faire pour l'instance de SALT : <?php
define
('salt\I18N_MODE'I18n::MODE_USE_GENERATED); // a définir tout le temps

define('salt\I18N_GENERATE'TRUE); // a définir une seule fois, ou à chaque mise à jour de SALT, AVANT Salt::config();
Salt::config(); // va lancer la regeneration et arreter l'application.
        // Affiche : "SALT I18n classes generated - exit application. Please remove salt\I18N_GENERATE constant"
La méthode generate() prend un paramètre optionnel $display qui peut être passé à TRUE pour que SALT affiche un compte rendu des opérations effectuées.
Sinon il est possible de récupérer son retour qui sera la liste des locales générées.

Vérification

Il est également possible de demander à SALT de vérifier la cohérence des fichiers de langues avec le code suivant : <?php
$i18n 
I18n::getInstance('APP'RELATIVE);
$i18n->check(); // va vérifier tout les fichiers de langues
// ou pour les fichiers de SALT :
define('salt\I18N_CHECK'TRUE);
Salt::config(); // lance la vérification et arrête l'exécution
La méthode check() prend un paramètre optionnel $display qui peut être passé à TRUE pour que SALT affiche un compte rendu des opérations effectuées.
Sinon il est possible de récupérer son retour qui sera un tableau associatif indexé par les locales et dont la valeur est un tableau contenant 3 clés :

Sécurité

Si le dossier contenant vos fichiers .yml (lang) est dans votre arborescence web, n'oubliez pas de le protéger avec un .htaccess.
Par exemple : <IfModule mod_version.c>
    <IfVersion >= 2.4>
        Require all denied
    </IfVersion>
    <IfVersion < 2.4>
        Deny from all
    </IfVersion>
</IfModule>

<IfModule !mod_version.c>
    <IfModule mod_authz_core.c>
        Require all denied
    </IfModule>
    <IfModule !mod_authz_core.c>
        Deny from all
    </IfModule>
</IfModule>
Ce fichier est ajouté automatiquement dans le dossier des classes générées (cache), mais pas dans le dossier des locales.