前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
首页程序员面试题库Intl supportedLocalesOf 地区回退检测
JavaScript数据处理

用户的语言浏览器不支持时,Intl 会怎么回退?应用要怎么兜底?

Intl.DateTimeFormat.supportedLocalesOf(['xx-YY', 'fr-CA']) 返回运行时真正支持的那部分语言。直接 new 一个 formatter 时,引擎会按传入顺序协商:de-XX 不支持就退到 de,全都不支持就用运行环境的默认语言,不报错。所以最终用了哪种语言要看 resolvedOptions().locale,而且浏览器能格式化,不代表你的应用有这个语言的翻译。

前端进阶之旅 · 一题精讲更新于 2026.09.29
JavaScript#数据处理
先看核心答案读代码示例
理解线索

运行时能力和应用翻译是两层

  1. 请求列表按优先级排好的语言,比如 navigator.languages
  2. 运行时协商Intl 从列表里找它有数据的语言,找不到就逐级去掉子标签
  3. 应用回退文案翻译、日期格式策略由应用自己决定兜底语言

Intl 的默认回退是运行环境的语言,服务端 Node 和用户浏览器可能不一样。应用最好在入口统一决定一个语言,同时交给 Intl 和翻译文案,别让两边各自回退。

核心回答

先记住这个答案

Intl 系列的构造器,比如 Intl.DateTimeFormat 和 Intl.NumberFormat,都会先做语言协商:按传入的语言列表依次查找运行时有数据的语言,de-XX 找不到就去掉地区退到 de;整个列表都找不到,就用运行环境的默认语言。这个过程不报错,所以最终用的语言要用 resolvedOptions().locale 查看。supportedLocalesOf(list) 则是单独做检测,返回列表里运行时支持的那部分。应用自己的翻译文案不会跟着 Intl 回退,这一层要应用自己兜底。

  • supportedLocalesOf 返回支持的子集,不支持的被过滤掉
  • 构造器会从 de-XX 退到 de,再退到默认语言
  • 最终用的语言以 resolvedOptions().locale 为准
  • 格式非法的标签会直接抛 RangeError

检测支持和查看最终结果

先看运行时对几个语言标签的反应。

supportedLocalesOf 和 resolvedOptionsJavaScript
// xx-YY 是不存在的语言,会被过滤掉;标签大小写会被规范化
console.log(Intl.DateTimeFormat.supportedLocalesOf(['xx-YY', 'zh-hans-cn', 'fr-CA']).join(' '))

// 构造器按顺序协商,第一个不支持就用第二个
console.log(new Intl.DateTimeFormat(['xx-YY', 'fr-CA']).resolvedOptions().locale)
// 地区不认识就去掉地区
console.log(new Intl.DateTimeFormat('de-XX').resolvedOptions().locale)

try {
  new Intl.NumberFormat('en_US') // 下划线不是合法的 BCP 47 写法
} catch (err) {
  console.log(err.name)
}
查看输出与解释
zh-Hans-CN fr-CA
fr-CA
de
RangeError

supportedLocalesOf 返回的是请求列表的子集,并且把 zh-hans-cn 规范成了 zh-Hans-CN。语言标签的格式叫 BCP 47,由语言、可选的书写系统(如 Hans 简体)、可选的地区组成,中间用连字符连接。 后两行说明构造器的回退:列表里第一个不支持就用下一个,地区不认识就只保留语言。合法但不支持的标签会被静默处理,格式不合法的标签,比如用下划线的 en_US,才会抛 RangeError。 localeMatcher 选项控制匹配算法:lookup 是规范定义的逐级去子标签,结果可预测;默认的 best fit 允许引擎用自己认为更合适的匹配。需要跨浏览器结果一致时用 lookup。

应用自己的语言回退

浏览器支持法语格式化,不代表你的应用有法语翻译。页面上日期是法语,按钮是英文,这种半吊子体验很常见。所以应用要在入口按「自己有的翻译」选定一个语言,再把它同时交给 Intl 和翻译文案。

按应用支持的语言协商JavaScript
const APP_LOCALES = ['zh-CN', 'en-US', 'ja-JP'] // 我们有翻译文案的语言

function pickLocale(requested, fallback = 'en-US') {
  for (const tag of requested) {
    const want = new Intl.Locale(tag).maximize() // 补全书写系统和地区
    const hit = APP_LOCALES.find((l) => {
      const have = new Intl.Locale(l).maximize()
      return have.language === want.language && have.script === want.script
    })
    if (hit) return hit
  }
  return fallback
}

console.log(pickLocale(['fr-FR', 'en-GB'])) // 没有法语,英式英文退到 en-US
console.log(pickLocale(['ja'])) // ja 补全后匹配 ja-JP
console.log(pickLocale(['zh-SG'])) // 新加坡用简体中文
console.log(pickLocale(['zh-TW'])) // 繁体不能退到简体
查看输出与解释
en-US
ja-JP
zh-CN
en-US

Intl.Locale 的 maximize() 会补全省略的部分:zh-TW 补全成 zh-Hant-TW,而 zh-SG 补全成 zh-Hans-SG。比较语言加书写系统,就能让新加坡用户用上简体中文,同时避免把台湾用户退到简体。直接按「语言前缀相同」匹配,zh-TW 就会错误地得到简体。 请求列表在浏览器里一般来自 navigator.languages,是用户在浏览器设置里排好序的语言。选好之后存进 cookie 或用户设置,服务端渲染和客户端都用这一个值。

回答前,多想一步

容易答错的地方

以为不支持的语言会让构造器报错
合法但不支持的标签只会触发回退,最后用运行环境的默认语言,不报错。只有格式不合法的标签才抛 RangeError。所以「没报错」不代表用上了你想要的语言。
只检查 Intl,不检查翻译文案
Intl 只负责日期、数字、排序这些格式。按钮文字、错误提示是应用自己的翻译资源,Intl 支持某个语言,不代表这些资源存在。语言选择要同时看两边。
试着用自己的话回答

面试官还会怎么问?

每次格式化前都要调 supportedLocalesOf 吗?

不需要。构造器自己会协商,supportedLocalesOf 主要用于能力检测和测试,比如启动时确认服务器的 Node 带了完整的语言数据。正常做法是入口选定语言,建好 formatter 缓存起来复用。

为什么服务端渲染的日期格式和浏览器不一样?

Node 如果是精简 ICU 构建,可能只带英文数据,其他语言都回退到英文,而浏览器支持完整的语言数据。官方发布的 Node 从 13 版开始默认带完整 ICU,但一些精简的 Docker 镜像或自己编译的版本不一定有。部署时用 supportedLocalesOf 检查一下就知道。

用户传来的语言标签怎么处理才安全?

先用 Intl.getCanonicalLocales() 规范化,它对非法标签会抛 RangeError,可以 try/catch 后丢弃。然后只在应用支持的语言列表里匹配,不要把用户输入原样拼进文件路径或者用来加载翻译文件。

从一道题,走向一组知识

把知识连起来

数据处理

JSON.stringify 碰到 BigInt 直接报错,怎么序列化才不丢精度?

两题都在讲「不报错不代表没问题」:Intl 悄悄回退语言,Number(bigint) 悄悄丢精度。

数据处理

JSON.stringify 遇到循环引用会报什么错?除了手动删属性还有什么办法?

语言协商和循环引用都是边界处理问题,一个处理用户环境,一个处理对象结构,都要提前想好兜底方案。

参考资料

  • JavaScript:数据处理参考资料

示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。

本题目录
  1. 先记住这个答案
  2. 检测支持和查看最终结果
  3. 应用自己的语言回退
  4. 容易答错的地方
  5. 面试官还会怎么问
  6. 把知识连起来
读懂,再试着讲出来

先看核心答案,再读代码。最后展开追问,检查自己有没有遗漏边界。

试着回答追问
浏览全部面试题理解原理,也关注真实的使用场景。回到顶部 ↑