ResourceBundle
Die ResourceBundle API ist für den Zugriff auf Textressourcen das Mittel der Wahl:
ResourceBundle.getBundle([name])sucht nach Ressourcen unter Anwendung folgender Startegie:
Einmal gefundene Resource Bundle werden gecached. Wenn in einem gefundenen Resource Bundle der Wert zu einem Schlüssel nicht gefunden wird, nutzt die Resource Bundle API den gleichen Algorithmus, um einen Wert zum selben Schlüssel in weniger spezialisierten Bundles zu finden.
Die ResourceBundle API bietet insgesamt drei Ansatzpunkte zur Nutzung:
public class MyResourceBundle extends ListResourceBundle {
private static final Object[][] CONTENTS = new Object[][] {
{"city", "London"},
{"town", "Dover"}
};
@Override
protected Object[][] getContents() {
return CONTENTS;
}
}
public class MyResourceBundle_de extends ListResourceBundle {
private static final Object[][] CONTENTS = new Object[][] {
{"city", "Berlin"},
{"town", "Hannover"}
};
@Override
protected Object[][] getContents() {
return CONTENTS;
}
}
:
/**
* Implementierung eines ResourceBundles unter Nutzung einer externen Resource, hier: Datenbanktabelle
* (Pseudocode)
*
* Tabellenstruktur:
* key – der Schlüssel (not null)
* root – Standardwert (not null)
* de – Spezialisierung deutsch
* it - Spezialisierung italiano
* :
*/
public abstract class DBResourceBundle extends ResourceBundle {
// Injektion der Resource Database etc.
private Properties properties;
public DBResourceBundle() {
buildProperties();
}
protected abstract String getColumnName();
public Enumeration getKeys() {
return properties.propertyNames();
}
protected Object handleGetObject(String key) {
return properties.getProperty(key);
}
/**
* Laden der Properties aus einer Datenbanktabelle.
*/
private void buildProperties() {
// 1. lese alle Einträge der Ressourcentabelle wenn die Spalte getColumnName() nicht leer ist
// 2. Für alle Einträge: properties.setProperty(schlüssel, wert);
}
}
// Ausimplementierungen
public class MyResourceBundle extends DBResourceBundle {
protected String getColumnName() {
return "root";
}
}
public class MyResourceBundle_de extends DBResourceBundle {
protected String getColumnName() {
return "de";
}
}
public class MyResourceBundle_it extends DBResourceBundle {
protected String getColumnName() {
return "it";
}
}
:
Diese Varianten dürfen dabei für die Definition von ResourceBundle-Hierarchien beliebig gemischt werden! Eigene Ausprägungen von Locale können auch verwendet werden, das Default Locale wird gegebenenfalls über die JVM Startparameter -Duser.language und -Duser.region vorgegeben (statt –Duser.region in der Form country_variant kann auch –Duser.country und –Duser.variant explizit festgelegt werden).
Dynamische ResourceBundle
Sollen Textressourcen zur Laufzeit geändert werden, muss ResourceBundle.clearCache() gerufen werden. Alle ResourceBundle Instanzen werden verworfen und beim nächsten Zugriff durch ResourceBundle.getBundle neu geladen. Diese Dynamik setzt voraus, dass alle Zugriffe auf Ressourcen über ResourceBundle.getBundle initiiert werden. Man sollte deshalb das Ablegen von ResourceBundles in Klassenvariablen unterlassen und immer ResourceBundle.getBundle nutzen. Die ResourceBundle API ist für die Bereitstellung hoch dynamischer Textressourcen allerdings nicht geeignet!
Parametrisierung von Textressourcen - die MessageFormat API
Textressourcen mit Parametern sind mittels der MessageFormat API zu realisieren. Auch hier erfolgt die Internationalisierung der Formatierung auf der Basis von Locale.getDefault(). Beim Rendering der Parameter delegiert MessageFormat das Locale an die eingesetzten spezialisierten Formatter.
// eigentlich aus ResourceBundle holen, hier nur skizziert
String text = "Um {1,time} am {1,date} haben Sie einen Betrag von {0,number,integer} Euro überwiesen.";
// MessageFormat Instanzen können wiederverwendet werden, müssen
// aber in nebenläufigen Umgebungen synchronisiert werden
MessageFormat messageFormat = new MessageFormat(text);
:
// Formatierung:
String msg = messageFormat.format(new Object[]{ new Double(66.66), new Date()});
ResourceBundle in Java EE Umgebungen
ResourceBundle cached die Bundlehierarchien per Classloader, auf diese Weise sind die ResourceBundle der verschiedenen Java EE Anwendungsmodule voneinander isoliert. Die Initialisierung der ResourceBundle ist synchronisiert, da ResourceBundle nach der Initialisierung unveränderlich sind muss der Zugriff auf die eigentlichen Ressourcen nicht synchronisiert sein. ResourceBundle können also unter normalen Umständen (ResourceBundle.clearCache wird selten gerufen, kein exotisches Classloading im Container) problemlos in Java EE Umgebungen eingesetzt werden. Zu beachten ist auch: ResourceBundle.clearCache ist im gleichen Classloader-Context zu rufen, für welchen die Initialisierung erfolgen soll. Das erreicht man, indem man den Classloader (wenn er bekannt ist) explizit vorgibt, oder indem man ResourceBundle.clearCache im Context der Anwendung aufruft.
Der Aufruf von Locale.getDefault() bei der Verwendung von ResourceBundle und MessageFormat muss verhindert werden. Das Default-Locale der Serverumgebung sagt nichts über die Lokalisierung des Clients der Anfrage. Man muss sich also zunächst die gewünschte Lokalisierung des Clients beschaffen und gegebenenfalls merken:
// Lesen der bevorzugten Nutzer-Locale aus den Angaben des Browser-Requests..
Locale usersPrefLocale = request.getLocale();
// .. oder alternativ, wenn der Browser nicht die korrekte Lokalisierung besitzen kann aus anderen Quellen
Locale usersPrefLocale = someService.getUsersPrefLocale(userdaten);
// Setzen des Nutzer-Locale an der Session
session.setValue("preferredLocale", usersPrefLocale);
Anschließend werden bei der Verwendung von ResourceBundle und MessageFormat diese Lokalisierungsangaben explizit verwendet:
// Lesen der Nutzer-Locale aus der Session
Locale usersPrefLocale = (Locale) session.getValue("preferredLocale");
// Nutzung lokalisierter Bundles und Formater
ResourceBundle messages = ResourceBundle.getBundle("MyResourceBundle", usersPrefLocale);
MessageFormat messageFormat = new MessageFormat(messages.getString("MyResourceBundle"), usersPrefLocale);
:
Hinweise:
Referenzen
Blueprint: Designing Enterprise Applications
Java Tutorial on Internationalization