Ein Leitfaden zum ResourceBundle

1. Überblick

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

Es ist immer schwierig, diese Anwendungen zu warten und zu erweitern. Die Fähigkeit, gleichzeitig mit verschiedenen lokalisierungsspezifischen Daten zu arbeiten, ist in der Regel 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 generell Hardcoding-Beschriftungen oder Schaltflächennamen.

Glücklicherweise können wir auf Java setzen, das uns diese Klasse zur Verfügung stellt, wodurch wir alle oben genannten Probleme lösen können.

Einfach ausgedrückt ermöglicht das ResourceBundle unserer Anwendung das Laden von Daten aus unterschiedlichen Dateien, die ländereinstellungsspezifische Daten enthalten. **

1.1. ResourceBundles

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

Es ist wichtig, dass wir einen Ländercode hinzufügen, wenn bereits ein Sprachcode vorhanden ist, oder eine Plattform, wenn Sprach- und Ländercodes vorhanden sind

Schauen wir uns die Beispieldateinamen an:

  • ExampleResource

  • ExampleResource de__

  • ExampleResource de US

  • ExampleResource en US UNIX__

Die Standarddatei für jedes Datenpaket ist immer eine Datei ohne Suffixe - ExampleResource . Da es zwei Unterklassen von ResourceBundle gibt:

  • PropertyResourceBundle und ListResourceBundle ** können wir Daten in Eigenschaftsdateien sowie in Java-Dateien austauschbar aufbewahren.

Jede Datei muss einen für das Gebietsschema spezifischen Namen und eine entsprechende Dateierweiterung haben, beispielsweise ExampleResource en US.properties oder Example en.java__.

1.2. Property-Dateien - PropertyResourceBundle

Eigenschaftendateien werden durch __PropertyResourceBundle dargestellt. Sie speichern Daten in Form von Paßwörtern, bei denen die Groß-/Kleinschreibung beachtet wird.

Analysieren wir eine Beispieleigenschaftendatei:

# Buttons
continueButton continue
cancelButton=cancel

! Labels
helloLabel:hello

Wie wir sehen können, gibt es drei verschiedene Arten, Schlüsselpaare zu definieren.

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

1.3. Java-Dateien - ListResourceBundle

Um unsere sprachspezifischen Daten speichern zu können, müssen Sie zunächst eine Klasse erstellen, die ListResourceBundle erweitert und die __getContents () - Methode überschreibt. Die Klassennamenskonvention ist dieselbe wie für Eigenschaftendateien.

Für jede Locale müssen wir eine eigene 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 gegenüber Property-Dateien einen großen Vorteil, da sie die Möglichkeit haben, jedes gewünschte Objekt zu speichern - nicht nur Strings.

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

2. Ressourcenpakete verwenden

Wir wissen bereits, wie Ressourcenpakete definiert werden, also können wir sie verwenden.

Betrachten wir das kurze Code-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 unseren Locale definieren, es sei denn, wir möchten nicht den Standard verwenden

Danach rufen wir eine statische Factory-Methode von ResourceBundle auf. Wir müssen den Paketnamen mit seinem Paket/Verzeichnis und dem Gebietsschema als Parameter übergeben.

Es gibt auch eine Factory-Methode, für die nur ein Bündelname erforderlich ist, wenn das Standardgebietsschema in Ordnung ist. Sobald wir das Objekt haben, können wir die Werte anhand ihrer Schlüssel abrufen.

Außerdem zeigt das Beispiel, dass wir getString (String key) , getObject (String key), und getStringArray (String key) verwenden können, um die gewünschten Werte zu erhalten.

3. Auswahl der richtigen Bündelressource

  • Wenn wir eine Bündelressource verwenden möchten, ist es wichtig zu wissen, wie Java Bündeldateien auswählt. **

Stellen wir uns vor, dass wir mit einer Anwendung arbeiten, für die Beschriftungen in Polnisch erforderlich sind. Die Standardeinstellung für JVM ist jedoch Locale.US .

Zu Beginn sucht die Anwendung nach den Dateien im Klassenpfad, die für das gewünschte Gebietsschema geeignet sind. Es beginnt mit dem spezifischsten 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 ohne Plattformprüfung zurückgegriffen.

  • Falls keine Übereinstimmung besteht, wird versucht, das Standard-Bundle zu lesen. ** Alles sollte klar sein, wenn wir die Reihenfolge der ausgewählten Dateinamen betrachten:

  • Label pl PL UNIX__

  • Label pl PL

  • Label pl__

  • Label de US

  • Label de__

  • Etikette

Wir sollten bedenken, dass jeder Name sowohl .java - als auch .properties -Dateien darstellt, der erste hat jedoch Vorrang vor den letzteren.

Wenn keine passende Datei vorhanden ist, wird eine MissingResourceException ausgelöst.

4. Erbe

Ein weiterer Vorteil des Ressourcenpaketkonzepts ist die Vererbung von Immobilien. Dies bedeutet, dass Schlüsselwertpaare, die in weniger spezifischen Dateien enthalten sind, von denen geerbt werden, die höher im Vererbungsbaum sind.

Nehmen wir an, wir haben drei Eigenschaftsdateien:

#resource.properties
cancelButton = cancel

#resource__pl.properties
continueButton = dalej

#resource__pl__PL.properties
backButton = cofnij

Das für __Locale ("pl", "PL") abgerufene Ressourcenpaket würde alle drei Schlüssel/Werte im Ergebnis zurückgeben. Es lohnt sich zu erwähnen, es gibt keinen Rückgriff auf das Standard-Ländereinstellungspaket , sofern die Vererbung von Immobilien in Betracht gezogen wird.

Außerdem befinden sich ListResourceBundles und PropertyResourceBundles nicht in derselben Hierarchie.

Wenn eine Eigenschaftendatei im Klassenpfad gefunden wird, werden Schlüssel-Wert-Paare nur von Eigenschaftsdateien 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, sein Verhalten zu ändern.

Wir machen dies, indem wir ResourceBoundle.Control erweitern und seine Methoden überschreiben.

Zum Beispiel können wir die Zeit für das Speichern der Werte im Cache ändern oder die Bedingung bestimmen, wann der Cache neu geladen werden soll.

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

public class ExampleControl extends ResourceBundle.Control {

    @Override
    public List<Locale> getCandidateLocales(String s, Locale locale) {
        return Arrays.asList(new Locale("pl", "PL"));
    }
}

Der Zweck dieser Methode besteht darin, die Art und Weise der Auswahl von Dateien im Klassenpfad zu ändern. Wie wir sehen können, gibt ExampleControl nur polierte Locale zurück, unabhängig von der Standardeinstellung oder definierten Locale .

6. UTF-8

Da es immer noch viele Anwendungen mit JDK 8 oder älteren Versionen gibt, ist es zu wissen, dass vor Java 9 ListResourceBundles einen weiteren Vorteil gegenüber PropertyResourceBundles hatte. Da Java-Dateien String-Objekte speichern können, können sie alle Zeichen enthalten, die von der UTF-16 -Kodierung unterstützt werden.

Im Gegensatz dazu lädt PropertyResourceBundle Dateien standardmäßig mit der ISO 8859-1 -Codierung, die weniger Zeichen als UTF-8 enthält (was Probleme für unsere polnischen Sprachbeispiele verursacht).

Um Zeichen zu speichern, die über UTF-8 hinausgehen, können Sie den Native-To-ASCII -Konverter - native2ascii verwenden. Es konvertiert alle Zeichen, die nicht mit ISO 8859-1 kompatibel sind, indem es sie in die \ uxxxx -Notation codiert.

Hier ist ein Beispielbefehl:

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

Und lassen Sie uns sehen, wie Eigenschaften vor und nach einer Codierungsänderung aussehen:

#Before
polishHello=cześć

#After
polishHello=cze\u015b\u0107

Glücklicherweise gibt es diese Unannehmlichkeiten in Java 9 nicht mehr. JVM liest Eigenschaftsdateien in UTF-8 -Codierung, und es ist kein Problem bei der Verwendung nicht lateinischer Zeichen.

7. Fazit

BundleResource enthält viel von dem, was wir zur Entwicklung einer mehrsprachigen Anwendung benötigen. Die von uns behandelten Funktionen machen die Manipulation verschiedener Ländereinstellungen ziemlich unkompliziert.

Wir vermeiden auch Hardcoding-Werte, sodass wir die unterstützten Locales -Dateien erweitern können, indem Sie einfach neue Locale -Dateien hinzufügen, sodass unsere Anwendung reibungslos geändert und verwaltet werden kann.