Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Getting the user's region with navigator.language

For some time, I've been using something like this to get my user's country (ISO-3166):

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

I've always assumed the string would be similar to en-US -- where the country would hold the 2nd position of the array.

I am thinking this assumption is incorrect. According to MDN docs, navigator.language returns: "string representing the language version as defined in BCP 47." Reading BCP 47, the primary language subtag is guaranteed to be first (e.g., 'en') but the region code is not guaranteed to be the 2nd subtag. There can be subtags that preceed and follow the region subtag.

For example "sr-Latn-RS" is a valid BCP 47 language tag:

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

Is the value returned from navigator.language a subset of BCP 47 containing only language and region? Or is there a library or regex that is commonly used to extract the region subtag from a language tag?

like image 703
Jeff Avatar asked Aug 29 '16 19:08

Jeff


People also ask

How do I find user locale?

To get the user's locale in the browser, access the first element of the languages property on the navigator object, e.g. navigator. languages[0] . The property returns an array of strings that represent the user's preferred languages.

What does navigator language return?

The Navigator. language read-only property returns a string representing the preferred language of the user, usually the language of the browser UI.

How is locale determined?

A locale is a location-based language setting that determines which conversational settings and strings to display. The user specifies their locale using settings on their device. They may change this setting whenever they wish, including during a conversation with a Business Messages agent.


2 Answers

Your solution is based on the false premise that the browser's language tag reliably matches the user's country. E.g., I have set my browser language to German, even though I am living nowhere near Germany at the moment, but rather in the United States.

Also, for example in Chrome, many language packs do not require you to specify the region modifier. Setting Chrome's display language to German

enter image description here

provides the following language tag:

> navigator.language
< "de"

No region tag at all, and a fairly common language.

Bottom line is, my browser setup results in language tag de, even though I live in the United States.


A more accurate and possibly reliable way to determine the user's location would be to derive it from the IP address associated with the request. There are numerous services that offer this service. ip-api.com is one of them:

$.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>
like image 54
TimoStaudinger Avatar answered Sep 18 '22 14:09

TimoStaudinger


Regex found here: 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'
like image 21
Jeff Avatar answered Sep 20 '22 14:09

Jeff