国际化字符串操作需用标准api:intl.segmenter按字素切分、intl.datetimeformat/numberformat格式化、localecompare排序、icu messageformat处理占位符,避免基础方法误拆unicode字符。

JS 字符串操作在国际化场景下容易出错,核心问题在于基础方法(如 .length、.charAt()、.substring()、正则匹配)不理解 Unicode 字素(grapheme cluster),把一个用户看到的“字”错误拆成多个码元。比如 ???? 或带音标的 é、ç、한글复合音节,都会被误处理。解决方案不是回避,而是用标准 API 替代。
用 Intl.Segmenter 正确切分“人眼可见字符”
用户感知的“一个字符”可能是多个 UTF-16 码元,.split('') 或循环遍历会破坏视觉完整性。必须使用语言感知的切分方式:
- 创建 Segmenter 实例时指定 locale 和粒度:
new Intl.Segmenter('zh', { granularity: 'grapheme' }) - 对 emoji 组合、阿拉伯语连字、越南文声调组合等均能准确识别为单个字素
- 兼容性需留意:Chrome ≥90、Safari ≥15.4、Node.js ≥19;旧环境可用
grapheme-splitter回退
日期和数字格式化必须走 Intl.DateTimeFormat / Intl.NumberFormat
手动拼接字符串(如 date.getFullYear() + '-' + (date.getMonth()+1))完全忽略地区规则,会导致日语显示“2024/04/05”、德语用点作千分位、法语小数点用逗号等错误。
- 日期格式化示例:
new Intl.DateTimeFormat('ja-JP').format(new Date())→"2026/06/16" - 货币格式化示例:
new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(1234.56)→"1.234,56 €" - 始终显式传入 locale(如
navigator.language),不依赖默认行为
排序和比较要用 localeCompare 或 Intl.Collator
直接用 === 或 .sort() 按码点排序,会使德语 Österreich 排在 USA 后面、中文按 Unicode 码位乱序、俄语字母顺序错乱。
- 简单比较:
'café'.localeCompare('cafe', 'fr', { sensitivity: 'base' })→0(忽略重音) - 数组排序:
['日本', 'Österreich', 'USA'].sort((a, b) => a.localeCompare(b, 'de')) - 服务端(Node.js)需确保 ICU 数据完整,否则降级行为不可靠
占位符替换不能用模板字符串硬编码结构
`Hello ${name}, you have ${count} item${count === 1 ? '' : 's'}` 在法语、阿拉伯语或俄语中会失效——主谓宾顺序不同、复数形式不止单复数、还有语法性别变化。
- 应使用基于 ICU MessageFormat 的方案,如
@formatjs/intl或i18next - 资源中定义消息:
{ "message": "You have {count, number} {count, plural, one {item} other {items}}" } - 运行时由库根据 locale 自动选择正确词形,无需业务代码判断











