Перенаправление языков работает на настольном компьютере, но не на мобильном браузере

Я собрал небольшой скрипт на 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

Другие вопросы по тегам