Rileva la lingua del browser in PHP


144

Uso il seguente script PHP come indice per il mio sito Web.

Questo script dovrebbe includere una pagina specifica a seconda della lingua del browser (rilevata automaticamente).

Questo script non funziona bene con tutti i browser, quindi include sempre index_en.phpper qualsiasi lingua rilevata (la causa del problema è molto probabilmente un problema con alcune intestazioni Accept-Language non prese in considerazione).

Potresti suggerirmi una soluzione più solida?

<?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+ viene fornito con il locale_accept_from_http()quale ottiene la lingua preferita dall'intestazione Accept-Language. Dovresti sempre preferire questo metodo a un metodo scritto da solo. Controlla il risultato in un elenco di espressioni regolari che provi e determina la lingua della pagina in quel modo. Vedi PHP-I18N per un esempio.
Caw

2
Il problema locale_accept_from_http()è che potresti non supportare il risultato migliore che restituisce, quindi devi comunque analizzare tu stesso l'intestazione per trovare il prossimo .
Xeoncross,

La risposta accettata dovrebbe essere cambiata in una di quelle che tengono conto di più lingue.
Pekka,

includi e richiedi accadono al momento della compilazione di php, quindi in pratica includi tutto l'indice * .php e ne mostri solo uno - spreco di risorse
Michael

Risposte:


361

perché non lo tieni semplice e pulito

<?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
I codici di lingua olandese, greco e sloveno sono una lettera. Sembra meglio esplodere in questo modo: php.net/manual/tr/reserved.variables.server.php#90293
Trante

10
@trante: perché dici che sono una lettera? Olandese ( nl), greco ( el) e sloveno ( sl) sembrano tutti due lettere: msdn.microsoft.com/en-us/library/ms533052(v=vs.85).aspx
Peter K.,

16
Questo codice non esamina l'intero elenco. Che cosa succede se la plprima priorità frè la seconda nell'elenco della mia lingua? Avrei preso l'inglese invece del francese.
Kos,

24
Manca di rilevare le priorità e non è compatibile con codici diversi da due lettere
Áxel Costas Pena

3
Non ci sono altre lunghezze che due lettere! Vai nel tuo browser preferito e cambia la priorità della lingua e lo vedrai.
Gigala,

76

Accept-Language è un elenco di valori ponderati (vedereparametro q ). Ciò significa che solo guardare la prima lingua non significa che sia anche la più preferita; in effetti, unvalore q di 0 significa assolutamente inaccettabile.

Quindi, invece di guardare solo la prima lingua, analizza l'elenco delle lingue accettate e delle lingue disponibili e trova la corrispondenza migliore:

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

Se findMatchesrestituisce un array vuoto, non è stata trovata alcuna corrispondenza ed è possibile ricorrere alla lingua predefinita.


Ciao, la sceneggiatura funzionava bene e ora basta. potrebbe essere che se SESSIONE sul server sono disattivati ​​questo script non funzionerà?
GibboK,

@GIbboK: No, questo è indipendente dalle sessioni.
Gumbo,

Corretto ma preferisco la soluzione @diggersworld ... meglio scrivere meno codice
lrkwz

Qualcuno può dirmi chi è come è qdeciso il valore ? Grazie
Phantom007,

@ Phantom007 Dipende dalla preferenza: 0 = Non voglio questa lingua, 1 = Voglio sempre questa lingua.
Skyost,

43

Le risposte esistenti sono un po 'troppo dettagliate, quindi ho creato questa versione più piccola, con corrispondenza automatica.

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

E l'uso del campione:

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

Fonte completa qui


6
Questo è geniale ed esattamente ciò di cui avevo bisogno per un particolare progetto oggi. L'unica aggiunta che ho fatto è quella di consentire alla funzione di accettare una lingua predefinita e ricadere su quella se non c'è corrispondenza tra le lingue disponibili e HTTP_ACCEPT_LANGUAGEs.
Scott,

7
Oh, un riassunto delle mie modifiche è qui: gist.github.com/humantorch/d255e39a8ab4ea2e7005 (l'ho anche combinato in un file per semplicità)
Scott,

2
Metodo molto bello! Forse dovresti controllare se $ langs contiene già una voce per la lingua. mi è successo che la lingua preferita era en-US, 2nd de e 3rd en, il tuo metodo mi ha sempre dato de, perché il primo valore di en è stato sovrascritto dalla terza voce
Peter Pint,

Produce anche un avviso PHP se non viene trovata alcuna corrispondenza. Sarebbe bello gestirlo con grazia.
Simon East,

26

Il modo ufficiale per gestirlo è utilizzare la libreria HTTP PECL . A differenza di alcune risposte qui, questo gestisce correttamente le priorità della lingua (valori-q), corrispondenze di lingua parziali e restituirà la corrispondenza più vicina, o quando non ci sono corrispondenze, ricade nella prima lingua dell'array.

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

Come usare:
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
Ho trovato un link funzionante, quindi ho aggiornato la tua risposta per includerlo.
Simon East,

Tutti e tre questi link sembrano essere morti e non sembrano avere istruzioni per l'installazione facilmente googleable (anche questa funzione è deprecata in base alla loro pagina)
Brian Leishman

11

Il problema con la risposta selezionata sopra è che l'utente può avere la prima scelta impostata come lingua che non è nella struttura del caso, ma è impostata una delle altre scelte linguistiche. Dovresti ripetere il ciclo finché non trovi una corrispondenza.

Questa è una soluzione super semplice che funziona meglio. I browser restituiscono le lingue in ordine di preferenza, in modo da semplificare il problema. Mentre il designatore della lingua può contenere più di due caratteri (ad es. "EN-US"), in genere i primi due sono sufficienti. Nel seguente esempio di codice sto cercando una corrispondenza da un elenco di lingue conosciute di cui il mio programma è a conoscenza.

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

Spero che questa sia una soluzione semplice e veloce che puoi facilmente usare nel tuo codice. Lo sto usando in produzione da un po 'di tempo.


3
"I browser restituiscono le lingue in ordine di preferenza" - Potrebbero farlo, ma non dovresti dipendere da quello. Usa i qvalori per determinare la preferenza, ecco cosa dice la specifica che dovresti fare.
Quentin,

7

Prova questo:

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

Ehi, potresti spiegare la regex che dovrebbe catturare il valore q[0-1]{0,1}.\d{0,4} ? Innanzitutto immagino che intendi \.invece che .giusto? E q non è sempre della forma 0.1324o qualcosa del genere? Non sarebbe quindi sufficiente scrivere 0\.?\d{0,4}? Se lo q=1.0hai, puoi andare nella parte opposta.
Adam,

Sarebbe bello vedere un esempio di utilizzo qui.
Simon East,

2
@SimonEast var_dump( getDefaultLanguage());
jirarium

4

Il seguente script è una versione modificata del codice di Xeoncross (grazie per quel Xeoncross) che ricade su un'impostazione della lingua predefinita se nessuna lingua corrisponde a quella supportata, o se viene trovata una corrispondenza sostituisce l'impostazione della lingua predefinita con una nuova in base alla priorità della lingua.

In questo scenario il browser dell'utente è impostato in ordine di priorità su spagnolo, olandese, inglese americano e inglese e l'applicazione supporta solo inglese e olandese senza variazioni regionali e l'inglese è la lingua predefinita. L'ordine dei valori nella stringa "HTTP_ACCEPT_LANGUAGE" non è importante se per qualche motivo il browser non ordina i valori correttamente.

$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

Penso che il modo più pulito sia questo!

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

Questo non tiene conto delle priorità linguistiche all'interno dell'intestazione.
Simon East,

0

Tutto quanto sopra con fallback a 'en':

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

... o con fallback della lingua predefinita e array di lingue conosciute:

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

Una linea:

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

Esempi:

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

Provare,

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

Grazie a


0

Veloce e semplice:

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

NOTA: il primo codice di lingua è ciò che viene utilizzato dal browser, il resto sono altre lingue che l'utente ha impostato nel browser.

Alcune lingue hanno un codice regionale, ad es. it-IT, altri hanno solo il codice della lingua, ad es. sk.

Se vuoi solo la lingua e non la regione (es. En, fr, es, ecc.), Puoi usare:

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

-1

Ho questo, che imposta un cookie. E come puoi vedere, controlla prima se la lingua è pubblicata dall'utente. Perché la lingua del browser non parla sempre dell'utente.

<?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
Immagino che non puoi inviare le intestazioni quando hai già fatto eco alle cose?

2
Penso che il rientro alla base di questo post abbia un senso, ovvero fornire all'utente un modo per cambiare la lingua e ricordare questa decisione. Il rilevamento della lingua dovrebbe essere eseguito una sola volta per indovinare meglio la prima selezione.
danijar,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.