Internationalisierung mit XSLT

(Auszug aus "Java und XSLT" von Eric M. Burke)

In diesem Abschnitt beschäftigen wir uns mit den wichtigsten Techniken der Internationalisierung (i18n) mit XSLT. (i18n steht für internationalization: i + 18 Buchstaben + n.) Obwohl Java und XSLT i18n bestens unterstützen, stellt die Integration in eine funktionierende Anwendung eine Herausforderung dar. Dieser Abschnitt soll dabei helfen, die häufigsten Probleme zu vermeiden.

Der Entwurf der XSLT-Stylesheets

Am einfachsten kann i18n realisiert werden, wenn für jede unterstützte Sprache ein separates XSLT-Stylesheet bereitgestellt wird. Das bedingt allerdings einen wesentlich höheren Aufwand, denn XSLT-Stylesheets enthalten neben der reinen Information für die Darstellung einen gewissen Grad an Programmierlogik. Um dies zu verdeutlichen, betrachten wir directory.xml im folgenden Beispiel. Es handelt sich um eine einfache XML-Datei, die mit einem deutschen bzw. einem englischen XSLT-Stylesheet transformiert wird.

Beispiel: directory.xml

<?xml version="1.0" encoding="ISO-8859-1"?>
<verzeichnis>
    <angestellter kategorie="manager">
        <name>Hans Böhme</name>
        <telefon>45623-21</telefon>
    </angestellter>
    <angestellter kategorie="programmierer">
        <name>Klaus Debes</name>
        <telefon>45623-31</telefon>
    </angestellter>
    <angestellter kategorie="programmierer">
        <name>Volker Stephan</name>
        <telefon>45623-32</telefon>
    </angestellter>
</verzeichnis>

Die Darstellung aus der folgenden Abbildung zeigt, wie diese XML-Daten mit einem XSLT-Stylesheet nach HTML transformiert werden.

XSLT-Ausgabe auf Deutsch

Abbildung: XSLT-Ausgabe auf Deutsch

Das folgende Beispiel zeigt schließlich das XSLT-Stylesheet, das die Ausgabe erzeugt.

Beispiel: directory_basic.xslt

<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="html" encoding="ISO-8859-1"/>
    <xsl:template match="/">
        <html>
            <head>
                <title>Angestelltenverzeichnis</title>
            </head>
            <body>
                <h1>Angestelltenverzeichnis</h1>
                <table cellpadding="4" cellspacing="0" border="1">
                    <tr>
                        <th>Name</th>
                        <th>Kategorie</th>
                        <th>Telefon</th>
                    </tr>
                    <xsl:for-each select="verzeichnis/angestellter">
                        <tr>
                            <td>
                                <xsl:value-of select="name"/>
                            </td>
                            <td>
                                <xsl:choose>
                                    <xsl:when test="@kategorie='manager'">
                                        <xsl:text>Manager</xsl:text>
                                    </xsl:when>
                                    <xsl:when test="@kategorie='programmierer'">
                                        <xsl:text>Programmierer</xsl:text>
                                    </xsl:when>
                                    <xsl:otherwise>
                                        <xsl:text>Sonstiges</xsl:text>
                                    </xsl:otherwise>
                                </xsl:choose>
                            </td>
                            <td>
                                <xsl:value-of select="telefon"/>
                            </td>
                        </tr>
                    </xsl:for-each>
                </table>
            </body>
        </html>
    </xsl:template>
</xsl:stylesheet>

Wie man sieht, ist nur ein sehr kleiner in die Logik des Stylesheets eingebetteter Teil des XSLT-Codes sprachspezifisch. Für die Unterstützung einer anderen Sprache müßte jedoch ein komplett neues Stylesheet geschrieben werden.

Glücklicherweise gibt es eine Lösung für dieses Problem. XSLT-Stylesheets können andere Stylesheets importieren, wobei Templates und Variablen im importierenden Stylesheet Vorrang vor denselben Einträgen des importierten Stylesheets haben. Indem wir die sprachspezifischen Inhalte isolieren, können wir <xsl:import> verwenden, um verschiedene Sprachen zu unterstützen, während wir die gesamte Stylesheet-Logik wiederverwenden. Das folgende Beispiel zeigt eine überarbeitete Version unseres XSLT-Stylesheets.

Beispiel: directory_de.xslt

<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="html" encoding="ISO-8859-1"/>
    <!-- sprachspezifische Inhalte -->
    <xsl:variable name="sprache.SeitenTitel" select="'Angestelltenverzeichnis'"/>
    <xsl:variable name="sprache.SpalteName" select="'Name'"/>
    <xsl:variable name="sprache.SpalteKategorie" select="'Kategorie'"/>
    <xsl:variable name="sprache.SpalteTelefon" select="'Telefon'"/>
    <xsl:variable name="sprache.Manager" select="'Manager'"/>
    <xsl:variable name="sprache.Programmierer" select="'Programmierer'"/>
    <xsl:variable name="sprache.Sonstiges" select="'Sonstiges'"/>
    <xsl:template match="/">
        <html>
            <head>
                <title><xsl:value-of select="$sprache.SeitenTitel"/></title>
            </head>
            <body>
                <h1><xsl:value-of select="$sprache.SeitenTitel"/></h1>
                <table cellpadding="4" cellspacing="0" border="1">
                    <tr>
                        <th><xsl:value-of select="$sprache.SpalteName"/></th>
                        <th><xsl:value-of select="$sprache.SpalteKategorie"/></th>
                        <th><xsl:value-of select="$sprache.SpalteTelefon"/></th>
                    </tr>
                    <xsl:for-each select="verzeichnis/angestellter">
                        <tr>
                            <td>
                                <xsl:value-of select="name"/>
                            </td>
                            <td>
                                <xsl:choose>
                                    <xsl:when test="@kategorie='manager'">
                                        <xsl:value-of select="$sprache.Manager"/>
                                    </xsl:when>
                                    <xsl:when test="@kategorie='programmierer'">
                                        <xsl:value-of select="$sprache.Programmierer"/>
                                    </xsl:when>
                                    <xsl:otherwise>
                                        <xsl:value-of select="$sprache.Sonstiges"/>
                                    </xsl:otherwise>
                                </xsl:choose>
                            </td>
                            <td>
                                <xsl:value-of select="telefon"/>
                            </td>
                        </tr>
                    </xsl:for-each>
                </table>
            </body>
        </html>
    </xsl:template>
</xsl:stylesheet>

Dieses XSLT-Stylesheet unterstützt die Internationalisierung wesentlich besser. Der gesamte sprachspezifische Inhalt wurde in Form von Variablen deklariert, die von importierenden Stylesheets überschrieben werden können. Die Namenskonvention sprache. macht das Stylesheet übersichtlicher; ist jedoch kein Bestandteil der XSLT-Spezifikation. Abgesehen von dieser Änderung ist das Stylesheet identisch zum vorherigen.

Die englische Version des Stylesheets ist im folgenden Beispiel zu sehen.

Beispiel: directory_en.xslt

<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:import href="directory_de.xslt"/>
    <xsl:output method="html" encoding="ISO-8859-1"/>
    <!-- sprachspezifischer Inhalt -->
    <xsl:variable name="sprache.SeitenTitel" select="'Employee Directory'"/>
    <xsl:variable name="sprache.SpalteName" select="'Name'"/>
    <xsl:variable name="sprache.SpalteKategorie" select="'Category'"/>
    <xsl:variable name="sprache.SpalteTelefon" select="'Phone'"/>
    <xsl:variable name="sprache.Manager" select="'Manager'"/>
    <xsl:variable name="sprache.Programmierer" select="'Programmer'"/>
    <xsl:variable name="sprache.Sonstiges" select="'Other'"/>
</xsl:stylesheet>

Die englische Version des Stylesheets ist viel kürzer, da es nur die sprachspezifischen Variablen überschreibt. Der Schlüssel ist das Element <xsl:import>:

<xsl:import href="directory_de.xslt"/>

Aufgrund der XSLT-Konfliktlösungsregeln überschreiben die in directory_en.xslt definierten Variablen diejenigen aus directory_de.xslt . Dieselben Regeln gelten auch für Templates. Das ist dann wichtig, wenn das importierende Stylesheet neben einer Textübersetzung auch ein anderes Verhalten zeigen soll.

Die folgende Zeile ist optional:

<xsl:output method="html" encoding="ISO-8859-1"/>

In diesem Beispiel sind die Ausgabemethode und -kodierung identisch zur deutschen Version des Stylesheets, so daß diese Zeile keine Auswirkungen hat. Das importierende Stylesheet könnte jedoch eine andere Ausgabemethode und -kodierung verwenden, wenn das gewünscht ist.

Verwenden Sie das folgende Kommando, um mit Xalan die englische Transformation durchzuführen:

$ java org.apache.xalan.xslt.Process -IN directory.xml -XSL directory_en.xslt

Die folgende Abbildung zeigt das Ergebnis dieser Transformation in einem Webbrowser.

Ausgabe auf Englisch

Abbildung: Ausgabe auf Englisch

Hinweis:
In den Beispiel-Stylesheets zu i18n in diesem Kapitel wurde allgemeine Funktionalität in einem Stylesheet plaziert. Importierende Stylesheets ersetzen dann den sprachspezifischen Text. Dieselbe Technik kann auf jedes Stylesheet angewendet werden und ist besonders nützlich, wenn XSLT-Code für bestimmte Browser maßgeschneidert werden muß. Der Großteil des Codes ist für die meisten Browser kompatibel und sollte in einem wiederverwendbaren Stylesheet untergebracht werden. Die veränderlichen Teile werden dann in Browser-spezifische Stylesheets ausgelagert, die jeweils das allgemeine Stylesheet importieren.

Kodierungen

Eine Zeichenkodierung ist eine numerische Repräsentation eines bestimmten Zeichens. (Detaillierte Informationen über Zeichenkodierungen finden Sie in Java Internationalization von Andy Deitsch und David Czarnecki, O’Reilly.) Die US-ASCII-Kodierung des Buchstabens A ist z.B. 65. Wenn ein Computer eine Datei in US-ASCII-Kodierung liest oder schreibt, wird jedes Zeichen durch genau ein Byte repräsentiert. Von diesem Byte werden aber tatsächlich nur sieben Bit für die Kodierung des Zeichens verwendet, da das erste (»most significant«) Bit immer 0 sein muß. Deshalb können mit US-ASCII nur 128 verschiedene Zeichen dargestellt werden, was natürlich ein Problem für Sprachen darstellt, die aus mehr als 128 Zeichen bestehen. Für diese Sprachen muß eine andere Kodierung gewählt werden.

Die umfassendste Zeichenkodierung wird ISO/IEC 10646 oder Universal-Character-Set (UCS) genannt und verwendet für jedes Zeichen eine 32-Bit-Darstellung. Diese ermöglicht zwar die Identifizierung jedes Zeichens aus jeder Sprache, wird aber von den meisten Computerprogrammen nicht verstanden. Es führt auch zu einem nicht unerheblichen Speicherverbrauch, wenn für jedes Zeichen immer 32 Bit verwendet werden.

Unicode ist die offizielle Implementierung von ISO/IEC 10646, arbeitet aber momentan nur mit 16-Bit-Zeichen. UCS Transformation Formats (UTFs) wurden entworfen, um UCS-Kodierungen zu unterstützen und gleichzeitig mit bestehender Software und anderen Kodierungen kompatibel zu bleiben. UTF-8 und UTF-16 sind die gebräuchlichsten Transformationsformate, und alle XML-Parser und XSLT-Prozessoren müssen beide unterstützen.

UTF-8 ist die effizienteste und am einfachsten zu nutzende Kodierung für englischen Text. Weil die ersten 128 UTF-8-Zeichen mit den US-ASCII-Zeichen identisch sind, können viele Anwendungen UTF-8 kodierte Dateien problemlos nutzen. Wenn zusätzliche Zeichen benötigt werden, verwendet UTF-8 bis zu drei Byte pro Zeichen.

UTF-16 ist für Chinesisch, Japanisch und Koreanisch effizienter als UTF-8. Bei UTF-16 benötigt jedes Zeichen zwei Byte, während viele bei der UTF-8-Kodierung drei Bytes benötigen. Entweder UTF-8 oder UTF-16 sollten funktionieren. Es ist allerdings ratsam, eine Transformation mit beiden Kodierungen zu testen, um herauszufinden, mit welcher die kleinsten Dateien für die jeweiligen Daten erzeugt werden. Viele Anwendungen und Betriebssysteme, besonders einige Unix- und Linux-Varianten, bieten eine bessere Unterstützung für UTF-8-Kodierung.

Wie in fast jedem XSLT-Beispiel dieses Buches zu sehen war, bestimmt das Element <xsl:output> die Kodierung des XSLT-Ergebnisbaums:

<xsl:output method="html" encoding="UTF-16"/>

Fehlt dieses Element, verwendet der XSLT-Prozessor standardmäßig UTF-8- oder UTF-16-Kodierung. (Die XSLT-Spezifikation definiert nicht, wie der Prozessor zwischen UTF-8 und UTF-16 wählt.)

XML und XSLT erzeugen

Die XML-Eingabedaten, das XSLT-Stylesheet und das Ergebnis der Transformation müssen weder dieselbe Zeichenkodierung noch dieselbe Sprache verwenden. Ein XSLT-Stylesheet kann z.B. in UTF-16 kodiert sein, als Ausgabemethode aber UTF-8 verwenden:

<?xml version="1.0" encoding="UTF-16"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="html" encoding="UTF-8"/>
  ...

Auch wenn in der ersten Zeile bereits UTF-16 spezifiziert wird, ist es wichtig, daß auch der Texteditor, mit dem das Stylesheet geschrieben und gespeichert wurde, tatsächlich UTF-16-Kodierung verwendet. Ansonsten liefern Werkzeuge wie XMLSpy Fehlermeldungen wie in der folgenden Abbildung.

Fehlerdialog

Abbildung: Fehlerdialog

Um die Dinge noch komplizierter zu machen, gibt es verschiedene Varianten von UTF-16. In der Kodierung UTF-16 Little-Endian (UTF-16LE) steht das niederwertige Byte des Zwei-Byte-Zeichens vor dem höchstwertigen Byte. In der Kodierung UTF-16 Big-Endian (UTF-16BE) ist es genau umgekehrt. Glücklicherweise erkennen XML-Parser die Kodierung einer Datei an einer Markierung der Byte-Ordnung. In einer Datei mit UTF-16LE-Kodierung ist das erste Doppelbyte 0xFFFE und bei UTF-16BE-Dateien ist es 0xFEFF.

Für das folgende Beispiel in Chinesisch wurde der Textprozessor NJStar Chinese verwendet, um die chinesischen Zeichen einzugeben. Es handelt sich um einen Editor, mit dem man Ideogramme eingeben und mit verschiedenen Kodierungen abspeichern kann. Die Windows NT-Version von Notepad kann Dateien im Unicode-Format UTF-16LE speichern, während die Version für Windows 2000 zusätzlich UTF-8 und UTF-16BE unterstützt.

Wenn alles andere fehlschlägt, können kodierte Textdateien mit der Javaklasse java.io.OutputStreamWriter erzeugt werden:

FileOutputStream fos = new FileOutputStream("myFile.xml");
// der OutputStreamWriter spezifiziert die Kodierung der Datei
PrintWriter pw = new PrintWriter(new OutputStreamWriter(fos, "UTF-16"));
...auf pw wird wie auf jeden anderen PrintWriter geschrieben
pw.close( );

Alles zusammen

Das Zusammenspiel aller Teilkomponenten bildet oft den kompliziertesten Teil der Internationalisierung. Um das Konzept zu demonstrieren, arbeiten wir mit XML-Dateien, XSLT-Stylesheets und einem Servlet, die zusammen jede Kombination von Deutsch, Englisch und Chinesisch unterstützen. Ein einfaches HTML-Formular ermöglicht es dem Nutzer, die XML-Datei und das XSLT-Stylesheet für die Transformation zu bestimmen. Die entsprechende Webseite ist in der folgenden Abbildung zu sehen.

Auswahl der Sprachen v. XML-Daten u. XSLT-Stylesheet

Abbildung: Auswahl der Sprachen von XML-Daten und XSLT-Stylesheet

Wie Sie sehen, existieren drei Versionen der XML-Datei, eine für jede Sprache. Abgesehen von der Sprache sind diese Dateien jedoch identisch. Es gibt ebenfalls drei Versionen des XSLT-Stylesheets, und der Nutzer kann jede Kombination von XML- und XSLT-Sprache auswählen. Auch die Zeichenkodierung des Ergebnisses der Transformation ist konfigurierbar. UTF-8 und UTF-16 sind Unicode-kompatibel und sind in der Lage, die englischen und chinesischen Zeichen direkt darzustellen. ISO-8859-1 kann dagegen nur erweiterte Zeichensätze mit Zeichenreferenzen wie &#25991; darstellen.

In diesem Beispiel spezifiziert ein Nutzer seine Sprachvorlieben direkt. Es ist aber auch denkbar, ein Servlet zu schreiben, das den HTTP-Header Accept-Language verwendet, in dem eine Liste von bevorzugten Sprachen angegeben werden kann:

de, en, zh

Eine Anwendung kann eine passende Sprache und Zeichenkodierung aus dieser Liste auswählen, ohne den Nutzer danach zu fragen. Kapitel 13 des Buches Java Servlet-Programmierung von Jason Hunter (O’Reilly) präsentiert eine detaillierte Diskussion dieser Technik und stellt die Klasse LocaleNegotiator vor, die mehr als 30 Sprachen auf die jeweils passende Zeichenkodierung abbildet.

Die folgende Abbildung zeigt die Ergebnisse von drei verschiedenen Transformationen. Im ersten Fenster wurde das chinesische XSLT-Stylesheet auf die chinesische XML-Datei angewendet. Im zweiten Fenster wurde das deutsche Stylesheet auf die englische XML-Datei angewendet, und im dritten das englische Stylesheet auf die chinesische XML-Datei.

Verschiedene Sprachkombinationen

Abbildung: Verschiedene Sprachkombinationen

Die Zeichenkodierung ist im allgemeinen für den Nutzer transparent. Das Umschalten auf eine andere Kodierung ändert nichts an der Ausgabe aus obiger Abbildung. Der Unterschied wird jedoch sichtbar, wenn die Quellen der Seite angezeigt werden. Wenn die Ausgabe z.B. mit UTF-8 kodiert ist, werden die chinesischen Zeichen im HTML-Quellcode angezeigt. Mit der Kodierung ISO-8859-1 sieht der Quellcode ungefähr so aus:

<html>
    <head>
        <META http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
        <title>&#20013;&#25991;XSLT</title>
    </head>
    <body>
        <h1>&#20013;&#25991;XSLT</h1>
        ...der Rest der Seite wurde weggelassen

Wie Sie sehen, wurden die chinesischen Zeichen durch entsprechende Zeichenreferenzen wie &#20013; ersetzt. Der XSLT-Prozessor erzeugt diese Werte automatisch, wenn die Ausgabekodierung die Zeichen nicht direkt darstellen kann.

Zeichensätze von Browsern

Die neuesten Versionen der wichtigsten Webbrowser können ohne Probleme UTF-8- und UTF-16-kodierte Zeichen darstellen. Die Konfiguration der Fonts ist das Hauptproblem. Beim Internet Explorer muß die Menü-Option Ansicht → Codierung → Automatische Auswahl aktiviert sein. Beim Netscape 6 ist die Option Ansicht → Zeichencodierung → Automatische Erkennung vergleichbar. Wenn Sie beim Testen der Beispiele Fragezeichen und vermischten Text sehen, ist das ein Hinweis darauf, daß die nötigen Fonts auf Ihrem System nicht installiert sind.

Für die Chinesisch-Beispiele dieses Kapitels müssen die Windows 2000-Fonts SimHei und SimSun installiert sein. Diese und andere Fonts sind bei Windows 2000 enthalten, werden aber nicht automatisch installiert, sondern nur, wenn die entsprechenden Spracheinstellungen im Optionsfenster Ländereinstellungen getroffen werden. Dieses Fenster befindet sich in der Systemsteuerung von Windows 2000.

Für UNIX- bzw. Linux-Versionen ist das Hilfemenü von Netscape 6 ein guter Startpunkt. Die Option International Users verweist auf eine Webseite mit Hinweisen auf Zeichensätze für die verschiedensten Versionen von Unix und Linux, unter denen Netscape läuft.

XML-Daten

Jede der drei XML-Dateien dieses Beispiels folgt dem Format des folgenden Beispiels. Die XML-Daten bestehen lediglich aus Übersetzungen aus dem Deutschen in eine andere Sprache. Alle drei Dateinamen folgen derselben Konvention: zahlen_deutsch.xml, zahlen_englisch.xml und zahlen_chinesisch.xml.

Beispiel: zahlen_englisch.xml

<?xml version="1.0" encoding="ISO-8859-1"?>
<zahlen>
    <sprache>Englisch</sprache>
    <zahl deutsch="eins">one</zahl>
    <zahl deutsch="zwei">two</zahl>
    <zahl deutsch="drei">three</zahl>
    <zahl deutsch="vier">four</zahl>
    <zahl deutsch="fünf">five</zahl>
    <zahl deutsch="sechs">six</zahl>
    <zahl deutsch="sieben">seven</zahl>
    <zahl deutsch="acht">eight</zahl>
    <zahl deutsch="neun">nine</zahl>
    <zahl deutsch="zehn">ten</zahl>
</zahlen>

XSLT-Stylesheets

Das Stylesheet zahlen_deutsch.xslt ist im folgenden Beispiel zu sehen. Es folgt demselben Muster, das schon früher in diesem Kapitel vorgestellt wurde, indem es die sprachspezifischen Daten als eine Sammlung von Variablen isoliert.

Beispiel: zahlen_deutsch.xslt

<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="html" encoding="ISO-8859-1"/>
    <xsl:variable name="sprache.SeitenTitel">XSLT in Deutsch</xsl:variable>
    <xsl:variable name="sprache.TabellenCaption">Hier ist eine Tabelle mit Zahlen:</xsl:variable>
    <xsl:variable name="sprache.Heading">Deutsch</xsl:variable>
    <xsl:template match="/">
        <html>
            <head>
                <title><xsl:value-of select="$sprache.SeitenTitel"/></title>
            </head>
            <body>
                <xsl:apply-templates select="zahlen"/>
            </body>
        </html>
    </xsl:template>
    <xsl:template match="zahlen">
        <h1><xsl:value-of select="$sprache.SeitenTitel"/></h1>
        <xsl:value-of select="$sprache.TabellenCaption"/>
        <table border="1">
            <tr>
                <th><xsl:value-of select="$sprache.Heading"/></th>
                <th><xsl:value-of select="sprache"/></th>
            </tr>
            <xsl:apply-templates select="zahl"/>
        </table>
    </xsl:template>
    <xsl:template match="zahl">
        <tr>
            <td><xsl:value-of select="@deutsch"/></td>
            <td><xsl:value-of select="."/></td>
        </tr>
    </xsl:template>
</xsl:stylesheet>

Die Ausgabekodierung dieses Stylesheets ist ISO-8859-1. Dies kann (und wird) jedoch vom Servlet überschrieben. Das englische Stylesheet zahlen_englisch.xslt ist im folgenden Beispiel zu sehen.

Beispiel: zahlen_englisch.xslt

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:import href="zahlen_deutsch.xslt"/>
    <xsl:variable name="sprache.SeitenTitel">XSLT in English</xsl:variable>
    <xsl:variable name="sprache.TabellenCaption">Here is a table of numbers:</xsl:variable>
    <xsl:variable name="sprache.Heading">Englisch</xsl:variable>
</xsl:stylesheet>

Das chinesische Stylesheet zahlen_chinese.xslt ist hier nicht abgebildet, da es genau wie das englische Stylesheet strukturiert ist. In beiden Fällen wird zahlen_deutsch.xslt importiert, und die drei Variablen werden mit den jeweiligen Übersetzungen überschrieben.

Webseite und Servlet

Der Nutzer startet auf der Webseite aus der Abbildung Auswahl der Sprachen von XML-Daten und XSLT-Stylesheet. Der HTML-Code dieser Seite ist im folgenden Beispiel zu sehen. Die Auswahl für die Sprache und die Kodierung werden an das Servlet übermittelt, sobald der Nutzer den Button »Senden« betätigt.

Beispiel: i18n.html

<html>
    <head>
        <title>Internationalisierung</title>
    </head>
    <body>
        <form method="post" action="/chap8/languageDemo">
            <table border="1">
                <tr valign="top">
                    <td>XML-Sprache:</td>
                    <td>
                        <input type="radio" name="xmlLanguage" checked="checked" value="deutsch"> Deutsch<br />
                        <input type="radio" name="xmlLanguage" value="englisch"> Englisch<br />
                        <input type="radio" name="xmlLanguage" value="chinesisch"> Chinesisch
                    </td>
                </tr>
                <tr valign="top">
                    <td>XSLT-Sprache:</td>
                    <td>
                        <input type="radio" name="xsltLanguage" checked="checked" value="deutsch"> Deutsch<br />
                        <input type="radio" name="xsltLanguage" value="englisch"> Englisch<br />
                        <input type="radio" name="xsltLanguage" value="chinesisch"> Chinesisch
                    </td>
                </tr>
                <tr valign="top">
                    <td>Zeichenkodierung:</td>
                    <td>
                        <input type="radio" name="charEnc" value="ISO-8859-1"> ISO-8859-1<br />
                        <input type="radio" name="charEnc" value="UTF-8" checked="checked"> UTF-8<br />
                        <input type="radio" name="charEnc" value="UTF-16"> UTF-16<br />
                    </td>
                </tr>
            </table>
            <p><input type="submit" name="submitBtn" value="Senden"></p>
        </form>
    </body>
</html>

Im folgenden Beispiel ist das Servlet LanguageDemo.java zu sehen. Es nimmt die Daten des Eingabeformulars der Webseite i18n.html entgegen und führt die XSLT-Transformation durch.

Beispiel: Servlet LanguageDemo.java

package chap8;

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.xml.transform.*;
import javax.xml.transform.stream.*;

/**
 * Erlaubt eine Kombination von deutschem, englischem und chinesischem XML
 * und XSLT.
 */
public class LanguageDemo extends HttpServlet {
  
  public void doPost(HttpServletRequest req, HttpServletResponse res)
      throws ServletException, IOException {
    ServletContext ctx = getServletContext( );
        
    // das sind die benötigten Parameter des HTML-Formulars
    String xmlLang = req.getParameter("xmlLanguage");
    String xsltLang = req.getParameter("xsltLanguage");
    String charEnc = req.getParameter("charEnc");
        
    // erzeuge systemabhängige Pfadnamen
    String xmlFileName = ctx.getRealPath(
      "/WEB-INF/xml/zahlen_" + xmlLang + ".xml");
    String xsltFileName = ctx.getRealPath(
      "/WEB-INF/xslt/zahlen_" + xsltLang + ".xslt");
        
    // vor dem Aufruf von HttpServletResponse.getWriter( ) ausführen
    res.setContentType("text/html; charset=" + charEnc);
        
    try {
      Source xmlSource = new StreamSource(new File(xmlFileName));
      Source xsltSource = new StreamSource(new File(xsltFileName));
      
      TransformerFactory transFact = TransformerFactory.newInstance( );
      Transformer trans = transFact.newTransformer(xsltSource);
      
      trans.setOutputProperty(OutputKeys.ENCODING, charEnc);
      
      // Bemerkung: res.getWriter( ) benutzt die Kodierung,
      // die mit res.setContentType( ) eingestellt wurde
      trans.transform(xmlSource, new StreamResult(res.getWriter( )));
      
    } catch (TransformerConfigurationException tce) {
      throw new ServletException(tce);
    } catch (TransformerException te) {
      throw new ServletException(te);
    }
  }
}

Nachdem die drei Anfrageparameter für XML, XSLT und die Kodierung ermittelt wurden, konvertiert das Servlet die XML- und XSLT-Namen in reale Dateinamen:

String xmlFileName = ctx.getRealPath("/WEB-INF/xml/zahlen_" + xmlLang + ".xml");
String xsltFileName = ctx.getRealPath("/WEB-INF/xslt/zahlen_" + xsltLang + ".xslt");

Aufgrund der konsistenten Benennung von XML-Dateien und XSLT-Stylesheets können die Dateinamen auf einfache Weise bestimmt werden. Im nächsten Schritt wird der Content-Type der Antwort festgelegt:

// vor dem Aufruf von HttpServletResponse.getWriter( ) ausführen
res.setContentType("text/html; charset=" + charEnc);

In diesem Schritt wird dem Servlet-Container mitgeteilt, mit welcher Kodierung die Antwort zum Client geschickt werden soll. Dies wird als Content-Type im Header der HTTP-Antwort eingetragen, so daß der Browser die zu erwartende Kodierung bestimmen kann. In unserem Beispiel entsprechen die drei möglichen Zeichenkodierungen den folgenden Content-Types:

Content-Type: text/html; charset=ISO-8869-1
Content-Type: text/html; charset=UTF-8
Content-Type: text/html; charset=UTF-16

Als nächstes verwendet das Servlet das Interface javax.xml.transform.Source und die Klasse javax.xml.transform.stream.StreamSource, um die XML- und XSLT-Dateien zu lesen:

Source xmlSource = new StreamSource(new File(xmlFileName));
Source xsltSource = new StreamSource(new File(xsltFileName));

Durch die Verwendung von java.io.File kann StreamSource die Kodierung der XML- und XSLT-Dateien korrekt ermitteln, indem die XML-Deklaration der Dateien ausgewertet wird. Der Konstruktor StreamSource akzeptiert auch die Parameter InputStream und Reader. Besondere Vorsicht ist bei den Reader-Konstruktoren geboten, da Implementierungen von Reader die Standard-Zeichenkodierung von Java verwenden, die beim Start der VM festgelegt wird. Mit dem InputStreamReader kann eine Kodierung folgendermaßen festgelegt werden:

Source xmlSource = new StreamSource(new InputStreamReader(new FileInputStream(xmlFileName), "UTF-8"));

Weitere Informationen zu Zeichenkodierungen in Java finden Sie in der JavaDoc-Beschreibung des Paketes java.lang.

Unser Servlet überschreibt dann die Kodierung des XSLT-Stylesheets:

trans.setOutputProperty(OutputKeys.ENCODING, charEnc);

Dies hat Vorrang vor der im Element <xsl:output> festgelegten Kodierung, siehe das Beispiel zahlen_deutsch.xslt.

Schließlich führt das Servlet die Transformation durch und sendet das Ergebnis an einen mit HttpServletResponse erzeugten Writer:

// Bemerkung: res.getWriter( ) benutzt die Kodierung,
// die mit res.setContentType( ) eingestellt wurde
trans.transform(xmlSource, new StreamResult(res.getWriter( )));

Wie der Kommentar besagt, sollte der Servlet-Container den Writer für die im Content-Type des HTTP-Headers festgelegte Zeichenkodierung konfigurieren.

Lösungen für Probleme bei der Internationalisierung

Die folgenden Dinge sollten Sie bei Problemen prüfen. Beseitigen Sie offensichtliche Fehler:

  • Besuchen Sie eine Website mit der Sprache, die Sie erzeugen wollen. Bei der China Daily Website gibt es z.B. die Option, die Seite in chinesisch anzuzeigen. Damit stellen Sie sicher, daß Ihr Browser die richtigen Fonts lädt.
  • Testen Sie Ihre Anwendung mit deutschen XML-Daten und XSLT-Stylesheets, um sicherzustellen, daß die Transformationen korrekt ausgeführt werden.
  • Führen Sie die XSLT-Transformation an der Kommandozeile durch. Speichern Sie das Ergebnis in eine Datei und zeigen Sie diese mit einem Unicode-kompatiblen Texteditor an. Wenn das nichts nützt, zeigen Sie die Datei mit einem Binäreditor an, um zu sehen, wie die Zeichen kodiert sind.
  • Prüfen Sie, ob Ihr XML-Parser diese Kodierungen überhaupt unterstützt. (Die vom Xerces-Parser von Apache unterstützten Kodierungen sind unter den General FAQs dokumentiert.)

Wenn Sie mit diesen Tests das Problem nicht finden, versuchen Sie folgendes:

  • Bleiben Sie bei der Kodierung UTF-8, bis die Probleme gelöst sind. Es ist die universellste Kodierung von allen.
  • Prüfen Sie, ob das Servlet den richtigen Content-Type im Datei-Header setzt:
Content-Type: text/html; charset=UTF-8
  • Prüfen Sie, ob das XSLT-Stylesheet im Element <xsl:output> die richtige Kodierung setzt, und überschreiben Sie diese gegebenenfalls:
transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
  • Fügen Sie im Servlet Code ein, der das Ergebnis der Transformation nicht an einen Writer von HttpServletResponse, sondern in eine Datei schreibt. Prüfen Sie diese Datei mit einem Unicode-kompatiblen Texteditor.
  • Verwenden Sie java.io.File oder java.io.InputStream anstelle von java.io.Reader, wenn Sie die XML- und XSLT-Dateien einlesen.

   

zum Seitenanfang

<< zurück vor >>

 

 

 

Tipp der data2type-Redaktion:
Zum Thema Java & XSLT bieten wir auch folgende Schulungen zur Vertiefung und professionellen Fortbildung an:

Copyright © 2002 O'Reilly Verlag GmbH & Co. KG
Für Ihren privaten Gebrauch dürfen Sie die Online-Version ausdrucken.
Ansonsten unterliegt dieses Kapitel aus dem Buch "Java und XSLT" denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.

O’Reilly Verlag GmbH & Co. KG, Balthasarstraße 81, 50670 Köln, kommentar(at)oreilly.de