Titel | Inhalt | Suchen | Index | DOC | Handbuch der Java-Programmierung, 3. Auflage |
<< | < | > | >> | API | Kapitel 17 - Utility-Klassen II |
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 Programmes 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:
public Locale(String language, String country) |
java.util.Locale |
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:
public static Locale getDefault() public String getLanguage() public String getCountry() public String getVariant() |
java.util.Locale |
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:
001 /* Listing1704.java */ 002 003 import java.util.*; 004 005 public class Listing1704 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 } |
Listing1704.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 graphischen Oberfläche ausgeben - allerdings ohne die Möglichkeit, auf die Formatierung der Ausgabe Einfluß 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, muß es mit der statischen Methode getNumberInstance der Klasse NumberFormat erzeugt werden. In diesem Fall muß 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.1: 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, muß 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 /* Listing1705.java */ 002 003 import java.text.*; 004 005 public class Listing1705 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 } |
Listing1705.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 muß. 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 /* Listing1706.java */ 002 003 import java.util.*; 004 import java.text.*; 005 006 public class Listing1706 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 } |
Listing1706.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, muß 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, daß 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, muß 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 /* Listing1711.java */ 002 003 import java.util.*; 004 005 public class Listing1711 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 } |
Listing1711.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 plazieren. 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 muß 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, muß "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.11
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 muß 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.11 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, 3. Auflage, Addison Wesley, Version 3.0.1 |
<< | < | > | >> | API | © 1998-2003 Guido Krüger, http://www.javabuch.de |