Egal, ob wir einen einfachen Blog oder eine ausgeklügelte, moderne Single-Page-Anwendung (SPA) entwickeln – wenn wir i18n in einer Webanwendung einsetzen, taucht oft eine wichtige Frage auf: Wie erkennen wir deine Sprachpräferenz? Das ist wichtig, weil wir dir immer die beste Nutzererfahrung bieten wollen. Wenn du in deinem Browser eine Reihe bevorzugter Sprachen festgelegt hast, wollen wir unser Bestes geben, um unsere Inhalte in diesen bevorzugten Sprachen anzuzeigen.
In diesem Artikel schauen wir uns drei verschiedene Möglichkeiten an, wie du deine Spracheinstellungen erkennen kannst: über das navigator.languages-Objekt des Browsers (auf dem Client), über den Accept-Language HTTP-Header (auf dem Server) und über Geolokalisierung mit der IP-Adresse (auf dem Server).
Client-seitig: Das navigator.languages Objekt
Moderne Browser bieten ein navigator.languages-Objekt, das wir verwenden können, um alle bevorzugten Sprachen abzurufen, die du in deinem Browser festgelegt hast.

Die Spracheinstellungen in Firefox
Angesichts der obigen Einstellungen würden wir, wenn wir die Firefox-Konsole öffnen und den Wert von navigator.languages überprüfen, Folgendes erhalten:

Die Codes für die Gebietsschemata stimmen mit denen in unseren Browsereinstellungen überein
navigator.languages ist in allen modernen Webbrowsern verfügbar und kann in der Regel bedenkenlos verwendet werden. Lass uns also eine wiederverwendbare JavaScript-Funktion schreiben, die uns zeigt, welche Sprache(n) der aktuelle Nutzer bevorzugt.
function getBrowserLocales(options = {}) {
const defaultOptions = {
languageCodeOnly: false,
};
const opt = {
...defaultOptions,
...options,
};
const browserLocales =
navigator.languages === undefined
? [navigator.language]
: navigator.languages;
if (!browserLocales) {
return undefined;
}
return browserLocales.map(locale => {
const trimmedLocale = locale.trim();
return opt.languageCodeOnly
? trimmedLocale.split(/-|_/)[0]
: trimmedLocale;
});
}
getBrowserLocales() überprüft das navigator.languages<2>-Array und greift auf navigator.language zurück, wenn das Array nicht verfügbar ist. Es ist erwähnenswert, dass in einigen Browsern, wie Chrome, navigator.language die UI-Sprache sein wird, die wahrscheinlich die Sprache ist, auf die das Betriebssystem eingestellt ist. Das ist anders als navigator.languages, was die vom Benutzer festgelegten bevorzugten Sprachen im Browser selbst hat.
✋🏽 Hinweis » Wenn du Internet Explorer unterstützt, musst du die Eigenschaften navigator.userLanguage und navigator.browserLanguage verwenden. Natürlich musst du auch alle Vorkommen von const im obigen Code durch var ersetzen.
Unsere Funktion hat auch eine praktische languageCodeOnly Option, mit der du die Ländercodes von Locales entfernen kannst, bevor sie zurückgegeben werden. Das kann praktisch sein, wenn unsere App die regionalen Nuancen einer Sprache nicht wirklich berücksichtigt, z.B. wenn wir nur eine Version von englischen Inhalten haben.

Mit languageCodeOnly: true, bekommen wir die Sprachen ohne Ländercodes
Server-seitig: Der Accept-Language HTTP-Header
Wenn du deine Spracheinstellungen in einem modernen Browser festlegst, sendet der Browser im Gegenzug einen HTTP-Header, der diese Spracheinstellungen bei jeder Anfrage an den Server übermittelt. Dies ist der Accept-Language Header, und sieht meistens so aus: Accept-Language: en-CA,ar-EG;q=0.5.
Im Header siehst du deine bevorzugten Sprachen, jeweils mit einem Gewicht, das durch einen q Wert festgelegt ist. Wenn kein expliziter q-Wert angegeben ist, wird der Standardwert 1.0 angenommen. Im obigen Header-Wert gibt der Client an, dass du kanadisches Englisch bevorzugst (mit einem Gewicht von q = 1.0), dann ägyptisches Arabisch (mit einem Gewicht von q = 0.5).
Wir können diesen standardmäßigen HTTP-Header verwenden, um die bevorzugten Spracheinstellungen des Benutzers zu bestimmen. Lass uns eine Klasse namens HttpAcceptLanguageHeaderLocaleDetector schreiben, die das erledigt. Wir verwenden hier PHP, aber du kannst jede Sprache verwenden, die du möchtest; der Accept-Language-Header sollte in allen Umgebungen gleich (oder ähnlich genug) sein.
<?php
class HttpAcceptLanguageHeaderLocaleDetector
{
const HTTP_ACCEPT_LANGUAGE_HEADER_KEY = 'HTTP_ACCEPT_LANGUAGE';
public static function detect()
{
$httpAcceptLanguageHeader = static::getHttpAcceptLanguageHeader();
if ($httpAcceptLanguageHeader == null) {
return [];
}
$locales = static::getWeightedLocales($httpAcceptLanguageHeader);
$sortedLocales = static::sortLocalesByWeight($locales);
return array_map(function ($weightedLocale) {
return $weightedLocale['locale'];
}, $sortedLocales);
}
private static function getHttpAcceptLanguageHeader()
{
if (isset($_SERVER[static::HTTP_ACCEPT_LANGUAGE_HEADER_KEY])) {
return trim($_SERVER['HTTP_ACCEPT_LANGUAGE']);
} else {
return null;
}
}
private static function getWeightedLocales($httpAcceptLanguageHeader)
{
if (strlen($httpAcceptLanguageHeader) == 0) {
return [];
}
$weightedLocales = [];
// Wir zerlegen die Zeichenfolge 'en-CA,ar-EG;q=0.5' an den Kommas,
// und iteriere über das resultierende Array von einzelnen Locales. Sobald
// wenn wir fertig sind, sollte $weightedLocales so aussehen
// [['locale' => 'en-CA', 'q' => 1.0], ['locale' => 'ar-EG', 'q' => 0.5]]
foreach (explode(',', $httpAcceptLanguageHeader) as $locale) {
// trenne den Sprachschlüssel ("ar-EG") von seinem Gewicht ("q=0.5")
$localeParts = explode(';', $locale);
$weightedLocale = ['locale' => $localeParts[0]];
if (count($localeParts) == 2) {
// explizites Gewicht z. B. 'q=0.5'
$weightParts = explode('=', $localeParts[1]);
// hol dir den '0.5'-Teil und wandle ihn in eine Fließkommazahl um
$weightedLocale['q'] = floatval($weightParts[1]);
} else {
// kein Gewicht in der Zeichenfolge angegeben, d.h. implizites Gewicht von 'q=1.0'
$weightedLocale['q'] = 1.0;
}
$weightedLocales[] = $weightedLocale;
}
return $weightedLocales;
}
/**
* Sortiere nach hoch- bis niedrigem `q`-Wert
*/
private static function sortLocalesByWeight($locales)
{
usort($locales, function ($a, $b) {
// usort wird die Float-Werte, die wir hier zurückgeben, in Ganzzahlen umwandeln,
// was unser Sortieren durcheinanderbringen kann. Statt also den `q` zu subtrahieren,
// Werte und die Differenz zurückzugeben, vergleichen wir die `q`-Werte und
// Gibt explizit Ganzzahlen zurück.
if ($a['q'] == $b['q']) {
return 0;
}
if ($a['q'] > $b['q']) {
return -1;
}
return 1;
});
return $locales;
}
}
Dieser lange Code ist eigentlich nicht sehr kompliziert. In der einzigen öffentlichen Methode detect() führt unsere Klasse Folgendes aus:
- Holt den rohen Zeichenfolgenwert des
Accept-LanguageHeaders, z. B."en-CA,ar-EG;q=0.5" - Verwende die Hilfsmethode
getWeightedLocales(), um die Header-Zeichenfolge in ein Array zu parsen, das wie[['locale' => 'en-CA', 'q' => 1.0], ['locale' => 'ar-EG', 'q' => 0.5]]aussieht. - Verwende die Hilfsmethode
sortLocalesByWeight(), um das obige Array von höchstem zu niedrigstemq-Wert zu sortieren. - Zieh die
locale-Werte aus dem sortierten Array und gib ein Array zurück, das so aussieht:['en-CA', 'ar-EG'].
Jetzt kannst du unsere neue Klasse verwenden, um ein schönes, verwendbares Array von Locale-Codes basierend auf dem Accept-Language HTTP-Header zu erhalten.
<?php $locales = HttpAcceptLanguageHeaderLocaleDetector::detect(); // => ['en-CA', 'ar-EG']
Server-seitig: Geolokalisierung nach IP-Adresse
Manchmal fehlt der Accept-Language-Header bei Anfragen an unseren Server. In diesen Fällen möchten wir vielleicht die IP-Adresse des Nutzers verwenden, um sein Land zu bestimmen und daraus die Sprache oder Spracheinstellung abzuleiten.
✋🏽 Vorsicht » Die Geolokalisierung sollte nur als letzte Möglichkeit genutzt werden, um deine Spracheinstellung zu erkennen, da das oft zu einer falschen Sprache führen kann. Zum Beispiel, wenn wir sehen, dass du aus Kanada kommst, nehmen wir dann an, dass deine bevorzugte Sprache Englisch oder Französisch ist? Beide sind formelle und weit verbreitete Sprachen im Land. Und natürlich könntest du einer arabischsprachigen Minderheit angehören oder ein spanischsprachiger Besucher sein.
Verwendung von MaxMind für Geolokation
Um das Land des Nutzers anhand der IP-Adresse der Anfrage zu bestimmen, nutzen wir die MaxMind PHP API und die MaxMind GeoIP-Datenbank. MaxMind ist ein Unternehmen, das einige IP-bezogene Produkte anbietet. Darunter sind zwei, die für uns hier interessant sind:
- Die GeoIP2-Datenbanken — das sind die kommerziellen Geolokalisierungsdatenbanken von MaxMind, die latenzarm und abonnementbasiert sind. Du möchtest vielleicht auf diese upgraden, wenn du aktuellere oder schnellere Datenbanken willst.
- Die GeoLite2-Datenbanken – das sind die kostenlosen Geolokalisierungsdatenbanken von MaxMind, und obwohl sie laut Berichten weniger genau sind als ihre kommerziellen Pendants, reichen sie mehr als aus, um loszulegen. Wir werden hier eine GeoLite2-Datenbank verwenden. Beachte, dass du Maxmind auf deiner öffentlichen Webseite erwähnen und auf deren Seite verlinken musst, wenn du eine ihrer kostenlosen Datenbanken verwendest.
Um die Datenbank zu installieren, melde dich einfach für ein kostenloses MaxMind-Konto an. Du erhältst eine E-Mail mit einem Anmeldelink. Folge dem Link und melde dich an. Sobald du das getan hast, solltest du auf deiner Kontozusammenfassung-Seite landen.

Klick auf den Link Datenbanken herunterladen auf der Kontozusammenfassungsseite
Das bringt dich zu einer Seite mit der Liste der kostenlosen GeoLite2-Datenbanken. Hol dir die Länder-Binärdatenbank von dort.

Wir benötigen die binäre Länderdatenbank für unsere Zwecke
Lege die Datei, die du heruntergeladen hast, irgendwo in deinem Projekt ab.
Du brauchst außerdem die MaxMind PHP API, um mit der Datenbank zu arbeiten. Wir können das mit Composer installieren.
composer require geoip2/geoip2:~2.0
Peter Kahls Country-to-Locale-Paket
Wir benötigen noch ein weiteres Paket, bevor wir zu unserem Code kommen. Um die locales oder Sprachen eines Landes zu bestimmen, verwenden wir Peter Kahls country-to-locale Paket. Wir können es auch mit Composer installieren.
composer require peterkahl/country-to-locale
IP-Adress-Lokalisierungs-Klasse
Mit unserem Setup kommen wir nun zu unserer eigenen Klasse IpAddressLocaleDetector.
<?php
require '../vendor/autoload.php';
use GeoIp2\Database\Reader;
use peterkahl\locale\locale;
class IpAddressLocaleDetector
{
const MAX_MIND_DB_FILEPATH =
__DIR__ . '/GeoLite2-Country_20200121/GeoLite2-Country.mmdb';
private static $maxMindDbReader;
public static function detect()
{
$ipAddress = static::getIpAddress();
try {
$record = static::getMaxMindDbReader()->country($ipAddress); // Ruft das Land für die angegebene IP-Adresse ab.
$locales = locale::country2locale($record->country->isoCode);
$normalizedLocales = str_replace('_', '-', $locales);
return explode(',', $normalizedLocales);
} catch (Exception $ex) {
return null;
}
}
private static function getIpAddress()
{
return $_SERVER['REMOTE_ADDR'];
}
private static function getMaxMindDbReader()
{
if (static::$maxMindDbReader == null) {
static::$maxMindDbReader = new Reader(static::MAX_MIND_DB_FILEPATH);
}
return static::$maxMindDbReader;
}
}
Unsere Klasse ist relativ einfach. Ähnlich wie HttpAcceptLanguageHeaderLocaleDetector hat es eine öffentliche Methode, detect(), die Folgendes tut:
- Hol die IP-Adresse der Anfrage aus dem globalen
$_SERVER-Array. - Übergibt diese IP-Adresse an die Methode
countryvonReaderin der MaxMind-Datenbank, die versucht, anhand der IP-Adresse ein Land zu ermitteln. - Verwendet Peter Kahls
locale::country2locale(), um die Sprachen eines bestimmten Landes zu ermitteln. - Normalisiert die übernommenen Locales, sodass
"en_CA,ar_EG"zu"en-CA,ar-EG"wird. - Gibt die normalisierten locales als Array zurück, z.\u00a0B.
["en-CA", "ar-EG"].
📖 Mehr erfahren » Der MaxMind Reader hat viele weitere Methoden. Sieh dir die offizielle API-Dokumentation an, wenn du etwas tiefer in die Informationen eintauchen möchtest, die in den MaxMind-Datenbanken verfügbar sind.
Serverseitig: Kaskadierende Locale-Erkennung
Angesichts der beiden serverseitigen Erkennungsstrategien, die wir oben behandelt haben, können wir eine kleine detect_user_locales()-Funktion schreiben, die zuerst die HTTP-Header-Strategie versucht.
<?php
require './HttpAcceptLanguageHeaderLocaleDetector.php';
require './IpAddressLocaleDetector.php';
function detect_user_locales()
{
$locales = HttpAcceptLanguageHeaderLocaleDetector::detect();
if (count($locales) == 0) {
$locales = IpAddressLocaleDetector::detect();
}
if (count($locales) == 0) {
// auf eine Standardsprache zurückgreifen, in diesem Fall Englisch
$locales = ['en'];
}
return $locales;
}
Wenn die HTTP-Header-Erkennung fehlschlägt, versucht detect_user_locales(), die IP-Geolokalisierung zu verwenden. Wenn letzteres keine Ergebnisse bringt, wird die Funktion auf eine Standardsprache zurückgreifen.
Wenn du es richtig machst, kann das Erkennen der Sprache des Nutzers dabei helfen, ein besseres Nutzererlebnis in unseren Web-Apps zu schaffen. Gott sei Dank sind das navigator.languages Objekt und der Accept-Language HTTP-Header verfügbar, um unsere Vermutungen bei der Erkennung der Spracheinstellungen zu reduzieren.
Wenn du und dein Team an einer internationalisierten Webanwendung arbeitet, schau dir Phrase für eine professionelle, entwicklerfreundliche i18n-Plattform an. Mit einer flexiblen CLI und API, Übersetzungs-Synchronisierung mit GitHub- und Bitbucket-Integration, Over-the-Air (OTA)-Übersetzungen und vielem mehr bist du mit Phrase in Sachen i18n bestens aufgestellt, sodass du dich auf deine Geschäftslogik konzentrieren kannst.
Schau dir alle Phrase-Funktionen für Entwickler an und sieh selbst, wie sie deine Software-Lokalisierungs-Workflows optimieren kann.



