Un guide du ResourceBundle

1. Vue d'ensemble

De nombreux développeurs de logiciels, au cours de leur carrière professionnelle, ont l'opportunité de développer des systèmes ou des applications multilingues. Ceux-ci sont généralement destinés aux utilisateurs finaux de différentes régions ou de différentes zones linguistiques.

Il est toujours difficile de maintenir et d'étendre ces applications. Une capacité à fonctionner avec diverses données spécifiques de localisation en même temps est généralement cruciale. La modification des données de l'application doit être aussi simple que possible sans nécessiter de recompilation. C'est pourquoi nous évitons généralement les noms d'étiquettes ou de boutons codés en dur.

Heureusement, nous pouvons miser sur Java qui nous fournit cette classe, ce qui nous aide à résoudre tous les problèmes mentionnés ci-dessus.

En termes simples, ResourceBundle permet à notre application de charger des données à partir de fichiers distincts contenant des données spécifiques aux paramètres régionaux.

1.1. ResourceBundles

La première chose que nous devons savoir est que tous les fichiers d' un même ensemble de ressources doivent être dans le même package / répertoire et avoir un nom de base commun . Ils peuvent avoir des suffixes spécifiques aux paramètres régionaux indiquant la langue, le pays ou la plate-forme séparés par un symbole de soulignement.

Il est important que nous puissions ajouter le code du pays s'il existe déjà un code de langue, ou une plate-forme si des codes de langue et de pays sont présents.

Regardons des exemples de noms de fichiers:

  • ExempleResource
  • ExempleResource_en
  • ExempleResource_en_US
  • ExempleResource_en_US_UNIX

Le fichier par défaut pour chaque ensemble de données est toujours un fichier sans suffixe - ExampleResource . Comme il existe deux sous-classes de ResourceBundle : PropertyResourceBundle et ListResourceBundle , nous pouvons conserver de manière interchangeable des données dans des fichiers de propriétés ainsi que des fichiers java.

Chaque fichier doit avoir un nom spécifique à la localisation et une extension de fichier approprié , par exemple, ExampleResource_en_US.properties ou Example_en.java .

1.2. Fichiers de propriétés - PropertyResourceBundle

Les fichiers de propriétés sont représentés par PropertyResourceBundle. Ils stockent les données sous la forme de paires clé-valeur sensibles à la casse.

Analysons un exemple de fichier de propriétés:

# Buttons continueButton continue cancelButton=cancel ! Labels helloLabel:hello 

Comme nous pouvons le voir, il existe trois styles différents pour définir des paires clé-valeur.

Tous sont équivalents, mais le premier est probablement le plus populaire parmi les programmeurs Java . Il est intéressant de savoir que nous pouvons également mettre des commentaires dans des fichiers de propriétés. Les commentaires commencent toujours par # ou ! .

1.3. Fichiers Java - ListResourceBundle

Tout d'abord, pour stocker nos données spécifiques à la langue, nous devons créer une classe qui étend ListResourceBundle et remplace la méthode getContents () . La convention de nom de classe est la même que pour les fichiers de propriétés.

Pour chaque Locale, nous devons créer une classe Java distincte.

Voici un exemple de classe:

public class ExampleResource_pl_PL extends ListResourceBundle { @Override protected Object[][] getContents() { return new Object[][] { {"currency", "polish zloty"}, {"toUsdRate", new BigDecimal("3.401")}, {"cities", new String[] { "Warsaw", "Cracow" }} }; } }

Les fichiers Java ont un avantage majeur par rapport aux fichiers de propriétés qui est la possibilité de contenir n'importe quel objet que nous voulons - pas seulement des chaînes.

D'un autre côté, chaque modification ou introduction d'une nouvelle classe java spécifique aux paramètres régionaux nécessite la recompilation d'une application alors que les fichiers de propriétés peuvent être étendus sans effort supplémentaire.

2. Utiliser des ensembles de ressources

Nous savons déjà comment définir des ensembles de ressources, nous sommes donc prêts à les utiliser.

Considérons l'extrait de code court:

Locale locale = new Locale("pl", "PL"); ResourceBundle exampleBundle = ResourceBundle.getBundle("package.ExampleResource", locale); assertEquals(exampleBundle.getString("currency"), "polish zloty"); assertEquals(exampleBundle.getObject("toUsdRate"), new BigDecimal("3.401")); assertArrayEquals(exampleBundle.getStringArray("cities"), new String[]{"Warsaw", "Cracow"});

Tout d'abord, nous pouvons définir notre Locale , sauf si nous ne voulons pas utiliser celui par défaut.

Après cela, appelons une méthode de fabrique statique de ResourceBundle . Nous devons passer le nom du bundle avec son package / répertoire et la locale comme paramètres.

Il existe également une méthode d'usine qui ne nécessite un nom de bundle que si la locale par défaut est correcte. Dès que nous avons l'objet, nous pouvons récupérer les valeurs par leurs clés.

En outre, l'exemple montre que nous pouvons utiliser getString (clé de chaîne) , getObject (clé de chaîne) et getStringArray (clé de chaîne) pour obtenir les valeurs souhaitées.

3. Sélection de la ressource de bundle appropriée

Si nous voulons utiliser une ressource de bundle, il est important de savoir comment Java sélectionne les fichiers de bundle.

Imaginons que nous travaillions avec une application qui a besoin d'étiquettes en polonais mais que votre locale JVM par défaut est Locale.US .

Au début, l'application recherchera les fichiers dans le chemin de classe correspondant à la locale que vous demandez. Il commence par le nom le plus spécifique, c'est-à-dire contenant une plate-forme, un pays et une langue.

Ensuite, cela passe à plus général. S'il n'y a pas de correspondance, il revient à la locale par défaut sans vérification de plate-forme cette fois.

En cas de non-correspondance, il essaiera de lire le bundle par défaut. Tout devrait être clair lorsque nous examinons l'ordre des noms de fichiers sélectionnés:

  • Label_pl_PL_UNIX
  • Label_pl_PL
  • Label_pl
  • Label_en_US
  • Label_en
  • Étiquette

Nous devons garder à l' esprit que chaque nom représente les deux .java et .properties fichiers, mais l'ancien a préséance sur celui - ci. Lorsqu'il n'y a pas de fichier approprié, une MissingResourceException est levée.

4. Héritage

Un autre avantage du concept de regroupement de ressources est l'héritage de propriété. Cela signifie que les paires clé-valeur incluses dans des fichiers moins spécifiques sont héritées par celles qui sont plus élevées dans l'arborescence d'héritage.

Supposons que nous ayons trois fichiers de propriétés:

#resource.properties cancelButton = cancel #resource_pl.properties continueButton = dalej #resource_pl_PL.properties backButton = cofnij

L'ensemble de ressources récupéré pour les paramètres régionaux («pl», «PL») renverrait les trois clés / valeurs dans le résultat. Il convient de mentionner qu'il n'y a pas de retour au bundle de paramètres régionaux par défaut en ce qui concerne l'héritage de propriété.

De plus, ListResourceBundles et PropertyResourceBundles ne sont pas dans la même hiérarchie.

Ainsi, si un fichier de propriétés est trouvé dans le chemin de classe, les paires clé-valeur sont héritées uniquement des fichiers de propriétés. La même règle s'applique aux fichiers Java.

5. Personnalisation

Tout ce que nous avons appris ci-dessus concernait l'implémentation par défaut de ResourceBundle . Cependant, il existe un moyen de modifier son comportement.

Nous faisons cela en étendant ResourceBoundle.Control et en remplaçant ses méthodes.

Par exemple, nous pouvons modifier l'heure de conservation des valeurs dans le cache ou déterminer la condition à laquelle le cache doit être rechargé.

Pour une meilleure compréhension, préparons une courte méthode à titre d'exemple:

public class ExampleControl extends ResourceBundle.Control { @Override public List getCandidateLocales(String s, Locale locale) { return Arrays.asList(new Locale("pl", "PL")); } }

Le but de cette méthode est de changer la manière de sélectionner les fichiers dans le chemin de classe. Comme nous pouvons le voir, ExampleControl ne retournera que les paramètres régionaux polish , quels que soient les paramètres régionaux par défaut ou définis .

6. UTF-8

Étant donné qu'il existe encore de nombreuses applications utilisant JDK 8 ou des versions antérieures, il convient de savoir qu'avant Java 9, ListResourceBundles avait un autre avantage par rapport à PropertyResourceBundles . Comme les fichiers Java peuvent stocker des objets String, ils peuvent contenir n'importe quel caractère pris en charge par le codage UTF-16 .

Au contraire, PropertyResourceBundle charge les fichiers par défaut en utilisant l' encodage ISO 8859-1 , qui a moins de caractères que UTF-8 (ce qui pose des problèmes pour nos exemples en polonais).

In order to save characters which are beyond UTF-8, we can use the Native-To-ASCII converter – native2ascii. It converts all characters that aren't compliant with ISO 8859-1 by encoding them to \uxxxx notation.

Here's an example command:

native2ascii -encoding UTF-8 utf8.properties nonUtf8.properties

And let's see how properties look like before and after a change of encoding:

#Before polishHello=cześć #After polishHello=cze\u015b\u0107

Fortunately, this inconvenience exists no longer in Java 9. JVM reads property files in UTF-8 encoding, and there's no problem in using non-Latin characters.

7. Conclusion

BundleResource contains much of what we need to develop a multilingual application. The features we've covered make manipulation of different locales pretty straightforward.

Nous avons aussi des valeurs à éviter hardcoding, ce qui nous permet d'élargir les prises en charge les versions localisées en ajoutant simplement de nouveaux paramètres régionaux fichiers permettant notre application à modifier et maintenue en douceur.

Comme toujours, l'exemple de code est disponible dans over sur GitHub.