# Fluent 语义范围与验收矩阵（0.2.0）

## 对标与声明

语法目标是 Fluent FTL 1.0 的资源语法，而不是只支持消息中若干占位符。根包继续对照 `python-fluent/fluent.syntax 0.19.0`；运行时对照 `@fluent/bundle 0.19.1` 的字符串、数字、epoch 毫秒日期模型。JS Intl profile 能从 FTL 资源实际生成用户可见本地化消息。**这不是所有 Fluent 实现、所有 ICU/CLDR 版本、DOM overlay 或官方认证的合规声明。**

语法和运行时是两个公共包：原有语法工具不依赖 Intl；运行时不是把 FTL 当作 ICU/MF2 字符串转发，而是在 MoonBit AST 上执行作用域、引用、选择、函数与恢复策略。宿主只负责三个标准 Intl 操作。

## 语法构造逐项覆盖

|FTL 构造|语法、序列化与数据模型|运行时用途|可复核验收|
|---|---|---|---|
|Resource、空行、LF/CRLF|完整资源、Unicode scalar span|多份资源按顺序加入同一 bundle|`tests/fixtures`、`oracle_wbtest.mbt`|
|Message，有值/仅属性|保留 Identifier/value/attributes/comment|按消息名或属性生成字符串；无值有诊断|`runtime/resolver_wbtest.mbt`|
|Term 与独立命名空间|TermReference 与可选调用参数|私有术语、隔离命名参数、嵌套调用恢复作用域|同上 term parameters|
|Attribute、术语属性选择器|类型化属性节点与引用|消息属性求值；术语属性可用作合法 selector|运行时参考差分|
|Pattern、TextElement、多行缩进|保留文本并按 FTL 缩进规则解析|文本与表达式拼接，双向隔离默认开启|语法 oracle、运行时 bidi 用例|
|Placeable、嵌套 inline expression|结构化节点，不按正则替换|递归求值并执行全局配额|运行时差分与 limits 测试|
|StringLiteral|保留转义拼写|解码 `\\`、`\"`、`\uNNNN`、`\UNNNNNN`，surrogate 转替代字符|运行时 literal escapes|
|NumberLiteral|保留小数文本|binary64 数值及 minimumFractionDigits 精度|leading zero、decimal plural 差分|
|VariableReference|`$name`|外部带类型变量；缺失给占位符和诊断；术语局部缺参不误用外部变量|scope/unknown 测试|
|MessageReference、TermReference|区分命名空间、属性与参数|跨资源引用、环检测|reference、cycle、cross-resource 差分|
|FunctionReference、CallArguments、NamedArgument|标识符、位置/命名参数与顺序约束|NUMBER、DATETIME、受信任应用回调|builtin/custom/error 测试|
|SelectExpression、Variant|数字/标识符键、默认分支、嵌套选择|按源顺序匹配精确值或 locale plural，默认兜底|multilingual oracle|
|Comment、GroupComment、ResourceComment|附着注释与独立注释|不参与求值，不作为消息公开|上游 fixtures|
|Junk、Annotation、非法资源恢复|错误码、参数、消息与 span|add_resource 报告语法诊断，并保留其它有效条目|语法 oracle、junk recovery|

对手工构造 AST 的规范输出应使用 `serialize_checked`；普通 serializer 不是词法验证器。运行时仅接受源字符串并私有持有解析结果，没有不可信可变 AST 注入接口。输入语法限制、输出 canonical 排版与原文件字节保真是不同概念，均不冒充源码格式保留器。

## 运行时合同与平台

- `Bundle::new(locale, provider, use_isolating=true)` 显式选择 locale 与后端。`intl.provider()` 在 JS 使用真实 Intl；其它目标明确返回 unsupported，不自动套用英语规则。应用可在任意目标提供三个格式化回调。
- `add_resource(source, allow_overrides=false)` 同时支持多资源组合。消息/术语重复默认保留先前定义并返回 duplicate；显式覆盖按后加入定义替换。属性按参考实现采用最后一个属性。Junk 附加诊断是相对于 fluent.js 静默丢弃坏条目的 API 增强。
- `format(id, arguments, attribute)` 返回 `Formatted {value, errors, locale}`，而不是在遇到一个缺失变量时终止整个应用。错误码分为 syntax、duplicate、unknown-reference、unknown-attribute、missing-value、unknown-variable、unknown-function、function、cycle、plural、format、limit。
- Number/DateTime 使用 binary64；DateTime 是 Unix epoch 毫秒，不是秒。应用的数字选项支持 currency/style 等 Intl 能力；翻译文本的 NUMBER/DATETIME 命名选项采用 fluent.js 的白名单，**不等同 Python runtime 的所有选项**。跨类型转换不继承不兼容选项。精度选项影响 PluralRules。
- DateTime 的 timeZone 可由应用的类型化值显式携带；不携带时遵循宿主时区。示例与差分测试固定 UTC。Intl 的 locale 协商、ICU 数据和日期格式会随宿主而异，不能假定跨 Node 版本字节相同。
- FSI/PDI 只围绕多元素 pattern 的 placeable；禁用是应用显式选择。字符串不是 HTML，使用方必须在 DOM/邮件/终端上下文正确转义，不能直接当可信 markup。
- 自定义函数是受信任应用代码。回调以 Result 返回业务错误；不是沙箱，不承诺中断无限循环、捕获 MoonBit abort 或限制应用回调自行分配内存。求值开始时复制资源/函数目录，回调更新 bundle 不会改变当前解析快照。
- `Localizer` 使用调用方排序的 bundle 链，按有值消息或属性选择整个 bundle；其内部引用不去其它 locale 补洞。未实现自动 BCP47/Accept-Language 协商、跨 bundle 术语混合或异步文件/网络加载。

## 有界资源政策与非目标

每份资源最多 1,000,000 UTF-16 单位，整个 bundle 最多 10,000 条消息/术语（超限在任何添加前拒绝）。每次 format 最多 10,000 步、64 层、100 个 placeable，每个输出值/组合 pattern 最多 100,000 UTF-16 单位、最多 100 条诊断。达到上限有恢复诊断；这些是本项目安全策略，不是 Fluent 语法不完整。调用方仍应限制总 bundle 数、并发、CPU 与输入总量。

不支持 Temporal 对象、自定义 FluentType 类层级、HTML/DOM overlay、异步加载器、可执行翻译脚本、内置全语言 CLDR 数据库。非 JS 平台缺省没有 Intl 数据后端，不能把“核心通过测试”写成“所有目标都有真实本地化格式化”。不做性能胜过上游的声明。语法完整性的验收依据是覆盖表与固定参考语料，而非未经证明的百分百声明。

## 与参考实现的明确兼容差异

运行时参考语料共 253 项。250 项以 `@fluent/bundle@0.19.1` 的实际字符串与错误数量作为断言；另外三项嵌套术语用例明确采用保存/恢复外层参数的合同，不冒称 JS 完全一致。

JS 0.19.1 在嵌套术语返回后将参数置空，随后可能读到外部同名变量或产生缺参错误。MoonFluent 恢复进入内层术语前的参数，这与独立执行的 `fluent.runtime==0.4.0` 作用域行为一致：

|固定用例|JS 0.19.1 实测|MoonFluent / Python 作用域合同|
|---|---|---|
|term-nested-parameter-after|outer/inner/{$case}，1 诊断|outer/inner/outer，0 诊断|
|term-nested-external-after|outer/Inner/external，0 诊断|outer/Inner/outer，0 诊断|
|term-nested-no-param-after|{$missing}/{$missing}，1 诊断|缺参仍为局部缺参，0 诊断|

第三项中 Python 原始字符串为 `missing/missing`；本项目保留 Fluent 风格 `{$missing}/{$missing}`。这是显式记录的占位符方言差异，不能把转换后的字符串说成 Python 原始输出完全相同。三个输入、两种上游的原始结果、依赖哈希和唯一整串投影均保留于 `scripts/runtime-oracle`；CI 重新执行两个上游，生成器不会调用 MoonFluent 来构造期望。错误码采用本项目稳定模型，不承诺上游异常类名/措辞逐字一致。
