IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Développons en Java v 2.20   Copyright (C) 1999-2021 Jean-Michel DOUDOUX.   
[ Précédent ] [ Sommaire ] [ Suivant ] [ Télécharger ]      [ Accueil ] [ Commentez ]


 

17. L'internationalisation

 

chapitre 1 7

 

Niveau : niveau 3 Intermédiaire 

 

La localisation consiste à adapter un logiciel aux caractéristiques locales de l'environnement d'exécution telles que la langue et la monnaie. Le plus gros du travail consiste à traduire toutes les phrases et les mots. Les classes nécessaires sont incluses dans le package java.util.

Ce chapitre contient plusieurs sections :

 

17.1. Les objets de type Locale

Un objet de type Locale identifie une langue et un pays donné.

 

17.1.1. La création d'un objet Locale

Exemple ( code Java 1.1 ) :
locale_US = new Locale("en","US"); 
locale_FR = new Locale("fr","FR");

Le premier paramètre est le code langue (deux caractères minuscules conformes à la norme ISO-639 : exemple "de" pour l'allemand, "en" pour l'anglais,  "fr" pour le français, etc ...)

Le deuxième paramètre est le code pays (deux caractères majuscules conformes à la norme ISO-3166 : exemple : "DE" pour l'Allemagne, "FR" pour la France,"US" pour les Etats Unis, etc ...). Ce paramètre est obligatoire : si le pays n'a pas besoin d'être précisé, il faut fournir une chaîne vide.

Exemple ( code Java 1.1 ) :
Locale locale = new Locale("fr", "");

Un troisième paramètre peut permettre de préciser d'avantage la localisation par exemple la plate-forme d'exécution (il ne respecte aucun standard car il ne sera défini que dans l'application qui l'utilise) :

Exemple ( code Java 1.1 ) :
Locale locale_unix = new Locale("fr","FR", "UNIX"); 
Locale locale_windows = new Locale("fr","FR", "WINDOWS");

Ce troisième paramètre est optionnel.

La classe Locale définit des constantes pour certaines langues et pays :

Exemple ( code Java 1.1 ) : ces deux lignes sont équivalentes
Locale locale_1 = Locale.JAPAN; 
Locale locale_2 = new Locale("ja", "JP");

Lorsque l'on précise une constante représentant une langue alors le code pays n'est pas défini.

Exemple ( code Java 1.1 ) : ces deux lignes sont équivalentes
Locale locale_3 = Locale.JAPANESE; 
Locale locale_2 = new Locale("ja", "");

Il est possible de rendre la création d'un objet Locale dynamique :

Exemple ( code Java 1.1 ) :
static public void main(String[] args) {
   String langue = new String(args[0]);
   String pays = new String(args[1]);
   locale = new Locale(langue, pays);
}

Cet objet ne sert que d'identifiant qu'il faut passer, par exemple, à des objets de type ResourceBundle qui eux possèdent le nécessaire pour réaliser la localisation. En fait, la création d'un objet Locale pour un pays donné ne signifie pas que l'on va pouvoir l'utiliser.

 

17.1.2. L'obtention de la liste des Locales disponibles

La méthode getAvailableLocales() permet de connaître la liste des Locales reconnues par une classe sensible à l'internationalisation

Exemple ( code Java 1.1 ) : avec la classe DateFormat
import java.util.*;
import java.text.*;

public class Available {
   static public void main(String[] args) {
      Locale liste[] = DateFormat.getAvailableLocales();
      for (int i = 0; i < liste.length; i++)
      {
         System.out.println(liste[i].toString());
         // toString retourne le code langue et le code pays séparé d'un souligné
      }
   }
}

La méthode Locale.getDisplayName() peut être utilisée à la place de toString() pour obtenir les noms du code langue et du code pays.

 

17.1.3. L'utilisation d'un objet Locale

Il n'est pas obligatoire de se servir du même objet Locale avec les classes sensibles à l'internationalisation.

Cependant la plupart des applications utilisent l'objet Locale par défaut qui est initialisé par la machine virtuelle avec les paramètres de la machine hôte. La méthode Locale.getDefault() permet de connaître cet objet Locale.

 

17.2. La classe ResourceBundle

Il est préférable de définir un ResourceBundle pour chaque catégorie d'objet (exemple un par fenêtre) : ceci rend le code plus clair et plus facile à maintenir, évite d'avoir des ResourceBundle trop importants et réduit l'espace mémoire utilisé car chaque ressource n'est chargée que lorsque l'on en a besoin.

 

17.2.1. La création d'un objet ResourceBundle

Conceptuellement, chaque ResourceBundle est un ensemble de sous-classes qui partagent la même racine de nom.

Exemple :
TitreBouton
TitreBouton_de
TitreBouton_en_GB
TitreBouton_fr_FR_UNIX

Pour sélectionner le ResourceBundle approprié il faut utiliser la méthode ResourceBundle.getBundle().

Exemple ( code Java 1.1 ) :
Locale locale = new Locale("fr", "FR");
ResourceBundle messages = ResourceBundle.getBundle("TitreBouton", locale);

Le premier argument contient le type d'objet à utiliser (la racine du nom de cet objet).

Le second argument de type Locale permet de déterminer quel fichier sera utilisé : il ajoute le code pays et le code langue séparés par des soulignés de la racine du nom.

Si la classe désignée par l'objet Locale n'existe pas, alors la méthode getBundle() recherche celle qui se rapproche le plus. L'ordre de recherche sera le suivant :

Exemple :
TitreBouton_fr_CA_UNIX
TitreBouton_fr_FR
TitreBouton_fr
TitreBouton_en_US
TitreBouton_en
TitreBouton

Si aucune n'est trouvée alors getBundle lève une exception de type MissingResourceException.

 

17.2.2. Les sous-classes de ResourceBundle

La classe abstraite ResourceBundle possède deux sous-classes : PropertyResourceBundle et ListResourceBundle.

La classe ResourceBundle est une classe flexible : le passage d'un PropertyResourceBundle à ListResourceBundle se fait sans impact sur le code. La méthode getBundle() recherche le ResourceBundle désiré qu'il soit dans un fichier .class ou propriétés.

 

17.2.2.1. L'utilisation de PropertyResourceBundle

Un PropertyResourceBundle est rattaché à un fichier propriétés. Ces fichiers propriétés ne font pas partie du code source java. Ils ne peuvent contenir que des chaines de caractères. Pour stocker d'autres objets, il faut utiliser des objets ListResourceBundle.

La création d'un fichier propriétés est simple : c'est un fichier texte qui contient des paires clé-valeur. La clé et la valeur sont séparées par un signe =. Chaque paire doit être sur une ligne séparée.

Exemple ( code Java 1.1 ) :
texte_suivant = suivant
texte_precedent = precedent

Le nom du fichier propriétés par défaut se compose de la racine du nom suivi de l'extension .properties.

Exemple : TitreBouton.properties.

Dans une autre langue, anglais par exemple, le fichier s'appellerait : TitreBouton_en.properties

Exemple ( code Java 1.1 ) :
texte_suivant = next
texte_precedent = previous

Les clés sont les mêmes, seule la traduction change.

Le nom de fichier TitreBouton_fr_FR.properties contient la racine (Titrebouton), le code langue (fr) et le code pays (FR).

 

17.2.2.2. L'utilisation de ListResourceBundle

La classe ListResourceBundle gère les ressources sous forme d'une liste encapsulée dans un objet. Chaque ListResourceBundle est donc rattaché à un fichier .class. On peut y stocker n'importe quel objet spécifique à la localisation.

Les objets ListResourceBundle contiennent des paires clé-valeur. La clé doit être une chaîne qui caractérise l'objet. La valeur est un objet de n'importe quelle classe.

Exemple ( code Java 1.1 ) :
class TitreBouton_fr extends ListResourceBundle {
   public Object[][] getContents() {
      return contents;
   }
   static final Object[][] contents = {
      {"texte_suivant", "Suivant"},
      {"texte_precedent", "Precedent"},
   };
}

 

17.2.3. L'obtention d'un texte d'un objet ResourceBundle

La méthode getString() retourne la valeur de la clé précisée en paramètre.

Exemple ( code Java 1.1 ) :
String message_1 = messages.getString("texte_suivant");
String message_2 = TitreBouton.getString("texte_suivant");

 

17.3. Un guide pour réaliser la localisation

 

17.3.1. L'utilisation d'un ResourceBundle avec un fichier propriétés

Il faut toujours créer le fichier propriété par défaut. Le nom de ce fichier commence avec le nom de base du ResourceBundle et se termine avec le suffixe .properties

Exemple ( code Java 1.1 ) :
#Exemple de fichier propriété par défaut (TitreBouton.properties)
texte1 = suivant
texte2 = precedent
texte3 = quitter

Les lignes de commentaires commencent par un #. Les autres lignes contiennent les paires clé-valeur. Une fois le fichier défini, il ne faut plus modifier la valeur de la clé qui pourrait être appelée dans un programme.

Pour ajouter le support d'autres langues, il faut créer des fichiers propriétés supplémentaires qui contiendront les traductions. Les fichiers ne diffèreront que par les valeurs associées aux clés, ces dernières restant identiques entre les fichiers.

Exemple ( code Java 1.1 ) :
#Exemple de fichier propriété en anglais (TitreBouton_en.properties)
texte1 = next
texte2 = previous
texte3 = quit

Lors de la programmation, il faut créer un objet Locale. Il est possible de créer un tableau qui contient la liste des Locale disponibles en fonction des fichiers propriétés créés.

Exemple ( code Java 1.1 ) :
Locale[] locales = { Locale.GERMAN, Locale.ENGLISH };

Dans cet exemple, l'objet Locale.ENGLISH correspond au fichier TitreBouton_en.properties. L'objet Locale.GERMAN ne possédant pas de fichier propriétés défini, le fichier par défaut sera utilisé.

Il faut créer l'objet ResourceBundle en invoquant la méthode getBundle de l'objet Locale.

Exemple ( code Java 1.1 ) :
ResourceBundle titres = ResourceBundle.getBundle("TitreBouton", locales[1]);

La méthode getBundle() recherche en premier une classe qui correspond au nom de base, si elle n'existe pas alors elle recherche un fichier de propriétés. Lorsque le fichier est trouvé, elle retourne un objet PropertyResourceBundle qui contient les paires clé-valeur du fichier

Pour retrouver la traduction d'un texte, il faut utiliser la méthode getString() d'un objet ResourceBundle

Exemple ( code Java 1.1 ) :
String valeur = titres.getString(key);

Lors du débogage, il peut être utile d'obtenir la liste des paires d'un objet ResourceBundle. La méthode getKeys() retourne un objet Enumeration qui contient toutes les clés de l'objet.

Exemple ( code Java 1.1 ) :
ResourceBundle titres =ResourceBundle.getBundle("TitreBouton", locales[1});
Enumeration cles = titres.getKeys();
while (cles.hasMoreElements()) {
   String cle = (String)cles.nextElement();
   String valeur = titres.getString(cle);
   System.out.println("cle = " + cle +
      ", " +   "valeur = " + valeur);
}

 

17.3.2. Des exemples de classes utilisant PropertiesResourceBundle

Exemple ( code Java 1.1 ) : Sources de la classe I18nProperties
/* 
Test d'utilisation de la classe PropertiesResourceBundle
pour internationaliser une application 
13/02/99 
*/ 

import java.util.*; 

/** 
 * Description de la classe I18nProperties 
 * 
 * @version       0.10 13 fevrier 1999 
 * @author        Jean Michel DOUDOUX 
 */ 
public class I18nProperties { 
  /** 
   * Constructeur de la classe 
   */ 
  public I18nProperties() { 

    String texte; 
    Locale locale; 
    ResourceBundle res; 

    System.out.println("Locale par defaut : "); 
    locale = Locale.getDefault(); 
    res = ResourceBundle.getBundle("I18nPropertiesRessources", locale); 
    texte = (String)res.getObject("texte_suivant"); 
    System.out.println("texte_suivant = "+texte); 
    texte = (String)res.getObject("texte_precedent"); 
    System.out.println("texte_precedent = "+texte); 

    System.out.println("\nLocale anglaise : "); 
    locale = new Locale("en",""); 
    res = ResourceBundle.getBundle("I18nPropertiesRessources", locale); 
    texte = (String)res.getObject("texte_suivant"); 
    System.out.println("texte_suivant = "+texte); 
    texte = (String)res.getObject("texte_precedent"); 
    System.out.println("texte_precedent = "+texte); 

    System.out.println("\nLocale allemande : "+
       "non définie donc utilisation locale par defaut "); 
    locale = Locale.GERMAN; 
    res = ResourceBundle.getBundle("I18nPropertiesRessources", locale); 
    texte = (String)res.getObject("texte_suivant"); 
    System.out.println("texte_suivant = "+texte); 
    texte = (String)res.getObject("texte_precedent"); 
    System.out.println("texte_precedent = "+texte); 
  } 

 /** 
   * Pour tester la classe 
   * 
   * @param      args[]         arguments passes au programme 
   */ 
  public static void main(String[] args) { 
    I18nProperties i18nProperties = new I18nProperties(); 
  } 

}

Exemple ( code Java 1.1 ) : Contenu du fichier I18nPropertiesRessources.properties
texte_suivant=suivant 
texte_precedent=Precedent

Exemple ( code Java 1.1 ) : Contenu du fichier I18nPropertiesRessources_en.properties
texte_suivant=next 
texte_precedent=previous

Exemple ( code Java 1.1 ) : Contenu du fichier I18nPropertiesRessources_en_US.properties
texte_suivant=next 
texte_precedent=previous

 

17.3.3. L'utilisation de la classe ListResourceBundle

Il faut créer autant de sous-classes de ListResourceBundle que de langues désirées : ceci va générer un fichier .class pour chacune des langues .

Exemple ( code Java 1.1 ) :
TitreBouton_fr_FR.class
TitreBouton_en_EN.class

Le nom de la classe doit contenir le nom de base plus le code langue et le code pays séparés par des soulignés. A l'intérieur de la classe, un tableau à deux dimensions est initialisé avec les paires clé-valeur. Les clés sont des chaines qui doivent être identiques dans toutes les classes des différentes langues. Les valeurs peuvent être des objets de n'importe quel type.

Exemple ( code Java 1.1 ) :
import java.util.*;

public class TitreBouton_fr_FR extends ListResourceBundle {
   public Object[][] getContents() {
      return contents;
   }

   private Object[][] contents = {
      { "texte_suivant", new String(" suivant ")},
      { "Numero", new Integer(4) }
   };
}

Il faut définir un objet de type Locale

Il faut créer un objet de type ResourceBundle en appelant la méthode getBundle() de la classe Locale

Exemple ( code Java 1.1 ) :
ResourceBundle titres=ResourceBundle.getBundle("TitreBouton", locale);

La méthode getBundle() recherche une classe qui commence par TitreBouton et qui est suivie par le code langue et le code pays précisés dans l'objet Locale passé en paramètre

La méthode getObject() permet d'obtenir la valeur de la clé passée en paramètres. Dans ce cas une conversion est nécessaire.

Exemple ( code Java 1.1 ) :
Integer valeur = (Integer)titres.getObject("Numero");

 

17.3.4. Des exemples de classes utilisant ListResourceBundle

Exemple ( code Java 1.1 ) : Sources de la classe I18nList
/* 
Test d'utilisation de la classe ListResourceBundle
pour internationaliser une application 
13/02/99 
*/ 

import java.util.*; 

/** 
 * Description de la classe I18nList 
 * 
 * @version       0.10 13 fevrier 1999 
 * @author        Jean Michel DOUDOUX 
 */ 
public class I18nList { 
  /** 
   * Constructeur de la classe 
   */ 
  public I18nList() { 

    String texte; 
    Locale locale; 
    ResourceBundle res; 

    System.out.println("Locale par defaut : "); 
    locale = Locale.getDefault(); 
    res = ResourceBundle.getBundle("I18nListRessources", locale); 
    texte = (String)res.getObject("texte_suivant"); 
    System.out.println("texte_suivant = "+texte); 
    texte = (String)res.getObject("texte_precedent"); 
    System.out.println("texte_precedent = "+texte); 

    System.out.println("\nLocale anglaise : "); 
    locale = new Locale("en",""); 
    res = ResourceBundle.getBundle("I18nListRessources", locale); 
    texte = (String)res.getObject("texte_suivant"); 
    System.out.println("texte_suivant = "+texte); 
    texte = (String)res.getObject("texte_precedent"); 
    System.out.println("texte_precedent = "+texte); 

    System.out.println("\nLocale allemande : "+
       "non définie donc utilisation locale par defaut "); 
    locale = Locale.GERMAN; 
    res = ResourceBundle.getBundle("I18nListRessources", locale); 
    texte = (String)res.getObject("texte_suivant"); 
    System.out.println("texte_suivant = "+texte); 
    texte = (String)res.getObject("texte_precedent"); 
    System.out.println("texte_precedent = "+texte); 
  } 

 /** 
   * Pour tester la classe 
   * 
   * @param      args[]         arguments passes au programme 
   */ 
  public static void main(String[] args) { 
    I18nList i18nList = new I18nList(); 
  } 

}

Exemple ( code Java 1.1 ) : Sources de la classe I18nListRessources
/* 
test d'utilisation de la classe ListResourceBundle pour 
internationaliser une application 
13/02/99 
*/ 

import java.util.*; 

/** 
 * Ressource contenant les traductions françaises 
 * langue par defaut de l'application 
 * 
 * @version       0.10 13 fevrier 1999 
 * @author        Jean Michel DOUDOUX 
 * 
 */ 

public class I18nListRessources extends ListResourceBundle { 
  public Object[][] getContents() { 
    return contents; 
  } 

   //tableau des mots clés et des valeurs 

   static final Object[][] contents = { 
   {"texte_suivant", "Suivant"}, 
   {"texte_precedent", "Precedent"}, 
  }; 
}

Exemple ( code Java 1.1 ) : Sources de la classe I18nListRessources_en
/* 
test d'utilisation de la classe ListResourceBundle pour 
internationaliser une application 
13/02/99 
*/ 

import java.util.*; 

/** 
 * Ressource contenant les traductions anglaises 
 * 
 * @version       0.10 13 fevrier 1999 
 * @author        Jean Michel DOUDOUX 
 * 
 */ 
public class I18nListRessources_en extends ListResourceBundle { 
  public Object[][] getContents() { 
    return contents; 
  } 

  //tableau des mots clés et des valeurs 

  static final Object[][] contents = { 
    {"texte_suivant", "Next"}, 
    {"texte_precedent", "Previous"}, 
  }; 
}

Exemple ( code Java 1.1 ) : Sources de la classe I18nListRessources_en_US
/* 
test d'utilisation de la classe ListResourceBundle pour 
internationaliser une application 
13/02/99 
*/ 
import java.util.*; 

/** 
 * Ressource contenant les traductions américaines 
 * 
 * @version 0.10 13 fevrier 1999 
 * @author Jean Michel DOUDOUX 
 * 
 */ 
public class I18nListRessources_en_US extends ListResourceBundle { 
  public Object[][] getContents() { 
    return contents; 
  } 

  //tableau des mots clés et des valeurs 

  static final Object[][] contents = { 
    {"texte_suivant", "Next"}, 
    {"texte_precedent", "Previous"}, 
  }; 
}

 

17.3.5. La création de sa propre classe fille de ResourceBundle

La troisième solution consiste à créer sa propre sous-classe de ResourceBundle et à surcharger la méthode handleGetObject().

Exemple ( code Java 1.1 ) :
abstract class MesRessources extends ResourceBundle {
   
   public Object handleGetObject(String cle)  {
      if(cle.equals(" texte_suivant "))
         return " Suivant " ;
      if(cle.equals(" texte_precedent "))
         return "Precedent " ;
         return null ;
   }
}

stop Attention : la classe ResourceBundle contient deux méthodes abstraites : handleGetObjects() et getKeys(). Si l'une des deux n'est pas définie alors il faut définir la sous-classe avec le mot clé abstract.

Il faut créer autant de sous-classes que de Locale désirées : il suffit simplement d'ajouter dans le nom de la classe le code langue et le code pays avec éventuellement le code variant.

Exemple ( code Java 1.1 ) : Sources de la classe I18nResource
/* 
Test d'utilisation d'un sous classement 
de la classe ResourceBundle pour 
internationaliser une application 
13/02/99 
*/ 
import java.util.*; 
/** 
 * Description de la classe I18nResource 
 * 
 * @version       0.10 13 fevrier 1999 
 * @author        Jean Michel DOUDOUX 
 */ 
public class I18nResource { 
  /** 
   * Constructeur de la classe 
   */ 
  public I18nResource() { 

    String texte; 
    Locale locale; 
    ResourceBundle res; 
  

    System.out.println("Locale par defaut : "); 
    res = ResourceBundle.getBundle("I18nResourceBundle"); 
    texte = (String)res.getObject("texte_suivant"); 
    System.out.println("texte_suivant = "+texte); 
    texte = (String)res.getObject("texte_precedent"); 
    System.out.println("texte_precedent = "+texte); 
  

    System.out.println("\nLocale anglaise : "); 
    locale = new Locale("en",""); 
    res = ResourceBundle.getBundle("I18nResourceBundle", locale); 
    texte = (String)res.getObject("texte_suivant"); 
    System.out.println("texte_suivant = "+texte); 
    texte = (String)res.getObject("texte_precedent"); 
    System.out.println("texte_precedent = "+texte); 

    System.out.println("\nLocale allemande : "+
       "non définie donc utilisation locale par defaut "); 
    locale = Locale.GERMAN; 
    res = ResourceBundle.getBundle("I18nResourceBundle", locale); 
    texte = (String)res.getObject("texte_suivant"); 
    System.out.println("texte_suivant = "+texte); 
    texte = (String)res.getObject("texte_precedent"); 
    System.out.println("texte_precedent = "+texte); 
  } 

 /** 
   * Pour tester la classe 
   * 
   * @param      args[]         arguments passés au programme 
   */ 
  public static void main(String[] args) { 
    I18nResource i18nResource = new I18nResource(); 
  } 

}

Exemple ( code Java 1.1 ) : Sources de la classe I18nResourceBundle
/* 
Test d'utilisation de la derivation de la classe ResourceBundle pour 
internationaliser une application 
13/02/99 
*/ 

import java.util.*; 

/** 
 * Description de la classe I18nResourceBundle 
 * C'est la classe contenant la locale par défaut 
 * Contient les traductions de la locale française (langue par defaut) 
 * Elle hérite de ResourceBundle 
 * 
 * @version       0.10 13 février 1999 
 * @author        Jean Michel DOUDOUX 
 */ 
public class I18nResourceBundle extends ResourceBundle { 
   protected Vector table; 

   public I18nResourceBundle() { 
      super(); 
      table = new Vector(); 
      table.addElement("texte_suivant"); 
      table.addElement("texte_precedent"); 
   } 

   public Object handleGetObject(String cle)  { 
      if(cle.equals(table.elementAt(0))) return "Suivant" ; 
      if(cle.equals(table.elementAt(1))) return "Precedent" ; 
      return null ; 
   } 

   public Enumeration getKeys()  { 
      return table.elements(); 
   } 

}

Exemple ( code Java 1.1 ) : Sources de la classe I18nResourceBundle_en
/* 
Test d'utilisation de la derivation de la classe ResourceBundle pour 
internationaliser une application 
13/02/99 
*/ 

import java.util.*; 

/** 
 * Description de la classe I18nResourceBundle_en 
 * Contient les traductions de la locale anglaise 
 * Elle hérite de la classe contenant la locale par défaut 
 * 
 * @version       0.10 13 février 1999 
 * @author        Jean Michel DOUDOUX 
 */ 
public class I18nResourceBundle_en extends I18nResourceBundle { 
   public Object handleGetObject(String cle)  { 
      if(cle.equals(table.elementAt(0))) return "Next" ; 
      if(cle.equals(table.elementAt(1))) return "Previous" ; 
      return null ; 
   } 
}

Exemple ( code Java 1.1 ) : Sources de la classe I18nResourceBundle_fr_FR
/* 
Test d'utilisation de la dérivation de la classe ResourceBundle pour 
internationaliser une application 
13/02/99 
*/ 

import java.util.*; 

/** 
 * Description de la classe I18nResourceBundle_fr_FR 
 * Contient les traductions de la locale française 
 * Elle hérite de la classe contenant la locale par défaut 
 * 
 * @version       0.10 13 février 1999 
 * @author        Jean Michel DOUDOUX 
 */ 
public class I18nResourceBundle_fr_FR extends I18nResourceBundle { 

   /** 
    * 
    * Retourne toujours null car la locale francaise correspond 
    * a la locale par defaut 
    * 
    */ 
   public Object handleGetObject(String cle)  { 
      return null ; 
   } 
}

 


Développons en Java v 2.20   Copyright (C) 1999-2021 Jean-Michel DOUDOUX.   
[ Précédent ] [ Sommaire ] [ Suivant ] [ Télécharger ]      [ Accueil ] [ Commentez ]