- PVSM.RU - https://www.pvsm.ru -

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

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

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

И так что имеем:

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

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

Решение:
Для определения языка пользователя используем суперглобальный массив $_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 [2]
И главный код языка — расширенный код языка в моем случае это «ru-ru» и «en-us» тут расширенный код языка указывает на регион использования языка у меня это United States.
Временами возникает недопонимание с тем как пометить языки, когда списки кодов ISO содержат как двухбуквенные так и трехбуквенные коды (иногда несколько трехбуквенных кодов). Сейчас все действительные коды перечислены в одном IANA реестре [3], который для языка принимает только одно значение из списков 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 [2] в качестве дефолтного языка я передал английский, и для всег языков что не находятся в массиве $langs будет возвращен en.

Скачать библиотечку можно тут [4]

Автор: La2ha

Источник [5]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/php-2/20379

Ссылки в тексте:

[1] тут: http://ru.wikipedia.org/wiki/%D0%A1%D0%BF%D0%B8%D1%81%D0%BE%D0%BA_%D1%81%D1%82%D1%80%D0%B0%D0%BD_%D0%B8_%D1%82%D0%B5%D1%80%D1%80%D0%B8%D1%82%D0%BE%D1%80%D0%B8%D0%B9,_%D0%B3%D0%B4%D0%B5_%D1%80%D1%83%D1%81%D1%81%D0%BA%D0%B8%D0%B9_%D1%8F%D0%B2%D0%BB%D1%8F%D0%B5%D1%82%D1%81%D1%8F_%D0%BE%D1%84%D0%B8%D1%86%D0%B8%D0%B0%D0%BB%D1%8C%D0%BD%D1%8B%D0%BC_%D1%8F%D0%B7%D1%8B%D0%BA%D0%BE%D0%BC

[2] ISO 639: http://ru.wiktionary.org/wiki/%D0%92%D0%B8%D0%BA%D0%B8%D1%81%D0%BB%D0%BE%D0%B2%D0%B0%D1%80%D1%8C:ISO_639

[3] IANA реестре: http://www.iana.org/assignments/language-subtag-registry

[4] тут: http://la2ha.ru/dev/web/php/codeigniter/libraries_helpers/lang_detect

[5] Источник: http://habrahabr.ru/post/159129/