Грамотное определение языка пользователя

  • Tutorial
Сейчас работаю над сайтом, который претендует на глобальность, естественно и с мультыязычностью у него должно быть все в порядке.

О том как отображать информацию на разных языках тут речи идти не будет. Разговор пойдет о том как определить язык пользователя, и выбрать из доступных на сайте языковых версий наиболее подходящую.

Кому лень читать — посмотрите скринкаст он правда не очень получился, поэтому тут не выкладываю.

И так что имеем:
  • PHP
  • Фреймворк CodeIgniter (класс писался для этого фреймворка, но его можно использовать где угодно, внеся небольшие изменения)

Задача:
Определить язык пользователя и если пользователь русскоговорящий (Русский, Беларус, Украинец полный список тут) показываем ему информацию на русском. Если нет то на английском.
Все это нужно оформить в виде класса или функции с возможностью быстро задавать что-то вроде ссылок с языка пользователя на язык лучший для его понимания на сайте.

Решение:
Для определения языка пользователя используем суперглобальный массив $_SERVER, а точнее — его элемент $_SERVER['HTTP_ACCEPT_LANGUAGE'] в нем описываются предпочтения клиента относительно языка. Данная информация извлекается из HTTP-заголовка Accept-Language, который присылает клиент серверу.
В моем случае это была строка
ru-ru,ru;q=0.8,en-us;q=0.6,en;q=0.4

Эта строка содержит языки пользователя, которые он предпочитает, и их приоритеты выражаются через q, ели q для языка не задано, то предполагается, что оно будет равно 1. Если постараться отобразить ее в более менее читаемом виде то она выглядит так:
Array
(
    [ru-ru] => 1
    [ru] => 0.8
    [en-us] => 0.6
    [en] => 0.4
)

Отсюда видно что я предпочитаю русский язык, а на втором месте у меня английский.
Языки написаны в двух форматах главный код языка это «ru» и «en» в моем случае, который относится к языкоывм стандартам ISO 639
И главный код языка — расширенный код языка в моем случае это «ru-ru» и «en-us» тут расширенный код языка указывает на регион использования языка у меня это United States.
Временами возникает недопонимание с тем как пометить языки, когда списки кодов ISO содержат как двухбуквенные так и трехбуквенные коды (иногда несколько трехбуквенных кодов). Сейчас все действительные коды перечислены в одном IANA реестре, который для языка принимает только одно значение из списков ISO. Если доступен двухбуквенный код ISO, то он будет один в реестре. Иначе реестр будет содержать один трехбуквенный код. Это упростит вещи.

С теорией разобрались переходим к практике:
Напишем конструктор контроллера класса:
public function __construct()
    {
        if (($list = strtolower($_SERVER['HTTP_ACCEPT_LANGUAGE']))) {
            if (preg_match_all('/([a-z]{1,8}(?:-[a-z]{1,8})?)(?:;q=([0-9.]+))?/', $list, $list)) {
                $this->language = array_combine($list[1], $list[2]);
                foreach ($this->language as $n => $v)
                    $this->language[$n] = $v ? $v : 1;
                arsort($this->language, SORT_NUMERIC);
            }
        } else $this->language = array();
    }

Тут мы обрабатываем строку возвращаемую $_SERVER['HTTP_ACCEPT_LANGUAGE'] так чтобы это получился массив вида
Array
(
    [ru-ru] => 1
    [ru] => 0.8
    [en-us] => 0.6
    [en] => 0.4
)

Отсортированный по убыванию приоритета языка(значение q)

Далее создаем метод находящую наиболее подходящий язык.
Первым пареметром в нее передается язык используемый по умолчанию, вторым массив ключами которого будут языки которые есть на сайте, а значениями ссылки на него с других языков выглядит массив примерно так:
$langs=array(
            'ru'=>array('ru','be','uk','ky','ab','mo','et','lv'),
            'de'=>'de'
        );

Код метода:
 public function getBestMatch($default, $langs)
    {
        $languages=array();
        foreach ($langs as $lang => $alias) {
            if (is_array($alias)) {
                foreach ($alias as $alias_lang) {
                    $languages[strtolower($alias_lang)] = strtolower($lang);
                }
            }else $languages[strtolower($alias)]=strtolower($lang);
        }

        foreach ($this->language as $l => $v) {
            $s = strtok($l, '-'); // убираем то что идет после тире в языках вида "en-us, ru-ru"
            if (isset($languages[$s]))
                return $languages[$s];
        }
        return $default;
    }

В функции урезаются языки формата главный код языка — расширенный код языка до формата главный код языка т.к. необходимость в Английской и Американской версии языка врядли возникнет, а при желании всегда можно дописать.
Результатом ее выполнения будет наиболее подходящий язык пользователя, в формате ISO 639 в качестве дефолтного языка я передал английский, и для всех языков что не находятся в массиве $langs будет возвращен en.

Скачать библиотечку можно тут
Ads
AdBlock has stolen the banner, but banners are not teeth — they will be back

More

Comments 20

    0
    К сожалению, полный разбор HTTP_ACCEPT_LANGUAGE вряд ли имеет смысл, потому как: 1) тяжеловесен 2) если сайт не поддерживает язык с наибольшим весом, в общем-то, всё равно какой язык предлагать взамен.
      0
      Тут я бы поспорил :) Например, мне кажется, украинцам или белорусам русский язык будет предпочтительнее английского
        0
        Так давайте же поспорим %)
        Имею мнение, что мало кто на клиенте вручную выставляет веса в порядке be_BY, ru_RU и uk_UA, ru_RU. Если знаете ОС и браузеры, в которых такие веса выставлены по умолчанию при установке, поделитесь пожалуйста.
          0
          Когда я в опере выставил язык интерфейса на ураинский, то вторым в списке предпочитаемых языков выставился русский. Хотя я не берусь утверждать, что так будет у всех: могла учитываться локаль системы или то, что ранее основням языком стоял русский.
            0
            Я говорил об этом
            если сайт не поддерживает язык с наибольшим весом, в общем-то, всё равно какой язык предлагать взамен
            Т.е. даже если у человека после украинского языка идёт английский, по-моему логичнее ему предложить русский язык при отсутствии украинской локализации

            Ну а, скажем, немцу я бы всё же английский предложил :)
              +1
              Ну, так вы контекст фразы во внимание возьмите ;) Речь идет о том, что всё равно какой из менее весомых объявленных языков использовать.

              Кстати, вы наталкиваете на толковую функциональность для библиотеки, которая могла бы использоваться в клиентском приложении: получаем код языка с максимальным весом; пытаемся установить локаль приложения; если не получается, проходим по дереву родственных языков, пытаясь отыскать тот, для которого предусмотрена локализация. Главное вовремя остановиться, а иначе и до праязыков дойти можно.
                0
                Согласен. BTW, мы обычно в своих проектах так и делаем: берём язык с наибольшим приоритетом и смотрим есть ли у нас такая локализаиця. Если нет, то смотрим таблицу соответствия языка пользователя и языковой группы, в которую он входит, и, соответственно, предлагаем ему имеющуюя у нас локализацию из этой группы
      • UFO just landed and posted this here
          +3
          Да, выбор конечно, это при первом входе, потом используются куки, где хранится выбранный язык
            +2
            Аналогично. У меня система, все программы итд — на английском(русская локаль даже не установлена). Сайтами предпочитаю пользоваться на английском(даже если есть русская локализация). И очень раздражает когда мне какой-нить сайт показывает русскую версию ориентируясь на моем географ. положении а не на локали системы.
              +3
              Но в статье то как раз описывается правильный способ, учитывающий именно предпочтения пользователя, которые, как правило, браузер выставляет по локали системы.
                0
                Да я вижу, я же не на статью отвечал а на комментарий.
              +3
              Кажется вы чего-то не поняли. Статья как раз о том, чтобы дать выбор ВАМ. У вас же в HTTP_ACCEPT_LANGUAGE перечислены те языки, на которых вы хотите видеть сайты, правда? Если нет, то почемы вы возмущаетесь, что сайт выдает вам страницы на том языке, который вы запросили?
                0
                Ключевая фраза «раз за разом»
                +1
                HTTP_ACCEPT_LANGUAGE является единственно верным способом выбора языка страницы. Использование cookie таковым не является, так как противоречит основным концепциям, заложенным в HTTP, что приводит к архитектурным проблемам в серверном коде. В частности использование cookie мешает масштабированию сервисов.

                К сожалению, совеменные браузеры еще не доросли до нормальной поддержки языков и клиентскую логику приходится реализовывать в серверном ПО. Переключение языков должно быть простым и удобным, сделанным кнопкой на панели инструментов. Не надо будет искать, куда дизайнер вставил переключалку: она всегда на одном и том же месте.
                  +1
                  W3C прямо заявляет, что этот способ не должен расцениваться как «единственно верный».
                    0
                    Тут важна грань между «как должно быть» и «что можно сделать с текущими технологиями». С текущими технолгиями и практиками их применения HTTP_ACCEPT_LANGUAGE действительно невозможно использовать как «единственно верный» способ т.к. пользователям надо давать возможность выбора языка и этот выбор где-то должен запоминаться. Клиентское ПО не дает простого сопособа для этого. Потому W3C и дает такие рекомендации.

                    Просто в последние годы наметилась тенденция по использованию всех тех возможностей, которые были заложены в HTTP 12 лет назад. Появилась мода на REST, на HATEOAS. Мобильные браузеры научились позволять пользователю легко, в два клика, выставлять заголовки, переключающие сайт в вид для мобильных устройств и для настольных ПК. Поэтому у меня есть надежда, что в ближайшие год-два новейшие версии браузеров научаться более широко использовать заголовки типа ACCEPT*, а через несколько лет этим можно будет пользоваться на серверах.
                      0
                      Не факт, что так быть должно.
                      Взгляните: клиентское ПО не даёт простого способа, потому что это никому не нужно — хватает одной базовой локали (соотвествующей системой), и одной дополнительной (обычно английский). Это предпочтения, выставлющиеся на этапе установки браузера.
                      Есть, конечно, вероятность, что ваши надежды оправдаются. Но есть и обратная.
                  –2
                  Очередная бесполезная статья школьника, открывшего для себя Accept-Language.
                    +2
                    Почему бесполезная, если бы я нашел эту статью на момент поиска решения своей задачи, она бы мне помогла. И я не думаю что я один такой, кто открывает для себя Accept-Language. К тому же в статье описан не один из сотен более простых способов определения языка описанных ранее, а с синонимами для разных языков.

                  Only users with full accounts can post comments. Log in, please.