使用 PHP HTTP_ACCEPT_LANGUAGE 服务器变量

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/6038236/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-25 23:10:46  来源:igfitidea点击:

Using the PHP HTTP_ACCEPT_LANGUAGE server variable

phphttpinternationalization

提问by Beanie

I created a PHP script that checks the HTTP_ACCEPT_LANGUAGEand loads the website using the appropriate language from the 1st two characters:

我创建了一个 PHP 脚本,它HTTP_ACCEPT_LANGUAGE使用第一个两个字符中的适当语言检查和加载网站:

          $http_lang = substr($_SERVER["HTTP_ACCEPT_LANGUAGE"],0,2);
      switch ($http_lang) {
        case 'en':
          $SESSION->conf['language'] = 'english';
          break;
        case 'es':
          $SESSION->conf['language'] = 'spanish';
          break;
        default:
          $SESSION->conf['language'] = $PREFS->conf['languages'][$SESSION->conf['language_id']];
      }

If I change the language to Spanish in Firefox the website loads in Spanish fine. However I have had several reports that people in Colombia see the website in english.

如果我在 Firefox 中将语言更改为西班牙语,则网站会以西班牙语加载。但是,我收到了几份报告,称哥伦比亚人会看到英文网站。

Details: "es-co" LCID = 9226 Spanish(Colombia)

详细信息:“es-co”LCID = 9226 西班牙语(哥伦比亚)

Anyone have any ideas as to why this is happening? I thought this was the best way to check what language users support.

任何人都知道为什么会发生这种情况?我认为这是检查用户支持哪些语言的最佳方式。

回答by mario

A more contemporary method would be to use http_negotiate_language():

更现代的方法是使用http_negotiate_language()

 $map = array("en" => "english", "es" => "spanish");
 $conf_language= $map[ http_negotiate_language(array_keys($map)) ];

If you don't have the http extensioninstalled (and not the intl one as well), there is yet another workaround in the comments (user-note #86787 (Nov 2008; by Anonymous)):

如果您没有安装http 扩展程序(也没有安装intl扩展程序),评论中还有另一种解决方法(用户注释 #86787(2008 年 11 月;匿名者)):

<?php 
/* 
  determine which language out of an available set the user prefers most 

  $available_languages        array with language-tag-strings (must be lowercase) that are available 
  $http_accept_language    a HTTP_ACCEPT_LANGUAGE string (read from $_SERVER['HTTP_ACCEPT_LANGUAGE'] if left out) 
*/ 
function prefered_language ($available_languages,$http_accept_language="auto") { 
    // if $http_accept_language was left out, read it from the HTTP-Header 
    if ($http_accept_language == "auto") $http_accept_language = isset($_SERVER['HTTP_ACCEPT_LANGUAGE']) ? $_SERVER['HTTP_ACCEPT_LANGUAGE'] : ''; 

    // standard  for HTTP_ACCEPT_LANGUAGE is defined under 
    // http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4 
    // pattern to find is therefore something like this: 
    //    1#( language-range [ ";" "q" "=" qvalue ] ) 
    // where: 
    //    language-range  = ( ( 1*8ALPHA *( "-" 1*8ALPHA ) ) | "*" ) 
    //    qvalue         = ( "0" [ "." 0*3DIGIT ] ) 
    //            | ( "1" [ "." 0*3("0") ] ) 
    preg_match_all("/([[:alpha:]]{1,8})(-([[:alpha:]|-]{1,8}))?" . 
                   "(\s*;\s*q\s*=\s*(1\.0{0,3}|0\.\d{0,3}))?\s*(,|$)/i", 
                   $http_accept_language, $hits, PREG_SET_ORDER); 

    // default language (in case of no hits) is the first in the array 
    $bestlang = $available_languages[0]; 
    $bestqval = 0; 

    foreach ($hits as $arr) { 
        // read data from the array of this hit 
        $langprefix = strtolower ($arr[1]); 
        if (!empty($arr[3])) { 
            $langrange = strtolower ($arr[3]); 
            $language = $langprefix . "-" . $langrange; 
        } 
        else $language = $langprefix; 
        $qvalue = 1.0; 
        if (!empty($arr[5])) $qvalue = floatval($arr[5]); 

        // find q-maximal language  
        if (in_array($language,$available_languages) && ($qvalue > $bestqval)) { 
            $bestlang = $language; 
            $bestqval = $qvalue; 
        } 
        // if no direct hit, try the prefix only but decrease q-value by 10% (as http_negotiate_language does) 
        else if (in_array($langprefix,$available_languages) && (($qvalue*0.9) > $bestqval)) { 
            $bestlang = $langprefix; 
            $bestqval = $qvalue*0.9; 
        } 
    } 
    return $bestlang; 
} 
?>

回答by 2072

I used the regex from @GabrielAnderson and devised this function which behaves according to RFC 2616 (when no quality value is given to a language, it defaults to 1).

我使用了@GabrielAnderson 的正则表达式,并设计了这个函数,它的行为符合 RFC 2616(当语言没有质量值时,它默认为 1)。

When several languages share the same quality value, the most specific are given priority over the less specific ones. (this behaviour is not part of the RFC which provides no recommendation for this specific case)

当几种语言具有相同的质量值时,最具体的语言优先于不太具体的语言。(此行为不是 RFC 的一部分,RFC 未针对此特定情况提供任何建议)

function Get_Client_Prefered_Language ($getSortedList = false, $acceptedLanguages = false)
{

    if (empty($acceptedLanguages))
        $acceptedLanguages = $_SERVER["HTTP_ACCEPT_LANGUAGE"];

        // regex inspired from @GabrielAnderson on http://stackoverflow.com/questions/6038236/http-accept-language
    preg_match_all('/([a-z]{1,8}(-[a-z]{1,8})*)\s*(;\s*q\s*=\s*(1|0\.[0-9]+))?/i', $acceptedLanguages, $lang_parse);
    $langs = $lang_parse[1];
    $ranks = $lang_parse[4];


        // (create an associative array 'language' => 'preference')
    $lang2pref = array();
    for($i=0; $i<count($langs); $i++)
        $lang2pref[$langs[$i]] = (float) (!empty($ranks[$i]) ? $ranks[$i] : 1);

        // (comparison function for uksort)
    $cmpLangs = function ($a, $b) use ($lang2pref) {
        if ($lang2pref[$a] > $lang2pref[$b])
            return -1;
        elseif ($lang2pref[$a] < $lang2pref[$b])
            return 1;
        elseif (strlen($a) > strlen($b))
            return -1;
        elseif (strlen($a) < strlen($b))
            return 1;
        else
            return 0;
    };

        // sort the languages by prefered language and by the most specific region
    uksort($lang2pref, $cmpLangs);

    if ($getSortedList)
        return $lang2pref;

        // return the first value's key
    reset($lang2pref);
    return key($lang2pref);
}

Example:

例子:

print_r(Get_Client_Prefered_Language(true, 'en,en-US,en-AU;q=0.8,fr;q=0.6,en-GB;q=0.4'));

Outputs:

输出:

Array
    (
        [en-US] => 1
        [en] => 1
        [en-AU] => 0.8
        [fr] => 0.6
        [en-GB] => 0.4
    )

As you can notice, 'en-US' appears in first position despite the fact that 'en' was first in the given string.

如您所见,尽管 'en' 在给定字符串中排在第一位,但 'en-US' 出现在第一位。

So you could use this function and just replace your first line of code by:

所以你可以使用这个函数,只需将你的第一行代码替换为:

$http_lang = substr(Get_Client_Prefered_Language(),0,2);

回答by Nev Stokes

Do you know if this is happening for allvisitors to your site from Colombia? Users are usually free to alter the language settings of their browsers — or to have them altered for them by whoever is in charge of the computer. As zerkms recommends, try logging IP addresses and their headers.

您知道这种情况是否发生在来自哥伦比亚的您网站的所有访问者身上?用户通常可以自由更改浏览器的语言设置——或者让计算机负责人为他们更改。按照 zerkms 的建议,尝试记录 IP 地址及其标头。

If you have the intl extensioninstalled you can use Locale::lookupand Locale::acceptFromHttpto get a best-fit choice of language from the users browser settings and a list of what translations you have available.

如果您安装了intl 扩展,您可以使用Locale::lookupLocale::acceptFromHttp从用户浏览器设置和可用翻译列表中获得最适合的语言选择。

Locale::acceptFromHttp($_SERVER['HTTP_ACCEPT_LANGUAGE']); # e.g. "en_US"

回答by Beanie

In the end I went with this solution:

最后我采用了这个解决方案:

if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
  preg_match_all('/([a-z]{1,8}(-[a-z]{1,8})?)\s*(;\s*q\s*=\s*(1|0\.[0-9]+))?/i', $_SERVER['HTTP_ACCEPT_LANGUAGE'], $lang_parse);
  if (count($lang_parse[1])){
    $langs = array_combine($lang_parse[1], $lang_parse[4]);
    foreach ($langs as $lang => $val){
      if ($val === '') $langs[$lang] = 1;
    }
    arsort($langs, SORT_NUMERIC);
  }
  foreach ($langs as $lang => $val){
    if (strpos($lang,'en')===0){
      $language = 'english';
      break;
    } else if (strpos($lang,'es')===0){
      $language = 'spanish';
    }
  }
}

I would like to thank AJ for the links. Also thanks to all that replied.

我要感谢 AJ 的链接。也谢谢大家的回复。

回答by Steely Wing

I will use full locale code to refer language, because like zh-TWand zh-CNis 2 different language.

我将使用完整的语言环境代码来引用语言,因为喜欢zh-TWzh-CN是 2 种不同的语言。

function httpAcceptLanguage($httpAcceptLanguage = null)
{
    if ($httpAcceptLanguage == null) {
        $httpAcceptLanguage = $_SERVER['HTTP_ACCEPT_LANGUAGE'];
    }

    $languages = explode(',', $httpAcceptLanguage);
    $result = array();
    foreach ($languages as $language) {
        $lang = explode(';q=', $language);
        // $lang == [language, weight], default weight = 1
        $result[$lang[0]] = isset($lang[1]) ? floatval($lang[1]) : 1;
    }

    arsort($result);
    return $result;
}

// zh-TW,en-US;q=0.7,en;q=0.3
echo $_SERVER['HTTP_ACCEPT_LANGUAGE'];
/*
    Array
    (
        [zh-TW] => 1
        [en-US] => 0.7
        [en] => 0.3
    )
 */
print_r(httpAcceptLanguage());

回答by Gabriel Anderson

if you want to store languages in array, i do this:

如果你想在数组中存储语言,我这样做:

preg_match_all('/([a-z]{1,8}(-[a-z]{1,8})?)\s*(;\s*q\s*=\s*(1|0\.[0-9]+))?/i',  'pt-br,pt;q=0.8,en-us;q=0.5,en,en-uk;q=0.3', $lang_parse);
$langs = $lang_parse[1];
$rank = $lang_parse[4];
for($i=0; $i<count($langs); $i++){
    if ($rank[$i] == NULL) $rank[$i] = $rank[$i+1];
}

this output an array to languages e other with values

这将输出一个数组到其他语言的值

preg_match_all('/([a-z]{1,8}(-[a-z]{1,8})?)\s*(;\s*q\s*=\s*(1|0\.[0-9]+))?/i', 'pt-br,pt;q=0.8,en-us;q=0.5,en,en-uk;q=0.3', $lang_parse);
$langs = $lang_parse[1];
$rank = $lang_parse[4];
$lang = array();
for($i=0; $i<count($langs); $i++){
    $lang[$langs[$i]] = ($rank[$i] == NULL) ? $rank[$i+1] : $rank[$i];
}

this output an array like this:

这会输出一个像这样的数组:

Array
(
    [pt-br] => 0.8
    [pt] => 0.8
    [en-us] => 0.5
    [en] => 0.3
    [en-uk] => 0.3
)

回答by user462990

I put my trust in the skilled programmers who work for PHP and think ahead. Here is my version of a label for the Google translator drop down.

我信任那些为 PHP 工作并提前思考的熟练程序员。这是我为 Google 翻译器下拉的标签版本。

function gethttplanguage(){
    $langs = array(     
            'en',// default
            'it',
            'dn',
            'fr',
            'es'         
    );
    $questions = array(
    "en" => "If you wish to see this site in another language click here",
    "it" => "Se vuole vedere questo sito in italiano clicca qui",
    "dn" => "Hvis du ?nsker at se denne hjemmeside i danske klik her",
    "fr" => "Si vous voulez visualiser ce site en fran?ais, cliquez ici",
    "es" => "Si quieres ver este sitio en espa?ol haga clic aquí"
    );
    $result = array();  
    http_negotiate_language($langs, &$result);  
    return $questions[key($result)];
}