Eine Anleitung zum ResourceBundle

1. Übersicht

Viele Softwareentwickler haben während ihrer beruflichen Laufbahn die Möglichkeit, mehrsprachige Systeme oder Anwendungen zu entwickeln. Diese sind normalerweise für Endbenutzer aus verschiedenen Regionen oder Sprachgebieten bestimmt.

Es ist immer schwierig, diese Anwendungen zu warten und zu erweitern. Die Fähigkeit, gleichzeitig mit verschiedenen lokalisierungsspezifischen Daten zu arbeiten, ist normalerweise von entscheidender Bedeutung. Die Änderung der Anwendungsdaten sollte so einfach wie möglich sein, ohne dass eine Neukompilierung erforderlich ist. Aus diesem Grund vermeiden wir im Allgemeinen das Hardcodieren von Etiketten- oder Schaltflächennamen.

Glücklicherweise können wir uns auf Java verlassen, das uns diese Klasse bietet, die uns hilft, alle oben genannten Probleme zu lösen.

Einfach ausgedrückt, ermöglicht das ResourceBundle unserer Anwendung, Daten aus verschiedenen Dateien zu laden, die länderspezifische Daten enthalten.

1.1. ResourceBundles

Das erste, was wir wissen sollten, ist, dass sich alle Dateien innerhalb eines Ressourcenpakets im selben Paket / Verzeichnis befinden und einen gemeinsamen Basisnamen haben müssen . Sie können länderspezifische Suffixe haben, die Sprache, Land oder Plattform angeben und durch einen Unterstrich getrennt sind.

Es ist wichtig, dass wir den Ländercode anhängen, wenn bereits ein Sprachcode vorhanden ist, oder die Plattform, wenn Sprach- und Ländercodes vorhanden sind.

Schauen wir uns Beispieldateinamen an:

  • ExampleResource
  • ExampleResource_en
  • ExampleResource_en_US
  • ExampleResource_en_US_UNIX

Die Standarddatei für jedes Datenpaket ist immer eine ohne Suffixe - ExampleResource . Da es zwei Unterklassen von ResourceBundle gibt : PropertyResourceBundle und ListResourceBundle , können wir Daten sowohl in Eigenschaftendateien als auch in Java-Dateien austauschbar aufbewahren.

Jede Datei muss einen länderspezifischen Namen und eine geeignete Dateierweiterung haben , z. B. ExampleResource_en_US.properties oder Example_en.java .

1.2. Eigenschaftendateien - PropertyResourceBundle

Eigenschaftendateien werden durch PropertyResourceBundle dargestellt. Sie speichern Daten in Form von Schlüssel-Wert-Paaren, bei denen zwischen Groß- und Kleinschreibung unterschieden wird.

Lassen Sie uns eine Beispiel-Eigenschaftendatei analysieren:

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

Wie wir sehen können, gibt es drei verschiedene Arten, Schlüssel-Wert-Paare zu definieren.

Alle von ihnen sind gleichwertig, aber der erste ist wahrscheinlich der beliebteste unter Java- Programmierern. Es ist wichtig zu wissen, dass wir auch Kommentare in Eigenschaftendateien einfügen können. Kommentare beginnen immer mit # oder ! .

1.3. Java-Dateien - ListResourceBundle

Um unsere sprachspezifischen Daten zu speichern, müssen wir zunächst eine Klasse erstellen, die ListResourceBundle erweitert und die Methode getContents () überschreibt . Die Klassennamenkonvention ist dieselbe wie für Eigenschaftendateien.

Für jedes Gebietsschema müssen wir eine separate Java-Klasse erstellen.

Hier ist eine Beispielklasse:

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" }} }; } }

Java-Dateien haben einen großen Vorteil gegenüber Eigenschaftendateien, nämlich die Möglichkeit, jedes gewünschte Objekt aufzunehmen - nicht nur Strings.

Andererseits erfordert jede Änderung oder Einführung einer neuen länderspezifischen Java-Klasse eine Neukompilierung einer Anwendung, während Eigenschaftendateien ohne zusätzlichen Aufwand erweitert werden können.

2. Verwenden Sie Ressourcenpakete

Wir wissen bereits, wie Ressourcenpakete definiert werden, und sind daher bereit, sie zu verwenden.

Betrachten wir das Shortcode-Snippet:

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"});

Erstens können wir unser Gebietsschema definieren , es sei denn, wir möchten nicht das Standardgebietsschema verwenden.

Rufen wir danach eine statische Factory-Methode von ResourceBundle auf . Wir müssen den Bundle-Namen mit seinem Paket / Verzeichnis und dem Gebietsschema als Parameter übergeben.

Es gibt auch eine Factory-Methode, für die nur dann ein Bundle-Name erforderlich ist, wenn das Standardgebietsschema in Ordnung ist. Sobald wir das Objekt haben, können wir Werte über ihre Schlüssel abrufen.

Darüber hinaus zeigt das Beispiel, dass wir getString (String-Schlüssel) , getObject (String-Schlüssel) und getStringArray (String-Schlüssel) verwenden können , um die gewünschten Werte abzurufen .

3. Auswahl der richtigen Bundle-Ressource

Wenn wir eine Bundle-Ressource verwenden möchten, ist es wichtig zu wissen, wie Java Bundle-Dateien auswählt.

Stellen wir uns vor, wir arbeiten mit einer Anwendung, die Beschriftungen in polnischer Sprache benötigt, aber Ihr Standard- JVM- Gebietsschema ist Locale.US .

Zu Beginn sucht die Anwendung im Klassenpfad nach den Dateien, die für das gewünschte Gebietsschema geeignet sind. Es beginnt mit dem spezifischsten Namen, dh einem Namen, der eine Plattform, ein Land und eine Sprache enthält.

Dann geht es allgemeiner. Wenn keine Übereinstimmung vorliegt, wird dieses Mal auf das Standardgebietsschema zurückgegriffen, ohne dass die Plattform überprüft wird.

Falls keine Übereinstimmung vorliegt, wird versucht, das Standardpaket zu lesen. Alles sollte klar sein, wenn wir uns die Reihenfolge der ausgewählten Dateinamen ansehen:

  • Label_pl_PL_UNIX
  • Label_pl_PL
  • Label_pl
  • Label_en_US
  • Label_de
  • Etikette

Wir sollten bedenken, dass jeder Name sowohl .java- als auch .properties- Dateien darstellt, wobei ersterer Vorrang vor letzterem hat. Wenn keine geeignete Datei vorhanden ist, wird eine MissingResourceException ausgelöst.

4. Vererbung

Ein weiterer Vorteil des Ressourcenpaketkonzepts ist die Eigenschaftsvererbung. Dies bedeutet, dass Schlüssel-Wert-Paare, die in weniger spezifischen Dateien enthalten sind, von denen geerbt werden, die im Vererbungsbaum höher sind.

Nehmen wir an, wir haben drei Eigenschaftendateien:

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

Das für das Gebietsschema abgerufene Ressourcenpaket ("pl", "PL") würde alle drei Schlüssel / Werte im Ergebnis zurückgeben. Es ist erwähnenswert, dass bei der Vererbung von Eigenschaften kein Rückgriff auf das Standard-Gebietsschema-Bundle erfolgt.

Was mehr ist, Listresourcebundles und PropertyResourceBundles sind nicht in der gleichen Hierarchie.

Wenn also eine Eigenschaftendatei im Klassenpfad gefunden wird, werden Schlüssel-Wert-Paare nur von Eigenschaftendateien geerbt. Die gleiche Regel gilt für Java-Dateien.

5. Anpassung

Alles, was wir oben gelernt haben, war über die Standardimplementierung von ResourceBundle . Es gibt jedoch eine Möglichkeit, das Verhalten zu ändern.

Dazu erweitern wir ResourceBoundle.Control und überschreiben seine Methoden.

Beispielsweise können wir die Zeit ändern, in der Werte im Cache gespeichert werden, oder die Bedingung festlegen, wann der Cache neu geladen werden soll.

Zum besseren Verständnis bereiten wir als Beispiel eine kurze Methode vor:

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

Der Zweck dieser Methode besteht darin, die Art der Auswahl von Dateien im Klassenpfad zu ändern. Wie wir sehen können, gibt ExampleControl nur das polnische Gebietsschema zurück , unabhängig vom Standard- oder definierten Gebietsschema .

6. UTF-8

Since there're still many applications using JDK 8 or older versions, it's worth to know that prior to Java 9ListResourceBundles had one more advantage over PropertyResourceBundles. As Java files can store String objects, they are able to hold any character supported by UTF-16 encoding.

On the contrary, PropertyResourceBundle loads files by default using ISO 8859-1 encoding, which has fewer characters than UTF-8 (causing problems for our Polish language examples).

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.

Wir vermeiden auch Werte zu, so dass wir die unterstützten erweitern Locales durch einfaches Hinzufügen von neuen Locale - Dateien unserer Anwendung ermöglicht wird sanft geändert und gepflegt.

Wie immer ist der Beispielcode über GitHub verfügbar.