Browsersprache in PHP erkennen


144

Ich verwende das folgende PHP-Skript als Index für meine Website.

Dieses Skript sollte abhängig von der Sprache des Browsers eine bestimmte Seite enthalten (automatisch erkannt).

Dieses Skript funktioniert nicht mit allen Browsern und enthält daher immer index_en.phpalle erkannten Sprachen (die Ursache des Problems ist höchstwahrscheinlich ein Problem, bei dem einige Accept-Language-Header nicht berücksichtigt werden).

Könnten Sie mir bitte eine robustere Lösung vorschlagen?

<?php
// Open session var
session_start();
// views: 1 = first visit; >1 = second visit

// Detect language from user agent browser
function lixlpixel_get_env_var($Var)
{
     if(empty($GLOBALS[$Var]))
     {
         $GLOBALS[$Var]=(!empty($GLOBALS['_SERVER'][$Var]))?
         $GLOBALS['_SERVER'][$Var] : (!empty($GLOBALS['HTTP_SERVER_VARS'][$Var])) ? $GLOBALS['HTTP_SERVER_VARS'][$Var]:'';
     }
}

function lixlpixel_detect_lang()
{
     // Detect HTTP_ACCEPT_LANGUAGE & HTTP_USER_AGENT.
     lixlpixel_get_env_var('HTTP_ACCEPT_LANGUAGE');
     lixlpixel_get_env_var('HTTP_USER_AGENT');

     $_AL=strtolower($GLOBALS['HTTP_ACCEPT_LANGUAGE']);
     $_UA=strtolower($GLOBALS['HTTP_USER_AGENT']);

     // Try to detect Primary language if several languages are accepted.
     foreach($GLOBALS['_LANG'] as $K)
     {
         if(strpos($_AL, $K)===0)
         return $K;
     }

     // Try to detect any language if not yet detected.
     foreach($GLOBALS['_LANG'] as $K)
     {
         if(strpos($_AL, $K)!==false)
         return $K;
     }
     foreach($GLOBALS['_LANG'] as $K)
     {
         //if(preg_match("/[[( ]{$K}[;,_-)]/",$_UA)) // matching other letters (create an error for seo spyder)
         return $K;
     }

     // Return default language if language is not yet detected.
     return $GLOBALS['_DLANG'];
}

// Define default language.
$GLOBALS['_DLANG']='en';

// Define all available languages.
// WARNING: uncomment all available languages

$GLOBALS['_LANG'] = array(
'af', // afrikaans.
'ar', // arabic.
'bg', // bulgarian.
'ca', // catalan.
'cs', // czech.
'da', // danish.
'de', // german.
'el', // greek.
'en', // english.
'es', // spanish.
'et', // estonian.
'fi', // finnish.
'fr', // french.
'gl', // galician.
'he', // hebrew.
'hi', // hindi.
'hr', // croatian.
'hu', // hungarian.
'id', // indonesian.
'it', // italian.
'ja', // japanese.
'ko', // korean.
'ka', // georgian.
'lt', // lithuanian.
'lv', // latvian.
'ms', // malay.
'nl', // dutch.
'no', // norwegian.
'pl', // polish.
'pt', // portuguese.
'ro', // romanian.
'ru', // russian.
'sk', // slovak.
'sl', // slovenian.
'sq', // albanian.
'sr', // serbian.
'sv', // swedish.
'th', // thai.
'tr', // turkish.
'uk', // ukrainian.
'zh' // chinese.
);

// Redirect to the correct location.
// Example Implementation aff var lang to name file
/*
echo 'The Language detected is: '.lixlpixel_detect_lang(); // For Demonstration
echo "<br />";    
*/
$lang_var = lixlpixel_detect_lang(); //insert lang var system in a new var for conditional statement
/*
echo "<br />";    

echo $lang_var; // print var for trace

echo "<br />";    
*/
// Insert the right page iacoording with the language in the browser
switch ($lang_var){
    case "fr":
        //echo "PAGE DE";
        include("index_fr.php");//include check session DE
        break;
    case "it":
        //echo "PAGE IT";
        include("index_it.php");
        break;
    case "en":
        //echo "PAGE EN";
        include("index_en.php");
        break;        
    default:
        //echo "PAGE EN - Setting Default";
        include("index_en.php");//include EN in all other cases of different lang detection
        break;
}
?>

3
Mit PHP 5.3.0+ locale_accept_from_http()wird die bevorzugte Sprache aus dem Accept-LanguageHeader abgerufen . Sie sollten diese Methode immer einer selbstgeschriebenen Methode vorziehen. Überprüfen Sie das Ergebnis anhand einer Liste regulärer Ausdrücke, die Sie versuchen, die Seitensprache auf diese Weise zu bestimmen. Ein Beispiel finden Sie in PHP-I18N .
Caw

2
Das Problem dabei locale_accept_from_http()ist, dass Sie möglicherweise nicht das beste Ergebnis unterstützen, das zurückgegeben wird, sodass Sie den Header immer noch selbst analysieren müssen , um das nächstbeste zu finden .
Xeoncross

Die akzeptierte Antwort darauf sollte in eine Antwort geändert werden, die mehrere Sprachen berücksichtigt.
Pekka

include und require's finden zum kompilierungszeitpunkt von php statt. grundsätzlich schließen sie den gesamten index * .php ein und zeigen nur einen an - ressourcenverschwendung
Michael

Antworten:


361

Warum hältst du es nicht einfach und sauber?

<?php
    $lang = substr($_SERVER['HTTP_ACCEPT_LANGUAGE'], 0, 2);
    $acceptLang = ['fr', 'it', 'en']; 
    $lang = in_array($lang, $acceptLang) ? $lang : 'en';
    require_once "index_{$lang}.php"; 

?>

9
Die Sprachcodes für Niederländisch, Griechisch und Slowenisch bestehen aus einem Buchstaben. Es scheint besser, so zu explodieren: php.net/manual/tr/reserved.variables.server.php#90293
trante

10
@trante: Warum sagst du, dass es sich um einen Buchstaben handelt? Niederländisch ( nl), Griechisch ( el) und Slowenisch ( sl) scheinen alle zwei Buchstaben zu sein: msdn.microsoft.com/en-us/library/ms533052(v=vs.85).aspx
Peter K.

16
Dieser Code betrachtet nicht die gesamte Liste. Was ist, wenn pldie erste Priorität und frdie zweite in meiner Sprachliste steht? Ich würde Englisch statt Französisch bekommen.
Kos

24
Dies fehlt an der Erkennung von Prioritäten und ist nicht kompatibel mit Codes, die sich von zwei Buchstaben unterscheiden
Áxel Costas Pena

3
Es gibt keine anderen Längen als zwei Buchstaben! Gehen Sie in Ihren Lieblingsbrowser und ändern Sie die Sprachpriorität. Sie werden es sehen.
Gigala

76

Accept-Language ist eine Liste gewichteter Werte (sieheParameter q ). Das heißt, nur die erste Sprache zu betrachten, bedeutet nicht, dass sie auch am meisten bevorzugt wird. Tatsächlich bedeutet ein q- Wert von 0 überhaupt nicht akzeptabel.

Anstatt nur die erste Sprache zu betrachten, analysieren Sie die Liste der akzeptierten Sprachen und verfügbaren Sprachen und finden Sie die beste Übereinstimmung:

// parse list of comma separated language tags and sort it by the quality value
function parseLanguageList($languageList) {
    if (is_null($languageList)) {
        if (!isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
            return array();
        }
        $languageList = $_SERVER['HTTP_ACCEPT_LANGUAGE'];
    }
    $languages = array();
    $languageRanges = explode(',', trim($languageList));
    foreach ($languageRanges as $languageRange) {
        if (preg_match('/(\*|[a-zA-Z0-9]{1,8}(?:-[a-zA-Z0-9]{1,8})*)(?:\s*;\s*q\s*=\s*(0(?:\.\d{0,3})|1(?:\.0{0,3})))?/', trim($languageRange), $match)) {
            if (!isset($match[2])) {
                $match[2] = '1.0';
            } else {
                $match[2] = (string) floatval($match[2]);
            }
            if (!isset($languages[$match[2]])) {
                $languages[$match[2]] = array();
            }
            $languages[$match[2]][] = strtolower($match[1]);
        }
    }
    krsort($languages);
    return $languages;
}

// compare two parsed arrays of language tags and find the matches
function findMatches($accepted, $available) {
    $matches = array();
    $any = false;
    foreach ($accepted as $acceptedQuality => $acceptedValues) {
        $acceptedQuality = floatval($acceptedQuality);
        if ($acceptedQuality === 0.0) continue;
        foreach ($available as $availableQuality => $availableValues) {
            $availableQuality = floatval($availableQuality);
            if ($availableQuality === 0.0) continue;
            foreach ($acceptedValues as $acceptedValue) {
                if ($acceptedValue === '*') {
                    $any = true;
                }
                foreach ($availableValues as $availableValue) {
                    $matchingGrade = matchLanguage($acceptedValue, $availableValue);
                    if ($matchingGrade > 0) {
                        $q = (string) ($acceptedQuality * $availableQuality * $matchingGrade);
                        if (!isset($matches[$q])) {
                            $matches[$q] = array();
                        }
                        if (!in_array($availableValue, $matches[$q])) {
                            $matches[$q][] = $availableValue;
                        }
                    }
                }
            }
        }
    }
    if (count($matches) === 0 && $any) {
        $matches = $available;
    }
    krsort($matches);
    return $matches;
}

// compare two language tags and distinguish the degree of matching
function matchLanguage($a, $b) {
    $a = explode('-', $a);
    $b = explode('-', $b);
    for ($i=0, $n=min(count($a), count($b)); $i<$n; $i++) {
        if ($a[$i] !== $b[$i]) break;
    }
    return $i === 0 ? 0 : (float) $i / count($a);
}

$accepted = parseLanguageList($_SERVER['HTTP_ACCEPT_LANGUAGE']);
var_dump($accepted);
$available = parseLanguageList('en, fr, it');
var_dump($available);
$matches = findMatches($accepted, $available);
var_dump($matches);

Wenn findMatchesein leeres Array zurückgegeben wird, wurde keine Übereinstimmung gefunden und Sie können auf die Standardsprache zurückgreifen.


Hallo, das Skript hat gut funktioniert und jetzt hör auf. Könnte es sein, dass dieses Skript nicht funktioniert, wenn SESSION auf dem Server ausgeschaltet ist?
GibboK

@GIbboK: Nein, das ist unabhängig von Sitzungen.
Gumbo

Richtig, aber ich bevorzuge @diggersworld Lösung ... schreiben Sie besser weniger Code
lrkwz

Kann mir bitte jemand sagen, wer wie der Wert von qentschieden ist? Danke
Phantom007

@ Phantom007 Abhängig von der Präferenz: 0 = Ich möchte diese Sprache nicht, 1 = Ich möchte immer diese Sprache.
Skyost

43

Die vorhandenen Antworten sind etwas zu ausführlich, daher habe ich diese kleinere Version mit automatischer Anpassung erstellt.

function prefered_language(array $available_languages, $http_accept_language) {

    $available_languages = array_flip($available_languages);

    $langs;
    preg_match_all('~([\w-]+)(?:[^,\d]+([\d.]+))?~', strtolower($http_accept_language), $matches, PREG_SET_ORDER);
    foreach($matches as $match) {

        list($a, $b) = explode('-', $match[1]) + array('', '');
        $value = isset($match[2]) ? (float) $match[2] : 1.0;

        if(isset($available_languages[$match[1]])) {
            $langs[$match[1]] = $value;
            continue;
        }

        if(isset($available_languages[$a])) {
            $langs[$a] = $value - 0.1;
        }

    }
    arsort($langs);

    return $langs;
}

Und die Beispielnutzung:

//$_SERVER["HTTP_ACCEPT_LANGUAGE"] = 'en-us,en;q=0.8,es-cl;q=0.5,zh-cn;q=0.3';

// Languages we support
$available_languages = array("en", "zh-cn", "es");

$langs = prefered_language($available_languages, $_SERVER["HTTP_ACCEPT_LANGUAGE"]);

/* Result
Array
(
    [en] => 0.8
    [es] => 0.4
    [zh-cn] => 0.3
)*/

Vollständige Quelle hier


6
Das ist brillant und genau das, was ich heute für ein bestimmtes Projekt brauchte. Die einzige Ergänzung, die ich vorgenommen habe, besteht darin, der Funktion zu erlauben, eine Standardsprache zu akzeptieren und darauf zurückzugreifen, wenn keine Übereinstimmung zwischen verfügbaren Sprachen und HTTP_ACCEPT_LANGUAGEs besteht.
Scott

7
Oh, ein Kern meiner Änderungen ist hier: gist.github.com/humantorch/d255e39a8ab4ea2e7005 (ich habe es der Einfachheit halber auch in einer Datei zusammengefasst)
Scott

2
Sehr schöne Methode! Vielleicht sollten Sie überprüfen, ob $ langs bereits einen Eintrag für die Sprache enthält. ist mir passiert, dass die bevorzugte Sprache en-US war, 2. de und 3. en, Ihre Methode hat mir immer de gegeben, weil der erste Wert von en durch den 3. Eintrag überschrieben wurde
Peter Pint

Es wird auch eine PHP-Warnung ausgegeben, wenn keine Übereinstimmungen gefunden werden. Wäre schön, damit anmutig umzugehen.
Simon East

26

Der offizielle Weg, dies zu handhaben, ist die Verwendung der PECL-HTTP-Bibliothek . Im Gegensatz zu einigen Antworten hier werden hiermit die Sprachprioritäten (q-Werte) und Teilsprachenübereinstimmungen korrekt behandelt und die engste Übereinstimmung zurückgegeben. Wenn keine Übereinstimmungen vorhanden sind, wird auf die erste Sprache in Ihrem Array zurückgegriffen.

PECL HTTP:
http://pecl.php.net/package/pecl_http

Verwendung:
http://php.net/manual/fa/function.http-negotiate-language.php

$supportedLanguages = [
    'en-US', // first one is the default/fallback
    'fr',
    'fr-FR',
    'de',
    'de-DE',
    'de-AT',
    'de-CH',
];

// Returns the negotiated language 
// or the default language (i.e. first array entry) if none match.
$language = http_negotiate_language($supportedLanguages, $result);

1
Ich habe einen funktionierenden Link gefunden und Ihre Antwort so aktualisiert, dass sie ihn enthält.
Simon East

Alle drei dieser Links scheinen tot zu sein, und sie scheinen keine leicht zu googelnden Installationsanweisungen zu haben (auch diese Funktion ist laut ihrer Seite dafür veraltet)
Brian Leishman

11

Das Problem mit der oben ausgewählten Antwort besteht darin, dass der Benutzer möglicherweise seine erste Wahl als Sprache festgelegt hat, die nicht in der Fallstruktur enthalten ist, aber eine seiner anderen Sprachoptionen festgelegt ist. Sie sollten eine Schleife ausführen, bis Sie eine Übereinstimmung gefunden haben.

Dies ist eine super einfache Lösung, die besser funktioniert. Browser geben die Sprachen in der Reihenfolge ihrer Präferenz zurück, wodurch das Problem vereinfacht wird. Während der Sprachbezeichner mehr als zwei Zeichen enthalten kann (z. B. "EN-US"), sind normalerweise die ersten beiden ausreichend. Im folgenden Codebeispiel suche ich nach einer Übereinstimmung aus einer Liste bekannter Sprachen, die meinem Programm bekannt sind.

$known_langs = array('en','fr','de','es');
$user_pref_langs = explode(',', $_SERVER['HTTP_ACCEPT_LANGUAGE']);

foreach($user_pref_langs as $idx => $lang) {
    $lang = substr($lang, 0, 2);
    if (in_array($lang, $known_langs)) {
        echo "Preferred language is $lang";
        break;
    }
}

Ich hoffe, Sie finden dies eine schnelle und einfache Lösung, die Sie problemlos in Ihrem Code verwenden können. Ich benutze das schon eine ganze Weile in der Produktion.


3
"Browser geben die Sprachen in der Reihenfolge ihrer Präferenz zurück" - Sie könnten dies tun, aber Sie sollten sich nicht darauf verlassen. Verwenden Sie qWerte, um die Präferenz zu bestimmen. Laut Spezifikation sollten Sie dies tun.
Quentin

7

Probier diese:

#########################################################
# Copyright © 2008 Darrin Yeager                        #
# https://www.dyeager.org/                               #
# Licensed under BSD license.                           #
#   https://www.dyeager.org/downloads/license-bsd.txt    #
#########################################################

function getDefaultLanguage() {
   if (isset($_SERVER["HTTP_ACCEPT_LANGUAGE"]))
      return parseDefaultLanguage($_SERVER["HTTP_ACCEPT_LANGUAGE"]);
   else
      return parseDefaultLanguage(NULL);
   }

function parseDefaultLanguage($http_accept, $deflang = "en") {
   if(isset($http_accept) && strlen($http_accept) > 1)  {
      # Split possible languages into array
      $x = explode(",",$http_accept);
      foreach ($x as $val) {
         #check for q-value and create associative array. No q-value means 1 by rule
         if(preg_match("/(.*);q=([0-1]{0,1}.\d{0,4})/i",$val,$matches))
            $lang[$matches[1]] = (float)$matches[2];
         else
            $lang[$val] = 1.0;
      }

      #return default language (highest q-value)
      $qval = 0.0;
      foreach ($lang as $key => $value) {
         if ($value > $qval) {
            $qval = (float)$value;
            $deflang = $key;
         }
      }
   }
   return strtolower($deflang);
}

Hey, kannst du den regulären Ausdruck erklären, mit dem der q- Wert abgefangen werden soll [0-1]{0,1}.\d{0,4}? Zuerst denke ich, du meinst \.statt .richtig? Und ist q nicht immer von der Form 0.1324oder so? Wäre es dann nicht ausreichend zu schreiben 0\.?\d{0,4}? Wenn Sie haben q=1.0, können Sie in den anderen Teil gehen.
Adam

Es wäre toll, hier ein Anwendungsbeispiel zu sehen.
Simon East

2
@ SimonEast var_dump( getDefaultLanguage());
Jirarium

4

Das folgende Skript ist eine modifizierte Version des Xeoncross-Codes (danke für diesen Xeoncross), die auf eine Standardspracheneinstellung zurückgreift, wenn keine Sprachen mit den unterstützten übereinstimmen oder wenn eine Übereinstimmung gefunden wird, ersetzt sie die Standardspracheneinstellung durch eine neue entsprechend der Sprachpriorität.

In diesem Szenario wird der Browser des Benutzers in der Reihenfolge seiner Priorität auf Spanisch, Niederländisch, US-Englisch und Englisch eingestellt. Die Anwendung unterstützt nur Englisch und Niederländisch ohne regionale Unterschiede, und Englisch ist die Standardsprache. Die Reihenfolge der Werte in der Zeichenfolge "HTTP_ACCEPT_LANGUAGE" ist nicht wichtig, wenn der Browser die Werte aus irgendeinem Grund nicht korrekt sortiert.

$supported_languages = array("en","nl");
$supported_languages = array_flip($supported_languages);
var_dump($supported_languages); // array(2) { ["en"]=> int(0) ["nl"]=> int(1) }

$http_accept_language = $_SERVER["HTTP_ACCEPT_LANGUAGE"]; // es,nl;q=0.8,en-us;q=0.5,en;q=0.3

preg_match_all('~([\w-]+)(?:[^,\d]+([\d.]+))?~', strtolower($http_accept_language), $matches, PREG_SET_ORDER);

$available_languages = array();

foreach ($matches as $match)
{
    list($language_code,$language_region) = explode('-', $match[1]) + array('', '');

    $priority = isset($match[2]) ? (float) $match[2] : 1.0;

    $available_languages[][$language_code] = $priority;
}

var_dump($available_languages);

/*
array(4) {
    [0]=>
    array(1) {
        ["es"]=>
        float(1)
    }
    [1]=>
    array(1) {
        ["nl"]=>
        float(0.8)
    }
    [2]=>
    array(1) {
        ["en"]=>
        float(0.5)
    }
    [3]=>
    array(1) {
        ["en"]=>
        float(0.3)
    }
}
*/

$default_priority = (float) 0;
$default_language_code = 'en';

foreach ($available_languages as $key => $value)
{
    $language_code = key($value);
    $priority = $value[$language_code];

    if ($priority > $default_priority && array_key_exists($language_code,$supported_languages))
    {
        $default_priority = $priority;
        $default_language_code = $language_code;

        var_dump($default_priority); // float(0.8)
        var_dump($default_language_code); // string(2) "nl"
    }
}

var_dump($default_language_code); // string(2) "nl" 

1

Ich denke, der sauberste Weg ist dies!

 <?php
  $lang = substr($_SERVER['HTTP_ACCEPT_LANGUAGE'], 0, 2);
  $supportedLanguages=['en','fr','gr'];
  if(!in_array($lang,$supportedLanguages)){
     $lang='en';
  }
    require("index_".$lang.".php");

Dies berücksichtigt keine Sprachprioritäten innerhalb des Headers.
Simon East

0

Alle oben genannten mit Fallback auf 'en':

$lang = substr(explode(',',$_SERVER['HTTP_ACCEPT_LANGUAGE'])[0],0,2)?:'en';

... oder mit Standard-Sprach-Fallback und bekanntem Spracharray:

function lang( $l = ['en'], $u ){
    return $l[
        array_keys(
            $l,
            substr(
                explode(
                    ',',
                    $u ?: $_SERVER['HTTP_ACCEPT_LANGUAGE']
                )[0],
                0,
                2
            )
        )[0]
    ] ?: $l[0];
}

Eine Linie:

function lang($l=['en'],$u){return $l[array_keys($l,substr(explode(',',$u?:$_SERVER['HTTP_ACCEPT_LANGUAGE'])[0],0,2))[0]]?:$l[0];}

Beispiele:

// first known lang is always default
$_SERVER['HTTP_ACCEPT_LANGUAGE'] = 'en-us';
lang(['de']); // 'de'
lang(['de','en']); // 'en'

// manual set accept-language
lang(['de'],'en-us'); // 'de'
lang(['de'],'de-de, en-us'); // 'de'
lang(['en','fr'],'de-de, en-us'); // 'en'
lang(['en','fr'],'fr-fr, en-us'); // 'fr'
lang(['de','en'],'fr-fr, en-us'); // 'de'

0

Versuchen,

$lang = substr($_SERVER['HTTP_ACCEPT_LANGUAGE'], 0,2);

if ($lang == 'tr') {
include_once('include/language/tr.php');
}elseif ($lang == 'en') {
include_once('include/language/en.php');
}elseif ($lang == 'de') {
include_once('include/language/de.php');
}elseif ($lang == 'fr') {
include_once('include/language/fr.php');
}else{
include_once('include/language/tr.php');
}

Dank an


0

Schnell und einfach:

$language = trim(substr( strtok(strtok($_SERVER['HTTP_ACCEPT_LANGUAGE'], ','), ';'), 0, 5));

HINWEIS: Der erste Sprachcode wird vom Browser verwendet, der Rest sind andere Sprachen, die der Benutzer im Browser eingerichtet hat.

Einige Sprachen haben einen Regionalcode, z. en-GB, andere haben nur den Sprachcode, z. sk.

Wenn Sie nur die Sprache und nicht die Region (z. B. en, fr, es usw.) möchten, können Sie Folgendes verwenden:

$language =substr($_SERVER['HTTP_ACCEPT_LANGUAGE'], 0, 2);

-1

Ich habe diesen, der einen Cookie setzt. Und wie Sie sehen können, wird zunächst geprüft, ob die Sprache vom Benutzer veröffentlicht wurde. Weil die Browsersprache nicht immer über den Benutzer informiert.

<?php   
    $lang = getenv("HTTP_ACCEPT_LANGUAGE");
    $set_lang = explode(',', $lang);
    if (isset($_POST['lang'])) 
        {
            $taal = $_POST['lang'];
            setcookie("lang", $taal);
            header('Location: /p/');
        }
    else 
        {
            setcookie("lang", $set_lang[0]);
            echo $set_lang[0];
            echo '<br>';
            echo $set_lang[1];
            header('Location: /p/');
        } 
?>

11
Ich denke, Sie können keine Header senden, wenn Sie bereits etwas wiederholt haben?

2
Ich denke, die Einrückung hinter diesem Beitrag ist sinnvoll, um dem Benutzer die Möglichkeit zu geben, die Sprache zu wechseln und sich an diese Entscheidung zu erinnern. Die Spracherkennung sollte nur einmal durchgeführt werden, um die erste Auswahl bestmöglich zu erraten.
Danijar
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.