PHP/Tutorials/Sprachauswahl mittels Accept Language
Der Browser kann laut HTTP-Spezifikation mehrere Sprachen mitsenden, die der Benutzer bevorzugt. Der Benutzer kann dabei festlegen, in welcher Reihenfolge er diese bevorzugt. Dieser Artikel zeigt, wie Sie die im Browser (vom Nutzer) eingestellten Sprachen ermitteln, damit der Webserver die Seite in der passendsten Sprache ausliefern kann. 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.
Inhaltsverzeichnis
Ermitteln der Sprache des Browsers
/**
* Browsersprache ermitteln
*
* @param array allowed languages
* @param string default language
* @param mixed requested language (null = server default)
* @param bool strict mode (default true)
* @return string result language
*/
function lang_getfrombrowser ($allowed_languages, $default_language, $lang_variable = null, $strict_mode = true) {
// $_SERVER['HTTP_ACCEPT_LANGUAGE'] verwenden, wenn keine Sprachvariable mitgegeben wurde
if ($lang_variable === null) {
$lang_variable = $_SERVER['HTTP_ACCEPT_LANGUAGE'];
}
// wurde irgendwelche Information mitgeschickt?
if (empty($lang_variable)) {
// Nein? => Standardsprache zurückgeben
return $default_language;
}
// Den Header auftrennen
$accepted_languages = preg_split('/,\s*/', $lang_variable);
// Die Standardwerte einstellen
$current_lang = $default_language;
$current_q = 0;
// Nun alle mitgegebenen Sprachen abarbeiten
foreach ($accepted_languages as $accepted_language) {
// Alle Infos über diese Sprache rausholen
$res = preg_match (
'/^([a-z]{1,8}(?:-[a-z]{1,8})*)(?:;\s*q=(0(?:\.[0-9]{1,3})?|1(?:\.0{1,3})?))?$/i',
$accepted_language,
$matches
);
// war die Syntax gültig?
if (!$res) {
// Nein? Dann ignorieren
continue;
}
// Sprachcode holen und dann sofort in die Einzelteile trennen
$lang_code = explode ('-', $matches[1]);
// Wurde eine Qualität mitgegeben?
if (isset($matches[2])) {
// die Qualität benutzen
$lang_quality = (float)$matches[2];
} else {
// Kompabilitätsmodus: Qualität 1 annehmen
$lang_quality = 1.0;
}
// Bis der Sprachcode leer ist...
while (count ($lang_code)) {
// mal sehen, ob der Sprachcode angeboten wird
if (in_array (strtolower (join ('-', $lang_code)), $allowed_languages)) {
// Qualität anschauen
if ($lang_quality > $current_q) {
// diese Sprache verwenden
$current_lang = strtolower (join ('-', $lang_code));
$current_q = $lang_quality;
// Hier die innere while-Schleife verlassen
break;
}
}
// Wenn wir im strengen Modus sind, die Sprache nicht versuchen zu minimalisieren
if ($strict_mode) {
// innere While-Schleife aufbrechen
break;
}
// den rechtesten Teil des Sprachcodes abschneiden
array_pop ($lang_code);
}
}
// die gefundene Sprache zurückgeben
return $current_lang;
}
$allowed_langs = array ('es', 'en');
$lang = lang_getfrombrowser ($allowed_langs, 'es', null, false);
var_dump ($lang);
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-Spezifikation halten soll, oder nicht. Unten dazu mehr.
$lang_variable
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. Die HTTP-Spezifikation besagt nämlich, dass angenommen werden kann, dass alle Sprachen gleich gut akzeptiert werden, wenn keine Sprache mitgeliefert wird.
$accepted_languages
Daraufhin wird das Feld der mitgelieferten Sprachen nach dem Komma aufgetrennt. In dem Array $accepted_languages
stehen nun alle vom Browser akzeptierten Sprachen.
Qualitätswert der jeweiligen Sprache
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. 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. Zuerst wird ein regulärer Ausdruck verwendet, um zum einen die Syntax zu prüfen und zum zweiten die Informationen zu extrahieren.
Wenn die Syntax nicht gültig war, dann wird diese eine vom Browser gemachte Angabe einfach ignoriert und die Schleife wird fortgesetzt. Wenn die Syntax dagegen gültig war, dann wird die Sprache nach dem Bindestrich getrennt und im Array $lang_code
gespeichert.
Wenn eine Qualität mitgegeben wurde, dann wird diese verwendet, wenn dem nicht der Fall war, wird die Qualität 1 angenommen.
Sprach-Codes
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.
In der Schleife wird zuerst geprüft, ob die Sprache überhaupt verfügbar ist. Ist diese Bedingung erfüllt, folgt die nächste Abfrage, ob die angegebene Qualität größer als die aktuelle Qualität ist. 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. Daraufhin wird die While-Schleife abgebrochen, weil damit die Sprache erfolgreich gefunden wurde.
$strict_mode
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. 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.
Am Ende gibt die Funktion die gefundene Sprache zurück.
Weiterführende Hinweise
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-Fehlermeldung ausgeben. Dazu eignet sich die PHP-Funktion header()
.
Weblinks
- Das PHP-Handbuch: die Funktion preg_match
- Das PHP-Handbuch: die Funktion preg_split
- Das PHP-Handbuch: die Funktion array_pop
- Das PHP-Handbuch: die Funktion in_array
- Das PHP-Handbuch: die Funktion header
- Die HTTP-Spezifikation: Der Aufbau des Accept-Language-Feldes
- Die HTTP-Spezifikation: Der Fehlercode 406
- Die Apache-Dokumentation: Content-Negotiation bei statischen Seiten