Лучший способ определить локаль пользователя в браузере

203

У меня есть веб-сайт (Flash), локализованный на дюжину языков, и я хочу автоматически определить значение по умолчанию в зависимости от настроек браузера пользователя, чтобы минимизировать количество шагов для доступа к контенту.

К вашему сведению, я не могу использовать серверные сценарии из-за ограничений прокси, поэтому я думаю, что для решения проблемы подойдет JavaScript или ActionScript.

Вопросы:

  1. Какой лучший способ «угадать» локаль пользователя?

  2. Существуют ли какие-либо простые классы / функции, которые могут мне помочь (без сложных пакетов локализации)? Специально, чтобы разбить все возможные языки на меньшее количество (у меня есть переводы) умным способом.

  3. До какой степени я могу доверять такому решению?

  4. Любые другие обходные пути или предложения?

Theo.T
источник
Единственный способ, которым браузер может обмениваться метаданными о своем пользователе, а запрос - через URL и заголовки. Возьмите Firefox и взгляните на заголовки, которые отправляются при запросе. Довольно интересно изучить типичные заголовки запросов и ответов.
мП

Ответы:

188

Надлежащим способом является поиск HTTP - заголовка Accept-Language, отправленного на сервер. Он содержит упорядоченный, взвешенный список языков, которые пользователь настроил для своего браузера.

К сожалению, этот заголовок недоступен для чтения внутри JavaScript; все, что вы получите, это то navigator.language, что говорит вам, какая локализованная версия веб-браузера была установлена. Это не обязательно то же самое, что предпочтительный язык (и) пользователя. В IE вместо этого вы получаете systemLanguage(язык установленной ОС), browserLanguage(так же, как language) иuserLanguage (настраиваемый пользователем регион ОС), которые все также бесполезны.

Если бы мне пришлось выбирать между этими свойствами, я бы userLanguageсначала понюхал , а затем вернулся к ( languageи только после этого (если они не соответствуют ни одному из доступных языков) взгляду browserLanguageи, наконец,systemLanguage .

Если вы можете поместить серверный скрипт где-нибудь еще в сети, который просто читает заголовок Accept-Language и выплевывает его обратно в виде файла JavaScript со значением заголовка в строке, например:

var acceptLanguage= 'en-gb,en;q=0.7,de;q=0.3';

тогда вы можете включить <script src>, указывающий на эту внешнюю службу в HTML, и использовать JavaScript для анализа заголовка языка. Однако я не знаю ни одного существующего библиотечного кода для этого, поскольку синтаксический анализ Accept-Language почти всегда выполняется на стороне сервера.

Что бы вы ни делали в конечном итоге, вам, безусловно, нужно переопределить пользователя, потому что для некоторых людей оно всегда будет неверным. Часто проще всего вставить языковые настройки в URL (например, http: //www.example.com/en/site против http: //www.example.com/de/site) и позволить пользователю нажать связи между двумя. Иногда вам требуется один URL-адрес для обеих языковых версий, и в этом случае вам необходимо сохранить настройки в файлах cookie, но это может сбить с толку пользовательских агентов без поддержки файлов cookie и поисковых систем.

bobince
источник
11
От MDN developer.mozilla.org/en-US/docs/Web/API/NavigatorLanguage/… "HTTP-заголовок Accept-Language в каждом HTTP-запросе от браузера пользователя использует одно и то же значение для свойства navigator.languages, за исключением дополнительного Поле qvalues ​​(значения качества) (например, en-US; q = 0,8). "
S Meaden
3
Обновление: теперь (2020) существует экспериментальная функция, поддерживаемая всеми современными браузерами, которая возвращает массив языковых предпочтений: navigator.languages //["en-US", "zh-CN", "ja-JP"]это должно работать как минимум на 95% браузеров в 2020 году.
Корнелиус Ремер
1
navigator.languagesСогласно MDN и caniuse.com , теперь доступен во всех основных браузерах - попробуйте небольшой (~ 200 байт) пакет языков навигатора для самого современного и обратно совместимого подхода.
mindplay.dk
84

В Chrome и Firefox 32+ файл navigator.languages ​​содержит массив локалей в порядке предпочтения пользователя и является более точным, чем navigator.language, однако, чтобы сделать его обратно совместимым (Tested Chrome / IE / Firefox / Safari), затем используйте этот:

function getLang()
{
 if (navigator.languages != undefined) 
 return navigator.languages[0]; 
 else 
 return navigator.language;
}
Фиах Рейд
источник
2
Это не работает для IE9 и IE10. Для них вы должны изучить navigator.browserLanguage вместо этого.
user393274
2
OneLiner:function getLang(){ return ( navigator.language || navigator.languages[0] ); }
ХорхеГарза
4
@ChStark Разве это не должно быть return navigator.languages[0] || navigator.language;?
James_
23
Кроме того , правильный «один вкладыш» будет выглядеть следующим образом : return (navigator.languages && navigator.languages.length) ? navigator.languages[0] : navigator.language;. В противном случае вы получите исключение, если navigator.languagesоно не определено или пусто.
Макс Трукса
Для еще более компактной версииconst getLang = () => navigator.language || navigator.browserLanguage || ( navigator.languages || [ "en" ] ) [ 0 ]
Жоао Мигель Брандао
41

В этой статье предлагаются следующие свойства объекта навигатора браузера :

  • navigator.language (Netscape - Локализация браузера)
  • navigator.browserLanguage (специфичный для IE - локализованный язык браузера)
  • navigator.systemLanguage (Специфично для IE - ОС Windows - локализованный язык)
  • navigator.userLanguage

Сверните их в функцию JavaScript, и в большинстве случаев вы сможете угадать правильный язык. Убедитесь, что он корректно ухудшился, поэтому используйте div, содержащий ваши ссылки для выбора языка, чтобы, если нет javascript или метод не работал, пользователь все равно мог принять решение. Если это работает, просто скройте div.

Единственная проблема с этим на стороне клиента состоит в том, что либо вы обслуживаете все языки для клиента, либо вам нужно подождать, пока сценарий запустится, и определить язык, прежде чем запрашивать правильную версию. Возможно, использование самой популярной языковой версии по умолчанию будет раздражать наименьшее количество людей.

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

Фил Х
источник
Спасибо Фил за ваши предложения. Что касается статьи, ей 6 лет ... не уверен, что мы можем на нее положиться?
Theo.T
1
Я подозреваю, что все современные браузеры поддерживают navigator.language. Мой браузер (FF3 в Ubuntu 8.10) сообщает «en-GB». Если кто-то все еще использует IE6 - около 20% людей - тогда стоит это учитывать. IE6 появился 8 лет назад.
Фил Х
IE 8 не поддерживает navigator.language. Возможно IE 9 будет?
spig
36

Объединяя несколько способов, которые браузеры используют для хранения языка пользователя, вы получаете эту функцию:

const getNavigatorLanguage = () => {
  if (navigator.languages && navigator.languages.length) {
    return navigator.languages[0];
  } else {
    return navigator.userLanguage || navigator.language || navigator.browserLanguage || 'en';
  }
}

Сначала мы проверяем navigator.languagesмассив на его первый элемент.
Тогда мы получим или navigator.userLanguageили navigator.language.
Если это не удается, мы получаем navigator.browserLanguage.
Наконец, мы устанавливаем это, 'en'если все остальное потерпело неудачу.


А вот и сексуальный однострочник:

const getNavigatorLanguage = () => (navigator.languages && navigator.languages.length) ? navigator.languages[0] : navigator.userLanguage || navigator.language || navigator.browserLanguage || 'en';
Zenoo
источник
там, видимо, тоже что-то есть navigator.userLanguage(для IE). Вы можете добавить это, чтобы завершить его :)
Pors
Почему не просто navigator.languages[0] || navigator.userLanguage || navigator.language || navigator.browserLanguage || 'en' ?
Emerica
@Curse Потому что navigator.languages может бытьundefined
Zenoo
@ Zenoo Знаете ли вы, в каких браузерах?
Emerica
(navigator.languages || [])[0] || navigator.userLanguage || navigator.language || navigator.browserLanguage || 'en'
Стив Беннет
10

Существует разница между предпочитаемыми языками пользователя и языком системы / браузера.

Пользователь может настроить предпочтительные языки в браузере, и они будут использоваться navigator.language(s)и использоваться при запросе ресурсов с сервера, чтобы запрашивать контент в соответствии со списком языковых приоритетов.

Тем не менее, локаль браузера решит, как отобразить число, дату, время и валюту. Этот параметр, вероятно, является языком с самым высоким рейтингом, но нет никаких гарантий. В Mac и Linux локаль определяется системой независимо от языковых предпочтений пользователя. На Windows можно выбрать среди языков в списке предпочтений на Chrome.

Используя Intl ( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl ), разработчики могут переопределить / настроить язык, используемый для визуализации этих вещей, но есть элементы, которые не может быть переопределено, например <input type="date">формат.

Чтобы правильно извлечь этот язык, я нашел только один способ:

(new Intl.NumberFormat()).resolvedOptions().locale

( Intl.NumberFormat().resolvedOptions().localeтоже похоже на работу)

Это создаст новый экземпляр NumberFormat для локали по умолчанию, а затем снова прочитает локаль этих разрешенных опций.

Патрик Куллман - Неовичи
источник
5

Я провел небольшое исследование по этому вопросу, и я суммировал свои выводы в таблице ниже.

введите описание изображения здесь

Поэтому рекомендуемое решение - написать сценарий на стороне сервера, чтобы проанализировать Accept-Languageзаголовок и передать его клиенту для настройки языка сайта. Странно, что сервер понадобился бы для определения языковых предпочтений клиента, но сейчас это так. Существуют и другие способы для обнаружения языка, но чтение Accept-Languageзаголовка является рекомендуемым решением, насколько я понимаю.

Абхишек В.
источник
2

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

HTML5:

document.querySelector('html').getAttribute('lang')

Legacy:

document.querySelector('meta[http-equiv=content-language]').getAttribute('content')

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

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

jgmjgm
источник
1

Я использовал все ответы и создал однострочное решение:

const getLanguage = () => navigator.userLanguage || (navigator.languages && navigator.languages.length && navigator.languages[0]) || navigator.language || navigator.browserLanguage || navigator.systemLanguage || 'en';

console.log(getLanguage());
Кайо Сантос
источник