前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
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.PluralRules 选择复数形式
JavaScript数据处理

做多语言时,「1 file / 2 files」这种单复数怎么写才对?

Intl.PluralRules 不翻译句子,它告诉你一个数字在某种语言里属于哪个复数类别,比如 one、few 或 other。你按这个类别去文案表里取对应的句子。英语 1 是 one,其他是 other;俄语 1、2、5 分别是 one、few 和 many,所以 count === 1 的写法只适用于英语。

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

数字先分类,文案再选择

  1. locale决定有哪些类别,以及每个数字落进哪一类
  2. select()返回 zero、one、two、few、many、other 之一
  3. 文案表以类别为键存每种语言的句子,至少要有 other

类别名不等于数学上的单数复数。俄语的 21 属于 one,阿拉伯语的 0 有自己的 zero 类别。规则是语言语法决定的,只能交给 Intl.PluralRules,不要自己写判断。

核心回答

先记住这个答案

new Intl.PluralRules(locale).select(n) 返回数字 n 在这种语言里的复数类别,一共可能有六种:zero、one、two、few、many、other。每种语言用到其中几种:英语只有 one 和 other,俄语有 one、few、many 和 other,阿拉伯语六种全用,中文只有 other。它不负责翻译,应用要维护一张以类别为键的文案表,拿到类别后去表里取对应的句子。这样换语言时只换文案表,判断逻辑不用改。

  • select() 返回类别名,不返回文案
  • 不同语言类别数量不同,中文只有 other
  • 文案表一定要有 other 兜底
  • type: 'ordinal' 用来生成 1st、2nd、3rd

按类别从文案表里取句子

「你有 N 个文件」这句话,英语要区分 file 和 files,俄语要区分三种词形。用 count === 1 只能覆盖英语。

英语和俄语的类别对比JavaScript
const messages = {
  en: { one: '# file', other: '# files' },
  ru: { one: '# файл', few: '# файла', many: '# файлов', other: '# файла' }
}

function fileCount(locale, count) {
  const category = new Intl.PluralRules(locale).select(count)
  return messages[locale][category].replace('#', count)
}

console.log([1, 2, 5].map((n) => fileCount('en', n)).join(' / '))
console.log([1, 2, 5, 21].map((n) => fileCount('ru', n)).join(' / '))
查看输出与解释
1 file / 2 files / 5 files
1 файл / 2 файла / 5 файлов / 21 файл

英语 1 是 one,2 和 5 都是 other。俄语复杂得多:1 是 one,2 到 4 是 few,5 到 20 是 many,而 21 又回到了 one,因为俄语看的是末位数字。这种规则自己写 if 很容易漏,交给 Intl.PluralRules 就对了。 俄语文案里的 other 是给小数用的,比如 1.5。写文案表时可以用 resolvedOptions().pluralCategories 查一下这个语言到底有哪些类别,逐个补齐。

序数:1st、2nd、3rd 怎么来

排行榜、楼层、第几名这种「第几个」的场景,英语后缀是 st、nd、rd、th,规则也有例外。type: 'ordinal' 就是处理这种情况的。

用 ordinal 生成英语序数后缀JavaScript
const ordinal = new Intl.PluralRules('en-US', { type: 'ordinal' })
const suffix = { one: 'st', two: 'nd', few: 'rd', other: 'th' }

const rank = (n) => n + suffix[ordinal.select(n)]
console.log([1, 2, 3, 4, 11, 12, 13, 21, 22, 23].map(rank).join(' '))
查看输出与解释
1st 2nd 3rd 4th 11th 12th 13th 21st 22nd 23rd

11、12、13 是例外,后缀是 th 而不是 st、nd、rd。手写的话通常要先判断 n % 100 是不是在 11 到 13 之间,再看 n % 10。用 ordinal 模式,这些规则都包含在语言数据里了。 默认的 type: 'cardinal' 用于「多少个」,ordinal 用于「第几个」,同一个数字在两种模式下类别不同:英语里 2 在 cardinal 下是 other,在 ordinal 下是 two。

回答前,多想一步

容易答错的地方

只判断 count === 1
这只对英语这类两分法语言成立。俄语、波兰语、阿拉伯语都有更多类别,小数在很多语言里也有单独的规则。让对应语言的 Intl.PluralRules 决定类别。
文案表里没有 other
other 是所有语言都有的兜底类别。只写了 one 的文案表,碰到 2 就会拿到 undefined,页面上直接显示空白或报错。
把 select() 的结果直接显示给用户
one、few 这些是程序里的键名,不是文案,用户看到「few」毫无意义。它只用来查表。
试着用自己的话回答

面试官还会怎么问?

实际项目里要自己写文案表吗?

一般不用。FormatJS(react-intl)用 ICU MessageFormat,写成 {count, plural, one {# file} other {# files}} 这样的格式;i18next 用 files_one 和 files_other 这样带类别后缀的 key。它们内部都是调 Intl.PluralRules 选分支。理解 Intl.PluralRules 是为了看懂这套机制,排查翻译问题时知道该补哪个类别。

1 和 1.0 的类别一样吗?

直接传数字时一样,select(1.0) 和 select(1) 没有区别,结果都是 one。但如果界面上显示的是「1.0」,英语语法上要用复数「1.0 files」。这时构造时传 minimumFractionDigits: 1,这样 select(1) 会返回 other,和显示格式保持一致。

selectRange() 是做什么的?

它为一个范围选类别,比如「1–3 天」这种区间文案,selectRange(1, 3) 在英语里返回 other。这是比较新的方法,用之前先确认目标浏览器支持。

从一道题,走向一组知识

把知识连起来

数据处理

评论区的「5 分钟前」「昨天」,用 Intl.RelativeTimeFormat 怎么实现?

「3 天前」和「1 天前」的单复数,Intl.RelativeTimeFormat 内部已经按复数规则处理好了,不用再自己调 select()。

数据处理

中文没有空格怎么分词?emoji 的长度为什么不对?Intl.Segmenter 怎么用?

同属国际化文本处理,一个按语言决定词形,一个按语言决定词和句子的边界。

参考资料

  • JavaScript:数据处理参考资料

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

本题目录
  1. 先记住这个答案
  2. 按类别从文案表里取句子
  3. 序数:1st、2nd、3rd 怎么来
  4. 容易答错的地方
  5. 面试官还会怎么问
  6. 把知识连起来
读懂,再试着讲出来

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

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