Использование серверной переменной PHP HTTP_ACCEPT_LANGUAGE
Я создал скрипт 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 Испанский (Колумбия)
У кого-нибудь есть идеи относительно того, почему это происходит? Я подумал, что это лучший способ проверить, какой язык поддерживают пользователи.
7 ответов
Более современный метод будет использовать http_negotiate_language()
:
$map = array("en" => "english", "es" => "spanish");
$conf_language= $map[ http_negotiate_language(array_keys($map)) ];
Если у вас не установлено расширение http ( а также не intl), в комментариях есть еще один обходной путь ( примечание пользователя № 86787 (ноябрь 2008; от 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;
}
?>
Я использовал регулярное выражение из @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://stackru.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-US" появляется на первой позиции, несмотря на то, что "en" был первым в данной строке.
Таким образом, вы можете использовать эту функцию и просто заменить свою первую строку кода на:
$http_lang = substr(Get_Client_Prefered_Language(),0,2);
Знаете ли вы, происходит ли это для всех посетителей вашего сайта из Колумбии? Пользователи, как правило, могут свободно изменять языковые настройки своих браузеров или изменять их для тех, кто отвечает за компьютер. Как рекомендует zerkms, попробуйте зарегистрировать IP-адреса и их заголовки.
Если у вас установлено расширение intl, вы можете использовать Locale::lookup
а также Locale::acceptFromHttp
чтобы получить наилучший выбор языка из настроек браузера пользователя и список доступных вам переводов.
Locale::acceptFromHttp($_SERVER['HTTP_ACCEPT_LANGUAGE']); # e.g. "en_US"
Я буду использовать полный код языка для обозначения языка, потому что, как 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());
В конце концов я пошел с этим решением:
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 за ссылки. Также спасибо всем, что ответили.
Если вы хотите хранить языки в массиве, я делаю это:
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
)
Я доверяю опытным программистам, которые работают на PHP и думают о будущем. Вот моя версия ярлыка для гугл переводчика выпадающего списка.
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)];
}