Получение региона пользователя с помощью navigator.language

Некоторое время я использовал что-то вроде этого, чтобы получить страну моего пользователя (ISO-3166):

const region = navigator.language.split('-')[1]; // 'US'

Я всегда предполагал, что строка будет похожа на en-US - где страна будет занимать 2-ю позицию массива.

Я думаю, что это предположение неверно. Согласно документам MDN, navigator.language возвращает: "строка, представляющая языковую версию, как определено в BCP 47". При чтении BCP 47 первичный языковой подтэг гарантированно будет первым (например, 'en'), но код региона не обязательно будет вторым подтэгом. Могут быть подтэги, которые предшествуют и следуют за подтэгом региона.

Например "sr-Latn-RS" является действительным языковым тегом BCP 47:

sr                |  Latn           |  RS
primary language  |  script subtag  |  region subtag

Возвращено ли значение из navigator.language подмножество BCP 47, содержащее только язык и регион? Или есть библиотека или регулярное выражение, которые обычно используются для извлечения subtag региона из языкового тега?

7 ответов

Решение

Regex находится здесь: https://github.com/gagle/node-bcp47/blob/master/lib/index.js

var re = /^(?:(en-GB-oed|i-ami|i-bnn|i-default|i-enochian|i-hak|i-klingon|i-lux|i-mingo|i-navajo|i-pwn|i-tao|i-tay|i-tsu|sgn-BE-FR|sgn-BE-NL|sgn-CH-DE)|(art-lojban|cel-gaulish|no-bok|no-nyn|zh-guoyu|zh-hakka|zh-min|zh-min-nan|zh-xiang))$|^((?:[a-z]{2,3}(?:(?:-[a-z]{3}){1,3})?)|[a-z]{4}|[a-z]{5,8})(?:-([a-z]{4}))?(?:-([a-z]{2}|\d{3}))?((?:-(?:[\da-z]{5,8}|\d[\da-z]{3}))*)?((?:-[\da-wy-z](?:-[\da-z]{2,8})+)*)?(-x(?:-[\da-z]{1,8})+)?$|^(x(?:-[\da-z]{1,8})+)$/i;

let foo = re.exec('de-AT');      // German in Austria
let bar = re.exec('zh-Hans-CN'); // Simplified Chinese using Simplified script in mainland China

console.log(`region ${foo[5]}`); // 'region AT'
console.log(`region ${bar[5]}`); // 'region CN'

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

Также, например, в Chrome, во многих языковых пакетах не требуется указывать модификатор региона. Настройка языка отображения Chrome на немецкий

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

предоставляет следующий языковой тег:

> navigator.language
< "de"

Нет тега региона, и довольно распространенный язык.

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


Более точным и, возможно, надежным способом определения местоположения пользователя будет получение его из IP-адреса, связанного с запросом. Есть множество услуг, которые предлагают эту услугу. http://id-api.com/ является одним из них:

$.get("http://ip-api.com/json", function(response) {
  console.log(response.country);     // "United States"
  console.log(response.countryCode); // "US"
}, "jsonp");
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

Теперь вы можете извлечь регион из идентификатора локали, используя объект Locale в API интернационализации.

      const { region } = new Intl.Locale('sr-Latn-RS') // region => 'RS'

Обратите внимание, что в настоящее время это несовместимо с Internet Explorer.

Будьте осторожны у вас есть navigator.language а также navigator.languages,

язык:

 console.log(navigator.language); // "fr"

Языки:

 console.log(navigator.languages); // ["fr", "fr-FR", "en-US", "en"]

Чтобы найти страны, смотрите Википедию по ISO 3166-1 или используйте javascript lib:

В Firefox вы можете выбрать настройки языка в настройках:

Список языков насчитывает 269 пунктов, 192 из которых не содержат код региона.

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

Не используйте этот подход, чтобы найти пользователя. Это слишком ненадежно, потому что пользователь не может указать какой-либо регион, или потому что пользователь мог физически находиться в другом месте.

Если вы хотите найти пользователя, вам следует использовать Geolocation API.

Как сказал @TimoSta,

Попробуй это

$.getJSON('http://freegeoip.net/json/', function(result) {
   alert(result.country_code);
});

Получите от посетителей язык и код страны с помощью JavaScript (на стороне клиента). Смотрите ответ @noducks

Значение, которое вы получаете, связано с заголовком Accept-Language HTTP-запроса.

Значения заголовка могут быть довольно сложными, как

Accept-Language: da, en-GB;q=0.8, en;q=0.7

Как видно из названия, заголовок Accept-Language в основном определяет приемлемые языки, а не страны.

Языковой тег может также содержать дополнительную информацию о местоположении, как в "en-GB", но другие, например "en", этого не делают.

В случае, если нет, просто нет информации о стране.

Также не всегда возможно точно сопоставить язык, например "en", со страной. Если язык "en", страна может быть "GB", но также "US".

Что ты можешь сделать;

  • Определите только страну, если она есть в языке, как в "en-GB"
  • Если язык не содержит страну, у вас есть следующие варианты:
  • Несколько языков используются только в одной стране, например, "да", датский, на котором говорят только в Дании (я думаю, здесь), поэтому вы можете сопоставить эти случаи.
  • Вы можете использовать значение по умолчанию для других случаев, в зависимости от языка, например, сопоставить "en" с "GB"
  • Вы можете использовать общее значение по умолчанию, например, "США", во всех случаях невозможно определить страну.
  • Вы можете использовать дополнительную информацию, например, IP-адрес клиента, чтобы определить страну
  • Наконец, вы можете попросить пользователя ввести страну

Я собрал некоторую дополнительную информацию о заголовке Accept-Language здесь

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