Dans la première partie de cet article je vous avais montré une solution simple pour faire un site multi-langues en utilisant des variables.
Cette solution facile à mettre en place présente néanmoins quelques inconvénients. Notamment si un texte n'a pas été traduit.
En effet, en PHP, lorsque vous affichez une clé de tableau inexistante, rien ne se passe. Visuellement il n'est pas possible de voir si un texte n'est pas traduit dans une ou toutes les langues.
$text = array();
echo '<h1>'.$text['titre'].'</h1>';
// <h1></h1>
De plus il n'est pas possible de passer des variables dans le texte. Imaginons que vous vouliez afficher le nombre de produit enregistrés dans une catégorie.
$produit = 52;
$catégorie = 'Croquette pour chat';
echo '<p>Il y a '.$produit.' produits dans la catégorie "'.$catégorie.'" !!</p>';
// <p>Il y a 52 produits dans la catégorie "Croquette pour chat" !!</p>
Pour afficher ce résultat sur un site multi-langues vous devrez morceler votre phrase :
$text1 = "Il y a ";
$text2 = " produits dans la catégorie ";
$text3 = " !!";
echo '<p>'.$text1.$produit.text2.$catégorie.text3.'</p>';
// <p>Il y a 52 produits dans la catégorie Croquette pour chat</p>
$text1 = "There are ";
$text2 = " products in the category ";
$text3 = " ¿?";
echo '<p>'.$text1.$produit.text2.$catégorie.text3.'</p>';
// <p>There are 52 products in the category Croquette pour chat ¿?</p>
Pouah ! C'est pas concevable de travailler comme ça ! Heureusement pour vous j'ai conçu la class IziTranslate.
Faite un site multi-langue facile avec la class IziTranslate
Pour palier à ces manques j'ai codé la class IziTranslate. Son fonctionnement est simple :
- Créez une instance de la class.
- Ajoutez les différentes langues que vous souhaitez utiliser (1 ou 2, inutile d'en charger 50).
- Définissez la langue de référence.
- Définissez la langue d'affichage souhaitée.
La class vous permet d'intégrer des variables dans vos textes. Si un texte vient à manquer dans la langue en cours, la class cherchera automatiquement le texte dans la langue de référence.
Le code documenté
// class IziTranslate
class IziTranslate{
protected $txt = array(); // tableau contenant les textes
protected $langLst = array(); // tableau contenant la liste des langues
protected $langRef = ''; // identifiant de la langue de référence
protected $lang = ''; // identifiant de la langue qui sera renvoyée
protected $pathLang = ''; // chemin vers le dossier de langue
protected $debugMode = 0; // mode debug
Constructeur
Initialise la class IziTranslate.
Argument :
- $aPath = chemin vers le dossier qui contient les sous dossiers de langues
function __construct($aPath){
$this->pathLang = $aPath;
}
Fonction AddLanguage
Ajoute une langue. Pour changer de langue il faudra utiliser la fonction SetLanguage
.
Arguments :
- $aId = identifiant de la langue. l'identifiant est aussi le nom du dossier qui contient les textes
- $aName = nom de la langue [facultatif]
public function AddLanguage($aId, $aName=''){
// vérifie si la langue est déjà enregistré
if(isset($this->langLst[$aId]) || empty($aId)) return 0;
// enregistre la langue et son nom
$this->langLst[$aId] = $aName;
$this->txt[$aId] = array();
// défini la langue en cours si ce n'est pas déjà fait
if($this->lang=='') $this->lang = $aId;
// défini la langue de référence si ce n'est pas déjà fait
if($this->langRef=='') $this->langRef = $aId;
return 1;
}
Fonction SetLanguage
Définit la langue en cours.
Argument :
- $aId = identifiant de la langue
public function SetLanguage($aId){
// vérifie si la langue est déjà enregistré
if(!empty($this->langLst[$aId])) return 0;
$this->lang = $aId;
return 1;
}
Fonction LoadTxt
Charge un fichier dans toutes les langues. Pour cette class j'ai fait le choix de fichier de texte au format JSON. Le contenu de ces fichiers de texte est un tableau associatif php. Vous pouvez charger plusieurs fichiers, cela ne posera pas de problème tant que les identifiants de texte sont unique.
Argument :
- $aPath = fichier à charger
public function LoadTxt($aPath){
foreach($this->langLst as $k=>$v){
$p = $this->pathLang.'/'.$k.'/'.$aPath;
// verifie si le fichier existe
if(file_exists($p)){
// charge le fichier au format JSON, le décode et l'intégre aux texte déjà chargés
$this->txt[$k] = array_merge($this->txt[$k], json_decode(file_get_contents($p), true));
}
}
return 1;
}
Fonction SetRefLanguage
Définit la langue de référence
Argument :
- $aId = identifiant de la langue
public function SetRefLanguage($aId){
// vérifie si la langue existe
if(empty($this->langLst[$aId])) return 0;
// enregistre
$this->langRef = $aId;
return 1;
}
Fonction GetTxt
Renvoi un texte d'après son identifiant. Vous pouvez passer des valeurs de remplacement. Pour cela la fonction va chercher des occurences de type pourcentage+nombre+pourcentage. Vous pouvez passer une chaine de caractère si il n'y a qu'une valeur à remplacer ou un tableau.
Arguments:
- $aId = identifiant du texte
- $aReplace = texte ou tableau de remplacement
public function GetTxt($aId, $aReplace=''){
// renvoi un texte dans la langue en cours
// si le texte n'existe pas dans la langue en cours,
// la class essaye de le renvoyer dans la langue de référence
$r = (empty($this->txt[$this->lang][$aId]) ? $this->txt[$this->langRef][$aId]:$this->txt[$this->lang][$aId]);
if(empty($r)){
// si le texte n'existe pas non plus dans la langue de référence
// et que le mode debug est activé, renvoi un texte d'erreur
if($this->debugMode) $r = '[KEY NOT FOUND : '.$aId.']';
}else if(isset($aReplace)){
// il y a des valeurs de remplacement
// la fonction accepte un tableau (array) ou un texte (string)
// génère le tableau ou le texte à rechercher
if(is_array($aReplace)){
$nb = count($aReplace);
$s = array();
for($i=1; $i<=$nb; $i++){
$s[] = '%'.$i.'%';
}
}else $s = '%1%';
$r = str_replace($s, $aReplace, $r);
}
return $r;
}
Fonction DebutModeActive
Active le mode debug
public function DebutModeActive(){
$this->debugMode = 1;
}
}
Utilisation de la class IziTranslate
Imaginons une structure avec un dossier lang tel que :
- Le dossier qui contient nos dossiers est le dossier lang/
- Ce dossier contient un sous dossier pour chaque langue. Par clarté j'utlise toujours le code ISO 2 caractères des langues. FR = Français, EN = Anglais, IT = Italie, = DE = Allemand, etc
- Chaque dossier de langue contient les fichiers au format JSON
- Les fichiers JSON sont le résultat d'un export d'un tableau PHP
Pour l'exemple voici le contenu du fichier lang/fr/lang.json :
{"title":"Les chaussures de la vallée","prodTitle1":"Nous avons %1% produits dans cette catégorie","prodTitle2":"Nous avons %1% produits dans la catégorie %2%..."}
Notez que le texte prodTitle1 possède une valeur de remplacement (%1%) et prodTitle2 en possède deux (%1% et %2%). Vous pouvez utiliser plusieurs fois la même valeurs de remplacement.
// import la classe IziTranslate
require 'IziTranslate.class.php';
// initialise la langue
$lang = isset($_GET['lang']) ? $_GET['lang']:'fr';
// crée l'objet
// paramètre :
// - le nom du dossier qui contient les sous dossiers de langue
$IziTranslate = new IziTranslate('lang');
// ajoute les langues
// paramètres :
// - le nom du sous dossier qui contient la langue et qui servira d'identifiant
// - le nom de la langue (facultatif)
$IziTranslate->AddLanguage('fr');
// si la langue du document n'est pas le français, on rajoute la langue
if($lang!='fr') $IziTranslate->AddLanguage($lang);
// définit la langue de référence
// paramètre :
// - l'identifiant de la langue
$IziTranslate->SetRefLanguage('fr');
// définit la langue en cours
// paramètre :
// - l'identifiant de la langue
$IziTranslate->SetLanguage($lang);
// active le mode débug
$IziTranslate->DebutModeActive();
// charge les fichiers de texte
// paramètre :
// - le nom du fichier dans le sous dossier de la langue
$IziTranslate->LoadTxt('lang.json');
Pour faciliter l'usage de la class, on crée une fonction globale qui va appeller l'objet. Vous pouvez appeller cette fonction comme vous voulez, l'essentiel c'est que le nom soit suffisemment court pour ne pas surcharger le code.
// fonction d'appel global
// cela simplifie la lecture du code d'utiliser une fonction plutôt que d'utiliser un objet
function lTxt($id, $replace=''){
global $IziTranslate;
return $IziTranslate->GetTxt($id, $replace);
}
Pour afficher un texte, on appelle notre fonction globale et on passe en argument l'identifiant du texte.
echo '<h1>'.lTxt('title').'</h1>';
// <h1>Les chaussures de la vallée</h1>
Vous pouvez passer une variable pour que la class l'intégre dans le texte.
echo '<p>'.lTxt('prodTitle1', 35).'</p>';
// <p>Nous avons 35 produits dans cette catégorie</p>
Vous pouvez même passer plusieurs variables sous forme de tableau.
echo '<p>'.lTxt('prodTitle2', array(35, "chaussure de ville")).'</p>';
// <p>Nous avons 35 produits dans la catégorie chaussure de ville...</p>
Le mode debug permet d'afficher une erreur si un texte n'est trouvé ni dans la langue demandée, ni dans la langue de référence.
$obj->DebutModeActive();
$html .= '<p>'.lTxt('cette clé n existe pas').'</p>';
// <p>[KEY NOT FOUND : cette clé n existe pas]</p>
Récupitulatif complet de la class IziTranslate
Voici le code de la class IziTranslate, vous n'avez qu'à le copier coller dans un fichier que vous nommerez 'IziTranslate.class.php'
// class IziTranslate
// https://blog.niap3d.com/fr/1,10,news-45-Internationaliser-un-site-partie-2-.html
class IziTranslate{
protected $txt = array();
protected $langLst = array();
protected $langRef = '';
protected $lang = '';
protected $pathLang = '';
protected $debugMode = 0;
//
// constructeur
//
// argument :
// $aPath = chemin vers le dossier qui contient les sous dossiers de langues
//
function __construct($aPath){
$this->pathLang = $aPath;
}
//
// ajoute une langue
//
// argument :
// $aId = identifiant de la langue. l'identifiant est aussi le nom du dossier qui contient les textes
// $aName = nom de la langue [facultatif]
//
public function AddLanguage($aId, $aName=''){
// vérifie si la langue est déjà enregistré
if(isset($this->langLst[$aId]) || empty($aId)) return 0;
// enregistre la langue et son nom
$this->langLst[$aId] = $aName;
$this->txt[$aId] = array();
// défini la langue en cours si ce n'est pas déjà fait
if($this->lang=='') $this->lang = $aId;
// défini la langue de référence si ce n'est pas déjà fait
if($this->langRef=='') $this->langRef = $aId;
return 1;
}
//
// définit la langue en cours
//
// argument :
// $aId = identifiant de la langue
//
public function SetLanguage($aId){
// vérifie si la langue est déjà enregistré
if(!empty($this->langLst[$aId])) return 0;
$this->lang = $aId;
return 1;
}
//
// charge un fichier dans toutes les langues
//
// argument :
// $aPath = fichier à charger
//
public function LoadTxt($aPath){
foreach($this->langLst as $k=>$v){
$p = $this->pathLang.'/'.$k.'/'.$aPath;
// verifie si le fichier existe
if(file_exists($p)){
// charge le fichier au format JSON, le décode et l'intégre aux texte déjà chargés
$this->txt[$k] = array_merge($this->txt[$k], json_decode(file_get_contents($p), true));
}
}
return 1;
}
//
// définit la langue de référence
//
// argument :
// $aId = identifiant de la langue
//
public function SetRefLanguage($aId){
// vérifie si la langue existe
if(empty($this->langLst[$aId])) return 0;
// enregistre
$this->langRef = $aId;
return 1;
}
//
// renvoi un texte
//
// argument :
// $aId = identifiant du texte
// $aReplace = tableau de remplacement
//
public function GetTxt($aId, $aReplace=''){
// renvoi un texte dans la langue en cours
// si le texte n'existe pas dans la langue en cours,
// la class essaye de le renvoyer dans la langue de référence
$r = (empty($this->txt[$this->lang][$aId]) ? $this->txt[$this->langRef][$aId]:$this->txt[$this->lang][$aId]);
if(empty($r)){
// si le texte n'existe pas non plus dans la langue de référence
// et que le mode debug est activé, renvoi un texte d'erreur
if($this->debugMode) $r = '[KEY NOT FOUND : '.$aId.']';
}else if(isset($aReplace)){
// il y a des valeurs de remplacement
// la fonction accepte un tableau (array) ou un texte (string)
// génère le tableau ou le texte à rechercher
if(is_array($aReplace)){
$nb = count($aReplace);
$s = array();
for($i=1; $i<=$nb; $i++){
$s[] = '%'.$i.'%';
}
}else $s = '%1%';
$r = str_replace($s, $aReplace, $r);
}
return $r;
}
//
// active le mode debug
//
public function DebutModeActive(){
$this->debugMode = 1;
}
}
Historique
14/09/15
Bug à la ligne 98 :
}else if($aReplace!=''){
Si la valeur de remplacement est 0, la fonction ne fonctionnait pas et renvoyer le texte sans faire de remplacement.
Il suffit de remplacer la ligne 98 par :
}else if(isset($aReplace)){
Le prochaine article vous présentera une class pour faciliter la gestion des fichiers textes et leur traduction.
Si vous avez des questions ou besoin d'éclaircissement n'hésitez pas, les commentaires sont là pour ça.