Detect Browser Language in PHP

后端 未结 13 1370
孤城傲影
孤城傲影 2020-11-22 09:16

I use the following PHP script as index for my website.

This script should include a specific page depending on the browser\'s language (automatically detected).

13条回答
  •  情深已故
    2020-11-22 09:32

    Accept-Language is a list of weighted values (see q parameter). That means just looking at the first language does not mean it’s also the most preferred; in fact, a q value of 0 means not acceptable at all.

    So instead of just looking at the first language, parse the list of accepted languages and available languages and find the best match:

    // 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);
    

    If findMatches returns an empty array, no match was found and you can fall back on the default language.

提交回复
热议问题