使用navigator.language获取用户的地区

22

最近我一直在使用以下代码来获取用户所在的国家(ISO-3166):

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

我一直以为字符串会类似于en-US——国家代码会在数组的第二个位置

我认为这种假设是不正确的根据MDN文档navigator.language返回: "表示BCP 47中定义的语言版本的字符串。" 阅读BCP 47,主要语言子标记保证是第一个(例如,'en'),但区域代码不保证是第二个子标记。可能存在在区域子标记之前和之后的子标记。

例如"sr-Latn-RS"是有效的BCP 47语言标记:

sr                |  Latn           |  RS
primary language  |  script subtag  |  region subtag
< p > navigator.language 返回的值是否是仅包含语言和区域的 BCP 47 子集?或者是否有常用的库或正则表达式可以从语言标记中提取区域子标记?

7个回答

11

您的解决方案基于错误的前提,即浏览器语言标签可靠地匹配用户的国家/地区。例如,我将我的浏览器语言设置为德语,尽管我现在并不住在德国,而是在美国。

此外,例如在Chrome中,许多语言包不需要您指定区域修饰符。将Chrome的显示语言设置为德语

enter image description here

将提供以下语言标签:

> navigator.language
< "de"

没有地区标签,使用的是一种非常常见的语言。

总之,我的浏览器设置导致语言标签为de,尽管我住在美国。


更准确和可靠的确定用户位置的方法可能是根据请求相关的IP地址来推导。有许多提供此服务的服务机构。ip-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>


有趣。对于我的应用程序,100%的支持不是发货的先决条件。我所需要做的就是猜测用户所在的地区。但是在您的示例中,我认为您可能会混淆语言和地区。“de”是语言子标记而不是地区子标记。语言到地区不是一对一的关系。例如,“de-AT”表示德语(“de”)在奥地利(“AT”)使用。也许最好的方法是使用一些API的组合:Geolocation、navigator.languages和一些rest终端点。感谢您的回答,但我认为这并没有完全回答我的问题。 - Jeff

5

现在你可以使用国际化API中的Locale对象从语言环境标识符中提取区域信息。

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

请注意,目前这不兼容Internet Explorer浏览器。


2

这里找到了正则表达式: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'

为什么要使用正则表达式,当你可以像下面这样使用split函数:const parts = navigator.language.split('-'); const region = parts[parts.length-1] - Sufian Saory
切割后,区域不保证在数组的第二个位置。请参考上面的示例。 - Jeff
没错。但是上面的代码总是取最后一个,而不管它的实际位置。是吧? - Sufian Saory
不一定是最后一个位置。其他子标签可能会在地区之后出现。 - Jeff

2

如果 (Langage != Language) 否则如果 (Langages != Languages);) - Shaze

1
在Firefox中,您可以在偏好设置中选择语言设置:

enter image description here

语言列表共有269项,其中192项不包含任何地区代码。
当语言根据地点有不同的变体时,地区才有用。这样用户就可以告诉服务器他们更喜欢哪种语言变体的响应。
不要使用此方法来定位用户。这太不可靠了,因为用户可能没有指定任何地区,或者因为用户可能身处另一个地方。
如果您想定位用户,应该使用Geolocation API

请看我在TimoSta的回答中的评论:语言!=地区,且不是一对一的关系。我感兴趣的是获取用户的地区,而不是语言。 - Jeff
@Jeff 是的。使用 navigator.language 可以获取用户首选语言变体所使用的区域,而不是用户所在的区域。你可以通过使用地理位置 API 获取用户所在的区域。 - Oriol
我需要用户的许可才能获取位置,对吧?听起来一个全面的解决方案可能涉及使用许多这些和/或允许用户选择区域。但我只是想知道是否有一种不错的方法来解析BCP 47语言标签以提取区域(如果提供了区域)。我认为这是一个相当普遍的需求。 - Jeff

0

0

您收到的值源自HTTP请求的Accept-Language头。

头部的值可能非常复杂,例如

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

正如其名称所示,Accept-Language标头基本上定义了可接受的语言,而不是国家。

语言标签可能还包含其他位置信息,例如'en-GB',但其他标签如'en'则没有。

如果没有,则没有关于国家的信息。

有时候也不可能将语言(如'en')精确地映射到一个国家。 如果语言是'en',国家可能是'GB',但也可能是'US'。

你可以这样做:

  • 仅在语言中包含一个国家时确定该国家,例如'en-GB'
  • 如果语言不包含国家,则有以下选项:
  • 一些语言仅在一个国家使用,例如'da',丹麦语只在丹麦使用(我猜测),因此您可以映射这些情况。
  • 您可以根据语言使用默认值来处理其他情况,例如将'en'映射到'GB'
  • 对于所有无法确定国家的情况,您可以使用通用默认值,例如'US'
  • 您可以使用其他信息,例如客户端IP地址来确定国家
  • 最后,您可以要求用户输入国家

我收集了一些关于Accept-Language头的额外信息在这里


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接