Accueil
Rechercher:
sur developpez.com sur les forums
Forums | Tutoriels | F.A.Q's | Participez | Hébergement | Contacts
Accueil Conception Java DotNET Visual Basic  C  C++ Delphi MS-Office SQL & SGBD Oracle  4D  Business Intelligence
Club Emploi Blogs   TV   Dév. Web PHP XML Python Autres 2D-3D-Jeux Sécurité Windows Linux PC Mac
FORUM PHP FAQ PHP COURS PHP SOURCES PHP LIVRES PHP SCRIPTS PHP OUTILS PHP COMPARATIFS PHP TV Zend Framework

Internationalisation (i18n) en PHP avec XML

Date de publication : 15 Octobre 2007

Par Vincent Flauder (Internationalisation (i18n) en PHP avec XML)
 

Cet article a pour but de proposer une solution simple pour introduire plusieurs langues dans vos sites web, avec une interface administrateur.
  • Niveau : néophyte à intermédiaire/expert
  • Connaissances abordées :
  • XHTML : Les formulaires
  • PHP : PHP5 et les classes
  • XML : DTD et XML

I. Introduction
II. Le DTD ( Document Type Definition )
III. Le fichier XML
IV. La base de données SQL
V. La classe PHP : XMLEngine
VI. Utilisation de la classe
A. Extraction des données
B. Ajout d'éléments
C. Modification des éléments (traduction)
D. Suppression des éléments
E. Disponibilité des langues
VII. Sources


I. Introduction

Le principe du multi-langages est de modifier le contenu textuel de tous les éléments d'une page HTML en fonction de la langue choisie par l'utilisateur. Pour ce faire il faut stocker ces valeurs dans des fichiers externes, ces derniers seront au format XML et leur syntaxe contrôlée avec un DTD (Document Type Definition). Pour pouvoir manipuler ces fichiers XML, PHP nous offre un panel complet d'outils : les fonctions DOM.
L'utilisation d'une base de données de type MySQL ici permet de sauvegarder les langues utilisées ainsi qu'une variable afin de savoir si la langue est ouverte aux utilisateurs ou non.
Pour interagir avec la base de données j'ai utilisé une classe que j'ai moi même crée ( SQLManager.php ) ne sera pas détaillée dans cet article, mais elle est très documentée et vous pourrez apprendre à vous en servir si vous le voulez avec ses commentaires.
En résumé, la classe permet de se connecter au serveur et à une base de données, surtout elle génère les requêtes SQL à notre place et nous renvoie les résultats sous forme de tableau.

warning Ne pas confondre les fonctions DOM et les fonctions DOM XML, les fonctions DOM XML ne sont pas implémentées en PHP 5.

II. Le DTD ( Document Type Definition )

Le DTD sert à créer un modèle pour les documents XML, une hiérarchie stricte à respecter pour l'imbrication des éléments et leurs attributs. Lorsque l'on crée un document XML, on indique l'emplacement du DTD afin de "valider" le document. C'est nécessaire pour utiliser certaines fonctions DOM et permet d'homogénéiser tous les documents.
Le DTD utilise un langage propre à lui, mais simple à comprendre. Dans le fichier seront définis le nom des balises nécessaires ainsi que leurs attributs, en précisant si ces attributs et balises sont obligatoires ou facultatives.
Code DTD ( xmldoctype.dtd )

<?xml version="1.0" encoding="UTF-8"?>
<!ELEMENT language (xmldata*) >
<!-- 
     Définition du premier élément 'language' (l'élément racine du fichier)
     dans lequel on peut placer des éléments (xmldata) 
     * : l'étoile indique 0 ou plusieurs éléments 
 -->
	 
	<!ELEMENT xmldata (translation+) >
	<!ATTLIST xmldata id ID #REQUIRED >
	<!-- 
	     Définition de l'élément 'xmldata', on peut y placer des éléments (translation),
	     puis l'attribut 'id' est de type ID et est requis(obligatoire), le '+' signifie
		 qu'il faut au moins 1 élément. 
	 -->
		 
		<!ELEMENT translation (#PCDATA) >
		<!ATTLIST translation lang CDATA #REQUIRED >
		<!-- 
		     Pour finir voici la définition d'un élément 'translation' qui contient
		     des données (#PCDATA), ce qui signifie que c'est une grande chaîne de 
		     caractères pour simplifier. C'est cet élément qui contiendra nos textes.
		     Il possède un attribut 'lang' qui est aussi requis. 
		 -->
			 

III. Le fichier XML

Les fichiers XML seront notre base de données pour les textes du site, en suivant les définitions crées auparavant, il sera alors simple de conçevoir la 'base de données' (en mode Design sous Eclipse c'est très pratique et simple).
Code XML ( exemple de website.xml )

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE language SYSTEM "xmldoctype.dtd" >
<!-- On doit définir l'emplacement du DTD ainsi que l'élément utilisé comme racine-->

<language>

  <xmldata id="webPageTitle">
    <translation lang="fr">Démonstration de XMLEngine</translation>
    <translation lang="en">XMLEngine demonstration</translation>
  </xmldata>
  
</language>

IV. La base de données SQL

Pour notre base de données c'est très simple, on a juste besoin d'une table avec deux champs : l'un contenant le tag des langues utilisées, l'autre étant un booléen qui permet de savoir si la langue est ouverte aux utilisateurs ou non.

Table : Languages
Nom du champ Valeur Description
Language VARCHAR(2) Chaîne de caractères contenant le tag de la langue
IsAvailable BOOL ( TinyInt(1) ) Variable de contrôle pour l'accessibilité de la langue aux utilisateurs

V. La classe PHP : XMLEngine

Voici la partie centrale de notre solution, c'est cette classe qui nous permettra de manipuler les données des fichiers XML. La classe possède plusieurs fonctions permettant d'aider à l'administration via un panneau administrateur.

warning Les types soulignés dans ce tableau ne sont là qu'à titre indicatif. On ne doit pas les recopier dans le fichier source.
Définition des attributs de la classe :

Attributs
Nom Définition
private DOMDocument domFile Attribut dans lequel se trouve l'instance de la classe DOMDocument.
private string currentLanguage Attribut dans lequel on sauve la langue utilisée lors de la manipulation des fichiers XML.
private string currentFile Attribut où l'on stocke le chemin du fichier XML ouvert et en cours d'utilisation.
private fileID fileLock Attribut permettant de bloquer l'accès au fichier lors des modifications.

Définition des méthodes (fonctions) de la classe :

Méthodes
Nom Définition
public __construct(
string $filename,
string $language )
Constructeur de la classe, prend en argument le chemin du fichier XML à ouvrir, $filename, avec la langue $language à utiliser.
public string getCurrentFile() Méthode permettant de récupérer le nom du fichier en cours.
public string getItemValue(
string $itemID )
Méthode permettant d'extraire le contenu textuel d'une balise dont l'identifiant $itemID est passé en argument.
public string[] getItemNodeList() Méthode permettant de récupérer tous les identifiants des balises xmldata du fichier XML.
public changeFile(
string $newFile )
Méthode permettant de charger un nouveau fichier XML.
public changeLanguage(
string $newLanguage )
Méthode permettant de changer la langue utilisée.
public bool addXmlElement(
string $itemID,
string $lang,
string $itemDefaultValue )
Méthode permettant de créer un nouvel élément xmldata dont l'identifiant sera $itemID avec un enfant translation dans la langue $lang et avec le texte $itemDefaultValue.
public bool modifyXmlElement(
string $itemID,
string $lang,
string $newContent )
Méthode permettant de modifier le contenu textuel d'un élément dont l'ID est $itemID, dans la langue $lang, et le nouveau texte sera $newContent.
public bool removeXmlElement(
string $itemID,
string $lang )
Méthode permettant de supprimer un élément translation dont la langue est $lang, ceci dans l'élément xmldata qui a l'ID $itemID.
public bool completelyRemoveXmlElement(
string $itemID )
Méthode permettant de supprimer un élément xmldata dont l'ID est $itemID.
public bool isEmptyXml(
string $itemID,
string $lang )
Méthode permettant de vérifier qu'un élément translation est bien rempli dans la langue $lang.
public addLanguageToFile(
string $newLanguage )
Méthode permettant d'ajouter une nouvelle langue $newLanguage au fichier. Cette fonction crée un élément translation avec la nouvelle langue dans chaque élément xmldata s'il n'y en a pas.
public bool checkIntegrity(
string $lang )
Méthode permettant de vérifier que tous les éléments xmldata possèdent des éléments translation dans la langue $lang et qu'ils soient remplis.
public string __get(
string $itemID )
Méthode magique de PHP qui sera éxécutée lorsque l'on voudra lire un attribut de la classe. En utilisant les IDs des éléments xmldata comme attributs de classe, cela permet de retourner les contenus textuels des balises plus facilement.
private saveFile() Méthode permettant de sauvegarder le fichier XML de manière sécurisée.

VI. Utilisation de la classe

warning Dans les pages de code on ignorera les standards HTML afin d'aller à l'essentiel et d'éviter de se perdre, seulement les éléments nécessaires au bon fonctionnement de la solution seront présents. Aussi aucun style CSS n'a été défini, les styles sont insérés directement dans l'attribut STYLE pour les démonstrations présentes dans cet article.

A. Extraction des données


Pour commencer nous allons simplement utiliser la classe pour extraire des données :

Code PHP ( extract.php )
<?php
session_start();

//On ajoute le fichier de classe.
require_once 'include/XMLEngine.php';

/* On va utiliser une variable de session pour la langue de l'utilisateur.
*  Si la variable n'existe pas on la crée. */
if ( !isset( $_SESSION['Language'] ) )
{
	$_SESSION['Language'] = 'fr'; //Français par défaut.
}

//Création de l'instance de notre classe avec le fichier XML à ouvrir.
$xml = new XMLEngine( 'xml/website.xml', $_SESSION['Language'] );

/* Grâce à la fonction __get(), cette ligne suffit à afficher le contenu
*  de l'élément XMLDATA d'identifiant 'webPageName' en langue française.*/

echo $xml->webPageName;
?>

Il n'y a que cela pour l'extraction des données, les autres fonctions sont principalement conçues pour créer l'administration au travers d'une interface web.


B. Ajout d'éléments


Dans cette section nous allons voir comment ajouter des éléments, en créant dans un premier temps un formulaire HTML pour entrer les éléments nécessaires, puis la partie PHP qui traitera ce formulaire pour ajouter l'élément et afficher un message javascript, le tout en utilisant à chaque fois notre objet XML pour récupérer les textes du site :

Code PHP ( addForm.php )
<?php
/*
 * On reprend le même début qu'avant :
 */
session_start();
require_once 'include/XMLEngine.php';
if ( !isset( $_SESSION['Language'] ) )
{
	$_SESSION['Language'] = 'fr'; //Français par défaut.
}
$xml = new XMLEngine( 'xml/website.xml', $_SESSION['Language'] );

//On inclut la classe SQLManager et créer un objet.
require_once 'include/SQLManager.php';
$sql = new SQLManager( 'localhost', 'root', '', 'Test' );

/* Création du formulaire de saisie : */ ?>
<form action="addForm.php" method="post">

	<!-- On place un tableau dans le formulaire pour le mettre
	     en forme -->
	<table border="1">

		<!-- Titre -->
		<tr>
			<td colspan="2">
				<?php echo $xml->webPageName;?>
			</td>
		</tr>		

		<!-- Saisie de l ID de la balise -->
		<tr>
			<td>
				<?php echo $xml->inputID;?>
			</td>
			<td>
				<input type="text" name="itemId" />
			</td>
		</tr>

		<!-- Saisie du texte pour le nouveau contenu -->
		<tr>
			<td>
				<?php echo $xml->inputData;?>
			</td>
			<td>
				<textarea cols="20" rows="5" name="itemData"></textarea>
			</td>
		</tr>

		<!-- Création de la liste de choix des langues -->
		<tr>
			<td>
				<?php echo $xml->inputLang;?>
			</td>
			<td><?php 
				//On récupère toutes les langues de la base de données SQL
				$langs = $sql->selectQuery( '*', 'Languages', 0, '', '', '', '' );
				
				//Puis on créer la liste ?>
				<select name="itemLanguage" style="width:100%;"><?php 
					foreach ( $langs as $language )
					{?>
						<option value="<?php echo $language['Language'];?>"><?php echo $xml->{ $language['Language'].'Lang' };?></option><?php
					}?>
				</select>				
			</td>
		</tr>

		<!-- Bouton d envoi pour créer une balise XML -->
		<tr>
			<td colspan="2">
				<input style="width:100%;text-align:center;" type="submit" 
				name="submitCreateItem" value="<?php echo $xml->submitCreate;?>" />
			</td>
		</tr>
	</table>
</form>	<?php 
/*
 * Traitement du formulaire lorsqu'il est soumis
 */
if ( isset( $_POST['submitCreateItem'] ) )
{
	/*
	 * On fait alors appel à la fonction addXmlElement avec les
	 * variables du formulaire, comme la fonction renvoi un booleen
	 * on peut l inclure directement dans la condition du IF :
	 */
	if ( $xml->addXmlElement( $_POST['itemId'], $_POST['itemLanguage'], $_POST['itemData'] ) )
	{
		/* Réussite de l'ajout de l'élément
		 * On affiche un petit message javascript, toujours avec notre classe.
		 */ ?>
		<script type="text/javascript">
		<!--
			alert("<?php echo $xml->createXmlDone;?>");
		-->
		</script><?php		
	}
	else
	{?>
		<script type="text/javascript">
		<!--
			alert("<?php echo $xml->createXmlError;?>");
		-->
		</script><?php		
	}
}
?>

C. Modification des éléments (traduction)

Avec ceci on peut déjà créer des éléments dans les fichiers XML. Maintenant nous allons modifier les éléments dans le but de traduire ceux qui n'y sont pas. On va pour cela génèrer un formulaire HTML pour modifier les balises selon la langue de traduction choisie :
Code PHP ( traduceForm.php )
<?php
/*
 * On fait une nouvelle page,
 * On reprend le même début qu'avant :
 */
session_start();
require_once 'include/XMLEngine.php';

//On inclut la classe SQLManager et créer un objet.
require_once 'include/SQLManager.php';
$sql = new SQLManager( 'localhost', 'root', '', 'Test' );

if ( !isset( $_SESSION['Language'] ) )
{
	$_SESSION['Language'] = 'fr'; //Français par défaut.
}

$xml = new XMLEngine( 'xml/website.xml', $_SESSION['Language'] );

/*
 * On inclut ici le traitement du formulaire qui sera vu ensuite.
 * Il faut voir le 'require_once' comme un copier/coller, donc le
 * fichier 'traduceTreatement.php' est uniquement à inclure.
 */
require_once 'traduceFormTreatment.php';

/* Début du formulaire d'administration : */ ?>
<div style="height:300px;overflow:scroll;" align="center"><?php
/*
 * Pour commencer il faut créer un formulaire pour choisir la
 * langue dans laquelle traduire, on affichera ce formulaire que
 * si aucun autre n'a été envoyé.
 */
if ( !isset( $_POST['submitLangToTraduce'] ) && !isset( $_POST['submitTraduceFile'] ) )
{?>
<form action="traduceForm.php" method="post">
	<table border="1">
		<tr>
			<td><?php
				echo $xml->foreignLang;?>			
			</td>			
		</tr>
		
		<!-- On crée la liste de langues l'ol -->
		<tr>
			<td align="center"><?php 
				//On récupère toutes les langues de la base de données SQL
				$langs = $sql->selectQuery( '*', 'Languages', 0, '', '', '', '' );
				
				/* Puis on créer la liste */ ?>
				<select name="langToTraduce" style="width:100%;"><?php 
					foreach ( $langs as $language )
					{?>
						<option value="<?php echo $language['Language'];?>"><?php echo $xml->{ $language['Language'].'Lang' };?></option><?php
					}?>
				</select>			
			</td>
		</tr>
		
		<tr>
			<td colspan="2">
				<input type="submit" name="submitLangToTraduce" value="<?php echo $xml->submitChooseLang;?>" />
			</td>
		</tr>
	</table>
</form><?php 
} //Fin du formulaire de choix des langues

/*
 * Une fois la langue choisie ou le traitement effectué on affiche le formulaire 
 * avec les éléments du fichier website.xml
 */ 
if ( isset( $_POST['submitLangToTraduce']) || isset( $_POST['submitTraduceFile'] ) )
{
	/*On créer un nouvel objet xml pour la langue à traduire
	$xmlTraductor = new XMLEngine( 'xml/website.xml', $_POST['langToTraduce'] );*/ ?>
	
	<form action="traduceForm.php" method="post">
		<table border="1">
			
			<!-- Titre -->
			<tr>
				<td colspan="3">
					<?php echo $xml->webPageName;?><