PHP'de Tarayıcı Dilini Algılama


144

Web sitem için dizin olarak aşağıdaki PHP komut dosyasını kullanıyorum.

Bu komut dosyasının, tarayıcının diline (otomatik olarak algılanır) bağlı olarak belirli bir sayfa içermesi gerekir.

Bu komut dosyası tüm tarayıcılarda iyi çalışmaz, bu nedenle her zaman index_en.phpalgılanan dilleri içerir (sorunun nedeni büyük olasılıkla bazı Accept-Language üstbilgilerinin dikkate alınmamasıyla ilgili bir sorundur).

Bana daha sağlam bir çözüm önerebilir misiniz?

<?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+ ile birlikte locale_accept_from_http()gelen Accept-Languagebaşlıktan tercih edilen dili alır . Bu yöntemi her zaman kendi yazdığınız bir yönteme tercih etmelisiniz. Sonucu, denediğiniz normal ifadeler listesine göre kontrol edin ve sayfa dilini bu şekilde belirleyin. Örnek için PHP-I18N'ye bakınız .
caw

2
Sorun locale_accept_from_http()sen hala var bu yüzden döndürür iyi sonucu desteklemiyor olabilir ki başlığı çözümleme kendinizi bulmak için sonraki en iyi .
Xeoncross

Bunun kabul edilen cevabı, birden fazla dili dikkate alanlardan biriyle değiştirilmelidir.
Pekka

include ve gereksinim php derleme zamanında gerçekleşir, bu yüzden temelde tüm indeks * .php ve sadece birini gösterir - atıkların israfı
Michael

Yanıtlar:


361

neden basit ve temiz tutmuyorsun

<?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
Hollandaca, Yunanca ve Slovence dil kodları bir harftir. Böyle patlamak daha iyi görünüyor: php.net/manual/tr/reserved.variables.server.php#90293
trante

10
@trante: Neden tek harf olduklarını söylüyorsun? Felemenkçe ( nl), Yunanca ( el) ve Slovence ( sl) iki harf gibi görünür: msdn.microsoft.com/en-us/library/ms533052(v=vs.85).aspx
Peter K.

16
Bu kod tüm listeye bakmıyor. Dil listemde plbirinci öncelik ve frikinci ise ne olur ? Fransızca yerine İngilizce alırdım.
Kos

24
Bu, öncelikleri tespit
etmekten yoksundur

3
İki harften başka uzunluk yoktur! Favori tarayıcınıza gidin ve dil önceliğini değiştirin, göreceksiniz.
Gigala

76

Accept-Language , ağırlıklı değerlerin bir listesidir (bkz. Q parametresi). Bu, sadece birinci dile bakmak, onun da en çok tercih edildiği anlamına gelmez; aslında, bir q değeri 0 kabul edilemez anlamına gelir.

Bu nedenle, yalnızca ilk dile bakmak yerine, kabul edilen diller ve mevcut diller listesini ayrıştırın ve en iyi eşleşmeyi bulun:

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

Eğer findMatchesgetiri boş bir dizi, herhangi bir eşleşme bulunamadı ve varsayılan dili geri düşebilir.


Merhaba, komut dosyası iyi çalışıyordu ve şimdi durdu. SESSION sunucudaki bu komut dosyası kapatılırsa çalışmayacak mümkün olabilir?
GibboK

@GIbboK: Hayır, bu oturumlardan bağımsızdır.
Gumbo

Doğru ama @diggersworld çözümünü tercih ediyorum ... daha az kod yazmak daha iyi
lrkwz

Birisi bana kimin qkarar verdiğinin değerini söyleyebilir mi? Teşekkürler
Phantom007

@ Phantom007 Tercihe bağlı olarak: 0 = Bu dili istemiyorum, 1 = Her zaman bu dili istiyorum.
Skyost

43

Mevcut cevaplar biraz fazla ayrıntılı, bu yüzden bu daha küçük, otomatik eşleme sürümünü oluşturdum.

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

Ve örnek kullanım:

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

Burada tam gist kaynağı


6
Bu harika ve bugün belirli bir proje için tam olarak ihtiyacım olan şey. Yaptığım tek ek, işlevin varsayılan bir dili kabul etmesine izin vermek ve kullanılabilir diller ile HTTP_ACCEPT_LANGUAGE arasında bir eşleşme olmaması durumunda buna geri dönmek.
Scott

7
Oh, değişikliklerimle ilgili bir özet burada: gist.github.com/humantorch/d255e39a8ab4ea2e7005 (ayrıca basitlik için tek bir dosyada birleştirdim)
Scott

2
Çok güzel bir yöntem! Belki $ langs'in dil için zaten bir giriş içerip içermediğini kontrol etmelisiniz. tercih edilen dilin en-US, 2. de ve 3. en olduğu bana, yönteminiz her zaman de verdi, çünkü en'in ilk değerinin 3. giriş tarafından üzerine yazıldığından
Peter Pint

Herhangi bir eşleşme bulunmazsa bir PHP uyarısı da üretir. Bunu incelikle işlemek güzel olurdu.
Simon East

26

Bunu yapmanın resmi yolu PECL HTTP kitaplığını kullanmaktır . Buradaki bazı cevapların aksine, bu, dil önceliklerini (q değerleri), kısmi dil eşleşmelerini doğru şekilde işler ve en yakın eşleşmeyi döndürür veya eşleşme olmadığında dizinizdeki ilk dile geri döner.

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

Nasıl kullanılır:
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
Çalışan bir bağlantı buldum, bu yüzden cevabınızı dahil edecek şekilde güncelledik.
Simon East

Bu bağlantıların üçü de ölü gibi görünüyor ve kolayca Googleable kurulum talimatları yok gibi görünüyor (ayrıca bu işlev sayfalarına göre kullanımdan kaldırıldı)
Brian Leishman

11

Yukarıdaki seçilen yanıtla ilgili sorun, kullanıcının ilk seçimini vaka yapısında olmayan bir dil olarak ayarlamış olabileceği, ancak diğer dil seçeneklerinden birinin ayarlanmış olabileceğidir. Bir eşleşme bulana kadar döngü yapmalısınız.

Bu, daha iyi çalışan süper basit bir çözümdür. Tarayıcılar dilleri tercih sırasına göre döndürür, böylece sorunu basitleştirir. Dil belirleyici iki karakterden fazla olabilse de (örn. - "TR-US"), genellikle ilk ikisi yeterlidir. Aşağıdaki kod örneğinde, programımın bildiği bilinen diller listesinden bir eşleşme arıyorum.

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

Umarım bunu kodunuzda kolayca kullanabileceğiniz hızlı ve basit bir çözüm bulursunuz. Bunu bir süredir üretimde kullanıyorum.


3
"Tarayıcılar dilleri tercih sırasına göre döndürür" - Yapabilirler, ancak buna güvenmemelisiniz. qTercihi belirlemek için değerleri kullanın , spesifikasyonun yapmanız gerektiğini söylediği şey budur.
Quentin

7

Bunu dene:

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

Hey, q değerini yakalaması gereken normal ifadeyi açıklayabilir misiniz [0-1]{0,1}.\d{0,4}? Öncelikle demek tahmin \.yerine .sağ? Ve q her zaman formda değil 0.1324mi? O zaman yazmak yeterli olmaz 0\.?\d{0,4}mı? Eğer varsa q=1.0o zaman diğer bölüme gidebilirsiniz.
Adam

Burada bir kullanım örneği görmek harika olurdu.
Simon East

2
@SimonEast var_dump( getDefaultLanguage());
jirarium

4

Aşağıdaki komut dosyası, desteklenen dillerle eşleşen bir dil yoksa veya bir eşleşme bulunduğunda varsayılan dil ayarını yenisiyle değiştirirse, varsayılan dil ayarına geri dönen Xeoncross kodunun (bu Xeoncross için teşekkür ederiz) değiştirilmiş bir sürümüdür. dil önceliğine göre.

Bu senaryoda, kullanıcının tarayıcısı İspanyolca, Felemenkçe, ABD İngilizcesi ve İngilizceye öncelik sırasına göre ayarlanır ve uygulama yalnızca bölgesel varyasyonlar olmadan İngilizce ve Hollandaca'yı destekler ve İngilizce varsayılan dildir. "HTTP_ACCEPT_LANGUAGE" dizesindeki değerlerin sırası, bir nedenden dolayı tarayıcı değerleri doğru bir şekilde sipariş etmezse önemli değildir.

$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

Bence en temiz yol bu!

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

Bu, başlıktaki dil önceliklerini hesaba katmaz.
Simon East

0

Yukarıdakilerin tümü 'en' ye geri dönüş ile:

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

... veya varsayılan dil yedeği ve bilinen dil dizisiyle:

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

Tek çizgi:

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

Örnekler:

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

Deneyin,

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

Sayesinde


0

Hızlı ve basit:

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

NOT: İlk dil kodu tarayıcı tarafından kullanılan koddur, geri kalanı kullanıcının tarayıcıda kurduğu diğer dillerdir.

Bazı diller bir bölge koduna sahiptir, ör. tr-GB, diğerleri sadece dil koduna sahiptir, örn. sk.

Bölgeyi değil dili yalnızca istiyorsanız (ör. En, fr, es, vb.), Şunları kullanabilirsiniz:

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

-1

Bende bir kurabiye var. Gördüğünüz gibi, önce dilin kullanıcı tarafından yayınlanıp yayınlanmadığını kontrol eder. Çünkü tarayıcı dili her zaman kullanıcıyı anlatmaz.

<?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
Sanırım zaten yankılandığınızda başlık gönderemezsiniz?

2
Sanırım bu yazının arkasındaki girinti, kullanıcıya dili değiştirmenin ve bu kararı hatırlamanın bir yolunu sağlamak anlamlıdır. Dil seçimi, ilk seçimi en iyi şekilde tahmin etmek için yalnızca bir kez yapılmalıdır.
danijar
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.