使用 PHP HTTP_ACCEPT_LANGUAGE 服务器变量

发布于 2024-11-08 06:14:27 字数 751 浏览 0 评论 0原文

我创建了一个 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']];
      }

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

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

有人知道为什么会发生这种情况吗?我认为这是检查用户支持的语言的最佳方法。

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

          $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.

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

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

昵称有卵用 2024-11-15 06:14:27

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

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

如果您不这样做安装了 http 扩展 (也不是国际的),评论中还有另一种解决方法(用户注释 #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; 
} 
?>

A more contemporary method would be to use 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 extension installed (and not the intl one as well), there is yet another workaround in the comments (user-note #86787 (Nov 2008; by Anonymous)):

<?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; 
} 
?>
轻许诺言 2024-11-15 06:14:27

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

当多种语言具有相同的质量值时,最具体的语言会优先于不太具体的语言。 (此行为不是 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);
}

示例:

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

输出:

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

正如您所注意到的,尽管“en”在给定字符串中位于第一个位置,但“en-US”出现在第一个位置。

因此,您可以使用此函数并将第一行代码替换为:

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

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).

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)

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.

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

$http_lang = substr(Get_Client_Prefered_Language(),0,2);
猫瑾少女 2024-11-15 06:14:27

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

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

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

Do you know if this is happening for all visitors 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.

If you have the intl extension installed you can use Locale::lookup and Locale::acceptFromHttp to get a best-fit choice of language from the users browser settings and a list of what translations you have available.

Locale::acceptFromHttp($_SERVER['HTTP_ACCEPT_LANGUAGE']); # e.g. "en_US"
栀子花开つ 2024-11-15 06:14:27

最后我选择了这个解决方案:

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';
    }
  }
}

我要感谢 AJ 提供的链接。也感谢所有回复的人。

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.

习ぎ惯性依靠 2024-11-15 06:14:27

我将使用完整的区域设置代码来引用语言,因为 zh-TWzh-CN 是两种不同的语言。

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

I will use full locale code to refer language, because like zh-TW and zh-CN is 2 different language.

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());
甜点 2024-11-15 06:14:27

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

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];
}

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

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];
}

如下输出一个数组:

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

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
)
独享拥抱 2024-11-15 06:14:27

我信任那些从事 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)];
}

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.

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)];
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文