PHP에서 브라우저 언어 감지


144

내 웹 사이트의 색인으로 다음 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;
}
?>

3
PHP 5.3.0 이상에는 헤더 locale_accept_from_http()에서 원하는 언어를 가져 옵니다 Accept-Language. 항상이 방법을 자체 작성 방법보다 선호해야합니다. 시도하는 정규식 목록과 비교하여 결과를 확인하고 페이지 언어를 결정하십시오. 예제는 PHP-I18N 을 참조하십시오 .
caw

2
문제 locale_accept_from_http()는 반환하는 최상의 결과를 지원하지 않을 수 있으므로 여전히 헤더를 구문 분석 하여 next-best 를 찾습니다 .
Xeoncross

이에 대한 답변은 여러 언어를 고려한 답변 중 하나로 변경되어야합니다.
Pekka

include 및 require는 PHP의 컴파일 타임에 발생하므로 기본적으로 모든 index * .php를 포함하고 하나의 자원 낭비 만 보여주십시오
Michael

답변:


361

왜 간단하고 깨끗하게 유지하지 않습니까

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

?>

9
네덜란드어, 그리스어 및 슬로베니아어의 언어 코드는 하나의 문자입니다. :이 같은 폭발 더 나은 것 같다 php.net/manual/tr/reserved.variables.server.php#90293
trante

10
@trante : 왜 그들이 한 글자라고 말합니까? 네덜란드 ( nl), 그리스어 ( el)와 슬로베니아어 ( sl) 모두 두 글자로 나타납니다 msdn.microsoft.com/en-us/library/ms533052(v=vs.85).aspx
피터 K.

16
이 코드는 전체 목록을 보지 않습니다. 언어 목록에서 pl첫 번째 우선 순위이고 fr두 번째 인 경우 어떻게 합니까? 프랑스어 대신 영어를하겠습니다.
코스

24
이것은 우선 순위를 감지하지
못하고

3
두 글자 이외의 다른 길이는 없습니다! 선호하는 브라우저로 이동하여 언어 우선 순위를 변경하면 볼 수 있습니다.
Gigala

76

Accept-Language 는 가중치 값의 목록입니다 ( q 매개 변수참조). 그것은 단지 첫 번째 언어를 보더라도 그것이 가장 선호되는 것을 의미하지는 않습니다. 실제로 q 값이 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반환 하늘의 배열, 일치가 발견되지 않았다 당신은 기본 언어에 다시 떨어질 수있다.


안녕, 스크립트가 잘 작동하고 이제 중지합니다. 서버에서 세션을 끄면이 스크립트가 작동하지 않을 수 있습니까?
GibboK

@GIbboK : 아니요. 세션과 무관합니다.
Gumbo

올바른하지만 난 좋아 @diggersworld 솔루션 ... 더 나은 쓰기 적은 코드
lrkwz

누군가가 어떻게 q결정 의 가치가 있는지 말해 줄 수 있습니까 ? 감사합니다
Phantom007

@ Phantom007 기본 설정에 따라 다릅니다. 0 =이 언어를 원하지 않습니다. 1 = 항상이 언어를 원합니다.
Skyost

43

기존 답변은 너무 장황 하므로이 작은 자동 일치 버전을 만들었습니다.

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

전체 요점 소스는 여기


6
이것은 오늘날 특정 프로젝트에 필요한 훌륭하고 정확합니다. 내가 추가 한 유일한 기능은 함수가 기본 언어를 허용하고 사용 가능한 언어와 HTTP_ACCEPT_LANGUAGEs가 일치하지 않는 경우 해당 언어로 대체하는 것입니다.
Scott

7
아, 내 변화와 요지 여기에 있습니다 : gist.github.com/humantorch/d255e39a8ab4ea2e7005 (단순화를 위해 하나의 파일로 결합했습니다)
Scott

2
아주 좋은 방법! 아마도 $ langs에 이미 해당 언어에 대한 항목이 있는지 확인해야합니다. perferred 언어는 EN-US, 2 드 및 EN 3은 당신의 방법은 항상 준, 내게 일어난 것을 나 드, 욕실의 첫 번째 값이 3 항목에 의해 덮어 쓰기 한 원인
피터 파인트

또한 일치하는 항목이 없으면 PHP 경고가 생성됩니다. 이것을 정상적으로 처리하는 것이 좋을 것입니다.
Simon East

26

이를 처리하는 공식적인 방법은 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);

1
작동하는 링크를 찾았으므로 포함하도록 답변을 업데이트했습니다.
Simon East

이 세 링크는 모두 사용하지 않는 것으로 보이며 쉽게 Googleable 설치 지침이없는 것 같습니다 (또한이 기능은 해당 페이지에 따라 더 이상 사용되지 않습니다)
Brian Leishman

11

위에서 선택한 답변의 문제점은 사용자가 케이스 구조에없는 언어로 첫 번째 선택 세트를 가질 수 있지만 다른 언어 선택 중 하나가 설정되어 있다는 것입니다. 일치하는 것을 찾을 때까지 반복해야합니다.

이것은 더 잘 작동하는 매우 간단한 솔루션입니다. 브라우저는 선호하는 순서대로 언어를 반환하므로 문제가 간단 해집니다. 언어 지정자는 2자를 초과 할 수 있지만 (예 : "EN-US") 일반적으로 처음 2 개이면 충분합니다. 다음 코드 예제에서는 프로그램이 알고있는 알려진 언어 목록에서 일치하는 항목을 찾고 있습니다.

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

코드에서 쉽게 사용할 수있는 빠르고 간단한 솔루션을 찾으시기 바랍니다. 나는 이것을 프로덕션에서 꽤 오랫동안 사용 해왔다.


3
"브라우저는 기본 설정에 따라 언어를 반환합니다"— 그럴 수도 있지만 그에 의존해서는 안됩니다. q선호도를 결정하기 위해 값을 사용하십시오. 그것이 스펙이해야 할 일입니다.
Quentin

7

이거 한번 해봐:

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

Q 값을 잡아야하는 정규식을 설명 할 수 [0-1]{0,1}.\d{0,4}있습니까? 먼저 당신 \..옳은 대신에 의미 하는 것 같아요 ? 그리고 q는 항상 형식 0.1324이나 다른 것이 아닌가? 그렇다면 쓰기에 충분하지 0\.?\d{0,4}않습니까? 그렇다면 q=1.0다른 부분으로 갈 수 있습니다.
Adam

여기에서 사용 예제를 볼 수 있다면 좋을 것입니다.
Simon East

2
@SimonEast var_dump( getDefaultLanguage());
jirarium 5

4

다음 스크립트는 지원되는 언어와 일치하는 언어가 없거나 일치하는 언어가 발견되면 기본 언어 설정을 새 언어로 바꿉니다. 언어 우선 순위에 따라.

이 시나리오에서 사용자의 브라우저는 스페인어, 네덜란드어, 미국 영어 및 영어 우선 순위로 설정되며 응용 프로그램은 지역 변형없이 영어와 네덜란드어 만 지원하며 영어는 기본 언어입니다. 어떤 이유로 브라우저가 값을 올바르게 정렬하지 않으면 "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" 

1

가장 깨끗한 방법은 이것이라고 생각합니다!

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

이것은 헤더 내의 언어 우선 순위를 설명하지 않습니다.
Simon East

0

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

0

시험,

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

덕분에


0

빠르고 간단한 :

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

-1

쿠키를 설정하는 것이 있습니다. 보시다시피, 사용자가 언어를 게시했는지 먼저 확인합니다. 브라우저 언어가 항상 사용자에 대해 알려주는 것은 아닙니다.

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

11
이미 물건을 반향 할 때 헤더를 보낼 수없는 것 같아요?

2
나는이 글 뒤에 숨겨진 의미가 사용자에게 언어를 바꾸고이 결정을 기억할 수있는 방법을 제공하는 의미가 있다고 생각합니다. 언어 선택은 첫 번째 선택을 가장 잘 추측하기 위해 한 번만 수행해야합니다.
danijar
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.