如何获取浏览器的语言
前端开发过程中,有时我们需要获取用户浏览器所用的语言,进而向用户显示不同的内容。如何获取用户浏览器语言呢?
开始
最直接的,就是访问浏览器内置的 navigator.language
属性:
var lang = navigator.language
根据你的浏览器的设置,这段代码会返回不同的值,比如 zh-CN
、en-US
、zh-TW
、zh
之类,如下图:
这个值表示当前浏览器的首选语言,也即当浏览器发起 HTTP 请求时,HTTP 请求头中的 Accept-Language
字段的内容中排在最前面的语言。
浏览器还支持一个 navigator.languages
属性,返回一个数组,内容为当前浏览器首选的多个语言,前后顺序表示优先级顺序。
HTTP 请求头中的 Accept-Language
的值形如这样:
accept-language: zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7,zh-TW;q=0.6,ja;q=0.5
值以逗号 ,
分隔为几组,其中 q=0.9
这样的值表示优先级顺序。例如 en-US;q=0.8
是一组,表示 en-US
是浏览器可接受的语言之一,优先级是 0.8。如果省略 q
值则表示优先级为 1(最高)。
一般来说,获取到 navigator.language
即可以了,这个值即表示用户浏览器首选的语言,通常来说,这个语言也是浏览器界面所使用的语言。
但是……
浏览器首选语言 ≠ 浏览器界面语言
以 Chrome 为例,我们完全可以让 Chrome 的界面显示为英语(en-US),却让浏览器请求的首选语言为中文(zh-CN),也可以反过来,让界面显示为中文,请求的首选语言为英语。
这有什么差别呢?
界面(UI)语言是浏览器显示自身时使用的语言,比如浏览器菜单等处的文案所显示的语言,首选语言则是浏览器发起 HTTP 请求时所标注的语言,即 HTTP 头中 Accept-Language
字段中包含的语言。
上面的 navigator.language
、navigator.languages
属性,都只能获得浏览器的首选语言,而无法获得浏览器的界面语言。
那么……
我们可以通过 JavaScript 方法获取用户浏览器的界面语言吗?
通常来说,不可以。浏览器没有为 JavaScript 提供任何接口来访问浏览器的界面语言。
但是,实际操作中,我们居然真的能找到一些方法,有限制地判断当前浏览器的界面语言。
注:以下内容基于 Chrome 74。
首先,我们来看一个普通的标签:<input>
。
W3C 网站上,对 <input>
标签的各种属性有详细的定义,其中比较有趣的是对 type
属性为 submit
的 <input>
标签的说明:
When an input element's type attribute is in the Submit Button state, the rules in this section apply.The input element represents a button that, when activated, submits the form. If the element has a value attribute, the button's label must be the value of that attribute; otherwise, it must be an implementation-defined string that means "Submit" or some such. The element is a button, specifically a submit button.
注意其中我用粗体标出的部分,这一句的意思是:“如果这个 <input>
没有给定 value
属性,那么应当显示一个由实现方定义的含义为「提交」的值。”也就是说,submit
类型的 <input>
会有一个默认值,且这个值由实现方(比如浏览器)自行定义。
接下来,我们编写一个最简单的 HTML 代码片断进行试验:
<input type="submit">
简单测试即可发现,上面这个 <input>
按钮的确会显示一个默认的文本,根据浏览器界面语言的不同,值可能是“提交”、“Submit”之类。
可以看到,<input type="submit">
按钮上显示的文本语言,和浏览器界面的语言一致,即 W3C 标准中提到的“implementation-defined string”。(注:上面的测试在 Chrome 74 中进行,不同的浏览器中这个文本可能不同。)
类似的 <input type="file">
也会根据浏览器界面语言的不同显示不同的文本。
那么,是不是可以直接读取这个值,然后判断语言呢?
遗憾的是不能。如果直接用 JavaScript 去访问这个节点的 value
属性,只能得到一个空字符串,尽管它实际上显示了值。
<input type="submit" id="ipt">
console.log(document.getElementById('ipt').value); // 返回空字符串
看起来似乎山穷水尽了,直到偶然间读到这篇帖子,里面提到一个很有创意的点子:中文的“提交”和英文的“Submit”长度不一样,我们可以通过比较这个 <input>
按钮长度的方式来判断当前浏览器的界面语言。
一个 demo 见下图:
上图中的三个按钮的 HTML 如下:
<input type="submit">
<input type="submit" value="Submit">
<input type="submit" value="提交">
即第一个不设置 value
,这样它就会使用浏览器界面语言中定义的值,然后再添加两个值分别为英语和中文的 <input>
,看哪个的长度和第一个 <input>
的值相同,即可判断出当前浏览器的界面语言是英语还是中文。
完整 demo 参见这个链接:https://codepen.io/oldj/pen/ydyXqR 。
这样,通过判断 <input type="submit">
元素宽度的方式,我们便有了一个判断浏览器界面语言的方法。不过这个方法并不完美,只能做出一些有限制的判断,比如如果已知浏览器界面语言是英文或中文,则可以做出判断,但如果界面语言完全未知,判断起来就麻烦了。
另外,有一些语言不同但字符长度一样,甚至可能字符内容也一样,比如简体中文 zh-CN
与繁体中文 zh-TW
中的“提交”文字完全一样,西班牙语与葡萄牙语中表示提交的文字都是“Enviar”,这些情况下这个方法就无法区分了。
进一步……
上面提到,我们无法通过 document.getElementById('ipt').value
之类的方法来获取 <input type="submit">
按钮显示的文本值(即“implementation-defined string”),直接访问返回的会是一个空字符串。那还有其他方法吗?
测试了一下,发现还真的有,在提交 Form 表单的时候,这个“implementation-defined string”是会一起提交到服务器端的。
建一个 HTML 页面,内容中包含以下代码:
<form method="get">
<input type="submit" name="sub">
</form>
运行,点击那个提交按钮,表单会以 GET 的方式提交到当前地址:
可以看到,提交按钮的值被当作参数传了过来。
提交表单需要页面刷新,有办法直接在当前页面通过 JavaScript 拿到这个值吗?试验了一下,只需要将这个表单提交到当前页面上一个不可见的 <iframe>
即可。代码如下:
<form id="my_form" action="about:blank" target="ifr" method="get">
<input type="submit" name="sub">
</form>
<iframe name="ifr" width="1" height="1" style="display: none;"></iframe>
<div id="result">
The implementation-defined string of input[type=submit] is: <span></span>
</div>
<script>
function check () {
$('#my_form input[type=submit]').click()
setTimeout(() => {
let h = window.frames.ifr.window.location.href
//console.log(h)
let m = h.match(/sub=([^&]+)/)
$('#result span').html(m ? decodeURIComponent(m[1]) : 'unknow')
}, 100)
}
check()
</script>
运行,即可在当前页面获得提交按钮的“implementation-defined string”,如下图所示:
完整 demo 可见:https://codepen.io/oldj/pen/NZPeMo 。
这样,我们就得到了 <input type="submit">
的“implementation-defined string”,从而可以根据其值判断当前浏览器的界面语言(而不是首选语言)。当然,这个方法也有缺点,如果两种语言里表示“提交”的字符串相同(比如简体中文和繁体中文,葡萄牙语和西班牙语),那么就无法进行区分。
另外,以上测试都是在 Chrome 74 下进行的,其他浏览器中“implementation-defined string”有可能不同,比如 Windows 10 中文环境下的 IE 11、Edge 中的值不是“提交”,而是“提交查询内容”,Firefox 下则为“提交查询”。不过也正是因为多了一些字,倒是可以区分出简体中文与繁体中文了。
小结
- 可以通过
navigator.language
或navigator.languages
属性访问浏览器的首选语言,首选语言即浏览器发送 HTTP 请求时请求头中Accept-Language
的语言; - 浏览器首选语言与浏览器界面语言可以不同,
<input type="submit">
等没有指定value
属性的标签时会自动显示一个由浏览器实现自定义的值(“implementation-defined string”),这个值的语言通常与浏览器的界面语言一致; - 这个“implementation-defined string”无法通过 JavaScript 直接访问,但可以通过读取
<input type="submit">
元素宽度来判断,或者通过提交表单的形式获取。
评论:
秒啊!