PHP/Tutorials/Sprachauswahl mittels Accept Language

Aus SELFHTML-Wiki
< PHP‎ | Tutorials
Wechseln zu: Navigation, Suche

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.

Hinweis:
Dies ist eine Überarbeitung des Selfhtml-aktuell-Artikels Ermitteln der Sprache des Browsers von Christian Seiler aus dem Jahre 2003.

Ermitteln der Sprache des Browsers

Funktion lang_getfrombrowser
/**
 * 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;
}
Anwendung der Funktion
$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