From 33613a85afc4b1481367fbe92a17ee59c240250b Mon Sep 17 00:00:00 2001
From: Sven Eisenhauer
+Seit dem JDK 1.1 bietet Java Unterstützung für das Internationalisieren
+von Anwendungen. Auf diese Weise können Programme geschrieben
+werden, die nicht nur im eigenen, sondern auch in anderen Ländern
+der Erde verwendbar sind. Verschiedene Länder und Regionen unterscheiden
+sich nicht nur durch die verwendete Sprache und die zugrunde liegenden
+Schrift- und Zahlzeichen, sondern auch durch unterschiedliche Kalender
+und Zeitzonen, abweichende Zahlen-, Zeit- und Datumsformatierungen,
+eigene Währungen, unterschiedliche Telefonnummern- und Anschriftenkonventionen
+und so weiter.
+
+
+Die Begriffe Internationalisierung und Lokalisierung
+werden häufig synonym gebraucht oder miteinander verwechselt.
+Unter Internationalisierung wollen wir das Bündel technischer
+Maßnahmen verstehen, mit denen ein Programm für die Verwendung
+in verschiedenen Ländern und Sprachräumen vorbereitet wird.
+Unter Lokalisierung verstehen wir dagegen die konkrete Anpassung
+eines internationalisierten Programms an eine ganz bestimmte Sprache.
+Ein Teil der Internationalisierung eines Programms ist es also beispielsweise,
+wenn Textkonstanten nicht mehr fest im Programm enthalten sind, sondern
+zur Laufzeit aus Ressourcendateien geladen werden. Die Lokalisierung
+hingegen besteht darin, eben diese Textressourcen in die jeweilige
+Sprache zu übersetzen.
+
+
+Im angelsächsischen Sprachraum werden die beiden unhandlichen
+Begriffe meist abgekürzt. Statt »Internationalisierung«
+wird I18N gesagt, statt »Lokalisierung«
+L10N. Die Spielregel ist ganz einfach:
+Zwischen den ersten und letzten Buchstaben des jeweiligen Wortes steht
+die Gesamtzahl der Zeichen.
+Internationalisierung ist ein nicht-triviales Thema, das viele Facetten
+hat. Einige Beispiele dafür, in welcher Weise es von Java unterstützt
+wird, sind:
+
+Ausgangspunkt der Lokalisierung eines Java-Programms ist die Klasse
+Locale
+aus dem Paket java.util.
+Jede Instanz dieser Klasse identifiziert eine bestimmte geografische,
+kulturelle oder politische Region auf der Erde inklusive der Sprache,
+die dort üblicherweise verwendet wird. Ein Locale-Objekt
+kann wie folgt erzeugt werden:
+
+
+Das erste Argument ist ein String
+mit dem Code für die Sprache, wie er im Standard ISO-639
+definiert ist. Der Sprachcode wird kleingeschrieben und lautet z.B.
+»en« für englisch, »fr« für französich
+oder »de« für deutsch. Das zweite Argument gibt das
+Land gemäß dem Standard ISO-3166
+an, hier werden stets große Buchstaben verwendet. So stehen
+beispielsweise »US« für die USA, »GB« für
+England, »FR« für Frankreich und »DE« für
+Deutschland.
+
+
+Der Länderteil ist optional. Wird an seiner Stelle ein Leerstring
+übergeben, repräsentiert die Locale
+lediglich eine Sprache ohne spezifisches Land. »en« alleine
+steht dann beispielsweise für die englische Sprache, wie sie
+nicht nur in England, sondern auch in den USA oder Kanada verständlich
+wäre. Soll dagegen auf kanadische Besonderheiten abgestellt werden,
+ist als Land »CA« zu ergänzen. Für den französischsprachigen
+Teil von Kanada würde dagegen eine Locale
+aus »fr« und »CA« gebildet werden.
+
+
+Neben Sprach- und Länderkennung kann eine Locale
+einen dritten Parameter haben. Dieser wird als Variante bezeichnet
+und kann dazu verwendet werden, Betriebssysteme oder Konfigurationen
+voneinander zu unterscheiden. So werden lokalisierte Hilfetexte unter
+UNIX möglicherweise mit anderen Zeichensätzen oder Hilfsprogrammen
+arbeiten als unter Windows oder auf dem Macintosh. Wir wollen darauf
+allerdings nicht weiter eingehen.
+Die Klasse Locale
+bietet mit der statischen Methode getDefault
+die Möglichkeit, eine Locale
+zu beschaffen, die dem Land entspricht, in dem das laufende Programm
+ausgeführt wird. Mit getCountry
+kann der Länder-, mit getLanguage
+der Sprachcode und mit getVariant
+die Variante ermittelt werden:
+
+
+Mit getDefault
+kann ein Programm zur Laufzeit ermitteln, in welchem Land es läuft
+und welche Sprache dort gesprochen wird. Weiterhin stellt die Klasse
+noch einige Konstanten mit vordefinierten Locale-Objekten
+für wichtige Länder und Sprachen zur Verfügung.
+
+
+Das folgende Beispielprogramm ermittelt die Standard-Locale
+des aktuellen Rechners und die vordefinierten Locales für Deutschland,
+England und die USA und gibt sie auf der Console aus:
+
+
+
+
+
+
+ Titel
+ Inhalt
+ Suchen
+ Index
+ DOC
+ Handbuch der Java-Programmierung, 5. Auflage
+
+ <<
+ <
+ >
+ >>
+ API
+ Kapitel 17 - Utility-Klassen II
+
+
+
+
+
+17.4 Internationalisierung und Lokalisierung
+
+
+
+
+
+
+
+
+
+
+
+
+![]()
+
+
+
+![]()
+
+
+
+
+
+ Hinweis
+
+
+
+
+
+
+
+17.4.1 Die Klasse Locale
+
+
+
+
+
+
+
+
+
+
+public Locale(String language, String country)
+
+
+
+java.util.Locale
+
+
+
+
+
+
+
+![]()
+
+
+
+![]()
+
+
+
+
+
+ Hinweis
+
+
+
+
+
+
+
+
+
+
+public static Locale getDefault()
+
+public String getLanguage()
+public String getCountry()
+public String getVariant()
+
+
+
+java.util.Locale
+
+
+
+Listing 17.7: Darstellung einiger Locales
+
+
+
+
+
+001 /* Listing1707.java */
+002
+003 import java.util.*;
+004
+005 public class Listing1707
+006 {
+007 public static void main(String[] args)
+008 {
+009 System.out.println("Default: " + Locale.getDefault());
+010 System.out.println("GERMANY: " + Locale.GERMANY);
+011 System.out.println("UK : " + Locale.UK);
+012 System.out.println("US : " + Locale.US);
+013 }
+014 }
+
+
+Listing1707.java
+
+Die Ausgabe des Programm sieht hierzulande etwa so aus:
+
+
+Default: de_DE
+GERMANY: de_DE
+UK : en_GB
+US : en_US
+
+
+
+
+
+
+
+In früheren Abschnitten wurde schon gezeigt, wie Fließkommazahlen +erzeugt und verarbeitet werden können. Nach Umwandlung in einen +String +lassen sie sich mit den Methoden print +und println +auf der Console, in Dateien oder auf einer grafischen Oberfläche +ausgeben - allerdings ohne die Möglichkeit, auf die Formatierung +der Ausgabe Einfluss zu nehmen. Wir wollen uns in diesem Abschnitt +die Klasse DecimalFormat +aus dem Paket java.text +ansehen und zeigen, wie Ganz- und Fließkommazahlen mit ihrer +Hilfe formatiert werden können. + +
+DecimalFormat +ist eine Spezialisierung der Klasse NumberFormat +und dient zur Formatierung von Ganz- oder Fließkommazahlen in +Dezimaldarstellung unter Berücksichtigung länderspezifischer +Besonderheiten. Der Aufrufer legt dabei die Anzahl der Vor- oder Nachkommastellen +fest, und DecimalFormat +entscheidet, ob ein Punkt oder Komma als Dezimaltrennzeichen verwendet +werden soll. + +
+Eine Instanz der Klasse DecimalFormat +kann entweder mit new +oder durch Aufruf einer der Factory-Methoden der Klasse NumberFormat +erzeut werden: +
+
+
++public DecimalFormat(String pattern) ++ + |
++java.text.DecimalFormat | +
+
+
++public static NumberFormat getNumberInstance() +public static NumberFormat getNumberInstance(Locale locale) ++ + |
++java.text.NumberFormat | +
+Soll ein DecimalFormat-Objekt +für die aktuelle Locale +erzeugt werden, kann es durch Übergabe eines Formatstrings direkt +instanziert werden. Wird es dagegen für eine andere Locale +benötigt, muss es mit der statischen Methode getNumberInstance +der Klasse NumberFormat +erzeugt werden. In diesem Fall muss noch die Methode applyPattern +aufgerufen werden, um den Formatstring zuzuweisen: +
+
+
++public void applyPattern(String pattern) ++ + |
++java.text.NumberFormat | +
+
![]() |
+![]() |
+
+
+ +getNumberInstance +gibt einen Wert vom Typ NumberFormat +zurück (der Basisklasse von DecimalFormat). +Das ist zwar in westlichen Ländern in der Regel eine Instanz +von DecimalFormat. +Ganz sicher kann sich ein Programm aber nur sein, wenn es den Typ +des Rückgabewerts vor der Konvertierung nach DecimalFormat +mit instanceof +überprüft. |
+
+
|
+![]() |
+
+Der Formatstring definiert eine Maske zur Formatierung der Ausgabe. +Er besteht aus zwei Teilen, die durch ein Semikolon voneinander getrennt +sind. Der erste gibt die Formatierungsregeln für positive, der +zweite für negative Zahlen an. Der zweite Teil kann auch ausgelassen +werden. Dann werden negative Zahlen wie positive Zahlen mit vorangestelltem +Minuszeichen formatiert. Jedes Zeichen im Formatstring regelt die +Darstellung der korrespondierenden Ziffer. Der Formatstring kann folgende +Zeichen enthalten: + +
+
| Symbol | +Bedeutung |
| 0 | +Eine einzelne Ziffer |
| # | +Eine einzelne Ziffer. Wird ausgelassen, +falls führende Null. |
| . | +Dezimaltrennzeichen |
| , | +Tausendertrennzeichen |
| E | +Aktiviert Exponentialdarstellung |
| % | +Ausgabe als Prozentwert |
+Tabelle 17.2: Formatzeichen für DecimalFormat
+ ++Zusätzlich können beliebige Zeichen vorangestellt oder hinten +angefügt werden; sie werden dann unverändert ausgegeben. +Ungültige Formatstrings werden mit einer IllegalArgumentException +quittiert. Falls nach einem Semikolon ein eigener Formatstring für +negative Zahlen angegeben wird, muss er mit Ausnahme eines veränderten +Prefixes und Suffixes genau dem Formatstring für positive Zahlen +entsprechen. + +
+Die Formatierung einer Zahl wird durch Aufruf von format +ausgeführt: +
+
+
++public final String format(long number) +public final String format(double number) ++ + |
++java.text.DecimalFormat | +
+Die Methode gibt es sowohl mit einem long-Parameter +zur Ausgabe von Ganzzahlen als auch mit einem double +zur Ausgabe von Fließkommazahlen. Da der Formatstring bereits +bei der Konstruktion des Objekts übergeben wurde, kann format +mehrfach aufgerufen werden, um nacheinander weitere Zahlen zu formatieren. + +
+Das folgende Programm zeigt die beispielhafte Anwendung von DecimalFormat. +Es gibt die Zahl 1768.3518 für die aktuelle Locale +in unterschiedlichen Längen und Formatierungen auf der Console +aus: + + +
+
+
+
+001 /* Listing1708.java */
+002
+003 import java.text.*;
+004
+005 public class Listing1708
+006 {
+007 public static void print(double value, String format)
+008 {
+009 DecimalFormat df = new DecimalFormat(format);
+010 System.out.println(df.format(value));
+011 }
+012 public static void main(String[] args)
+013 {
+014 double value = 1768.3518;
+015 print(value, "#0.0");
+016 print(value, "#0.000");
+017 print(value, "000000.000");
+018 print(value, "#.000000");
+019 print(value, "#,###,##0.000");
+020 print(value, "0.000E00");
+021 }
+022 }
+
+ |
++Listing1708.java | +
+Die Ausgabe des Programms lautet:
+
+
+1768,4
+1768,352
+001768,352
+1768,351800
+1.768,352
+1,768E03
+
+
+
+
+Sie kommt wie folgt zustande: +
+Wenn das Programm mit englischer Locale
+aufgerufen worden wäre, wären in der Ausgabe Punkte und
+Kommata vertauscht (denn im englischen Sprachraum werden Kommata als
+Tausenderseparatoren und Punkte als Dezimaltrennzeichen verwendet).
+Die Ausgabe wäre dann wie folgt gewesen:
+
+
+1768.4
+1768.352
+001768.352
+1768.351800
+1,768.352
+1.768E03
+12345678.909
+
+
+
+
![]() |
+
+
+ +Neben der Ausgabesteuerung mit Hilfe des Formatstrings stellt DecimalFormat +noch weitere Methoden zur Konfiguration der Ausgabe zur Verfügung. +Beispiele sind etwa setGroupingSize +zur Einstellung der Größe der Tausendergruppen oder setDecimalSeparatorAlwaysShown +zur Festlegung, ob auch Ganzzahlen mit Dezimalzeichen ausgegeben werden +sollen. Zudem bietet DecimalFormat +eine Methode parse, +mit der lokalisierte Zahleneingaben eingelesen werden können. +Weitere Details zu diesen Methoden können in der API-Dokumentation +nachgelesen werden. |
+
+
|
+![]() |
+
+Neben Zahlen lassen sich mit dem Paket java.text +auch Datums- und Uhrzeitwerte unter Beachtung nationaler Besonderheiten +formatieren und in einen String +konvertieren. Die dafür vorgesehene Klasse DateFormat +wird zunächst (analog zu NumberFormat) +mit einer Factory-Methode instanziert und konfiguriert. Anschließend +wird format +aufgerufen, um den Ausgabestring zu erzeugen. + +
+Bei der Instanzierung gibt es einige Varianten zu unterscheiden: +
+
+
++public static final DateFormat getDateInstance( + int style, + Locale locale +) + +public static final DateFormat getTimeInstance( + int style, + Locale locale +) + +public static final DateFormat getDateTimeInstance( + int datestyle, + int timestyle, + Locale locale +) + +public final String format(Date date) ++ + |
++java.text.DateFormat | +
+Mit getDateInstance +wird eine Instanz zur Ausgabe eines Datums erzeugt, getTimeInstance +dient zur Ausgabe der Uhrzeit und getDateTimeInstance +zur kombinierten Ausgabe von Datum und Uhrzeit. Alle Methoden erwarten +ein Argument style, das mit +einer der Konstanten SHORT, +MEDIUM, +LONG, +FULL +oder DEFAULT +belegt werden muss. Es gibt an, wie lang die jeweilige Ausgabe später +sein soll und welche Daten sie enthalten soll. So werden beispielsweise +bei der kurzen Uhrzeit lediglich Stunden und Minuten ausgegeben, während +in den übrigen Stufen auch die Sekunden und andere Informationen +enthalten sind. Als zweites Argument wird die gewünschte Locale +angegeben; für die Standard-Locale +kann es auch ausgelassen werden. + +
+Das folgende Programm formatiert das aktuelle Datum und die aktuelle +Uhrzeit mit allen möglichen style-Parametern +und gibt die Ergebnisse auf der Console aus: + + +
+
+
+
+001 /* Listing1709.java */
+002
+003 import java.util.*;
+004 import java.text.*;
+005
+006 public class Listing1709
+007 {
+008 public static void print(Calendar cal, int style)
+009 {
+010 DateFormat df;
+011 df = DateFormat.getDateInstance(style);
+012 System.out.print(df.format(cal.getTime()) + " / ");
+013 df = DateFormat.getTimeInstance(style);
+014 System.out.println(df.format(cal.getTime()));
+015 }
+016
+017 public static void main(String[] args)
+018 {
+019 GregorianCalendar cal = new GregorianCalendar();
+020 print(cal, DateFormat.SHORT);
+021 print(cal, DateFormat.MEDIUM);
+022 print(cal, DateFormat.LONG);
+023 print(cal, DateFormat.FULL);
+024 print(cal, DateFormat.DEFAULT);
+025 }
+026 }
+
+ |
++Listing1709.java | +
+Die Ausgabe des Programms lautet:
+
+
+24.05.00 / 21:58
+24.05.2000 / 21:58:55
+24. Mai 2000 / 21:58:55 GMT+02:00
+Mittwoch, 24. Mai 2000 / 21.58 Uhr GMT+02:00
+24.05.2000 / 21:58:55
+
+
+
+
![]() |
+![]() |
+
+
+ +Leider ist nicht sehr genau dokumentiert, welche Felder des Datums +bzw. der Uhrzeit in Abhängigkeit von dem style-Parameter +tatsächlich ausgegeben werden, zudem ist die Formatierung länderspezifisch. +Wenn es also auf eine präzise definierte Formatierung ankommt, +muss ausprobiert werden, welche Ergebnisse jeweils zustande kommen, +und die am besten passende Variante gewählt werden. Alternativ +kann mit Hilfe der Klasse FieldPosition +auf jedes einzelne Feld zugegriffen und so eine genauere Steuerung +der Ausgabe erreicht werden. |
+
+
|
+![]() |
+
+Ein ausgeliefertes Java-Programm besteht in aller Regel nicht nur +aus Bytecode in Form von .class-Dateien, +sondern enthält weitere Dateien mit zusätzlichen Informationen. +Dazu zählen beispielsweise Textkonserven und Grafikdateien für +grafische Oberflächen, Sounddateien zur akustischen Untermalung +von Programmereignissen oder Hilfsdateien mit Klartextmeldungen zu +möglichen Programmfehlern. Diese zusätzlichen Informationen +werden als Ressourcen bezeichnet und müssen zusammen mit +dem Programm ausgeliefert werden. Die meisten von ihnen müssen +lokalisiert werden. + +
+Java stellt seit der Version 1.1 mit der Klasse ResourceBundle +aus dem Paket java.util +ein universelles Hilfsmittel zur Verwendung derartiger Ressourcen +zur Verfügung. Die Grundidee ist dabei einfach: Ein ResourceBundle +ist ein Java-Objekt, das eine Sammlung von Ressourcen zusammenfasst +und auf Anfrage einzeln zur Verfügung stellt. Jede Ressource +hat einen eindeutigen und für alle unterstützten Sprachvarianten +identischen Namen, mit dem auf sie zugegriffen werden kann. Ein konkretes +ResourceBundle +ist stets sprachabhängig und enthält nur die Ressourcen +für eine bestimmte Locale. + +
+Damit ist ein ResourceBundle +zunächst nicht viel mehr als eine Schlüssel-Wert-Collection +wie beispielsweise Hashtable +oder HashMap. +Der Clou liegt in der Tatsache, dass der Name des ResourceBundle +die Locale +angibt, deren Daten er enthält. Während der vordere Teil +fest und für alle lokalisierten Varianten gleich ist, ist der +hintere Teil Locale-spezifisch +und gibt Sprache, Land und gegebenenfalls Variante an. Lautet der +feste Teil beispielsweise »ImageResources«, wären »ImageResources_fr« +und »ImageResources_en_US« die Namen für die französiche +und US-englische Variante. + + + + +
+ResourceBundle +stellt eine statische Methode getBundle +zur Verfügung, mit der zu einem Basisnamen und einer vorgegebenen +Locale +ein ResourceBundle +beschafft werden kann. Diese gibt es in unterschiedlichen Ausprägungen: +
+
+
++public static final ResourceBundle getBundle(String baseName) + throws MissingResourceException + +public static final ResourceBundle getBundle( + String baseName, + Locale locale +) ++ + |
++java.util.ResourceBundle | +
+Die erste Variante besorgt ein ResourceBundle +für die aktuelle Default-Locale, +die zweite für die als Argument angegebene. In beiden Fällen +wird wie folgt vorgegangen: +
+Ist beispielsweise Deutschland die Default-Locale +und soll die Resource »MyTextResource« für Frankreich +beschafft werden, sucht getBundle +nacheinander nach folgenden Klassen: +
+Die Suche bricht ab, wenn die erste passende Klasse gefunden wurde. +Ist überhaupt keine passende Ressource vorhanden, löst getBundle +eine MissingResourceException +aus. Dies ist auch der Fall, wenn die gefundene Klasse nicht aus ResourceBundle +abgeleitet ist. + + + + +
+Eigene Resourcenklassen müssen aus ResourceBundle +abgeleitet werden und die beiden (in der Basisklasse abstrakten) Methoden +getKeys +und handleGetObject +überlagern: +
+
+
++public Enumeration getKeys() + +protected Object handleGetObject(String key) + throws MissingResourceException ++ + |
++java.util.ResourceBundle | +
+handleGetObject +liefert eine Ressource zu einem vorgegebenen Schlüssel, getKeys +eine Aufzählung aller vorhandenen Schlüssel. Gibt es zu +einem vorgegebenen Schlüssel keine Ressource, muss handleGetObject +null +zurückgeben. + +
+Das folgende Listing zeigt eine Klasse SimpleTextResource, +die zur Internationalisierung von einfachen Texten verwendet werden +kann. Die lokalisierten Varianten können dabei sehr einfach realisiert +werden, indem sie aus SimpleTextResource +abgeleitet werden. Sie müssen dann lediglich im Konstruktor die +Hashtable +mit den gewünschten Schlüssel-/Textpaaren füllen. + + +
+
+
+
+001 /* SimpleTextResource.java */
+002
+003 import java.util.*;
+004
+005 public class SimpleTextResource
+006 extends ResourceBundle
+007 {
+008 protected Hashtable data = new Hashtable();
+009
+010 public Enumeration getKeys()
+011 {
+012 return data.keys();
+013 }
+014
+015 public Object handleGetObject(String key)
+016 {
+017 return data.get(key);
+018 }
+019
+020 public ResourceBundle getParent()
+021 {
+022 return parent;
+023 }
+024 }
+
+ |
++SimpleTextResource.java | +
+Nun soll ein ResourceBundle +mit dem Namen »MyTextResource« erstellt werden, das Übersetzungen +zu den beiden Schlüsseln »Hi« und »To« liefert. +Dazu definieren wir zunächst eine Klasse MyTextResource, +die immer dann verwendet wird, wenn keine passende lokale Variante +gefunden wird. In unserem Fall soll sie die Texte in englischer Sprache +zur Verfügung stellen: + + +
+
+
+
+001 /* MyTextResource.java */
+002
+003 public class MyTextResource
+004 extends SimpleTextResource
+005 {
+006 public MyTextResource()
+007 {
+008 data.put("Hi", "Hello");
+009 data.put("To", "World");
+010 }
+011 }
+
+ |
++MyTextResource.java | +
+Des weiteren wollen wir eine allgemeine deutschsprachige und eine +spezielle schweizerische Variante zur Verfügung stellen: + + +
+
+
+
+001 /* MyTextResource_de.java */
+002
+003 public class MyTextResource_de
+004 extends SimpleTextResource
+005 {
+006 public MyTextResource_de()
+007 {
+008 data.put("Hi", "Hallo");
+009 data.put("To", "Welt");
+010 }
+011 }
+
+ |
++MyTextResource_de.java | +
+
+
+
+001 /* MyTextResource_de_CH.java */
+002
+003 public class MyTextResource_de_CH
+004 extends SimpleTextResource
+005 {
+006 public MyTextResource_de_CH()
+007 {
+008 data.put("Hi", "Grüezi");
+009 data.put("To", "Schweiz");
+010 }
+011 }
+
+ |
++MyTextResource_de_CH.java | +
+Will ein Client auf eine Ressource in einem ResourceBundle +zugreifen, tut er das nicht durch direkten Aufruf von handleGetObject, +sondern verwendet dazu eine der folgenden Methoden: +
+
+
++public Object getObject(String key) + throws MissingResourceException + +public String getString(String key) + throws MissingResourceException + +public final String[] getStringArray(String key) + throws MissingResourceException ++ + |
++java.util.ResourceBundle | +
+getObject +liefert die Ressource als Object, +getString +als String +und getStringArray +als String-Array. +Die letzten beiden Methoden dienen vornehmlich der Bequemlichkeit: +sie rufen selber getObject +auf und nehmen dem Aufrufer die anschließend erforderliche Typkonvertierung +ab. + +
+Das folgende Listing zeigt ein einfaches Testprogramm, das die Textressourcen +für verschiedene Lokalisierungen ermittelt und auf der Console +ausgibt. Das ResourceBundle +wird beschafft, indem getBundle +mit »MyTextResource« und der jeweils gewünschten Locale +als Argument aufgerufen wird. Anschließend wird auf die übersetzten +Texte mit Aufrufen von getString +zugegriffen: + + +
+
+
+
+001 /* Listing1714.java */
+002
+003 import java.util.*;
+004
+005 public class Listing1714
+006 {
+007 public static void sayHello(Locale locale)
+008 {
+009 System.out.print(locale + ": ");
+010 ResourceBundle textbundle = ResourceBundle.getBundle(
+011 "MyTextResource",
+012 locale
+013 );
+014 if (textbundle != null) {
+015 System.out.print(textbundle.getString("Hi") + ", ");
+016 System.out.println(textbundle.getString("To"));
+017 }
+018 }
+019
+020 public static void main(String[] args)
+021 {
+022 sayHello(Locale.getDefault());
+023 sayHello(new Locale("de", "CH"));
+024 sayHello(Locale.US);
+025 sayHello(Locale.FRANCE);
+026 }
+027 }
+
+ |
++Listing1714.java | +
+Die Ausgabe des Programms ist:
+
+
+de_DE: Hallo, Welt
+de_CH: Grüezi, Schweiz
+en_US: Hallo, Welt
+fr_FR: Hallo, Welt
+
+
+
+
+Die Default-Locale +war beim Testen »de_DE«. Zwar wurde keine passende Klasse +MyTextResource_de_DE definiert, +aber durch den oben beschriebenen Fallback-Mechanismus liefert getBundle +eine Instanz von MyTextResource_de. +Bei der nächsten Lokalisierung wird die erforderliche Klasse +MyTextResource_de_CH direkt +gefunden, und die Ausgabe erfolgt entsprechend. Zu den letzten beiden +Lokalisierungswünschen werden keine passenden Ressourcen gefunden. +getBundle +verwendet in beiden Fällen die Datei MyTextResource_de +zur Default-Locale, und die +Ausgaben erfolgen in deutscher Sprache. +
+
![]() |
+![]() |
+
+
+ +Damit die Suche nach Ressourcen und die (im nächsten Abschnitt +zu besprechende) Vaterverkettung richtig funktionieren, müssen +zu einer Ressource auch stets alle allgemeineren Ressourcen vorhanden +sein. Gibt es etwa - wie in unserem Beispiel - eine Ressource mit +der Endung »_de_CH«, so müssen auch die Ressourcen +mit der Endung »_de« und die Basisressource ohne lokale +Endung vorhanden sein. |
+
+
|
+![]() |
+
+
![]() |
+![]() |
+
+
+ +Um die Übersichtlichkeit zu verbessern, sollten Ressourcendateien +in eigenen Verzeichnissen gehalten werden. Da sie mit Hilfe des Classloaders +geladen werden, müssen sie in einem Verzeichnis abgelegt werden, +in dem auch eine Klasse gefunden werden würde. Man könnte +beispielsweise im Hauptpaket der Anwendung ein Unterverzeichnis resources +anlegen und alle Ressourcendateien dort platzieren. Der im Programm +zu verwendende Ressourcenname wird dann einfach vorne um »resources.« +ergänzt. Derartige Ressourcen, die über den Klassenpfad +erreichbar sind, werden auch dann gefunden, wenn das Programm in Form +einer .jar-Datei ausgeliefert wird. |
+
+
|
+![]() |
+
+Ein Aufruf von getObject +wird zunächst an die Methode handleGetObject +weitergeleitet. Ist deren Rückgabewert nicht null, +wird er an den Aufrufer übergeben. Andernfalls wird die Anfrage +an den Vater des ResourceBundles weitergereicht. Die Vaterverkettung +erfolgt bei der Instanzierung von ResourceBundle-Objekten +automatisch, indem das jeweils nächstmögliche allgemeinere +ResourceBundle +als Vater verwendet wird. So ist beispielsweise der Vater von »MyTextResource_de_DE« +die Ressource »MyTextResource_de«, und deren Vater ist »MyTextResource«. + +
+Durch diese Vaterverkettung muss ein spezielleres ResourceBundle +nur die Ressourcen zur Verfügung stellen, die sich gegenüber +dem Vater unterscheiden oder neu hinzugekommen sind. Alle unveränderten +Ressourcen brauchen dagegen nicht erneut definiert zu werden. Soll +beispielsweise eine »MyTextResource« für englischsprachige +Lokalisierungen definiert werden, die sich nur durch die Übersetzung +des Schlüssels »To« von der Basisressource unterscheidet, +muss »Hi« nicht definiert werden: + + +
+
+
+
+001 /* MyTextResource_en.java */
+002
+003 public class MyTextResource_en
+004 extends SimpleTextResource
+005 {
+006 public MyTextResource_en()
+007 {
+008 data.put("To", "World of English");
+009 }
+010 }
+
+ |
++MyTextResource_en.java | +
+Nach dem Anlegen und Übersetzen dieser Klasse würde ein
+Aufruf von Listing 17.14
+zu folgender Ausgabe führen:
+
+
+de_DE: Hallo, Welt
+de_CH: Grüezi, Schweiz
+en_US: Hello, World of English
+fr_FR: Hallo, Welt
+
+
+
+
+Die Übersetzung von »Hi« kommt nun aus der Vater-Ressource +MyTextResource. + + + + +
+Die vorangegangenen Beispiele haben an Textkonserven exemplarisch +gezeigt, wie man Ressourcen lokalisieren kann. Sollen einfache Texte +übersetzt werden, gibt es aber noch einen einfacheren Weg. Das +oben beschriebene Suchschema von getBundle +war nämlich insofern unvollständig, als nach jedem einzelnen +Schritt zusätzlich eine Datei desselben Names wie die aktuelle +Klasse, aber mit der Erweiterung .properties, +gesucht wird. Wird also beispielsweise in einem beliebigen Suchschritt +die Klasse MyResource_de_DE +nicht gefunden, so sucht getBundle +vor dem Übergang zum nächsten Schritt nach einer Datei mit +dem Namen MyResource_de_DE.properties. + +
+Ist eine solche vorhanden, wird eine Instanz der Klasse PropertyResourceBundle +erzeugt und an den Aufrufer zurückgegeben. PropertyResourceBundle +liest die Eingabedatei (sie muss dasselbe Format wie die in Abschnitt 14.4.4 +beschriebenen Property-Dateien haben) und speichert alle darin gefundenen +Schlüssel-Wert-Paare ab. Anschließend können diese +als gewöhnliche Text-Ressourcen vom Programm verwendet werden. + +
+Um beispielsweise die vorigen Beispiele um eine französische
+Übersetzung zu erweitern, könnten wir einfach eine Datei
+MyTextResource_fr.properties anlegen
+und ihr folgenden Inhalt geben:
+
+
+Hi=Salut
+To=monde francais
+
+
+
+
+Listing 17.14 würde
+nun folgende Ausgabe erzeugen:
+
+
+de_DE: Hallo, Welt
+de_CH: Grüezi, Schweiz
+en_US: Hello, World of English
+fr_FR: Salut, monde francais
+
+
+
| Titel + | Inhalt + | Suchen + | Index + | DOC + | Handbuch der Java-Programmierung, 5. Auflage, Addison +Wesley, Version 5.0.1 + |
| << + | < + | > + | >> + | API + | © 1998, 2007 Guido Krüger & Thomas +Stark, http://www.javabuch.de + |