Перенаправление языков работает на настольном компьютере, но не на мобильном браузере
Я собрал небольшой скрипт на PHP, который проверяет языковые настройки браузера и перенаправляет их на языковую версию сайта (WP multisite),
function redirect() {
$language = substr( $_SERVER["HTTP_ACCEPT_LANGUAGE"],0,2 );
switch( $language ) {
case 'sv':
header( 'Location: http://www.example.com/sv/' );
break;
case 'no':
header( 'Location: http://www.example.com/no/' );
break;
case 'da':
header( 'Location: http://www.example.com/da/' );
break;
default:
header( 'Location: http://www.example.com/' );
break;
}
}
if ( strlen($url) < 4 ) {
session_start();
if ( empty($_SESSION[ 'language' ]) ) {
$_SESSION[ 'language' ] = true;
redirect();
}
}
При тестировании с Mobile Safari или Mobile Chrome редирект не работает. Существуют ли какие-либо специальные выходные данные для языка принятия для мобильных браузеров, которые мне нужно учитывать?
Обновление: после некоторой дополнительной отладки я узнал это:
- Mobile Safari отображает правильный язык при отображении HTTP_ACCEPT_LANGUAGE, но не перенаправляет.
- Mobile Chrome (только для iOS, работает на Android) не отображает правильный язык (по умолчанию "en").
- iOS анализирует данные заголовка http в другом порядке, сравнивает iOS Chrome (en-US,en;q=0.8,sv;q=0.6) и OSX Chrome (sv,en-US;q=0.8,en;q=0.6).
6 ответов
ОБНОВЛЕНИЕ к моему предыдущему ответу
HTTP_ACCEPT_LANGUAGE устанавливается через заголовки и даст разные значения для всех. В моем случае я нахожусь в Южной Америке с настройкой компьютера на английском языке, поэтому мои заголовки lang имеют настройки на английском и испанском языках с уклоном в сторону английского.
session_start();
function redirectToLang($langCode){
// use if's instead of switch so that you can
// check exact matches and presence of a substring
if ($langCode == 'sv'){
$langPath = 'sv';
}else if (strpos($langCode, 'en') !== false){ // this would match en, en-CA, en-US
$langPath = 'en';
}else if ($langCode == 'no'){
$langPath = 'no';
}else{
$langPath = 'en';
}
// you should have no output from the server before this line!
// that is no echoes, print_r, var_dumps, headers, etc
header( 'Location: http://www.example.com/' . $langPath .'/' );
die();
}
function parseLang(){
// echo $_SERVER['HTTP_ACCEPT_LANGUAGE']; in my case
// Chrome Mac OS: en,es;q=0.8
// Chrome Android 5.1: en-US;en;q=0.8,es;q=0.6
// IE Windows Phone 8.1: en-US,en;q=0.5
// Safari iOS: en-US
// Chrome iOS: en-US,en;q=0.8
// get the lang and set a default
$lang = isset($_SERVER['HTTP_ACCEPT_LANGUAGE']) ? $_SERVER['HTTP_ACCEPT_LANGUAGE'] : 'en';
// parse the lang code. This can be as simple or as complex as you want
// Simple
$langCode = substr($lang, 0, 2); // in my case 'en'
// Semi Complex (credits to http://www.thefutureoftheweb.com/blog/use-accept-language-header)
$languages = array();
preg_match_all('/([a-z]{1,8}(-[a-z]{1,8})?)\s*(;\s*q\s*=\s*(1|0\.[0-9]+))?/i', $lang, $parsed);
if (count($parsed[1])) {
$languages = array_combine($parsed[1], $parsed[4]);
foreach ($languages as $lang => $val) {
if ($val === '') $languages[$lang] = 1;
}
arsort($languages, SORT_NUMERIC);
}
// var_dump($languages); in my case
// array (size=2)
// 'en' => int 1
// 'es' => string '0.8'
$langCode = key($languages); // in my case 'en'
return $langCode;
}
if (!isset($_SESSION['lang'])){
$langCode = parseLang();
$_SESSION['lang'] = $langCode;
redirectToLang($langCode);
}else{
// we already know the language and it is in $_SESSION
// no need to parseLang nor redirect
}
В моем случае все устройства перенаправляют правильно. Я думаю, что что-то происходит с логикой, которая вызывает redirect()
// this part
if ( strlen($url) < 4 ) {
session_start();
if ( empty($_SESSION[ 'language' ]) ) {
$_SESSION[ 'language' ] = true;
redirect();
}
}
и сессия var обходит логику перенаправления. Попробуйте приведенный выше код и удалите все файлы cookie и сеансы со всех устройств, чтобы $_SESSION['language']
var, который вы установили во время тестирования, не испортит результаты. Дайте нам знать, что происходит с вашей стороны.
Попробуйте это и дайте нам знать результат, пожалуйста
function redirect() {
$language = substr( $_SERVER["HTTP_ACCEPT_LANGUAGE"],0,2 );
switch( $language ) {
case 'sv':
header( 'Location: http://www.example.com/sv/' );
break;
case 'no':
header( 'Location: http://www.example.com/no/' );
break;
case 'da':
header( 'Location: http://www.example.com/da/' );
break;
default:
die('Default location');
/* if you get this message on mobile devices, then this line
$language = substr( $_SERVER["HTTP_ACCEPT_LANGUAGE"],0,2 );
is faulty. Perhaps as @chris85 mentioned, HTTP_ACCEPT_LANGUAGE is
not populated so mobile behaves as a default by not redirecting to
other languages. If this is the case, fix that line
and remove the die();*/
header( 'Location: http://www.example.com/' );
break;
}
die(); // leave this one in. It forces the server to flush data to the browser
}
Я цитирую.. "Более современный метод будет использовать http_negotiate_language():"
Вы проверяли это? Использование серверной переменной PHP HTTP_ACCEPT_LANGUAGE
Это прекрасно работает на моих настольных браузерах и мобильных устройствах. У меня тоже возникали проблемы с сеансом только на устройствах, и чаще всего я полагался на то, что переменная сеанса была пустой, чтобы выполнить требования моего состояния, когда на самом деле переменная еще существовала или просто не было экземпляра session_id().
Сброс очистит сессию.
Он также запустит редирект, если язык изменился.
<?php
session_start();
if (isset($_REQUEST['reset'])) {
unset($_SESSION);
$_SESSION['PREVIOUS_SESSION'] = '&cleared=1';
}
function redirect($loc) {
$_SESSION[ 'language' ] = true;
$_SESSION['last_language'] = $language;
header( 'Location: ?r='.$loc.$_SESSION['PREVIOUS_SESSION']);
}
$language = substr( $_SERVER["HTTP_ACCEPT_LANGUAGE"],0,2 );
if (( empty($_SESSION[ 'language' ]) ) || ($_SESSION['last_language'] != $language)) {
redirect($language);
}
echo '<pre>';
print_r($_SESSION);
echo '</pre>';
if (!empty($_SESSION['PREVIOUS_SESSION'])) {
unset($_SESSION['PREVIOUS_SESSION']);
}
?>
Вы должны действительно дать нам примеры того, какова ценность $_SERVER["HTTP_ACCEPT_LANGUAGE"]
для трех случаев.
В любом случае, обратите внимание, что в соответствии с RFC2616 HTTP / 1.1, выбор языка намного сложнее, чем просто получение двух первых символов заголовка.
Каждому языковому диапазону МОЖЕТ быть присвоен соответствующий показатель качества, который представляет собой оценку предпочтений пользователя для языков, указанных в этом диапазоне. Значение качества по умолчанию равно "q=1". Например,
Accept-Language: da, en-gb;q=0.8, en;q=0.7
будет означать: "Я предпочитаю датский, но приму британский английский и другие типы английского".
Ничто не говорит о том, что эти заголовки отсортированы, и что предпочтительный язык пользователя является первым в списке. И языковая конфигурация также не может быть настроена в браузере или ОС.
В идеале, чтобы выбрать лучший язык, вы должны разобрать этот заголовок следующим образом:
- Разбить строку на запятые
- Разделить каждую найденную подстроку на точку с запятой
- Если числовое значение не указано, используйте значение по умолчанию 1,0
- Сортировать результат по числовому значению
- Сравните этот список со списком языков, доступных на вашем сайте, и найдите лучший.
Вы действительно не должны полагаться на получение первых двух символов. Вы действительно должны полагаться на проверку всей строки и понимание того, каким должен быть лучший выбор языка. Эти строковые значения имеют конкретное значение, и, например, в одном из ваших случаев "проблемных" строк вы на самом деле выполняете наиболее подходящее поведение, чтобы показать en
вместо sv
, Очевидно, что вы можете написать логику, чтобы разбить язык принятия, исследовать составные части и предпринять соответствующие действия, но вы также можете рассмотреть возможность использования чего-то вроде:
http://php.net/http_negotiate_language
сделать это для вас. Вероятно, из быстрого поиска Google доступны десятки других сценариев, которые действительно работают с этим заголовком более подходящим способом, чем просто просмотр двух первых символов.
Также, вы можете проверить подобный вопрос здесь: Использование серверной переменной PHP HTTP_ACCEPT_LANGUAGE