![]() |
|
Isolierung Locale-spezifischer Daten Locale-spezifische Daten müssen an die Sprach- und Landeskonventionen des jeweiligen Endbenutzers angepasst werden. Der in einer Benutzerschnittstelle anzuzeigende Text ist das wohl offensichtlichste Beispiel derart Locale-spezifischer Daten. Thema dieses Unterkapitels ist die Erzeugung und das Laden von ResourceBundle-Objekten bzw. der Zugriff auf derartige Objekte, mit deren Hilfe Locale-spezifische Daten verwaltet werden können. Die Klasse ResourceBundle Aus konzeptioneller Sicht ist jedes ResourceBundle-Objekt eine Menge von Sub-klassen, die zueinander in Beziehung stehen und die denselben Basisnamen aufweisen. Die folgende Aufzählung illustriert die Verwendung dieser Klassen. Basisname der Klasse, die Beschriftungen von Buttons verwaltet, ist ButtonLabel. Die auf den Basisnamen folgenden Zeichen repräsentieren den Sprach- und den Landes-Code bzw. eine Variante einer Localen. ButtonLabel_de_DE entspricht also der Localen, die durch den Sprach-Code für Deutsch (de) und durch den Landes-Code für Deutschland (DE) spezifiziert wird. Um ein geeignetes ResourceBundle-Objekt auszuwählen, wird die Methode ResourceBundle.getBundle aufgerufen. Im folgenden Beispiel wird das ResourceBundle-Objekt von ButtonLabel für die Locale ausgewählt, die der Sprache Deutsch, dem Land Deutschland und der Plattform UNIX entspricht.
Locale currentLocale = new Locale("de", "DE", "UNIX"); Wenn eine ResourceBundle-Klasse für eine ausgewählte Locale nicht existiert, versucht die Methode getBundle, die bestmögliche Entsprechung zu laden. Wenn also bspw. keine Klasse für die Kombination ButtonLabel_de_DE_UNIX existiert, sucht die Methode getBundle in folgender Reihenfolge nach Alternativen:
Es ist empfehlenswert, stets eine Basisklasse ohne weitere Suffixe anzugeben, wie im Beispiel ButtonLabel. In diesem Fall ist eine Fehlersituation leicht zu vermeiden. Die abstrakte Klasse ResourceBundle beinhaltet zwei Subklassen: ListResourceBundle und PropertyResourceBundle. Die auszuwählende Subklasse hängt zum einen vom Datentyp und zum anderen von der Art, in der sie lokalisiert werden soll, ab. Ein PropertyResourceBundle-Objekt greift auf ein oder mehrere Properties Files zu. Übersetzbare Texte sollten daher immer in Properties Files gespeichert werden. Da diese ausschließlich aus Text bestehen und nicht Teil des Java-Quellcodes sind, können sie in beliebigen Texteditoren von Übersetzern erzeugt werden. Hierzu ist keinerlei Programmiererfahrung notwendig. Properties Files können ausschließlich aus Werten für String-Objekte bestehen. Müssen hingegen andere Datentypen gespeichert werden, so sind ListResourceBundle-Objekte zu verwenden. Der Zugriff von PropertyResourceBundle-Objekten auf Properties Files erfolgt analog zum Zugriff durch ResourceBundle-Objekte. Die Klasse ListResourceBundle verwaltet Ressourcen in Form einer Liste. Jedes ListResourceBundle-Objekt greift auf eine .class-Datei zu. In Erweiterung zu ResourceBundle-Objekten kann in einem ListResourceBundle-Objekt jedes Locale-spezifische Objekt gespeichert werden. Um eine zusätzliche Locale zu unterstützen, muss eine neue Quelldatei erzeugt und in eine .class-Datei kompiliert werden. Da aber Übersetzer üblicherweise keine Programmierer sind, sollten Textobjekte, die eine Übersetzung erfordern, nicht in einem ListResourceBundle-Objekt verwaltet werden. Die Klasse ResourceBundle ist extrem flexibel. Wenn bspw. zunächst ein Locale-spezifischer Text in ein ListResourceBundle-Objekt geladen wurde, anschließend aber entschieden wurde, ein PropertyResourceBundle-Objekt zu verwenden, so ist die Auswirkung auf den Code minimal. Der folgende Aufruf von getBundle lädt bspw. ein ResourceBundle-Objekt für eine geeignete Locale, in der das ButtonLabel-Objekt von einer .class-Datei oder von einem Properties File verwaltet wird:
ResourceBundle introLabels = ResourceBundle.getBundle("ButtonLabel", currentLocale); ResourceBundle-Objekte enthalten eine Liste von (Schlüssel-Wert)-Paaren. Ein Schlüssel wird immer als Zeichenkette angegeben, dessen Wert anschließend aus dem ResourceBundle-Objekt geladen wird. Der Wert ist das Locale-spezifische Objekt. Im folgenden Beispiel werden die Schlüssel OkSchluessel und AbbrechenSchluessel verwendet:
class ButtonLabel_de extends ListResourceBundle { // Deutsche Version return inhalte; } {"OkSchluessel", "OK"}, } } Um die Zeichenkette OK aus dem ResourceBundle-Objekt zu laden, muss der geeignete Schlüssel angegeben werden, wenn getString aufgerufen wird:
String okLabel = ButtonLabel.getString("OkSchluessel"); Es sollte darauf hingewiesen werden, dass das vorangehende Beispiel stark vereinfacht ist, da die String-Werte hart im Quellcode verdrahtet sind. Dies ist allerdings keine gute Programmierpraxis, da bspw. Übersetzer mit Properties Files arbeiten, die getrennt vom Quellcode sind. Ein Properties File beinhaltet stets Paare aus Schlüssel und Wert. Ein Beispiel hierfür ist im Folgenden beschrieben:
OkSchluessel = OK Vorbereitung von ResourceBundle-Objekten Wird eine Anwendung entwickelt, die über eine Benutzeroberfläche verfügt, so sind meist eine Vielzahl von Locale-spezifischer Objekte enthalten. Zu Beginn sollte daher der Programm-Code auf Objekte überprüft werden, die mit der Locale variieren. Diese Objekte können bspw. Instanzen folgender Klassen sein: String, Component, Graphics, Image, Color oder AudioClip. Diese Liste beinhaltet keine Objekte, die Zahlen, Datumsangaben oder Zeiten repräsentieren. Der Grund hierfür liegt darin, dass das Format dieser Daten zwar mit der Locale variiert, nicht aber die Objekte selbst. Anstelle einer Isolierung dieser Objekte in einem ResourceBundle-Objekt findet eine Formatierung durch spezielle Locale-sensitive Klassen statt. Dies wird im Folgenden detailliert erklärt. Im Allgemeinen werden die Objekte, die in einem ResourceBundle-Objekt gespeichert sind, vorab definiert; sie sind in einer fertig gestellten Anwendung (auch zur Laufzeit) nicht veränderbar. Es versteht sich aber von selbst, dass ein String-Objekt, das von einem Endbenutzer in einem Textfeld eingegeben werden soll, nicht in einem ResourceBundle-Objekt isoliert werden sollte, da eine derartige Zeichenkette von Tag zu Tag variieren kann. Die Zeichenkette ist daher eher für die jeweilige Programmausführung als für die Locale, in der das Programm läuft, spezifisch. Üblicherweise sind die meisten Objekte, die in einem ResourceBundle-Objekt isoliert werden, String-Objekte. Nicht alle String-Objekte sind allerdings Locale-spezifisch. Wenn ein String bspw. niemals einem Endbenutzer präsentiert wird, muss er auch nicht internationalisiert werden. Es wird erkennbar, dass die Entscheidung, welche String-Objekte zu internationalisieren sind, nicht immer eindeutig ist. Ein Beispiel hierfür sind Log-Dateien, die ein Endbenutzer normalerweise nicht einsieht. Das Problem liegt hierbei in den vielfältigen Auslegungsmöglichkeiten des Wortes normalerweise. ResourceBundle-Objekte können organisiert werden, indem jedes mit einer anderen Kategorie von Objekten geladen wird. So können bspw. alle Beschriftungen von Buttons in ein ResourceBundle-Objekt ButtonLabelsBundle geladen werden. Aus der Kategorisierung ergeben sich die folgenden Vorteile:
Die meisten Locale-spezifischen Daten bestehen aus ResourceBundle-Objekte und Properties Files Anhand dieses Abschnitts wird ein Java-Programm entwickelt, das die erklärten Konzepte verdeutlicht. Hierzu sind die folgenden Schritte auszuführen:
# Dies ist das Standard-ButtonsBundle.properties File
Es ist zu beachten, dass Kommentarzeilen immer mit dem Doppelkreuz ( #) beginnen. Die anderen Zeilen beinhalten die Paare aus Schlüssel und dazugehörigem Wert. Es ist allerdings darauf zu achten, den Schlüssel nach der Definition nicht weiter zu verändern, da dieser meist vom Quellcode referenziert wird. Die Werte hingegen werden von Übersetzern verändert, wenn neue Properties Files für weitere Sprachen geschaffen werden.
b1 = OK b2 = Cancel b3 = End
new Locale("de","DE"), } Die Aufrufe der Locale-Konstruktoren spezifizieren jeweils Sprach- und Landes-Code. Diese Codes stimmen mit den Properties Files überein, die in den ersten beiden Schritten erzeugt wurden.
Die Methode getBundle überprüft zuerst, ob eine .class-Datei existiert, die dem Basisnamen entspricht. Ist dies nicht der Fall, so wird nach Properties Files gesucht. In diesem Beispiel findet getBundle daher die entsprechenden Properties Files. Nach erfolgreicher Lokalisierung gibt getBundle ein PropertyResourceBundle-Objekt zurück, das mit den Paaren aus Schlüssel und Wert aus dem Properties File geladen wurde.Wenn kein Properties File für eine Locale existiert, so sucht die Methode getBundle nach einem Properties File, das dem gewünschten am nächsten kommt. Dieses Vorgehen wurde bereits beschrieben.
Enumeration buendleSchluessel = buttons.getKeys(); while (buendleSchluessel.hasMoreElements()) { String schluessel = (String)buendleSchluessel.nextElement(); } Setzt man das Programm zusammen und führt es aus, so ergibt sich die folgende Ausgabe. Die erste Gruppe der Zeilen zeigt die Werte, die getString für verschiedene Locale-Objekte zurückliefert. Die zweite Zeilengruppe zeigt die letzten drei Zeilen an, wenn mit der getKeys-Methode über die Schlüssel iteriert wird.
Locale = en_US, Schluessel = b2, Wert = Cancel
Schluessel = b3, Wert = Ende In diesem Abschnitt wird anhand eines Beispiels schrittweise die Verwendung des ListResourceBundle-Objekts dargestellt.Ein ListResourceBundle-Objekt greift grundsätzlich auf eine .class-Datei zu. Im ersten Schritt muss daher eine derartige Datei für jede unterstützte Locale generiert werden. Im Beispiel ist der Basisname des ListResourceBundle-Objekts AdressenBundle. Wie im vorangegangenen Beispiel werden zwei Locale-Objekte unterstützt, für die die Dateien AdressenBundle_de_DE.class und AdressenBundle_en_US.class erzeugt werden müssen. Die Klasse AdressenBundle ist im folgenden Code definiert. Wie bei Properties Files besteht auch der Klassenname aus dem Basisnamen und den Sprach- und Landes-Codes. Innerhalb der Klasse wird eine zweidimensionale Liste mit den Paaren aus Schlüssel und Wert initialisiert. Als Schlüssel werden in diesem Beispiel Name, Adresse und Alter verwendet. Die Schlüssel müssen als String-Objekte erzeugt werden und in jeder Klasse der Menge AdressenBundle identisch sein. Die Werte können einen beliebigen Objekttyp haben.
public class AdressenBundle_de_DE extends ListResourceBundle { public Object[][] ladeInhalte() { return inhalte; } {"Name", new String ("Fischer")},{"Adresse", new String("KOM - TU Darmstadt")},{"Alter", new Integer(29)}, } }
new Locale("en","US"), }; Jede Locale entspricht einer der AdressenBundle-Klassen. Die deutsche Locale, die mit de und DE angegeben wird, entspricht daher der Datei AdressenBundle_de_DE.class.Auch die Generierung des ListResourceBundle-Objekts erfolgt analog zum vorangegangenen Beispiel:
|
|
|