Teil von SELFHTML aktuell Teil von Artikel Teil von PHP

PHP:
Ermitteln der Sprache des Browsers

nach unten Christian Seiler
nach unten Hinweise zum Thema
nach unten Beispiel mit Erläuterungen
nach unten Weiterführende Links

Christian Seiler

Name: Christian Seiler
E-Mail: E-Mail self@christian-seiler.de

Bei Fragen zu diesem Beitrag bitte den Autor des Beitrags kontaktieren!

nach obennach unten

Hinweise zum Thema

Der Browser kann laut HTTP-Spezifikation mehrere Sprachen mitsenden, die der Benutzer bevorzugt. Der Benutzer kann dabei festlegen, in welcher Reihenfolge er diese bevorzugt. Der Webserver kann dann dem Browser die Seite in der passendsten Sprache liefern. Dieses Feature nennt man "Content negotiation".

Der Apache-Webserver bietet Mechanismen an, dies durchzuführen, jedoch erfordert dies, für jede unterschiedene Sprache eine eigene Datei anzulegen. Dies mag bei statischen Seiten sehr praktisch sein, bei dynamischen Seiten kann es den Wartungsaufwand beträchtlich erhöhen. Auch möchte man unter Umständen besser in den Mechanismus eingreifen können. Und schließlich kooperiert PHP auch mit anderen Webservern als dem Apache.

nach obennach unten

Beispiel mit Erläuterungen

Die Funktion

01 <?php
02 // Browsersprache ermitteln
03 function lang_getfrombrowser ($allowed_languages, $default_language, $lang_variable = null, $strict_mode = true) {
04         // $_SERVER['HTTP_ACCEPT_LANGUAGE'] verwenden, wenn keine Sprachvariable mitgegeben wurde
05         if ($lang_variable === null) {
06                 $lang_variable = $_SERVER['HTTP_ACCEPT_LANGUAGE'];
07         }
08 
09         // wurde irgendwelche Information mitgeschickt?
10         if (empty($lang_variable)) {
11                 // Nein? => Standardsprache zurückgeben
12                 return $default_language;
13         }
14 
15         // Den Header auftrennen
16         $accepted_languages = preg_split('/,\s*/', $lang_variable);
17 
18         // Die Standardwerte einstellen
19         $current_lang = $default_language;
20         $current_q = 0;
21 
22         // Nun alle mitgegebenen Sprachen abarbeiten
23         foreach ($accepted_languages as $accepted_language) {
24                 // Alle Infos über diese Sprache rausholen
25                 $res = preg_match ('/^([a-z]{1,8}(?:-[a-z]{1,8})*)'.
26                                    '(?:;\s*q=(0(?:\.[0-9]{1,3})?|1(?:\.0{1,3})?))?$/i', $accepted_language, $matches);
27 
28                 // war die Syntax gültig?
29                 if (!$res) {
30                         // Nein? Dann ignorieren
31                         continue;
32                 }
33                 
34                 // Sprachcode holen und dann sofort in die Einzelteile trennen
35                 $lang_code = explode ('-', $matches[1]);
36 
37                 // Wurde eine Qualität mitgegeben?
38                 if (isset($matches[2])) {
39                         // die Qualität benutzen
40                         $lang_quality = (float)$matches[2];
41                 } else {
42                         // Kompabilitätsmodus: Qualität 1 annehmen
43                         $lang_quality = 1.0;
44                 }
45 
46                 // Bis der Sprachcode leer ist...
47                 while (count ($lang_code)) {
48                         // mal sehen, ob der Sprachcode angeboten wird
49                         if (in_array (strtolower (join ('-', $lang_code)), $allowed_languages)) {
50                                 // Qualität anschauen
51                                 if ($lang_quality > $current_q) {
52                                         // diese Sprache verwenden
53                                         $current_lang = strtolower (join ('-', $lang_code));
54                                         $current_q = $lang_quality;
55                                         // Hier die innere while-Schleife verlassen
56                                         break;
57                                 }
58                         }
59                         // Wenn wir im strengen Modus sind, die Sprache nicht versuchen zu minimalisieren
60                         if ($strict_mode) {
61                                 // innere While-Schleife aufbrechen
62                                 break;
63                         }
64                         // den rechtesten Teil des Sprachcodes abschneiden
65                         array_pop ($lang_code);
66                 }
67         }
68 
69         // die gefundene Sprache zurückgeben
70         return $current_lang;
71 }
72 ?>

Beispiel

<?php
include_once ('./http_sprache.php');

$allowed_langs = array ('es', 'en');

$lang = lang_getfrombrowser ($allowed_langs, 'es', null, false);

var_dump ($lang);
?>

Erläuterung:

Die Funktion lang_getfrombrowser erwartet zwei obligatorische Parameter $allowed_languages und $default_language. Der erste ist ein Array der erlaubten Sprachcodes, die klein geschrieben werden müssen. Der zweite ist die Standardsprache, wenn keine andere Sprache gefunden werden kann. Es gibt noch zwei optionale Parameter, $lang_variable und $strict_mode. Wenn $lang_variable nicht null ist, dann wird dieser Parameter anstelle von $_SERVER['HTTP_ACCEPT_LANGUAGE'] verwendet. Der letzte Parameter gibt an, ob sich die Funktion exakt an die HTTP-Sepzifiaktion halten soll, oder nicht. Unten dazu mehr.

Als erstes wird geprüft, ob nun überhaupt etwas in $lang_variable enthalten ist. Wenn das nicht der Fall ist, wird direkt auf die Standardsprache zurückgegriffen werden (nach oben Zeilen 9-13). Die HTTP-Spezifikation besagt nämlich, dass angenommen werden kann, dass alle Sprachen gleich gut akzeptiert werden, wenn keine Sprache mitgeliefert wird.

Daraufhin wird das Feld der mitgelieferten Sprachen nach dem Komma aufgetrennt (nach oben Zeilen 15-16). In dem Array $accepted_languages stehen nun alle vom Browser akzeptierten Sprachen.

Jede dieser Sprachen kann mit einer Qualität übergeben werden, die sich zwischen 0 und 1 befindet. Je höher die Qualität ist, desto größer ist die Akzeptanz der Sprache auf der Seite des Benutzers. Als erstes wird die Standardsprache der aktuellen Sprache zugewiesen und ihr wird auch die Qualität 0 gegeben (nach oben Zeilen 18-20). Falls keine unterstützte Sprache gefunden wird, wird sie zurückgeliefert. Jede gefundene Sprache wird aber eine höhere Qualität haben und somit gegenüber der Standardsprache bevorzugt werden.

Nun werden in der foreach-Schleife die einzelnen Sprachen, die der Browser mitgeliefert hat, abgearbeitet (nach oben Zeilen 23-67). Zuerst wird ein regulärer Ausdruck verwendet, um zum einen die Syntax zu prüfen und zum zweiten die Informationen zu extrahieren (nach oben Zeilen 24-26).

Wenn die Syntax nicht gültig war, dann wird diese eine vom Browser gemachte Angabe einfach ignoriert und die Schleife wird fortgesetzt (nach oben Zeilen 28-32). Wenn die Syntax dagegen gültig war, dann wird die Sprache nach dem Bindestrich getrennt und im Array $lang_code gespeichert (nach oben Zeilen 34-35).

Wenn eine Qualität mitgegeben wurde, dann wird diese verwendet, wenn dem nicht der Fall war, wird die Qualität 1 angenommen (nach oben Zeilen 37-44).

Die HTTP-Spezifikation schreibt vor, dass der übergebene Sprachcode direkt zu interpretieren ist. Das Problem dabei ist, dass jemand, der z.B. die Sprache de-at akzeptiert, wahrscheinlich nichts dagegen hat, wenn er eine Seite in de ausgeliefert bekommt. Daher wurde der Sprachcode auch noch nach dem Bindestrich aufgetrennt. Nun wird solange der jeweils letzte Teil von der Sprache abgeschnitten, bis entweder das Array, das die Sprachteile beinhaltet, leer ist, oder eine Sprache, die verfügbar ist, gefunden wurde. (nach oben Zeilen 46-66).

In der Schleife wird zuerst geprüft, ob die Sprache überhaupt verfügbar ist (nach oben Zeilen 48-49). Ist diese Bedingung erfüllt, folgt die nächste Abfrage, ob die angegebene Qualität größer als die aktuelle Qualität ist (nach oben Zeilen 50-51). Wenn dies alles der Fall ist, dann wird die aktuelle Sprache auf diese Sprache gesetzt und die aktuelle Qualität auf diese Qualität gesetzt (nach oben Zeilen 52-54). Daraufhin wird die while-Schleife abgebrochen, weil damit die Sprache erfolgreich gefunden wurde (nach oben Zeilen 55-56).

Wenn die Sprache nicht verfügbar ist, entscheidet der Parameter $strict_mode, wie weiter zu verfahren ist. Wenn sich die Funktion wirklich konform verhalten soll, muss sie sich jetzt die nächste Sprache anschauen und darf diese Sprache nicht weiter analysieren. Also wird die while-Schleife abgebrochen (nach oben Zeilen 61-62). Wenn die Funktion etwas toleranter sein darf, wird dagegen vom $lang_code-Array das letzte Element mit array_pop entfernt und die Schleife wird fortgesetzt (nach oben Zeilen 64-65).

Am Ende gibt die Funktion die gefundene Sprache zurück (nach oben Zeilen 69-70).

Die komplette PHP-Datei steht auch zum ZIP-Datei Download bereit.

Beachten Sie:

Die Funktion verhält sich standardmäßig konform zur Spezifikation. Der Programmierer muss also im Klaren sein, dass er die Spezifikation missachtet, wenn er den vierten Parameter auf false setzt. Es kann jedoch, wie oben beschrieben, von Vorteil sein, die Spezifikation in diesem einen Punkt zu missachten.

Wenn man anstelle dieser Funktion das Feature des Apache-Webservers verwendet, dann kann man diesen Punkt jedoch nicht beeinflussen, denn der Apache-Webserver hält sich hier an die HTTP-Spezifikation.

Wenn keine passende Sprache gefunden wurde, gibt es eine andere mögliche Alternative, als eine Standardsprache zurückzuliefern. Die HTTP-Spezifikation beschreibt den Fehlercode 406, der immer dann vom Server generiert werden darf, wenn kein zu den Angaben des Browsers passendes Dokument gefunden werden kann. Dieser Fehlercode sollte laut Spezifikation mit einer Liste von verfügbaren Sprachen einhergehen. Um dies mit dieser Funktion zu bewerkstelligen, sollte man null als Standardsprache übergeben. Wenn die Funktion daraufhin null zurückgibt, weiß man, dass der Browser keine Sprache akzeptiert, die vom Programmierer angeboten wird und das Script kann nun die HTTP-Fehlermledung ausgeben. Dazu eignet sich die PHP-Funktion header.

Es gibt eine PEAR-Klasse HTTP, die eine (statische) Methode negotiateLanguage besitzt, die das Gleiche bewerkstelligen soll. Im Moment verhält sich diese Methode aber nicht standardkonform, denn sie ist z.T. nicht streng genug in der Syntaxprüfung. Außerdem versucht die Funktion anhand der Domäne (z.B. irgendein-provider.de wird als Deutschland »erkannt«) des Besuchers festzustellen, welche Sprache er/sie will, falls die andere Methode fehlschlägt. Die Domäne sagt aber nur u.U. etwas über die Herkunft des Benutzers aus und dieses Verhalten ist bei der PEAR-Methode nicht abstellbar, von daher ist von dem Gebrauch dieser Methode im Moment abzuraten.

nach obennach unten

Weiterführende Links

Die folgenden Stellen werden empfohlen, um das obige Beispiel besser zu verstehen, oder um weitere Möglichkeiten und Details zu erfahren.

deutschsprachige Seite Das PHP-Handbuch: die Funktion preg_match
deutschsprachige Seite Das PHP-Handbuch: die Funktion preg_split
deutschsprachige Seite Das PHP-Handbuch: die Funktion array_pop
deutschsprachige Seite Das PHP-Handbuch: die Funktion in_array
englischsprachige Seite Das PEAR-Handbuch: die Methode HTTP::negotiateLanguage
englischsprachige Seite Die HTTP-Spezifikation: Der Aufbau des Accept-Language-Feldes
englischsprachige Seite Die HTTP-Spezifikation: Der Fehlercode 406
englischsprachige Seite Die Apache-Dokumentation: Content-Negotiation bei statischen Seiten
Seite Michael Schröpl: Alternative Dokumentinhalte via HTTP

Teil von SELFHTML aktuell Teil von Artikel Teil von PHP

© 2007 bereichsübergreifende Seite Impressum