Kapitel 3. Web-Standards

Inhaltsverzeichnis

1. Hypertext Transfer Protocol HTTP
1.1. Funktionsweise von HTTP
1.2. Der Header Content-Type
1.2.1. Die Konfigurationsanweisung AddDefaultCharset
1.2.2. Die Konfigurationsanweisung AddCharset
1.3. Der Header Content-Language
1.3.1. Die Konfigurationsanweisung DefaultLanguage
1.3.2. Die Konfigurationsanweisung AddLanguage
1.4. Voreingestellte Werte
1.5. Der Header Accept-Charset
1.6. Der Header Accept-Language
1.7. Formulare
2. Extensible Markup Language XML
2.1. Perfomance-Erwägungen
2.2. XML in UTF-8
2.3. Sprachbestimmung
3. Hypertext Markup Language HTML
3.1. Das HTML-Attribut lang
3.2. Das Attribut http-equiv des meta-Elements.
3.3. XHTML
4. Content-Negotiation
4.1. Implementierung mit dem Apache
4.2. Praktische Erwägungen
4.2.1. Sprachpersistenz
4.2.2. Sprachumschaltung
4.2.3. Vertikale oder horizontale Aufteilung
5. Zusammenfassung

Die meisten modernenen textbezogenenem Standards sind von vorneherein so angelegt, dass sie Texte in beliebigen Sprachen adäquat verarbeiten können. Die für Web-Autoren relevanten Standards HTTP, XML und HTML werden in den nachfolgenden Abschnitten einer näheren Betrachtung unterzogen.

1. Hypertext Transfer Protocol HTTP

Das Akronym HTTP steht für Hypertext Transfer Protocol. Das Protokoll ist in [RFC2616] standardisiert. Obwohl der Name etwas anderes suggeriert, beschränkt sich seine Anwendung keineswegs auf die Übertragung von Hypertext, oder anderen Textformaten. Es dient heute vielmehr der Übermittlung beliebiger Daten, kann also Bilder, Sound, Video, Vektorgraphiken, ja ganze Programme oder Software-Archive übertragen. Im folgenden beschränken wir uns allerdings weitestgehend auf die Übertragung von Textdateien über HTTP.

Es sei noch angemerkt, dass sich alle Beispiele nur auf den Web-Server Apache beziehen. Andere Web-Server implementieren HTTP oftmals fehlerhaft, manche Features sind nicht vorhanden oder nur mit unverhältnismäßigem Aufwand oder durch Erwerb von Zusatzmodulen zu realisieren, und schließlich legt die große Verbreitung des Apache-Servers eine Beschränkung in der Darstellung auf diesen einen Server nahe.

1.1. Funktionsweise von HTTP

HTTP ist ein textbasiertes Protokoll, der prinzipielle Ablauf ist also von Menschen lesbar. Das Protokoll ist stets zweistufig: Der Web-Browser (Client) schickte eine Anfrage (einen Request) zum Server, woraufhin der Server eine Antwort zurückschickt. Sowohl die Anfrage als auch die Antwort bestehen aus zwei Teilen, einem Header mit Meta-Informationen über die Anfrage bzw. die Antwort, und einem Body, der die eigentlichen Informationen enthält.

Spielen wir als Beispiel einmal durch, was passiert, wenn wir mit einem Web-Browser die imaginäre Web-Seite http://www.gips.nicht/mein/home.html aufrufen. Der Browser wird in diesem Falle ein Datenpaket schicken, das in etwa so aussieht:

    GET /mein/home.html HTTP/1.1                           (1)(2)(3)
    Host: www.gips.nicht                                   (4)
    User-Agent: Microscape Browser 6.15                    (5)
    Connection: close                                      (6)
                                                           (7)
	  
1

Die erste Zeile beginnt stets mit dem Request-Typ. Außer GET ist hier für praktische Belange noch POST interessant, dass zur Übermittlung von Formular-Daten verwendet wird (auch HEAD zur Ermittlung von Meta-Informationen und PUT zum Datei-Upload sind in der Praxis noch relevant).

2

Durch Leerzeichen getrennt folgt der Pfad zum angeforderten Dokument.

3

Die erste Zeile wird abgeschlossen durch die Angabe des verwendeten Protokolls (hier HTTP) und der Protokoll-Version (zur Zeit aktuell ist 1.1).

4

Der (virtuelle) Hostname des Servers muss ab HTTP-Version 1.1 immer mitgesendet werden. Dies ist notwendig, weil der Server sonst nicht wissen kann, unter welchem Namen er aufgerufen wurde, denn er sieht nicht etwa die symbolische Adresse „www.gips.nicht“, sondern lediglich eine nummerische IP-Adresse wie 192.168.6.29, die aber beliebig vielen symbolischen Adressen zugeordnet sein kann. Durch den Host-Header erfährt der Server, welcher virtuelle Host vom Browser „gemeint“ war. Mit Hilfe dieser virtuellen Hosts ist es also möglich, dass ein und derselbe physikalische Server beliebig viele symbolische Adressen und sogar Domains bedienen kann. Um bei unserem Beispiel zu bleiben: Würden wir für die gleiche IP-Adresse auch noch die Namen „www.gips.doch“ und „das.gips.nicht“ vergeben, könnte unser Server - bei entsprechender Konfiguration - noch für zwei weitere komplette Sites eingesetzt werden.

5

Ein String, der den Browser identifiziert.

6

Der Verbindungstyp. In diesem Falle weisen wir den Server an, die Verbindung nach der Antwort zu schließen. Alternativ könnten wir mit dem Wert Keep-Alive den Server „bitten“, die Verbindung für weitere Anfragen offenzuhalten, weil wir beispielsweise wissen, dass wir noch einige Dutzend Bilder vom gleichen Server holen müssen.

7

Die abschließende Leerzeile teilt dem Server mit, dass der Request hier endet.

Man kann dieses Beispiel übrigens durchaus am eigenen Computer nachvollziehen, sofern das Programm telnet auf dem Rechner installiert ist. Unter Unix ruft man es in der Form telnet www.servername.com 80 auf (80 ist die Port-Nummer für HTTP) und tippt die obige Protokollausgabe ein. Unter Windows öffnet man eine DOS-Box und tut das gleiche wie unter Unix. Im Gegensatz zur Unix-Version sieht man allerdings nicht, was man eintippt. Am besten kopiert man den Text also aus einem anderen Fenster in die DOS-Box.

Der Server wird auf die Anfrage in jedem Fall antworten. Hat man sich nicht vertippt, wird er sogar das gewünschte Dokument ausliefern, in unserem Falle also /mein/home.html (auf Vertipper wird der Server mit einer entsprechenden Fehlermeldung antworten, die vom Format aber gleich aufgebaut ist). Allerdings ist es etwas mühselig, im Telnet-Fenster zu scrollen, um die Server-Antwort zu lesen. Bequemer geht es mit dem Programm wget, das auf den meisten Unix-Systemen vorinstalliert ist (unter Windows ist es in der Cygwin-Umgebung enthalten). Mit wget lassen sich Web-Dokumente über die Kommandozeile herunterladen, für unser Beispiel ließe sich das mit wget -s http://www.gips.nicht/mein/home.html bewerkstelligen (die Option -s, bzw. --save-headers weist wget an, die Header des Servers in der Datei mit abzuspeichern). Falls wget mit ...: Connection refused antwortet, müssen wir wahrscheinlich noch einen Proxy-Server angeben. Das geht, indem man in das Terminalfenster das Kommando http_proxy=http://proxy:3128/; export http_proxy eingibt. Statt proxy und 3128 muss natürlich der Name des Proxy-Servers und dessen Port eingegeben werden. Beide Informationen bekommt man zur Not von seiner Systemadministration.

Die Antwort des Servers sollte jedenfalls ungefähr wie folgt aussehen:

HTTP/1.1 200 OK                                            (1)(2)
Date: Mon, 09 Jun 2003 12:53:37 GMT
Server: Apache/1.3.26 (Linux/SuSE) PHP/4.2.2 mod_perl/1.27
Last-Modified: Mon, 09 Jun 2003 12:53:30 GMT
ETag: "f368c-f1-3ee4834a"
Accept-Ranges: bytes
Content-Length: 280
Keep-Alive: timeout=15, max=100
Connection: Keep-Alive
Content-Type: text/html                                    (3)

<html>                                                     (4)
<head>
  <meta http-equiv="Content-Type"
        content="text/html; charset=iso-8859-1" />         (5)
  <title>Meine Homepage</title>
</head>
<body>
  <h1>Willkommen auf meiner Homepage</h1>
  <p>
Die ist eine schöne Homepage, weil der Autor hat sie selbst gemacht.
  </p>
</body>
</html>
1

Die Antwort des Servers beginnt wiederum mit der Angabe des Protokolls und der Version.

2

Auf die Protokollangabe folgt ein nummerischer Status-Code (hier 200) und ein formfreier Text, der diesen nummerischen Status-Code im Klartext (hier: „OK“) beschreibt. Welche Status-Codes möglich sind, ist in [RFC2616], Abschnitt 10 genau festgelegt.

3

Hier beschreibt der Server, um welchen Typ von Datei es sich handelt, und genau dieser Header „Content-Type“ ist für uns interessant und wird unten noch näher betrachtet.

4

Wiederum durch eine Leerzeile abgetrennt folgt die eigentliche Datei, die wir angefordert haben.

5

Bemerkenswert ist, dass der Server - obwohl es sich beim angeforderten Dokument um eine Text-Datei handelt - keinerlei Angaben darüber macht, in welchem Codeset die Datei vorliegt. Dies ist erst dem Dokument selber zu entnehmen.

Tatsächlich wäre es allerdings durchaus möglich (durch eine entsprechende Konfiguration des Servers), dass dieser das Codeset doch schon in den Headern mitsendet. Wie dies auszusehen hätte, ist im folgenden Abschnitt beschrieben.

1.2. Der Header Content-Type

Das Format des Headers Content-Type ist in [RFC2616] genau beschrieben. Es besteht aus der Angabe eines Medientyps, optional gefolgt von zusätzlichen Parametern, die durch ein Semikolon vom Medientyp abgetrennt sein müssten. Medientypen bestehen jeweils aus einer allgemeinen Typbezeichnung (application, audio, image, message, model, multipart, text, video) und einem Untertyp; die vollständige Liste kann [IANA-MEDIATYPES] entnommen werden. Selbstdefinierte Typen und Untertypen müssen mit „x-“ beginnen (diese Einschränkung scheint für Microsoft nicht zu gelten, denn dort brilliert man gerne mit der Erfindung von Fantasietypen wie „image/pjpeg“).

Uns interessiert vornehmlich der Typ „text“ mit dem Untertyp „html“. In einer vollständigen Typbezeichnung müssen die beiden Bestandteile durch einen Schrägstrich getrennt werden, es würde sich also text/html als Bezeichnung ergeben. Die Kodierung eines Dokumentes ist in einem optionalen Parameter charset zum Medientyp anzugeben. Die vollständige Bezeichnung für ein HTML-Dokument, dass in UTF-8 kodiert ist, wäre also text/html; charset=utf-8.

Groß-/Kleinschreibung spielt keine Rolle, für maximale Portabilität sollte man die Medientyp-Angabe allerdings immer klein schreiben. Ein häufiger Fehler ist es, den Wert des Parameters charset in Anführungszeichen zu setzen (wie in text/html; charset="utf-8". Das ist falsch! Zwar sind Anführungszeichen prinzipiell in Parameterwerten erlaubt, jedoch nicht bei charset.

Wie bekommen wir jetzt aber den Server dazu, diesen Parameter bei der Auslieferung des Dokumentes mitzuschicken? Bei dynamisch generierten Dokumenten ist dies kein Problem, denn dort gibt das Programm, dass den Inhalt generiert, meistens zumindest den Header Content-Type ohnehin selber aus. Sofern der Server diesen Header nicht später wieder überschreibt, was durchaus möglich ist:

1.2.1. Die Konfigurationsanweisung AddDefaultCharset

Diese Konfigurationsanweisung gibt es in drei Varianten:

    AddDefaultCharset charset                              (1)
    AddDefaultCharset Off                                  (2)
    AddDefaultCharset On                                   (3)
	    
1

Allen Dokumenten, deren Charset nicht auf anderem Wege ermittelt werden kann, wird im Header der angegebene charset-Parameter mitgegeben.

2

Diese Anweisung schaltet das Feature ab.

3

Dies hat beim Apache die gleiche Wirkung wie AddDefaultCharset iso-8859-1.

Die Verwendung der Direktive ist eine beliebte Fehlerquelle, weil es selbst bei dynamisch generiertem Content die Charset-Angabe überschreibt. Wenn Browser also penetrant von einer falschen Kodierung der Dokumente auf dem eigenen Server ausgehen, sollte man die Server-Konfiguration nach dieser Anweisung durchsuchen.

In anderen Fällen kann es jedoch durchaus sinnvoll sein, die Anweisung zu benutzen. Gerade in Umgebungen, in denen viele Dokumente durch Authoring-Tools unter MS-Windows erzeugt werden, können damit fehlerhafte Angaben im Dokument (z. B. iso-8859-1 statt windows-1252) auf einen Schlag vom Server „repariert“ werden.

AddDefaultCharset ist ab Version 1.3.12 im Apache verfügbar.

1.2.2. Die Konfigurationsanweisung AddCharset

    AddCharset utf-8 .utf8
    AddCharset koi8-r .koi
    AddCharset iso-8859-1 .iso
          

Diese Anweisung erfordert, dass das Apache-Modul mod_mime in den Server einkompiliert und in der Konfiguration aktiviert wurde. Dies ist in der Voreinstellung der Fall.

Bei AddCharset gehen wir schon etwas weniger mit dem Holzhammer an das Problem heran. Sofern man eine Namenskonvention hat, kann man den Server einfach anweisen, den Parameter charset in Abhängigkeit vom Dateinamen, genauer gesagt der Dateiendung, zuzufügen. Bei allen Dateien, deren Name auf .utf8 endet, wird der Server UTF-8 als Charset angeben, bei .iso ISO-8859-1 und bei .koi KOI8-R.

Problematisch ist dabei allerdings, dass der Dateiname in gewissem Sinne „verhunzt“ wird. Würden wir beispielsweise auf die Idee kommen, hier die Endung .gif zu „missbrauchen“, werden sich gerade ältere Browser evtl. nur mit Mühe davon abbringen lassen, die Datei als Bild anzuzeigen, selbst, wenn der Server den korrekten Medientyp text/html mitgegeben hat.

Das Problem lässt sich jedoch umgehen, wie wir im Abschnitt 4, „Content-Negotiation“ über Content-Negotiation sehen werden.

1.3. Der Header Content-Language

Nimmt ein Browser eine falsche Kodierung für ein Dokument an, hat das in der Regel ziemlich augenfällige Konsquenzen. Entweder ist der Text von Fragezeichen oder kleinen Rechtecken durchsetzt, oder man sieht scheinbar völlig sinnlosen Zeichenmüll. Der Grund ist natürlich, dass der Browser den Byte-Strom fehlinterpretiert, und nicht in die korrekten Schriftzeichen umwandelt.

Weniger augenfällig ist die Notwendigkeit, auch die Sprache eines Dokumentes als Meta-Information mitzuliefern. Fangen wir mit einem (leider untauglichen) Beispiel in HTML an:

    <p>
They kill babies in the crib, and say <q>only the good die young ...</q>
    </p>
	

Aber hallo! Das Element q gibt es doch gar nicht in HTML, das muss doch &quot; heißen! Weit gefehlt, aber selbstverständlich gibt es das! Allerdings erst seit Dezember 1997 (siehe [HTML-4.0]), und heute, keine sechs Jahre später kann man natürlich nicht verlangen, dass schon alle neuen Browserversionen dieses Feature unterstützen. Der mit der größten Marktdurchdringung ignoriert es jedenfalls schlichtweg ...

Gedacht ist das Element für Zitate innerhalb eines Textblocks. Ein &quot; ist nämlich immer das Zeichen mit dem ASCII-Code 34, "doppelte" Anführungszeichen. Es gibt jedoch keine bekannte Sprache, in der dies tatsächlich die typographisch korrekte Darstellung für Zitate wäre, und für diesen Zweck wurde das HTML-Element q eingeführt. Je nach Dokumentensprache soll der Browser hier beispielsweise “englische Quotes”, „deutsche Gänsefüßchen“ oder «französische Guillemots» verwenden. Das macht allerdings nicht ein einziger gängiger Browser. Dass dies aber technisch durchaus machbar ist, beweist dieses Dokument. Es ist in DocBook-XML verfasst, und der fragliche Absatz sieht im Quelltext so aus:

    eingeführt. Je nach Dokumentensprache soll der Browser hier
    beispielsweise <quote lang="en">englische Quotes</quote>, <quote
    lang="de">deutsche Gänsefüßchen</quote> oder <quote
    lang="fr">französische Guillemots</quote> verwenden. Das macht
	  

Die Idee sollte dennoch klar sein. Auch jenseits der reinen Zuordnung zu Schriftzeichen kann die Dokumentensprache durchaus von Bedeutung für die Darstellung sein. Praktisch noch bedeutsamer ist dies für barrierefreien Zugang zu Dokumenten: Ein sogenannter Screen-Reader, der von Blinden genutzt werden kann, um textuelle Inhalte vorzulesen, sollte natürlich schon wissen, ob ein Dokument in englischer, deutscher oder chinesischer Sprache verfasst ist, weil die negativen Konsequenzen sonst von unfreiwilliger Komik bis zur völligen Unverständlichkeit führen können.

Die größte praktische Bedeutung besitzt diese Information allerdings dadurch, dass Browser in der Lage sind, gezielt Dokumente in einer bestimmten Sprache vom Server anzufordern, und dafür muss der Server natürlich ebenfalls ermitteln können, in welcher Sprachen ein Dokument jeweils vorhanden ist, um die gewünschte Version ausliefern zu können. Für Details dazu sei wiederum auf den später folgenden Abschnitt 4, „Content-Negotiation“ über Content-Negotiation verwiesen.

1.3.1. Die Konfigurationsanweisung DefaultLanguage

Die Möglichkeiten, den Server zur Übermittlung des Headers Content-Language zu bewegen, sind denen für Content-Type sehr ähnlich.

    DefaultLanguage Sprachkürzel
	  

DefaultLanguage (Achtung: Es heißt AddDefaultCharset, aber DefaultLanguage!) ist ab Version 1.3.4 im Apache verfügbar und erfordert mod_mime, das standardmäßig vorhanden ist.

Welche Sprachkürzel genau erlaubt sind, lässt sich [RFC3066] entnehmen; einige erlaubte, aber eher exotische Beispiele kann man sich auf [IANA-LANGUAGE-TAGS] anschauen. In der Praxis sollte man sich auf die gängigen Kürzel beschränken, und das sind die jeweils aus zwei Buchstaben bestehenden Sprachkürzel nach ISO 639, wie de für Deutsch, fr für Französich, en für Englisch, ru für Russisch, und so weiter. Optional, durch einen Bindestrich getrennt, kann auch noch nach Ländern variert werden. Ländercodes sind in ISO 3166 festgelegt, Beispiele sind US für die USA, FR für Frankreich, CH für die Schweiz, und so fort. Mit dem Kürzel de-CH würde man somit die Sprache Deutsch für die Schweiz identifizieren, mit en-US amerikanisches Englisch. Obwohl bei Sprachkürzeln Groß-/Kleinschreibung keine Rolle spielt, hat es sich eingebürgert, die Sprachbezeichnung klein, das optionale Land dagegen groß zu schreiben. Allerdings werden diese Angaben mehr und mehr komplett kleingeschrieben. Praktisch ist dies jedoch eine rein ästhetische Frage.

Am einfachsten macht man es sich, wenn man in einem einigermaßen modernen Browser nachschaut, welche Sprachen effektiv in welchen Varianten erkannt werden. Bei Mozilla (bzw. Netscape 6, 7, ...) geht das über Bearbeiten -> Voreinstellungen -> Navigator -> Sprachen, beim Internet Explorer über Extras -> Internet-Optionen -> Allgemein -> Sprachen.

1.3.2. Die Konfigurationsanweisung AddLanguage

    AddLanguage en    .en
    AddLanguage de    .de
    AddLanguage de-ch .de-ch
          

Diese Anweisung erfordert, dass das Apache-Modul mod_mime in den Server einkompiliert und in der Konfiguration aktiviert wurde. Dies ist in der Voreinstellung der Fall.

Ähnlich wie bei AddCharset (siehe Abschnitt 1.2.2, „Die Konfigurationsanweisung AddCharset“) wird hier eine Zuordnung von Sprachen zu Dateinamensendungen über eine Namenskonvention eingeführt. Heißt eine Datei print.html.de-ch, geht der Server also davon aus, dass es sich um ein Dokument für die deutschsprachige Schweiz handelt.

1.4. Voreingestellte Werte

Interessant ist noch die Frage, welche Werte für Sprache und Kodierung angenommen werden, wenn eine explizite Angabe fehlt. Für die Sprache ist das leicht zu beantworten, es gibt nämlich keinen Standardwert. In der Praxis wird der tatsächlich angenommene Wert entweder en-US, also amerikanisches Englisch, oder aber die Systemsprache sein. Beides hat dann allerdings eher programmiertechnische Gründe.

Bei der Dokumentenkodierung sieht es nicht ganz so einfach aus. Abschnitt 3.7.1 in [RFC2616] lässt sich eindeutig entnehmen, dass für diesen Fall ISO-8859-1 angenommen werden muss, jedenfalls soweit es sich um Textdokumente handelt. Tatsächlich werden viele Browser jedoch als Kodierung CP1252 bzw. Windows-1252 annehmen, was einen jedoch nicht gleich zu einem Aufschrei des Entsetzens veranlassen sollte: CP1252 erweitert ISO-8859-1 lediglich, und nur in Bereichen, die in ISO-8859-1 ohnehin nicht definiert sind. Selbst, wenn der Browser also mit seiner stillschweigenden Annahme danebenliegen sollte, ist das wenig schädlich. Wenn tatsächlich Bytes aus dem „verbotenen“ Bereich ankommen, dann ist eine fälschliche Darstellung zum Beispiel als Euro-Zeichen auch kaum weniger lästig oder gar gefährlich, denn Fragezeichen, Kästchen oder andere Ersatzdarstellungen.

Allerdings sollte dies nicht dazu verleiten, dieses zweifelhafte Feature auszunutzen. Etliche Browser auf etlichen Betriebssystemen werden bei CP1252-Dokumenten nämlich tatsächlich statt der unbekannten Zeichen Müll darstellen (Netscape 4.x unter älteren Linux-Versionen ist ein Beispiel dafür, wenngleich das seinen Grund auch im verwendeten X-Server haben könnte).

Ebenfalls relevant ist das Problem, das in [RFC2616] in Abschnitt 3.4.1 noch genannt wird, dass nämlich ältere Browser das Fehler einer Kodierungsangabe zum Anlass nahmen, die Kodierung zu „raten“. In Gebieten, in denen ISO-8859-1 Standard ist, wird man das Problem nur schwer nachvollziehen können, in anderen Sprachräumen sind Browser dagegen häufig darauf eingestellt, das für die jeweilige Region gängige Charset zu verwenden.

Und schließlich haben wir noch die „übereifrigen“ Browser, die meinen, weil Unicode doch das Allheilmittel gegen alles Übel dieser Welt sei, müssten Dokumente ohne Charset-Angabe auch unbedingt in Unicode, bzw. UTF-8 kodiert sein. Sie schießen damit leicht übers Ziel hinaus, und frustrieren höchstens die Benutzer, weil die Kodierung in der Praxis doch recht häufig fehlt.

1.5. Der Header Accept-Charset

Wir erinnern uns, dass HTTP ein Frage- und Antwort-Spiel ist. So, wie der Server angegeben kann, in welcher Form ein Dokument genau vorliegt, kann der Browser Wünsche dazu äußern, also mitteilen, wie er (bzw. die Benutzerin) die Dokumente gerne erhalten möchte. Der Browser übermittelt diese Vorlieben durch Header in seiner Anfrage an den Server. Diese Header beginnen allesamt mit dem Wort Accept.

Für die Dokumentenkodierung heißt dieser Header Accept-Charset. Ein praktisches Beispiel sieht so aus:

    Accept-Charset: ISO-8859-1, utf-8; q=0.7,*;q=0.7
	

Prinzipiell verständlich, ..., aber im Detail? Es wird klarer, wenn wir den Inhalt des Headers jeweils an den Kommata aufsplitten. Danach werden also scheinbar die drei Charsets ISO-8859-1, utf-8 und „*“ akzeptiert. Übrigens: Der Internet Explorer scheint keine Accept-Charset-Header an den Server zu übermitteln.

Der erste Teil ist klar, der Browser bevorzugt Dokumente in ISO-8859-1. Weiterhin akzeptiert er auch utf-8. Was bedeutet aber die Angabe „; q=0.7“? Jedem akzeptierten Typen kann eine Präferenz im Bereich zwischen 0 und 1 mitgegeben werden, wobei 0 die niedrigste und 1 die höchste Präferenz ausdrückt. Eine fehlende Angabe ist als 1 anzunehmen.

Im vorliegenden Fall drückt der Browser daher aus, dass Dokumente in ISO-8859-1 für ihn eine Qualität (daher die Abkürzung q) von 1 haben, Dokumente in utf-8 dagegen nur eine von 0,7.

Das letzte Charset * gibt es natürlich nicht. Es ist eine Auffangbedingung, der Stern bedeutet, dass der Browser mit allen Charsets etwas anfangen kann. Man darf getrost davon ausgehen, dass es sich bei dieser Pauschalaussage um eine glatte Lüge handelt. Und genau diese Pauschalaussagen haben den praktischen Nutzen des Verfahrens sehr stark eingeschränkt:

Vorsichtshalber schicken nämlich praktisch alle Browser bei allen Accept-Headern dieses Sternchen mit, weshalb man die Angabe in der Praxis mit Vorsicht genießen muss. Auch Browser, die keine PNGs darstellen können, werden z. B. meistens behaupten, sämtliche Bildformate darstellen zu können.

Als Ausschlusskriterium taugen die vom Browser übermittelten Präferenzen also nichts. Die beigefügten Qualitätswerte sind schon wesentlich nützlicher, weil sie zumindest eine optimale Auswahl des ausgelieferten Dokuments durch den Server erlauben.

1.6. Der Header Accept-Language

Dieser Header wird von praktisch allen Browser geschickt und sieht beispielsweise so aus:

    Accept-Language: de, fr;q=0.8, en-us;q=0.6, en;q=0.4, ru;q=02
	

Wir sollten jetzt in der Lage sein, diesen Header selbst zu deuten. Auf den Inhalt dieses Headers hat man auch in praktisch jedem Browser direkten Zugriff. Bei Mozilla (bzw. Netscape 6, 7, ...) geht das - wie bereits erwähnt - über Bearbeiten -> Voreinstellungen -> Navigator -> Sprachen, beim Internet Explorer über Extras -> Internet-Optionen -> Allgemein -> Sprachen. Man kann das nicht nur konfigurieren, es funktioniert sogar, wie man beispielsweise auf der Webseite http://www.debian.org/ testen kann. Je nachdem, wie der Browser eingestellt ist, wird die Seite in der bevorzugten Sprache ausgeliefert. Am Fuß der Seite findet sich eine Liste aller unterstützten Sprachen, und auch ein Link der weiterhilft, wenn die automatische Spracherkennung nicht funktioniert.

Mit dem Apache ist das keine Hexerei. Wir werden im Abschnitt 4, „Content-Negotiation“ über "Content-Negotiation lernen, wie wir es genauso schön hinbekommen.

1.7. Formulare

HTTP ist bidirektional, es lassen sich also nicht nur Daten vom Server auf den Client übertragen, sondern auch umgekehrt. In der Theorie gibt es für die umgekehrte Richtung zum Beispiel die Request-Methode PUT, mit der Dateien auf den Web-Server gespielt werden können, in der Praxis ist dies aus Sicherheitsgründen jedoch meist abgestellt. Relevant ist dagegen die Übertragung von Formulardaten mit der Methode POST (oder auch GET, wobei die Formularwerte durch ein Fragezeichen abgetrennt im URI übermittelt werden).

In welcher Sprache die übermittelten Daten vorliegen, ist in der Praxis wenig bedeutsam, das Charset dagegen spielt eine ziemlich entscheidende Rolle. Allerdings werden Formulardaten mit dem Medientyp application/x-www-form-urlencoded übermittelt, der keinen Parameter charset kennt.

Die Formulardaten werden einfach als Byte-Strom verschickt, wobei alle Sonderzeichen in der Form %XX kodiert werden (XX entspricht dabei dem Hexadezimalwert des jeweiligen Bytes). Sonderzeichen ist hier eigentlich der falsche Ausdruck, weil tatsächlich Bytes, also Binärdaten gemeint sind, die nur durch die Prozent-Form in 7-Bit-ASCII kodiert werden.

Die tatsächliche Kodierung der Daten entspricht dabei dem Charset der Seite, von der aus das Formular abgeschickt wurde. Geben wir in eine ISO-8859-1-Seite ein großes Ü ein, kommt beim Server die Folge %DC an (DC ist der Hexadezimal-Code des großen Ü). Stammt die Eingabe jedoch von einer Seite, die in UTF-8 kodiert ist, sieht das Skript, das auf dem Server die Daten verarbeitet, dagegen die Folge %C3%9C, denn in UTF-8 wird ein großes Ü durch zwei Bytes mit den Hexadezimalwerten C3 und 9C dargestellt.

Wie kann unser Skript aber nun feststellen, welches Charset gemeint ist? Die Antwort ist relativ einfach: Gar nicht! Die Konsequenz ist, dass man niemals ohne weitere Vorkehrungen das gleiche action-Attribut in Formularseiten, die in unterschiedlichen Charsets kodiert sind, verwenden sollte, denn je nach Charset wird exakt der gleiche Text in unterschiedlicher Kodierung im auswertenden Skript ankommen.

Eine naive Entgegnung könnte lauten, dass der Web-Browser ja einen Referer-Header mitsendet, der die zuletzt besuchte Seite angibt. Dieser Header wird von vielen Browsern aber mittlerweile der informationellen Selbstbestimmung der Benutzer zuliebe unterdrückt, und lässt sich außerdem nach Belieben fälschen, und das bereits mit einfachsten Mitteln. Dem Inhalt dieses Headers sollte man daher immer mit sehr großer Skepsis begegnen.

Jede Hoffnung, eine bestimmte Kodierung mit Sicherheit annehmen zu können, wird man aber spätestens dann aufgeben, wenn man einmal in einem Formular die Seitencodierung von Hand abändert. Der Browser wird in diesem Falle zwar die Formulareingaben in der Regel auf die ursprünglichen Werte zurücksetzen, die Daten dann aber trotzdem in der manuell veränderten Kodierung übermitteln. Es bleibt zu hoffen, dass zukünftige Browser das manuelle Verstellen des Charsets auf Formularseiten wenigstens mit einer Warnung quittieren.

In der Praxis kann man dem Problem also nur mit Schulterzucken und einem lapidaren „selber schuld!“ begegnen. Allerdings sollte man zumindest dafür sorgen, dass bei normaler Benutzung der Web-Site nichts passieren kann. Ein Negativ-Szenario sähe so aus: Der Web-Auftritt hat einen deutschen, einen russischen und einen chinesischen Bereich. In jedem Bereich gibt es ein Kontaktformular, das eine Mail versendet, und für alle drei Kontaktformulare wird das gleiche Skript auf dem Server verwendet (also das gleiche action-Attribut im Element form). Die versendete Mail ist zumindest für Russisch und Chinesisch definitiv zerschossen, wird im Mail-Client des Empfängers also nur als Zeichenmüll ankommen. Abhilfe: Entweder für jedes Charset ein separates Skript verwenden, oder aber einen versteckten Parameter, der das angenommene Charset (es sollte ja bekannt sein, welches Charset das Formular zumindest haben sollte) mit übergeben.

Angesichts der grundsätzlichen Schwierigkeiten, die die Übermittlung von Nicht-ASCII-Daten in HTML-Formularen verursacht, mutet es direkt harmlos an, dass alle Browser neuerer Bauart in allen ISO-8859-1-Formularen ohne jeden Skrupel auch den Zeichenvorrat von CP1252 (Windows-1252) verwenden. Insbesondere beim Euro-Zeichen heißt es also höllisch aufzupassen, denn in Windows-1252 hat es den Hexadezimalcode 80, während es in ISO-8859-15 (also Latin-9) den Code A4 hat. Der Hexadezimal-Code A4 ist wiederum in ISO-8859-1 (Latin-1) das allgmeine Währungssymbol ¤.

Nur vermeintliche Abhilfe schafft das Attribut accept-charset des HTML-Elements form. Wert dieses Attributes ist eine durch Kommata oder Leerzeichen getrennte Liste erlaubter Charsets. Default für dieses Attribut ist der reservierte Wert UNKNOWN und dies ist kein Zufall. Auch die Interpretation des Attributs bleibt nämlich im Dunkeln. Wird als Wert des Attributs zum Beispiel ISO-8859-1 angegeben, ist es keineswegs sicher, ob der Browser Daten aus anderen Charsets ignoriert, ablehnt, mit einer wohlgemeinten Warnung an den Benutzer bedenkt, selber konvertiert, oder aber das Attribut als Ganzes schlichtweg ignoriert. Wie bei der Angabe einer Liste zu verfahren sei, erschließt sich genausowenig. Enthält das Attribut zum Beispiel den Wert iso-8859-9, utf-8, koi8-r, hilft das nicht wirklich weiter, denn die Sache wird nicht dadurch besser, dass nur noch zwischen drei sich gegenseitig ausschließenden Kodierungen gewählt werden muss. Die Verwendung des Attributes schadet sicher nicht, aber wirkliche Sicherheit bei der Interpretation der Formulardaten lässt sich auch damit nicht erreichen.

Spätestens jetzt sollte es jedem dämmern, weshalb Unicode erfunden wurde ... Sind alle Seiten, und damit auch Formulare, durchgehend in UTF-8 kodiert, gibt es natürlich auch keine Verwirrung, was die Kodierung der Formulardaten angeht. Dafür muss sich die Webmasterin jedoch gelegentlich mit Beschwerden herumschlagen, weil Leute mit älteren Browsern Schwierigkeiten haben, die Seiten zu lesen.