MessageFormat 2 · 给人看的消息怎么写
把「You have 3 new messages」翻成几十种语言,要应付复数(英语 2 档、波兰语 4 档、阿拉伯语 6 档)、语序、性别一致、还要在句中加粗 / 加链接。MessageFormat 2(MF2)是 Unicode 给这个老问题的新标准:一套既能被译者读懂、又能跨实现(ICU4C / ICU4J / JS)复用的消息语法,并正进入 ECMA-402 的 Intl.MessageFormat 提案。
本系列按官方文档的脉络铺开:先看消息的语法构成,再到内置函数、选择与变体、声明、标记,最后是 JavaScript 里的 messageformat 包 API 与错误处理。每页都能改消息源、传值、换 locale,实时看真实引擎(打包进来的 messageformat v4)的输出。
为什么不能直接拼字符串
从朴素拼接到 printf 到 ICU MessageFormat 1,逐一看旧方案卡在哪:语序焊死、无法处理复数、嵌套语法难读。MF2 把「选择逻辑」与「分支文本」分离,并作为跨实现的 Unicode 标准收口。
一条消息长什么样
最小语法:文本 夹 占位符 {…},里面是 变量 name、字面量 |literal| 或带函数的表达式。看文本转义、引号内外两种字面量、user.name 点路径,以及 simple 何时升级成需要 {{…}} 的 complex message。
函数:给值挑一种本地化形式
内置函数全表:REQUIRED 的 :string / :number / :integer / :offset,DRAFT 的 :date / :time / :datetime / :currency / :unit / :percent。逐个跑、调选项——注意 MF2 用 length / fields / precision 这套可移植词汇,而非直接透传 Intl。
选择:同一条消息的多个版本
.match + 平铺变体 + 通配 * 解决复数 / 性别。拖 count、换 locale 看命中哪个变体与复数类别;精确键如何优先于类别、select=ordinal / exact、以及多选择子如何维度相乘。
声明:给输入注解、给中间值起名
.input {x :number … 给外部变量绑定默认格式(并兼任 .match 的选择子),.localy = {…} 命名中间结果复用。声明一次、正文处处用,使用处还能临时覆盖。出现声明即 complex message,正文要套 {{…}}。
标记:让译者也能安全地加粗、加链接
{#tag}…{/tag} / {#tag /} 只标出「这段要套某种装饰」,不绑定 HTML;宿主读 formatToParts() 自行渲染。左边写源、右边看标记被解释成真实 DOM,parts 里全是抽象标签——无法破坏结构、注入不了。
messageformat 包:在 JS 里跑 MF2
new MessageFormat(locales, source, options) + format() / formatToParts();自定义函数;以及数据模型层 parseMessage / stringifyMessage / validate / visit 与类型守卫。实时看消息解析成的对象树。
错误与回退:坏消息也要给个像样的输出
五类错误两个阶段:构建期(Syntax / DataModel)抛异常,运行期(Resolution / Function / Selection)回退到近似源占位串 + onError 上报、不中断。逐个触发看降级行为,以及那对看不见的 bidi 方向隔离符。
messageformat 包(v4)。本系列的 demo 都跑这个真实引擎;日期 / 货币等 DRAFT 函数已替你用 DraftFunctions 注册好,故可直接体验。相关链接
- MessageFormat 2 · 官方站点 messageformat.unicode.org 本系列对照的官方文档:语法参考(Functions / Literals / Variables / Markup / Matchers / Patterns / Errors)、各语言集成指南,以及一个在线 Playground。
- UTS #35 Part 9 · MessageFormat 规范本体 unicode.org LDML 的 MessageFormat 章节:消息语法的 ABNF、数据模型、解析 / 解析 / 格式化算法、默认函数注册表(含 REQUIRED / DRAFT 分档)与错误定义的权威来源。
-
messageformat · JS 实现 (v4)
github.com
本系列 demo 打包的引擎:
MessageFormat类、messageformat/functions的DefaultFunctions/DraftFunctions、数据模型工具与messageformat/cst具体语法树。 -
TC39 · Intl.MessageFormat 提案
tc39.es
把 MF2 引入 ECMA-402、成为浏览器内置
Intl.MessageFormat的提案。落地后,本系列的 JS 用法将不再需要安装包。 -
MDN · Intl 命名空间
developer.mozilla.org
Intl国际化对象总览:NumberFormat/DateTimeFormat/PluralRules等现有构造器。MF2 的:number/:date等内置函数即建立在这套底层格式化能力之上;Intl.MessageFormat提案落地后也将归入此页。
解析内核与二进制 AST
- intlify · Binary AST Snapshot v0.1 Writer + Decoder github.com intlify 解析器 crate 为 MF2 解析树实现的二进制快照格式:字符串 interning(FNV-1a)、8 字节对齐的定长记录、zero-copy 遍历,把「解析后的 AST」序列化成可校验、可直接按偏移读取的字节流,跳过重复解析。
- TC39 · JavaScript Binary AST (BinAST) 提案 github.com 把 JavaScript 源码预先解析成二进制 AST 再发给浏览器、跳过解析以加快启动的标准化尝试(现已搁置)。与上面 MF2 快照同源的思路:用紧凑二进制布局换取免重复 parse 的加载性能。
- V8 · Code caching for JavaScript developers v8.dev V8 缓存编译产物(bytecode / code cache)以复用、规避重复 parse 与 compile 的工程方案,是 BinAST 之外解决「解析慢」问题的另一条主流路径。