错误与回退:坏消息也要给个像样的输出
本地化的一个常见风险是「线上某条消息一出错就导致整页渲染失败」。MF2 把错误分成两个阶段:构建期(语法 /
数据模型错误)会抛异常——这类是开发期就该修的结构问题;而运行期(变量没传、函数不存在、操作数类型不对)不抛异常终止,而是回退到一个近似源文本的占位结果,并把错误经 onError 报出。局部出错不影响整条消息。
1 · 五类错误
| 类 | 阶段 | 触发 | 结果 |
|---|---|---|---|
Syntax |
构建期 | 括号没闭合、非法 token | 抛异常 |
DataModel |
构建期 | 缺 * 默认分支、变体键数不齐、重复声明 |
抛异常 |
Resolution |
运行期 | 变量没传、函数未注册、选项值非法 | 回退 + onError |
Function |
运行期 | 操作数类型不对(给 :number 传文字) |
回退 + onError |
Selection |
运行期 | 选择子无法产生可匹配的键 | 回退 + onError |
2 · 回退长什么样
运行期出错时,出问题的那个占位符会退化成近似源的占位串:变量退成 {$name}、字面量退成 {|literal|}、只有函数的退成 {:func}。其余正常部分照常输出。在 formatToParts() 里,它是一个独立的 fallback 零件(带
source),便于宿主识别与上报。
3 · 动手试:逐个触发
点预设按钮载入一条「有问题」的消息,看它属于哪一阶段、输出与 parts 如何降级:
4 · 方向隔离:那对看不见的字符
默认情况下,每个插值结果都会被一对方向隔离符(Unicode bidi isolates,U+2068 FSI … U+2069 PDI)括住。这是为了防止变量内容(如阿拉伯语名字、含数字的串)打乱整句的从左到右 / 从右到左排版。它们不可见、不占位,但确实在字符串里——下面把它们显形:
多数时候保留默认隔离即可——它让混合书写方向的消息排版稳定。只有当你确知内容方向、或要做精确的字符级处理时,才用 bidiIsolation: 'none' 关掉。需要识别这些控制符时,走 formatToParts() 看 bidiIsolation 零件最稳妥。