Wer immer schon wissen wollte, wie man eine automatische Sprachauswahl mit Apache baut, die auch mit mehreren im Browser definierten bevorzugten Sprachen korrekt umgeht, der findet am Ende dieses Blog-Beitrags unsere Lösung.
Aber eines nach dem anderen …
Wie es dazu kam
Durch eine Fehlermeldung von unserem System Monitoring Tool (nagios) sind wir eher zufällig auf ein Thema gestoßen, das uns letztens dann etwas länger beschäftigt hat. Unmittelbar nach dem erfolgten Relaunch einer Website meldete nagios, dass die Fehlermeldung „403 Zugriff verweigert“ zurückgeliefert wird. Interessanterweise funktionierten die Zugriffe über den Browser aber einwandfrei. Unser Interesse war geweckt und wir gingen der Sache auf den Grund.
Schnell fiel unser Verdacht auf die automatische Sprachauswahl (language negotiation), da die Website sowohl in Deutsch als auch in Englisch verfügbar ist. Ein kurzer Blick in die Konfiguration erhärtete unseren Verdacht:
1 2 3 4 5 6 7 |
# Deutsch RewriteCond %{HTTP:Accept-Language} ^de.*$ [NC] RewriteRule ^$ http://%{HTTP_HOST}/de/ [R,L] # English RewriteCond %{HTTP:Accept-Language} ^en.*$ [NC] RewriteRule ^$ http://%{HTTP_HOST}/en/ [R,L] |
Offensichtlich werden hier nur die Sprachen Deutsch und Englisch im HTTP Header Accept-Language berücksichtigt. Browser bzw. BenutzerInnen die andere Sprachen eingestellt haben werden überhaupt nicht weitergeleitet und erhalten besagte Fehlermeldung. Mit curl ließ sich dieses Verhalten sehr schnell verifizieren:
1 2 3 4 5 6 7 8 9 10 |
$ curl -I http://meinedomain.tld/ --header "Accept-Language: de" HTTP/1.1 302 Found Location: http://meinedomain.tld/de/ $ curl -I http://meinedomain.tld/ --header "Accept-Language: en" HTTP/1.1 302 Found Location: http://meinedomain.tld/en/ $ curl -I http://meinedomain.tld/ --header "Accept-Language: fr" HTTP/1.1 403 Forbidden |
Voilà! Zugriffe mit einem deutschen oder englischen Accept-Language Header werden also korrekt weitergeleitet. Ist Französisch eingestellt, wird der Fehlercode 403 zurück geliefert.
Durch eine kleine Erweiterung der Konfiguration – bei der nicht unterstütze Sprachen automatisch auf die Hauptsprache umgeleitet werden – ließ sich dieses Problem schnell beheben.
1 2 3 4 5 6 7 8 9 10 11 |
# Deutsch RewriteCond %{HTTP:Accept-Language} ^de.*$ [NC] RewriteRule ^$ http://%{HTTP_HOST}/de/ [R,L] # English RewriteCond %{HTTP:Accept-Language} ^en.*$ [NC] RewriteRule ^$ http://%{HTTP_HOST}/en/ [R,L] # Fallback to default Language (de) RewriteCond %{HTTP:Accept-Language} !^.*(de|en).*$ [NC] RewriteRule ^$ http://%{HTTP_HOST}/de/ [R,L] |
Soweit so gut! Jetzt wollten wir es aber genau wissen …
Um Zuge unserer Test stießen wir auf ein weiteres Problem: Was wenn die bevorzuge Sprache Französisch ist und Deutsch als „Zweitsprache“ definiert ist?
1 2 |
$ curl -I http://meinedomain.tld/ --header "Accept-Language: fr,de" HTTP/1.1 403 Forbidden |
Auch hier wurde eine Fehlermeldung generiert, da keine der drei Regeln „feuerte“. Uns stellte sich daher die Frage, ob bzw. wie wir sicherstellen können, dass die automatische Sprachauswahl auch für Sprachen niedriger Priorität korrekt funktioniert.
Die Lösung
Die Zielsetzung: Benutzer sollten automatisch auf die bevorzugte Sprache mit der höchsten Priorität umgeleitet werden. Hier hatte mein Kollege Ioannis die zündende Idee mit negativen Look aheads zu arbeiten. Daraus entwickelte sich folgende Lösung:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
# Deutsch RewriteCond %{HTTP:Accept-Language} ^((?!en|it).)*de.*$ [NC] RewriteRule ^$ http://%{HTTP_HOST}/de/ [R,L] # English RewriteCond %{HTTP:Accept-Language} ^((?!de|it).)*en.*$ [NC] RewriteRule ^$ http://%{HTTP_HOST}/en/ [R,L] # Italienisch RewriteCond %{HTTP:Accept-Language} ^((?!de|en).)*it.*$ [NC] RewriteRule ^$ http://%{HTTP_HOST}/it/ [R,L] # Default Language (de) RewriteCond %{HTTP:Accept-Language} !^.*(de|en|it).*$ [NC] RewriteRule ^$ http://%{HTTP_HOST}/de/ [R,L] |
Und so funktioniert’s
Die ersten drei Regeln leiten auf die deutsche, englische oder italienische Seite um, aber nur dann, wenn keine andere unterstützte Sprache vorher gelistet ist. Zum besseren Verständnis zwei Beispiele:
Accept-Language: fr,en-GB,de-AT
: leitet auf Grund der zweiten Regel auf die englische Seite weiter. Die erste Regel feuert nicht, da vor dem deutschen Sprachcode der englische gelistet ist.Accept-Language: de-AT,en-GB,it
: leitet auf die deutsche Seite weiter. Die Regeln zwei und drei feuern nicht, da der deutsche Sprachcode als erster gelistet ist.
Die letzte Regel übernimmt die Umleitung der nicht unterstützten Sprachen auf die Standardsprache (Deutsch). Sie feuert nur dann, wenn es keine Übereinstimmung zwischen angebotenen und angefragten Sprachen gibt.
Abschließende Hinweise
- Zugegeben, je mehr Sprachen zu berücksichtigen sind, desto komplexer wird das Setup. In der Regel muss man das aber nur einmalig festlegen.
- Es sei auch drauf hingewiesen, dass die negativen Look aheads CPU intensiv sind. Für high-traffic Sites mit komplexem Setup kann es daher sinnvoll sein die automatische Sprachauswahl anders – z.B. auf Applikationsebene oder mit einem dedizierten Webserver-Modul (z.B. nginx Accept Language Module) – zu lösen.
- Die beschriebene Lösung geht davon aus, dass die bevorzugten Sprachen nach ihrer Priorität geordnet sind (bevorzugte Sprachen zuerst).
- Gewichtungen (quality values) werden nicht berücksichtigt. Dies wird insbesondere bei „q=0“ – das für Sprache ablehnen steht – relevant.