本站日前在日语之外,新增了对英语(/en/)和简体中文(/zh/)的支持。访问首页时,会读取浏览器的语言设置,向英语设置的访客自动显示英文页面,向中文设置的访客自动显示中文页面。
本文以本站实际运行的代码为例,讲解位于其核心的 navigator.language / navigator.languages 这一浏览器 API。即使是没有服务器端处理的静态站点,也能用几十行 JavaScript 实现。
浏览器持有一份「语言愿望清单」
Chrome、Safari 等浏览器都有一项「希望以何种语言查看内容」的设置(在 Chrome 中为「设置 → 语言」)。它通常沿用操作系统的语言设置,因此许多人可能从未留意过。
在 JavaScript 中,可以通过以下两个属性读取该设置。
navigator.language // 优先级最高的单一语言(例如:"ja")
navigator.languages // 带优先级的语言列表(例如:["ja", "en-US", "en"])正如 MDN 的说明所述,navigator.languages 是一个只读数组,越靠前优先级越高,而 navigator.language 与其首个值一致。就判断而言,能够依次遍历候选项的 navigator.languages 更为便利。
值的格式是「语言标签」—— RFC 5646(BCP 47)
这些属性返回的 "ja"、"en-US" 等字符串,是 RFC 5646(通称 BCP 47)所规定的语言标签。它以连字符分隔,结构如下。
ja—— 仅语言(日语)en-US—— 语言+地区(美国英语)zh-CN—— 语言+地区(中国大陆的中文)zh-Hans—— 语言+文字体系(简体中文)
关键在于,即便是同一种语言,送达的标签也存在多种变体。例如中文,视环境不同可能以 zh、zh-CN、zh-TW、zh-Hans 等各种形式送达。因此下文的代码采取的方针,不是对标签做完全匹配,而是「只看开头的主语言部分」。
另外,同样的信息也会作为 HTTP 请求的 Accept-Language 头部(RFC 9110 §12.5.4)发送给服务器。虽然也有在服务器端判断并重定向的做法,但对于本站这样从 CDN 分发静态文件的站点,由于存在与缓存的兼容性问题,在浏览器端判断才是惯常做法。
实际的代码
本站首页的 <head> 中嵌入了以下脚本。
;(() => {
try {
// ① 若是从站内返回首页的情况,则什么都不做
if (document.referrer && new URL(document.referrer).origin === location.origin) return
const target = (l) => (l === 'en' ? '/en/' : l === 'zh' ? '/zh/' : null)
// ② 若存在通过语言切换明确选定的语言,则最优先
const stored = localStorage.getItem('preferred-lang')
if (stored) {
const t = target(stored)
if (t) location.replace(t)
return
}
// ③ 从上到下依次判断浏览器的优先语言列表
for (const l of navigator.languages || [navigator.language]) {
const s = (l || '').toLowerCase()
if (s.startsWith('ja')) return // 若日语排在前列,则就此显示
const t = target(s.slice(0, 2)) // "en-US" → "en","zh-CN" → "zh"
if (t) { location.replace(t); return }
}
} catch {}
})()与此同时,页脚的语言切换链接中加入了「记住所点击语言」的机关。
document.querySelectorAll('[data-lang-switch]').forEach((a) => {
a.addEventListener('click', () => {
try { localStorage.setItem('preferred-lang', a.dataset.langSwitch) } catch {}
})
})代码要点
将用户的意愿置于自动判断之上(②) —— 「虽然用的是英语浏览器,但想用日语阅读」这样的访客一定存在。我们将通过语言切换链接选定的语言保存到 localStorage,此后每次访问都优先于自动判断。若要引入自动重定向,建议将其与该机制配套使用。
在站内移动时不触发(①) —— 通过 document.referrer 确认「从何处而来」,若是来自本站内部的跳转,则跳过判断。若无此项,英语设置的访客在阅读日文页面时一点击 Logo,就会立刻被送往英文首页。
仅凭主语言判断(③) —— 由于语言标签存在变体,用 slice(0, 2) 仅取出主语言(en 或 zh)进行比较。如此便能将 zh-TW 的访客也引导至中文页面。
使用 location.replace() —— 与向 location.href 赋值不同,重定向的来源不会留在历史记录中,因此不会发生每按一次「后退」就再次被重定向的循环。
用 try...catch 包裹 —— 这样即使在私密浏览等无法使用 localStorage 的环境中,也不会因报错而中止。即便该脚本完全不运行,后果也仅仅是显示日文页面、手动的语言链接仍可使用而已,并无实质危害。
对搜索引擎的顾及
按语言自动重定向若做得过头,有可能导致搜索引擎无法找到各语言的页面。为此,本站同时做了以下三点。
- 将自动重定向仅限于首页(指向下层页面的链接照原样显示)
- 在所有页面输出
hreflang,明示各语言版本之间的对应关系(参见 Google 的文档) - 用
x-default告知面向不匹配任何语言的访客的默认页面
<link rel="alternate" hreflang="ja" href="https://kobayaxi.com/" />
<link rel="alternate" hreflang="en" href="https://kobayaxi.com/en/" />
<link rel="alternate" hreflang="zh-CN" href="https://kobayaxi.com/zh/" />
<link rel="alternate" hreflang="x-default" href="https://kobayaxi.com/" />小结
- 浏览器的语言设置可通过
navigator.languages读取(值为 RFC 5646 的语言标签) - 即便是静态站点,仅凭浏览器端的 JavaScript 也能实现语言的自动分流
- 添加「最优先用户的明确选择」「站内跳转时不触发」「仅以首页为对象」这三道防护,便能形成不带强迫感的自动判断
在本公司的数字支持业务中,我们也承接此类网站多语言支持的咨询。欢迎随时与我们联系。
