Deteksi Bahasa Browser dalam PHP

144

Saya menggunakan skrip PHP berikut sebagai indeks untuk situs web saya.

Skrip ini harus menyertakan halaman tertentu tergantung pada bahasa browser (terdeteksi secara otomatis).

Script ini tidak bekerja dengan baik dengan semua browser, jadi selalu termasuk index_en.phpuntuk bahasa yang terdeteksi (penyebab masalah kemungkinan besar adalah masalah dengan beberapa header Bahasa Terima yang tidak dipertimbangkan).

Bisakah Anda menyarankan saya solusi yang lebih kuat?

<?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;
}
?>
GibboK
sumber
3
PHP 5.3.0+ hadir dengan locale_accept_from_http()yang mendapatkan bahasa pilihan dari Accept-Languageheader. Anda harus selalu memilih metode ini daripada metode yang ditulis sendiri. Periksa hasilnya terhadap daftar ekspresi reguler yang Anda coba dan tentukan bahasa halaman seperti itu. Lihat PHP-I18N sebagai contoh.
gak
2
Masalahnya locale_accept_from_http()adalah bahwa Anda mungkin tidak mendukung hasil terbaik yang dikembalikan sehingga Anda masih memiliki penguraian header untuk menemukan yang terbaik berikutnya .
Xeoncross
Jawaban yang diterima untuk ini harus diubah menjadi salah satu dari mereka yang mempertimbangkan beberapa bahasa.
Pekka
sertakan dan butuhkan terjadi pada waktu kompilasi php jadi pada dasarnya Anda sertakan semua indeks * .php dan hanya tampilkan satu - buang sumber daya
Michael

Jawaban:

361

mengapa Anda tidak menjaganya agar tetap sederhana dan bersih

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

?>
Pramendra Gupta
sumber
9
Kode bahasa Belanda, Yunani dan Slovenia adalah satu huruf. Tampaknya lebih baik meledak seperti ini: php.net/manual/tr/reserved.variables.server.php#90293
trante
10
@trante: Mengapa Anda mengatakan itu satu huruf? Bahasa Belanda ( nl), Yunani ( el) dan Slovenia ( sl) semuanya nampak dua huruf: msdn.microsoft.com/en-us/library/ms533052(v=vs.85).aspx
Peter K.
16
Kode ini tidak melihat seluruh daftar. Bagaimana jika plprioritas pertama dan frkedua dalam daftar bahasa saya? Saya akan mendapatkan bahasa Inggris alih-alih bahasa Prancis.
Kos
24
Ini kurang mendeteksi prioritas, dan tidak kompatibel dengan kode yang berbeda dari dua huruf
Áxel Costas Pena
3
Tidak ada yang lain selain dua huruf! Buka peramban favorit Anda dan ubah prioritas bahasa dan Anda akan melihatnya.
Gigala
76

Bahasa Terima adalah daftar nilai tertimbang (lihatparameter q ). Itu berarti hanya dengan melihat bahasa pertama tidak berarti itu juga yang paling disukai; pada kenyataannya,nilai q dari 0 berarti tidak dapat diterima sama sekali.

Jadi, alih-alih hanya melihat bahasa pertama, parsing daftar bahasa yang diterima dan bahasa yang tersedia dan temukan yang paling cocok:

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

Jika findMatchesmengembalikan array kosong, tidak ada kecocokan yang ditemukan dan Anda dapat kembali menggunakan bahasa default.

Gumbo
sumber
Hai, skrip berfungsi dengan baik dan sekarang berhenti. mungkin saja jika SESI di server dimatikan skrip ini tidak akan berfungsi?
GibboK
@ IGbboK: Tidak, ini tidak tergantung pada sesi.
Gumbo
Benar tapi saya lebih suka solusi @diggersworld ... lebih baik tuliskan lebih sedikit kode
lrkwz
Adakah yang bisa memberi tahu saya siapa yang bagaimana nilai qdiputuskan? Terima kasih
Phantom007
@ Phantom007 Tergantung pada preferensi: 0 = Saya tidak ingin bahasa ini, 1 = Saya selalu ingin bahasa ini.
Skyost
43

Jawaban yang ada agak terlalu bertele-tele jadi saya membuat versi yang lebih kecil dan sesuai otomatis.

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

Dan penggunaan sampel:

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

Sumber inti lengkap di sini

Xeoncross
sumber
6
Ini brilian dan persis apa yang saya butuhkan untuk proyek tertentu hari ini. Satu-satunya tambahan yang saya buat adalah mengizinkan fungsi untuk menerima bahasa default dan kembali ke itu jika tidak ada kecocokan antara bahasa yang tersedia dan HTTP_ACCEPT Westphs.
Scott
7
Oh, intisari perubahan saya ada di sini: gist.github.com/humantorch/d255e39a8ab4ea2e7005 (Saya juga menggabungkannya menjadi satu file untuk kesederhanaan)
Scott
2
Metode yang sangat bagus! Mungkin Anda harus memeriksa apakah $ langs sudah berisi entri untuk bahasa tersebut. terjadi pada saya bahwa bahasa yang ditangguhkan adalah en-AS, 2 de dan 3 en, metode Anda selalu memberi saya de, karena nilai pertama en ditimpa oleh entri ke-3
Peter Pint
Ini juga menghasilkan peringatan PHP jika tidak ditemukan kecocokan. Akan menyenangkan untuk menangani ini dengan anggun.
Simon East
26

Cara resmi untuk menangani ini adalah menggunakan perpustakaan HTTP PECL . Tidak seperti beberapa jawaban di sini, ini dengan benar menangani prioritas bahasa (nilai-q), kecocokan sebagian bahasa dan akan mengembalikan kecocokan terdekat, atau ketika tidak ada kecocokan, kembali ke bahasa pertama dalam larik Anda.

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

Cara menggunakan:
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);
diggersworld
sumber
1
Saya menemukan tautan yang berfungsi, jadi perbarui jawaban Anda untuk memasukkannya.
Simon East
Ketiga tautan ini tampaknya sudah mati, dan mereka tampaknya tidak memiliki instruksi pemasangan yang mudah Googleable (juga fungsi ini tidak digunakan lagi menurut halaman mereka)
Brian Leishman
11

Masalah dengan jawaban yang dipilih di atas adalah bahwa pengguna mungkin memiliki pilihan pertama mereka ditetapkan sebagai bahasa yang tidak ada dalam struktur kasus, tetapi salah satu dari pilihan bahasa lainnya ditetapkan. Anda harus mengulang sampai menemukan kecocokan.

Ini adalah solusi super sederhana yang berfungsi lebih baik. Browser mengembalikan bahasa sesuai urutan preferensi, sehingga menyederhanakan masalah. Sementara penunjuk bahasa dapat lebih dari dua karakter (misalnya - "EN-US"), biasanya dua yang pertama sudah cukup. Dalam contoh kode berikut ini saya mencari kecocokan dari daftar bahasa yang dikenal oleh program saya.

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

Saya harap Anda menemukan ini solusi cepat dan sederhana yang dapat Anda gunakan dengan mudah dalam kode Anda. Saya sudah menggunakan ini dalam produksi untuk sementara waktu.

Darryl
sumber
3
"Browser mengembalikan bahasa sesuai urutan preferensi" - Mereka mungkin melakukannya, tetapi Anda tidak harus bergantung pada itu. Gunakan qnilai untuk menentukan preferensi, itulah yang menurut spec harus Anda lakukan.
Quentin
7

Coba yang ini:

#########################################################
# 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);
}
pengguna956584
sumber
Hei bisa Anda menjelaskan regex yang harus menangkap q nilai dengan [0-1]{0,1}.\d{0,4}? Pertama saya kira maksud Anda \.bukan .benar? Dan bukankah q selalu dalam bentuk 0.1324atau sesuatu? Bukankah itu sudah cukup untuk menulis 0\.?\d{0,4}? Jika sudah, q=1.0Anda bisa masuk ke bagian lain.
Adam
Akan bagus untuk melihat contoh penggunaan di sini.
Simon East
2
@SimonEast var_dump( getDefaultLanguage());
jirarium
4

Skrip berikut adalah versi modifikasi dari kode Xeoncross (terima kasih untuk Xeoncross itu) yang mundur ke pengaturan bahasa default jika tidak ada bahasa yang cocok dengan yang didukung, atau jika kecocokan ditemukan itu menggantikan pengaturan bahasa default dengan yang baru sesuai dengan prioritas bahasa.

Dalam skenario ini, browser pengguna diatur dalam urutan prioritas ke Spanyol, Belanda, Inggris AS dan Inggris dan aplikasi hanya mendukung bahasa Inggris dan Belanda tanpa variasi regional dan bahasa Inggris adalah bahasa default. Urutan nilai dalam string "HTTP_ACCEPT Westph" tidak penting jika karena alasan tertentu browser tidak memesan nilai dengan benar.

$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" 
Noel Whitemore
sumber
1

Saya pikir cara terbersih adalah ini!

 <?php
  $lang = substr($_SERVER['HTTP_ACCEPT_LANGUAGE'], 0, 2);
  $supportedLanguages=['en','fr','gr'];
  if(!in_array($lang,$supportedLanguages)){
     $lang='en';
  }
    require("index_".$lang.".php");
Mike Antoniadis
sumber
Ini tidak memperhitungkan prioritas bahasa dalam tajuk.
Simon East
0

Semua hal di atas dengan fallback ke 'en':

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

... atau dengan fallback bahasa default dan array bahasa yang dikenal:

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

Satu baris:

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

Contoh:

// 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'
Toby
sumber
0

Mencoba,

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

Terimakasih untuk

mrbengi
sumber
0

Cepat dan sederhana:

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

CATATAN: Kode bahasa pertama adalah apa yang digunakan oleh browser, sisanya adalah bahasa lain yang telah diatur pengguna di browser.

Beberapa bahasa memiliki kode wilayah, mis. id-GB, yang lain hanya punya kode bahasa, mis. sk.

Jika Anda hanya ingin bahasa dan bukan wilayah (mis. En, fr, es, dll.), Anda dapat menggunakan:

$language =substr($_SERVER['HTTP_ACCEPT_LANGUAGE'], 0, 2);
Justin Levene
sumber
-1

Saya punya yang ini, yang menetapkan cookie. Dan seperti yang Anda lihat, pertama-tama memeriksa apakah bahasa tersebut diposting oleh pengguna. Karena bahasa browser tidak selalu menceritakan tentang pengguna.

<?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/');
        } 
?>
Matthijs
sumber
11
Saya kira Anda tidak dapat mengirim header ketika Anda sudah menggemakan hal-hal?
2
Saya pikir indensi di balik postingan ini masuk akal, yaitu untuk memberi pengguna cara untuk mengganti bahasa, dan mengingat keputusan ini. Deteksi bahasa hanya boleh dilakukan sekali untuk menebak pilihan pertama.
danijar