Browsersprache-abhängige Rewrite-Regeln in htaccess

Immer wieder stolpern wir über die Anforderung, eben «schnell» ein paar Umleitungen einzurichten. In der Schweiz heisst das sehr oft, dass auch gleich mehrere Sprachen unterstützt werden müssen und der Besucher abhängig von der in seinem Browser eingestellten Sprache passend weitergeleitet werden soll. Meist greifen wir dann zu PHP, falls nicht sowieso ein CMS verwendet wird.

Dabei geht das auch – Apache als Webserver vorausgesetzt – mit einigermassen einfachen Rewrite-Regeln in .htaccess. Ein Beispiel:

RewriteEngine On

# französisch ...
RewriteCond %{HTTP:Accept-Language} ^((?!de|en|it).)*fr [NC]
RewriteRule ^$ /fr/ [L,R=301]

#italienisch
RewriteCond %{HTTP:Accept-Language} ^((?!de|en|fr).)*it [NC]
RewriteRule ^$ /it/ [L,R=301]

#englisch
RewriteCond %{HTTP:Accept-Language} ^((?!de|it|fr).)*en [NC]
RewriteRule ^$ /en/ [L,R=301]

#deutsch
RewriteCond %{HTTP:Accept-Language} ^((?!en|it|fr).)*de [NC]
RewriteRule ^$ /de/ [L,R=301]

# alle anderen
RewriteRule ^$ /de/ [L,R=301]

Die Aufgabenstellung war hier, den Besucher beim Aufruf einer Seite http://www.beispiel.example/ automatisch sprachabhängig auf die Unterstruktur /de/, /en/, /it/, /fr/ umzuleiten. Wir benutzen also RewriteRules, um einen leeren Pfad (^$) per 301-Redirect umzuleiten.

Um die Umleitung sprachabhängig zu gestalten haben wir uns hier entschieden, pro Sprache eine Umleitung mit RewriteRule zu machen, und per RewriteCond die vom Browser im Accept-Language-Header gewünschte Sprache zu berücksichtigen.

Ein solcher Accept-Language-Eintrag kann etwa so aussehen:

Accept-Language: de-CH,en-US,fr-CH

Der Eintrag enthält eine Liste der gewünschten Sprache in der Reihenfolge der Präferenz. In diesem Fall wünscht der Benutzer die Antwort bevorzugt auf Deutsch, wenn auf Deutsch nicht verfügbar, dann in Englisch und falls das auch nicht verfügbar ist auf Französisch.

Per RewriteCond müssen wir also einerseits berücksichtigen, ob eine der uns bekannten Sprachen gewünscht wird. Wenn ja, dann liefern wir diese Sprachvariante. Nur zeigt das Beispiel: Möglicherweise erlaubt uns der Anwender mehrere Sprachen, die wir zur Verfügung stellen, hat aber klare Präferenzen, die wir auch berücksichtigen sollten. Wir liefern also eine spezielle Sprachvariante nur dann aus, wenn sie gewünscht ist, aber auch keine der anderen Sprachen bevorzugt wird.

Unsere Lösung verwendet eine Regular Expression, die einerseits nach der von uns zur Verfügung gestellten Sprache sucht (jeweils die letzten beiden Zeichen in den obigen Suchausdrücken), aber gleichzeitig auch sicherstellt, dass keine andere der von uns angebotenen Sprachen vorher auftaucht. Letzteres erreichen wir etwas kryptisch mit einem negativen Lookbehind-Ausdruck: «((?!de|en|it).)*» im ersten RewriteCond stellt sicher, dass vor dem «fr»-Eintrag weder de, noch en, noch it auftauchen.

Die Erklärung, wieso die doppelte Klammer notwendig ist, spare ich mir hier – es hat im Wesentlichen damit zu tun, dass die verwendete Variante der regulären Ausdrücke Lookbehinds mit variabler Länge nur mit einem kleinen Trick erlaubt.

Am Ende der RewriteCond/RewriteRule-Paare haben wir ausserdem noch einen Fallback auf Deutsch hingestellt – wenn der Browser keine von uns angebotene Sprache anfordert, dann liefern wir ihm unsere bevorzugte Sprache, nämlich Deutsch.