2011-05-17 21 views
23

he creado un script PHP que comprueba el HTTP_ACCEPT_LANGUAGE y carga el sitio web utilizando el lenguaje apropiado a partir del 1 de dos personajes:Usando la variable de servidor PHP HTTP_ACCEPT_LANGUAGE

  $http_lang = substr($_SERVER["HTTP_ACCEPT_LANGUAGE"],0,2); 
     switch ($http_lang) { 
     case 'en': 
      $SESSION->conf['language'] = 'english'; 
      break; 
     case 'es': 
      $SESSION->conf['language'] = 'spanish'; 
      break; 
     default: 
      $SESSION->conf['language'] = $PREFS->conf['languages'][$SESSION->conf['language_id']]; 
     } 

Si puedo cambiar el idioma a español en Firefox el sitio web cargas en español bien Sin embargo, he recibido varios informes de personas en Colombia que ven el sitio web en inglés.

Detalles: "ES-CO" LCID = 9226 Español (Colombia)

Alguien tiene alguna idea de por qué está sucediendo esto? Pensé que esta era la mejor manera de verificar qué idioma es compatible con los usuarios.

+2

La mejor manera es registrar direcciones IP y sus encabezados. Y examine los registros más adelante – zerkms

+4

posible duplicado de [¿Cómo obtener el valor del idioma de $ _SERVER \ ['HTTP_ACCEPT_LANGUAGE' \] usando PHP?] (Http://stackoverflow.com/questions/2316476/how-to-get-the -language-value-from-serverhttp-accept-language-using-php) –

+0

¿Podría ser un problema de caso? Cambiarlo para cambiar (strtolower ($ http_lang)) podría ayudar. Aunque no estoy seguro – mjec

Respuesta

14

Un método más moderno sería utilizar http_negotiate_language():

$map = array("en" => "english", "es" => "spanish"); 
$conf_language= $map[ http_negotiate_language(array_keys($map)) ]; 

Si no tiene la http extension instalado (and not the intl one as well), hay otra solución en los comentarios (user-note #86787 (Nov 2008; by Anonymous)):

<?php 
/* 
    determine which language out of an available set the user prefers most 

    $available_languages  array with language-tag-strings (must be lowercase) that are available 
    $http_accept_language a HTTP_ACCEPT_LANGUAGE string (read from $_SERVER['HTTP_ACCEPT_LANGUAGE'] if left out) 
*/ 
function prefered_language ($available_languages,$http_accept_language="auto") { 
    // if $http_accept_language was left out, read it from the HTTP-Header 
    if ($http_accept_language == "auto") $http_accept_language = isset($_SERVER['HTTP_ACCEPT_LANGUAGE']) ? $_SERVER['HTTP_ACCEPT_LANGUAGE'] : ''; 

    // standard for HTTP_ACCEPT_LANGUAGE is defined under 
    // http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4 
    // pattern to find is therefore something like this: 
    // 1#(language-range [ ";" "q" "=" qvalue ]) 
    // where: 
    // language-range = ((1*8ALPHA *("-" 1*8ALPHA)) | "*") 
    // qvalue   = ("0" [ "." 0*3DIGIT ]) 
    //   | ("1" [ "." 0*3("0") ]) 
    preg_match_all("/([[:alpha:]]{1,8})(-([[:alpha:]|-]{1,8}))?" . 
        "(\s*;\s*q\s*=\s*(1\.0{0,3}|0\.\d{0,3}))?\s*(,|$)/i", 
        $http_accept_language, $hits, PREG_SET_ORDER); 

    // default language (in case of no hits) is the first in the array 
    $bestlang = $available_languages[0]; 
    $bestqval = 0; 

    foreach ($hits as $arr) { 
     // read data from the array of this hit 
     $langprefix = strtolower ($arr[1]); 
     if (!empty($arr[3])) { 
      $langrange = strtolower ($arr[3]); 
      $language = $langprefix . "-" . $langrange; 
     } 
     else $language = $langprefix; 
     $qvalue = 1.0; 
     if (!empty($arr[5])) $qvalue = floatval($arr[5]); 

     // find q-maximal language 
     if (in_array($language,$available_languages) && ($qvalue > $bestqval)) { 
      $bestlang = $language; 
      $bestqval = $qvalue; 
     } 
     // if no direct hit, try the prefix only but decrease q-value by 10% (as http_negotiate_language does) 
     else if (in_array($langprefix,$available_languages) && (($qvalue*0.9) > $bestqval)) { 
      $bestlang = $langprefix; 
      $bestqval = $qvalue*0.9; 
     } 
    } 
    return $bestlang; 
} 
?> 
+0

Para que el ejemplo del código funcione, tuve que reemplazar '$ langrange = strtolower ($ arr [3]); 'con' $ langrange = $ arr [3]; '. Tenga en cuenta que mis cadenas de lengua están en el formato es-CO en lugar de es-co como en la pregunta. –

+0

¿Alguien sabe qué pasó con 'http_negotiate_language'? No puedo encontrarlo en php.net ... – brasofilo

+0

@brasofilo Creo que ha sido retirado por completo para la extensión http v3.xx pecl: https://mdref.m6w6.name/http/Header/negotiate → API completamente nueva, y no está incluido en los documentos oficiales de PHP. – mario

5

¿Sabe si esto está sucediendo para todos visitantes a su sitio desde Colombia? Los usuarios son generalmente libres de alterar la configuración de idioma de sus navegadores, o de que sean alterados por quienes estén a cargo de la computadora. Como recomienda zerkms, intente registrar las direcciones IP y sus encabezados.

Si usted tiene la intl extension instalado puede utilizar Locale::lookup y Locale::acceptFromHttp para obtener un mejor ajuste elección del idioma de la configuración del navegador del usuario y una lista de lo traducciones que tiene disponible.

Locale::acceptFromHttp($_SERVER['HTTP_ACCEPT_LANGUAGE']); # e.g. "en_US" 
3

Al final fui con esta solución:

if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) { 
    preg_match_all('/([a-z]{1,8}(-[a-z]{1,8})?)\s*(;\s*q\s*=\s*(1|0\.[0-9]+))?/i', $_SERVER['HTTP_ACCEPT_LANGUAGE'], $lang_parse); 
    if (count($lang_parse[1])){ 
    $langs = array_combine($lang_parse[1], $lang_parse[4]); 
    foreach ($langs as $lang => $val){ 
     if ($val === '') $langs[$lang] = 1; 
    } 
    arsort($langs, SORT_NUMERIC); 
    } 
    foreach ($langs as $lang => $val){ 
    if (strpos($lang,'en')===0){ 
     $language = 'english'; 
     break; 
    } else if (strpos($lang,'es')===0){ 
     $language = 'spanish'; 
    } 
    } 
} 

me gustaría dar las gracias a AJ para los enlaces. También gracias a todos los que respondieron.

1

si desea almacenar los idiomas en serie, hago esto:

preg_match_all('/([a-z]{1,8}(-[a-z]{1,8})?)\s*(;\s*q\s*=\s*(1|0\.[0-9]+))?/i', 'pt-br,pt;q=0.8,en-us;q=0.5,en,en-uk;q=0.3', $lang_parse); 
$langs = $lang_parse[1]; 
$rank = $lang_parse[4]; 
for($i=0; $i<count($langs); $i++){ 
    if ($rank[$i] == NULL) $rank[$i] = $rank[$i+1]; 
} 

esta salida una serie de idiomas Es Otros valores

preg_match_all('/([a-z]{1,8}(-[a-z]{1,8})?)\s*(;\s*q\s*=\s*(1|0\.[0-9]+))?/i', 'pt-br,pt;q=0.8,en-us;q=0.5,en,en-uk;q=0.3', $lang_parse); 
$langs = $lang_parse[1]; 
$rank = $lang_parse[4]; 
$lang = array(); 
for($i=0; $i<count($langs); $i++){ 
    $lang[$langs[$i]] = ($rank[$i] == NULL) ? $rank[$i+1] : $rank[$i]; 
} 

esta salida una serie como esta:

Array 
(
    [pt-br] => 0.8 
    [pt] => 0.8 
    [en-us] => 0.5 
    [en] => 0.3 
    [en-uk] => 0.3 
) 
6

Utilicé la expresión regular de @GabrielAnderson y diseñé esta función que se comporta de acuerdo con RFC 261 6 (cuando no se otorga un valor de calidad a un idioma, se establece de manera predeterminada en 1).

Cuando varios idiomas comparten el mismo valor de calidad, los más específicos tienen prioridad sobre los menos específicos.(Este comportamiento no es parte de la RFC que proporciona ninguna recomendación para este caso concreto)

function Get_Client_Prefered_Language ($getSortedList = false, $acceptedLanguages = false) 
{ 

    if (empty($acceptedLanguages)) 
     $acceptedLanguages = $_SERVER["HTTP_ACCEPT_LANGUAGE"]; 

     // regex inspired from @GabrielAnderson on http://stackoverflow.com/questions/6038236/http-accept-language 
    preg_match_all('/([a-z]{1,8}(-[a-z]{1,8})*)\s*(;\s*q\s*=\s*(1|0\.[0-9]+))?/i', $acceptedLanguages, $lang_parse); 
    $langs = $lang_parse[1]; 
    $ranks = $lang_parse[4]; 


     // (create an associative array 'language' => 'preference') 
    $lang2pref = array(); 
    for($i=0; $i<count($langs); $i++) 
     $lang2pref[$langs[$i]] = (float) (!empty($ranks[$i]) ? $ranks[$i] : 1); 

     // (comparison function for uksort) 
    $cmpLangs = function ($a, $b) use ($lang2pref) { 
     if ($lang2pref[$a] > $lang2pref[$b]) 
      return -1; 
     elseif ($lang2pref[$a] < $lang2pref[$b]) 
      return 1; 
     elseif (strlen($a) > strlen($b)) 
      return -1; 
     elseif (strlen($a) < strlen($b)) 
      return 1; 
     else 
      return 0; 
    }; 

     // sort the languages by prefered language and by the most specific region 
    uksort($lang2pref, $cmpLangs); 

    if ($getSortedList) 
     return $lang2pref; 

     // return the first value's key 
    reset($lang2pref); 
    return key($lang2pref); 
} 

Ejemplo:

print_r(Get_Client_Prefered_Language(true, 'en,en-US,en-AU;q=0.8,fr;q=0.6,en-GB;q=0.4')); 

Salidas:

Array 
    (
     [en-US] => 1 
     [en] => 1 
     [en-AU] => 0.8 
     [fr] => 0.6 
     [en-GB] => 0.4 
    ) 

Como se puede notar, 'en -US 'aparece en la primera posición a pesar del hecho de que' en 'fue el primero en la cadena dada.

lo que podría utilizar esta función y simplemente reemplazar la primera línea de código por:

$http_lang = substr(Get_Client_Prefered_Language(),0,2); 
+0

Podría estar equivocado, pero ¿no debería la sentencia while en la función $ getRank ser una instrucción if? –

+0

Tienes razón, no recuerdo por qué usé 'while' en lugar de' if'. Podría haber sido enfatizar que la recursión continuaría mientras '$ rankings [$ j]' está definido ... – 2072

+0

Función realmente útil. Gracias Peter. – Frank

0

he confiado en los programadores expertos que trabajan para PHP y pensar en el futuro. Aquí está mi versión de una etiqueta para el traductor de Google desplegable.

function gethttplanguage(){ 
    $langs = array(  
      'en',// default 
      'it', 
      'dn', 
      'fr', 
      'es'   
    ); 
    $questions = array(
    "en" => "If you wish to see this site in another language click here", 
    "it" => "Se vuole vedere questo sito in italiano clicca qui", 
    "dn" => "Hvis du ønsker at se denne hjemmeside i danske klik her", 
    "fr" => "Si vous voulez visualiser ce site en français, cliquez ici", 
    "es" => "Si quieres ver este sitio en español haga clic aquí" 
    ); 
    $result = array(); 
    http_negotiate_language($langs, &$result); 
    return $questions[key($result)]; 
} 
2

voy a utilizar código de configuración regional completa para referirse idioma, porque al igual que zh-TW y zh-CN es 2 idioma diferente.

function httpAcceptLanguage($httpAcceptLanguage = null) 
{ 
    if ($httpAcceptLanguage == null) { 
     $httpAcceptLanguage = $_SERVER['HTTP_ACCEPT_LANGUAGE']; 
    } 

    $languages = explode(',', $httpAcceptLanguage); 
    $result = array(); 
    foreach ($languages as $language) { 
     $lang = explode(';q=', $language); 
     // $lang == [language, weight], default weight = 1 
     $result[$lang[0]] = isset($lang[1]) ? floatval($lang[1]) : 1; 
    } 

    arsort($result); 
    return $result; 
} 

// zh-TW,en-US;q=0.7,en;q=0.3 
echo $_SERVER['HTTP_ACCEPT_LANGUAGE']; 
/* 
    Array 
    (
     [zh-TW] => 1 
     [en-US] => 0.7 
     [en] => 0.3 
    ) 
*/ 
print_r(httpAcceptLanguage()); 
Cuestiones relacionadas