Использование переменной HTTP HTTP_ACCEPT_LANGUAGE

Я создал PHP-скрипт, который проверяет HTTP_ACCEPT_LANGUAGE и загружает веб-сайт с использованием соответствующего языка из 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 Испанский (Колумбия)

У кого-нибудь есть идеи, почему это происходит? Я думал, что это лучший способ проверить, какие языки поддерживают пользователи.

Более современным методом было бы использовать http_negotiate_language() :

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

Если у вас нет установленного расширения http ( а не intl ), в комментариях есть еще одно обходное решение ( примечание пользователя # 86787 (ноябрь 2008 г., анонимно) ):

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

Я использовал регулярное выражение от @GabrielAnderson и разработал эту функцию, которая ведет себя согласно RFC 2616 (когда значение качества не задано для языка, по умолчанию оно равно 1).

Когда несколько языков имеют одинаковое значение качества, наиболее конкретным приоритетом отдается менее конкретным. (это поведение не является частью 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('/([az]{1,8}(-[az]{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-US» появляется на первой позиции, несмотря на то, что «en» был первым в данной строке.

Таким образом, вы можете использовать эту функцию и просто заменить свою первую строку кода на:

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

Знаете ли вы, что это происходит для всех посетителей вашего сайта из Колумбии? Пользователи, как правило, могут изменять языковые настройки своих браузеров – или изменять их для них тем, кто отвечает за компьютер. Как рекомендует zerkms, попробуйте протоколировать IP-адреса и их заголовки.

Если у вас установлено внутреннее расширение, вы можете использовать Locale::lookup и Locale::acceptFromHttp чтобы получить наилучший выбор языка из настроек браузера пользователей и список доступных вам переводов.

 Locale::acceptFromHttp($_SERVER['HTTP_ACCEPT_LANGUAGE']); # eg "en_US" 

В итоге я пошел с этим решением:

 if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) { preg_match_all('/([az]{1,8}(-[az]{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 за ссылки. Также спасибо всем, что ответил.

Я буду использовать полный код языка для передачи языка, потому что, как zh-TW и zh-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()); 

если вы хотите хранить языки в массиве, я делаю это:

 preg_match_all('/([az]{1,8}(-[az]{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]; } 

это выводит массив на языки e другой со значениями

 preg_match_all('/([az]{1,8}(-[az]{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 ) 

Я доверяю опытным программистам, которые работают на 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)]; }