![]() |
|||||||||||||||||||||
|
Formatierung von Daten Nachdem die grundsätzliche Funktionsweise der Internationalisierung und die Isolierung von Daten betrachtet wurden, wird in diesem Abschnitt erklärt, wie Zahlen, Währungen, Datumsangaben, Zeitangaben und Text formatiert werden kann. Da derartige Datenelemente vom Endbenutzer verwendet werden, muss das jeweilige Format mit den verschiedenen kulturellen Konventionen konform sein. Zahlen und Währungen Programme operieren auf Zahlen in einer Art und Weise, die grundsätzlich unabhängig von einer Localen ist. Vor der Ausgabe einer Zahl muss daher ein Programmteil durchlaufen werden, der eine Zahl in einen String konvertiert, so dass ein Locale-sensitives Format erzeugt wird. Während bspw. im amerikanischen Raum das Zahlenformat mit Punkt (1234.56) verwendet wird, wird in Deutschland meist das Format mit Komma (1234,56) benutzt. Indem man die Methoden aufruft, die von der Klasse NumberFormat zur Verfügung gestellt werden, können Zahlen, Währungen und Prozentangaben entsprechend einer Localen formatiert werden. Problematisch ist allerdings, dass das NumberFormat unter Umständen die selbst spezifizierte Locale nicht unterstützt. Es kann aber leicht festgestellt werden, welche Definition der Localen von der Klasse NumberFormat unterstützt wird, indem die Methode getAvailableLocales folgendermaßen aufgerufen wird:
Locale[] locales = NumberFormat.getAvailableLocales(); Wenn NumberFormat die jeweils benötigte Locale nicht unterstützt, kann ein eigenes Format definiert werden. Dies wird im Folgenden detailliert erläutert. Die NumberFormat-Methoden können weiterhin dazu verwendet werden, primitive Zahlentypen, wie bspw. float oder double und ihre entsprechenden Objekttypen (Float und Double), zu formatieren. Das folgende Beispiel formatiert eine Double-Zahl entsprechend einer Localen. Indem die getNumberInstance-Methode aufgerufen wird, kann eine Locale-spezifische Instanz des NumberFormats erzeugt werden. Die Formatierungsmethode akzeptiert hierbei Double als Argument und gibt einen String zurück, der die formatierte Zahl enthält:
Double Preis = new Double(12.99); Die Ausgabe dieses Beispiels illustriert, wie das Format mit der jeweiligen Localen variiert:
12,99 de_DE Die Formatierung von Währungen erfolgt in ähnlicher Weise wie die von Zahlen. Hierzu ist die Methode getCurrencyInstance zu verwenden, die einen String zurückliefert, der die formatierte Zahl und eine Währungsangabe enthält. Das folgende Beispiel illustriert dies:
Double waehrung = new Double(12.99); Nach dem Aufruf ergibt sich die folgende Ausgabe:
12,99 DM de_DE Die Formatierung von Prozentangaben erfolgt analog mittels der Methode getPercentInstance. Selbstdefinierte Formate Zur Formatierung von Dezimalzahlen und damit zu deren Umsetzung in Locale-spezifische Strings kann die Klasse DecimalFormat verwendet werden. Diese Klasse ermöglicht die Kontrolle der Anzeige führender und folgender Nullen, von Präfixen und Suffixen, von Gruppentrennzeichen (bspw. Punkt oder Komma in Tausenderangaben) und von Dezimaltrennzeichen. Wenn derartige Formatierungssymbole verändert werden sollen, muss DecimalFormatSymbols zusammen mit der Klasse DecimalFormat verwendet werden. Diese beiden Klassen bieten eine erhebliche Flexibilität hinsichtlich der Formatierung von Zahlen an, können aber Programme auch sehr komplex machen. Es empfiehlt sich daher, anstelle von DecimalFormat und DecimalFormatSymbols die Klasse NumberFormat so oft wie möglich einzusetzen. Die Formateigenschaften von DecimalFormat werden stets mit einem Muster-String angegeben. Das Muster bestimmt hierbei, wie die formatierte Zahl aussehen soll. Das folgende Beispiel generiert eine Formatierung, indem ein Muster-String nach dem DecimalFormat-Konstruktor durchsucht wird. Die Methode erwartet einen Wert vom Typ double als Argument und erzeugt eine formatierte Zahl, die als String zurückgegeben wird. Mögliche Ausgaben dieses Beispiels sind in Tab. 6-3 dargestellt.
DecimalFormat formatierer= new DecimalFormat(muster); |
|||||||||||||||||||||
|
|||||||||||||||||||||
|
Tab. 6.3: Selbstdefinierte Zahlenmuster Ziel des vorangegangenen Beispiels ist die Generierung eines DecimalFormat-Objekts für eine Standard-Locale. Soll ein derartiges Objekt für eine Locale generiert werden, die keine Standard-Locale ist, so muss das Objekt als NumberFormat instantiiert und anschließend in den Typ DecimalFormat umgewandelt werden. Das so entstandene Objekt formatiert dann die Muster in einer Locale-sensitiven Art und Weise:
NumberFormat nf = NumberFormat.getNumberInstance(l); Bisher wurden solche Muster betrachtet, die der US-amerikanischen Konvention folgten. Symbole, wie bspw. das Dezimaltrennzeichen, der Gruppenseparator und das Minuszeichen können mit der Klasse DecimalFormatSymbols verändert werden. Im nächsten Beispiel wird mittels DecimalFormatSymbols einer Zahl ein ungewöhnliches Format zugewiesen. Hierzu werden Aufrufe der Methoden setDecimalSeparator, setGroupingSeparator und setGroupingSize verwendet.
DecimalFormatSymbols neueSymbole= new DecimalFormatSymbols(derzeitigeLocale); Als Ausgabe ergibt sich in diesem Fall 1#1111*111. Formatierung von Datum und Zeit Date-Objekte werden in Java dazu verwendet, Datums- und Zeitangaben zu repräsentieren. Wie in den vorangegangenen Beispielen muss ein Datumsobjekt vor der Ausgabe in einen String konvertiert werden. Die Formatierung ist hierbei in großem Maße kulturabhängig. In Java wird die DateFormat-Klasse dazu verwendet, Datums- und Zeitangaben in einer Locale-sensitiven Art und Weise zu formatieren. Wie bisher auch unterstützt DateFormat nicht alle Locale-Definitionen. Zur Feststellung, welche Localen unterstützt werden, kann die bereits beschriebene Methode getAvailableLocales verwendet werden. Die Formatierung von Datumsangaben mittels der Klasse DateFormat erfolgt in zwei Schritten. Zuerst wird ein Formatierer mittels der Methode getDateInstance generiert. Anschließend wird die Formatierungsmethode aufgerufen, die einen String mit dem formatierten Datum zurückliefert. Das folgende Beispiel illustriert dieses Vorgehen:
Date heutigesDatum; Die Ausgabe, die hier erzeugt wird, variiert mit der Localen:
1.2.1999 de_DE Dieses Beispiel wurde mit dem DEFAULT-Formatierungsstil ausgeführt, der einer der Stile ist, die in der Klasse DateFormat vordefiniert sind. Weitere Stile sind SHORT, MEDIUM, LONG und FULL. Tab. 6-4 zeigt, wie sich die Datumsangaben in Abhängigkeit vom gewählten Stil verändern. |
|||||||||||||||||||||
|
|||||||||||||||||||||
|
Tab. 6.4: Datumsstile in Java Da Date-Objekte sowohl Datums- als auch Zeitangaben repräsentieren, ist die Formatierung von Zeitangaben analog. Es muss allerdings die Methode getTimeInstance verwendet werden. Tab. 6-5 zeigt die Stile, die in Java für Zeitangaben zur Verfügung stehen. |
|||||||||||||||||||||
|
|||||||||||||||||||||
|
Tab. 6.5: Zeitangaben in Java Zur simultanen Darstellung von Datum und Zeit in einem String kann wie folgt vorgegangen werden:
DateFormat formatierer = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, derzeitigeLocale); Meist reichen die in Java vordefinierten Formate für Datums- und Zeitangaben aus. Soll dennoch eine Veränderung erfolgen, so kann mit Hilfe von SimpleDateFormat wie folgt vorgegangen werden: Zur Formatierung muss ein SimpleDateFormat-Objekt generiert werden, indem ein Muster-String angegeben wird. Dieses Vorgehen ist analog zur Formatierung von Zahlen und Währungen. Der Inhalt des Muster-Strings bestimmt dann das Format von Datum und Zeit. Das folgende Beispiel formatiert eine Datums- und eine Zeitangabe mit Hilfe eines Muster-Strings, der an den SimpleDateFormat-Konstruktor übergeben wird. Der zurückgegebene String enthält dann Datum und Zeit im gewünschten Format.
Date heute; |
|||||||||||||||||||||
|
|
Tab. 6.6: Datums- und Zeitformatierung in Java Tab. 6-6 zeigt verschiedene Formatierungsarten, die vom Benutzer als Muster spezifiziert wurden. Die Klasse SimpleDateFormat ist Locale-sensitiv. Wird SimpleDateFormat ohne einen Locale-Parameter instantiiert, so werden Datum und Zeit nach der Standard-Localen formatiert. Aus diesem Grund bestimmen sowohl Muster als auch Locale das Ausgabeformat. Für dasselbe Muster kann daher SimpleDateFormat verschiedene Formatierungen erzeugen, wenn sich die Locale ändert. Die format-Methode der Klasse SimpleDateFormat gibt einen String zurück, der aus Zahlen und Symbolen besteht. Wenn die in der Klasse SimpleDateFormat gekapselten Symbole den Anforderungen des Entwicklers nicht entsprechen, können sie mit Hilfe der Klasse DateFormatSymbols verändert werden. Derart können neue Namen für Monate, Wochentage und Zeitzonen festgelegt werden.Tab. 6-7 zählt die Methoden der Klasse DateFormatSymbols auf, mit deren Hilfe die Symbole verändert werden können. |
|
|
Tab. 6.7: Methoden der Klasse DateFormatSymbols Das folgende Beispiel ruft die Methode setWeekdays auf, um alle Wochentage von Kleinschreibung in Großschreibung umzuändern. Das erste Element des Array-Arguments von setWeekdays ist ein Null-String. Hierdurch enthält der Array ein Element anstatt leer initialisiert zu werden. Der Konstruktor der Klasse SimpleDateFormat akzeptiert das so modifizierte DateFormatSymbols-Objekt als Argument.
Date heute; System.out.print(standardTage[i] + " "); } String[] grosseTage = {"", "SONNTAG", "MONTAG", "DIENSTAG", "MITTWOCH", "DONNERSTAG", "FREITAG", "SAMSTAG"}; for (int i = 0; i < geaenderteTage.length; i++) { System.out.print(geaenderteTage[i] + " "); } formatierer = new SimpleDateFormat("E", symbols); Textinhalte Ein wichtiges Anwendungsgebiet der Internationalisierung ist das Angebot von Statusnachrichten in der jeweiligen Landessprache. Es wurden bereits die Mechanismen diskutiert, die übersetzbare Texte Locale-spezifisch isolieren. Üblicherweise ist dazu ein Text-String an ein ResourceBundle-Objekt zu übergeben. Wenn diese Daten allerdings variabel sind, müssen vor der Übersetzung einige weitere Schritte ausgeführt werden. Unter einer Verbundnachricht versteht man Daten, die variable Komponenten enthalten. Soll bspw. der Satz „Unsere Datenbank Adressdatenbank enthält derzeit 400 Einträge" internationalisiert werden, so ist sowohl die Anzahl der Einträge als auch der Name der Datenbank variabel, wenn mehrere Datenbanken verwendet werden. Eine mögliche Umsetzung dieses Satzes könnte darin bestehen, Satzelemente mit Variablen zu kombinieren:
int anzahlEintraege; String nachricht = textBundle.getString("einleitung") + textBundle.getString("datenbankName") + textBundle.getString("zwischentext")+ anzahlEintraege.toString() + textBundle.getString("endText"); Problematisch ist allerdings, dass dieser Satzaufbau zwar im Deutschen, nicht aber in vielen anderen Sprachen anwendbar ist. Der eigentliche Satzaufbau ist in diesem Beispiel fest vorgegeben und daher schlecht internationalisierbar. Zur Vermeidung dieses Problems sind in Java spezielle Mechanismen zu verwenden, die Verbundnachrichten mit Hilfe der Klasse MessageFormat verarbeiten. Es sei aber darauf hingewiesen, dass in Verbundnachrichten der eigentliche Text fragmentiert wird. Derartige Texte sind schwierig zu übersetzen und damit auch mit Kosten verbunden. Verbundnachrichten sollten daher nur eingesetzt werden, wenn es nicht zu vermeiden ist. Zur Internationalisierung einer Verbundnachricht müssen folgende Schritte durchlaufen werden, in denen die Klasse MessageFormat verwendet wird:
Dieses ResourceBundle-Objekt wird für jede Locale in einem Properties File gespeichert. Da das ResourceBundle-Objekt TextBundle heißt, muss das Properties File für die deutsche Locale TextBundle_de_DE.properties heißen. Der Inhalt dieser Datei sieht folgendermaßen aus:
muster = Unsere Datenbank {0} enthält derzeit {1,number, integer} Einträge.
Die erste Zeile des Properties Files enthält das Textmuster. Hierbei wird jede Variable der Nachricht durch ein Argument ersetzt, das in geschweiften Klammern steht. Jedes Argument beginnt mit einer Zahl, die auch als Argumentnummer bezeichnet wird, und die dem Elementindex entspricht, mit dem in einer Objektliste auf das Argument zugegriffen werden kann. Die Argumentnummern müssen dabei in keiner speziellen Reihenfolge vorliegen. Die einzige Bedingung ist, dass die Argumentnummer ein Gegenstück in der Liste der Argumentwerte findet. In diesem Fall wird durch den Wert 0 ein String angegeben, der den Namen der Datenbank enthält und durch den Wert 1 ein number-Objekt, das durch die Angabe des Typs integer weiter festgelegt wird.
texte.getString("Datenbank"), }; Im vierten Schritt wird ein MessageFormat-Objekt erzeugt. Hierzu ist die Locale zu setzen, da die Nachricht ein Number-Objekt enthält, das in einer Locale-sensitiven Art und Weise formatiert werden muss:
formatierer.setLocale(derzeitigeLocale);
String ausgabe = formatierer.format(texteArgumente); Das gesamte Programm sieht wie folgt aus:
import java.text.*; public class MessageFormatBeispiel { static void anzeige(Locale derzeitigeLocale) { ResourceBundle texte = ResourceBundle.getBundle("texteBundle", Object[] texteArgumente = { texte.getString("Datenbank"),new Integer(7) } } anzeige(new Locale("de", "DE")); } } In vielen Sprachen sind Singular und Plural eines Wortes verschieden. Dies wirft dann Probleme auf, wenn Verbundtexte verwendet werden, die Textvariablen enthalten. Ein Beispiel hierfür ist der Satz „2 Angestellte arbeiten in unserer Firma", wobei der Satzteil „2 Angestellte arbeiten" variabel sei. Zur Verarbeitung derartiger Textkomponenten kann die Klasse ChoiceFormat verwendet werden, die wiederum auf die Klasse MessageFormat zurückgreift, die im letzten Abschnitt eingeführt wurde. Zur Verarbeitung variabler Textelemente sind die folgenden Schritte auszuführen, die stark denjenigen ähneln, die im letzten Abschnitt besprochen wurden:Zuerst werden wiederum die Variablen des Textes identifiziert, im Beispiel also „2 Angestellte arbeiten." Anschließend werden die Textvariablen durch Argumente ersetzt, indem ein Muster erzeugt wird, das das
Die Verarbeitung des Arguments {0} ist komplex, da dieses Argument mit der Anzahl der Beschäftigen variiert. Um den Satz zur Laufzeit zu generieren, muss die Anzahl der Beschäftigten auf einen String abgebildet werden. Wird nur ein Angestellter beschäftigt, so muss der String "Ein Angestellter arbeitet" lauten. Diese Art der Abbildung wird durch die Klasse ChoiceFormat realisiert. Wenn in der Firma mehrere Angestellte arbeiten, so beinhaltet der Satz eine Zahl, anderenfalls das Wort "Ein". Mittels der Klasse MessageFormat ist es möglich, eine Zahl in einen Satz einzufügen.
Im Beispiel werden die Werte des ResourceBundle-Objekts in Properties Files gespeichert. Die Datei AuswahlBundle_de_DE.properties enthält daher die folgenden Zeilen:
muster = {} in unserer Firma.
Der Inhalt dieses Properties Files zeigt bereits, wie der Text erzeugt und formatiert werden wird. Die erste Zeile enthält gemäß Schritt 1 das Muster für MessageFormat. Die anderen Zeilen enthalten Satzteile, die das Argument {0} des Musters ersetzen. Der Satzteil mit dem Schlüssel mehrAngestellte enthält das Argument {1}, das durch die entsprechende Zahl der Angestellten ersetzt wird.
textForm.setLocale(derzeitigeLocale); Das ChoiceFormat-Objekt erlaubt die Auswahl eines bestimmten Strings in Abhängigkeit von einer Zahl des Typs double. Der Wertebereich dieser Zahl und die String-Objekte, auf die sie abgebildet werden kann, werden in Arrays spezifiziert:
String [] angestellteStrings= { bundle.getString("einAngestellter"), }; ChoiceFormat
ChoiceFormat auswahlForm = new ChoiceFormat(angestellteWerte, angestellteStrings);
Die setFormats-Methode weist den Argumenten des Nachrichtenmusters Format-Objekte zu. Vor dem Aufruf der setFormats-Methode muss die Methode applyPattern aufgerufen werden. Argumente des Format-Objekts sind auswahlForm, das auf das Element {0} abgebildet wird und die Methode NumberFormat.getInstance(), die auf das Element {1} abgebildet wird.
for (int angestellteZahl = 0; angestellteZahl < 3; angestellteZahl++) { textArgumente[0] = new Integer(angestellteZahl); } Das vollständige Programm lautet wie folgt:
import java.text.*; public class ChoiceFormatBeispiel { static void anzeige (Locale derzeitigeLocale) { ResourceBundle bundle = ResourceBundle.getBundle("AuswahlBundle", MessageFormat textForm = new MessageFormat(""); bundle.getString("einAngestellter"), } textForm.applyPattern(muster); for (int angestellteZahl = 0; angestellteZahl < 4; angestellteZahl++) { textArguments[0] = new Integer(angestellteZahl); } } anzeige(new Locale("de", "DE")); } } |
|
|