在 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;
}
?>
291943 次浏览

你为什么不保持它的简单和干净

<?php
$lang = substr($_SERVER['HTTP_ACCEPT_LANGUAGE'], 0, 2);
$acceptLang = ['fr', 'it', 'en'];
$lang = in_array($lang, $acceptLang) ? $lang : 'en';
require_once "index_{$lang}.php";


?>

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

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

// 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返回一个空数组,则未找到匹配项,可以使用默认语言。

我有这个,可以放一块饼干。如您所见,它首先检查语言是否由用户发布。因为浏览器语言并不总是告诉用户。

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

处理这个问题的正式方法是使用 PECL HTTP 库。与这里的一些答案不同,它正确地处理语言优先级(q 值)、部分语言匹配并返回最接近的匹配,或者当没有匹配时返回到数组中的第一种语言。

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

使用方法:
Http://php.net/manual/fa/function.http-negotiate-language.php

$supportedLanguages = [
'en-US', // first one is the default/fallback
'fr',
'fr-FR',
'de',
'de-DE',
'de-AT',
'de-CH',
];


// Returns the negotiated language
// or the default language (i.e. first array entry) if none match.
$language = http_negotiate_language($supportedLanguages, $result);

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

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

完整的要点来源在这里

下面的脚本是 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"

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

这是一个超级简单的解决方案,工作得更好。浏览器按优先顺序返回语言,从而简化了问题。虽然语言指示符可以多于两个字符(例如-“ 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;
}
}

我希望您发现这是一个快速而简单的解决方案,可以很容易地在代码中使用。我已经在制作中使用这个很长时间了。

以上所有都可以回到‘ 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];
}

一句话:

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'

试试这个:

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

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

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

试试看,

$lang = substr($_SERVER['HTTP_ACCEPT_LANGUAGE'], 0,2);


if ($lang == 'tr') {
include_once('include/language/tr.php');
}elseif ($lang == 'en') {
include_once('include/language/en.php');
}elseif ($lang == 'de') {
include_once('include/language/de.php');
}elseif ($lang == 'fr') {
include_once('include/language/fr.php');
}else{
include_once('include/language/tr.php');
}

多亏了

简单明了:

$language = trim(substr( strtok(strtok($_SERVER['HTTP_ACCEPT_LANGUAGE'], ','), ';'), 0, 5));

注意: 第一种语言代码是浏览器正在使用的语言,其余的是用户在浏览器中设置的其他语言。

一些语言有一个区域代码,例如 en-GB,其他语言只有语言代码,例如 sk。

如果只需要语言而不需要区域(例如 en、 fr、 es 等) ,可以使用:

$language =substr($_SERVER['HTTP_ACCEPT_LANGUAGE'], 0, 2);

不幸的是,这个问题的答案都没有考虑到一些有效的 HTTP_ACCEPT_LANGUAGE,例如:

  • q=0.8,en-US;q=0.5,en;q=0.3: 首先具有 q优先级值。
  • ZH-CN: 将整个语言代码大写(错误地)的旧浏览器。
  • *: 基本上是说“服务任何语言,你有”。

在对数千种不同的 Accept-Language (接受语言)进行了全面测试后,到达了我的服务器,下面是我的语言检测方法:

define('SUPPORTED_LANGUAGES', ['en', 'es']);


function detect_language($fallback='en') {
foreach (preg_split('/[;,]/', $_SERVER['HTTP_ACCEPT_LANGUAGE']) as $sub) {
if (substr($sub, 0, 2) == 'q=') continue;
if (strpos($sub, '-') !== false) $sub = explode('-', $sub)[0];
if (in_array(strtolower($sub), SUPPORTED_LANGUAGES)) return $sub;
}
return $fallback;
}

Php-intl 扩展中有一种方法:

 locale_accept_from_http($_SERVER['HTTP_ACCEPT_LANGUAGE'])

对于 LARAVEL 用户,这里有一行代码,它返回一个非常干净的首选语言集合(或数组) :

$langs = Str::of($_SERVER['HTTP_ACCEPT_LANGUAGE'])
->explode(',')
->transform(fn($lang) => Str::substr($lang, 0, 2))
->unique();

从 PHP5.3.0开始,有一个 Locale类与 php-intl扩展绑定在一起,它有一个方法:

echo Locale::acceptFromHttp($_SERVER['HTTP_ACCEPT_LANGUAGE']);

或程序风格:

locale_accept_from_http($_SERVER['HTTP_ACCEPT_LANGUAGE']);

Https://www.php.net/manual/en/locale.acceptfromhttp.php