在PHP中检测浏览器语言

我使用下面的PHP脚本作为我的网站的索引。

该脚本应该包含一个特定的页面,具体取决于浏览器的语言(自动检测)。

这个脚本在所有浏览器上都不能正常工作,所以它总是包含任何检测到的语言的index_en.php (问题的原因很可能是某些Accept-Language头没有考虑到的问题)。

你能不能建议我一个更强大的解决方案?

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

为什么不保持简单和干净

 <?php $lang = substr($_SERVER['HTTP_ACCEPT_LANGUAGE'], 0, 2); switch ($lang){ case "fr": //echo "PAGE FR"; include("index_fr.php");//include check session FR 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; } ?> 

Accept-Language是加权值列表(请参阅q参数)。 这意味着只看第一语言并不意味着它也是最受欢迎的; 实际上,0的q值意味着根本不可接受。

因此,不要只看第一种语言,而要解析所接受语言和可用语言的列表,并找到最佳匹配:

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

如果findMatches返回一个空数组,则没有找到匹配,您可以使用默认语言。

现有的答案有点过于冗长,所以我创建了这个更小的自动匹配版本。

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

和样本用法:

 //$_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 )*/ 

完整的主要来源在这里

对于那些偶然发现了像我这样的老问题的人来说,使用php_http扩展时也有一个可用的解决方案。 它将处理多种语言和q值,并且会使你的代码比这里的其他解决方案更清晰(在撰写本文的时候)。

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

如何使用: http : //www.php.net/manual/en/function.http-negotiate-language.php

上面选择的答案的问题是,用户可能将他们的第一选择设置为不是在结构中的语言,但是设置了他们的其他语言选择之一。 你应该循环,直到你找到一个匹配。

这是一个超级简单的解决方案,效果更好。 浏览器按照首选项的顺序返回语言,以便简化问题。 虽然语言指示符可以超过两个字符(例如 – “EN-US”),但通常前两个字符就足够了。 在下面的代码示例中,我正在从程序所了解的已知语言列表中寻找匹配项。

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

我希望你能找到这个简单易用的解决方案,你可以在代码中轻松使用它。 我在生产中已经使用了很长一段时间了。

试试这个:

 ######################################################### # 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); } 

以下脚本是Xeoncross代码的修改版本(谢谢Xeoncross),如果没有语言与支持的语言匹配,或者如果找到匹配项,则会使用新语言替换默认语言设置根据语言的优先级。

在这种情况下,用户的浏览器按照西班牙语,荷兰语,美国英语和英语的优先顺序进行设置,应用程序仅支持英语和荷兰语,没有地区差异,英语是默认语言。 如果由于某种原因浏览器没有正确排序值,那么“HTTP_ACCEPT_LANGUAGE”字符串中值的顺序就不重要了。

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

我认为最干净的方式是这个!

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

所有上面的回退到'EN':

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

或使用默认的语言回退和已知的语言数组:

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

例子:

 // 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' 

我有这个,它设置了一个cookie。 正如你所看到的,它首先检查语言是否由用户发布。 因为浏览器语言并不总是告诉用户。

 <?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/'); } ?>