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 ermitteln wir die Sprachpräferenz der User? Eine wichtige Frage: Schließlich wollen wir immer die beste User-Erfahrung bieten. 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 man die Locale eines Users erkennen kann: ü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 Objekt navigator.languages
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 navigator.languages überprüfen, Folgendes erhalten:

Die Codes für die Gebietsschemata (Locales, oder vereinfacht: Sprachen) 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-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 User festgelegten bevorzugten Sprachen im Browser selbst enthält.
✋🏽 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 Sprachen entfernen kannst, bevor sie zurückgesandt 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 er sieht meistens so aus: Accept-Language: en-CA,ar-EG;q=0.5.
Im Header siehst du deine bevorzugten Sprachen, jeweils mit einer Gewichtung, die 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 einer Gewichtung von q = 1.0), gefolgt von ägyptischem Arabisch (mit einer Gewichtung von q = 0.5).
Wir können diesen standardmäßigen HTTP-Header verwenden, um die bevorzugten Spracheinstellungen des Users 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 iterieren über das resultierende Array von einzelnen Locales. // Sobald
wir fertig sind, sollte $weightedLocales so aussehen:
// [['locale' => 'en-CA', 'q' => 1.0], ['locale' => 'ar-EG', 'q' => 0.5]]
foreach (explode(',', $httpAcceptLanguageHeader) as $locale) {
// trennt den Sprachen-Key ("ar-EG") von seiner Gewichtung ("q=0.5")
$localeParts = explode(';', $locale);
$weightedLocale = ['locale' => $localeParts[0]];
if (count($localeParts) == 2) {
// explizite Gewichtung 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 {
// keine Gewichtung in der Zeichenfolge angegeben, d.h. implizite Gewichtung von 'q=1.0'
$weightedLocale['q'] = 1.0;
}
$weightedLocales[] = $weightedLocale;
}
return $weightedLocales;
}
/**
* Sortiere nach hohem bis niedrigem `q`-Wert
*/
private static function sortLocalesByWeight($locales)
{
usort($locales, function ($a, $b) {
// usort wird die Float-Werte, die wir hier zurücksenden, in Ganzzahlen umwandeln,
// was unsere Sortierung durcheinanderbringen kann. Statt also den `q`-Wert zu subtrahieren,
// und die Differenz zurückzugeben, vergleichen wir die `q`-Werte und
// senden explizit die 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:
- Hole 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. - Ziehe die
locale-Werte aus dem sortierten Array und sende 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 zu erhalten, basierend auf dem Accept-Language HTTP-Header.
<?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 können wir die IP-Adresse des Users verwenden, um sein oder ihr Land zu bestimmen und daraus die Sprache oder Spracheinstellung abzuleiten.
✋🏽 Vorsicht » Geolokalisierung sollte nur als letzte Möglichkeit genutzt werden, um eine Spracheinstellung zu erkennen, da sie oft zu einer falschen Sprache führen kann. Wenn wir z. B. sehen, dass du aus Kanada kommst, nehmen wir dann an, dass deine bevorzugte Sprache Englisch oder Französisch ist? Beide sind offizielle Landessprachen und weit verbreitet. 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 User 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. Dazu gehören zwei Produkte, die für uns hier interessant sind:
- Die GeoIP2-Datenbanken sind die kommerziellen Geolokalisierungs-Datenbanken von MaxMind, die latenzarm und abonnementbasiert sind. Vielleicht solltest du auf diese upgraden, wenn du aktuellere oder schnellere Datenbanken willst.
- Die GeoLite2-Datenbanken sind die kostenlosen Geolokalisierungs-Datenbanken von MaxMind, und obwohl sie laut Berichten weniger genau sind als ihre kommerziellen Pendants, reichen sie für den Anfang mehr als aus. 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 Seite Kontozusammenfassung 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 sie eine öffentliche Methode, detect(), die Folgendes tut:
- Holt 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 (Sprachen), sodass
"en_CA,ar_EG"zu"en-CA,ar-EG"wird. - Gibt die normalisierten Locales als Array wieder, 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 Spracherkennung
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 Users dabei helfen, ein besseres User-Erlebnis in unseren Web-Apps zu schaffen. Gott sei Dank sind das Objekt navigator.languages 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 arbeiten, 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 können.




