修复测试
纯 MoonBit 实现、基于 Unicode CLDR 数据的本地化格式化内核。
moonl10n 把 (locale, value, options) 转换为符合区域习惯的字符串,不依赖 JavaScript 宿主的 Intl。项目面向 WASM-GC、JS 与 Native 后端,格式化规则由库内 数据决定,不依赖运行时环境提供国际化能力。
moonl10n
(locale, value, options)
Intl
en
zh
de
ja
fr
MoonBit 现有的 i18n 工作主要解决消息目录(MF2)问题,数字、日期、货币和复数的 最终渲染仍然经常交给宿主 Intl。这种方式在浏览器里可以使用,但编译到 Native、 WASM-GC 或嵌入式环境后,宿主兜底不再存在,应用会突然失去格式化能力。
moonl10n 补上的正是这一层:
项目当前处于早期 alpha 阶段,核心链路已经实现并通过测试。
1.2K
1.2万
1,2 Mio.
ar
latn
z
let locale = @core.Locale::parse("de") @number.format_decimal(1234567.891, locale) // 1.234.567,891 @number.format_currency(1234.5, "EUR", locale) // 1.234,50 € @number.format_percent(0.125, locale) // 13 % @number.format_compact(1234567.0, locale) // 1,2 Mio. @number.format_scientific(1234567.0, locale) // 1,235E6 @number.format_with_pattern(12.5, locale, "0.0 'kg'") // 12,5 kg let date = @datetime.DateTime::new(2026, 10, 18, hour=17, minute=42) @datetime.format_date(date, locale) // 18.10.2026 @datetime.format_time(date, locale) // 17:42 @datetime.format_pattern(date, locale, "EEEE, d. MMMM y") let end = @datetime.DateTime::new(2026, 10, 20) @datetime.format_date_range(date, end, locale) // 18.10.2026 - 20.10.2026 @datetime.format_pattern_with_policy( date, locale, "yyyy-MM-dd z", @datetime.Reject, ) // Err(UnsupportedField('z')) let rules = @plural.PluralRules::cached("ar") rules.select(3.0) // few @relative.format_relative(-3.0, @relative.Day, locale) // vor 3 Tagen
显式模式会保留字面空格,包括首尾空格。默认情况下,不支持的字段会被跳过; format_pattern_with_policy 和 format_time_with_policy 提供 Skip、 Preserve、Reject 三种策略。
format_pattern_with_policy
format_time_with_policy
Skip
Preserve
Reject
try_parse 执行严格校验,parse 保持尽力解析的兼容行为:
try_parse
parse
@core.Locale::try_parse("de-abc") // None @core.Locale::parse("de-abc").to_tag() // de @core.Locale::parse("sl-rozaj-biske-1994").to_tag() // sl-rozaj-biske-1994 @core.Locale::parse("zh-HK").maximize().to_tag() // zh-Hant-HK let available = [ @core.Locale::parse("en"), @core.Locale::parse("zh-Hans"), @core.Locale::parse("zh-Hant"), ] @core.Locale::negotiate("zh-HK, en;q=0.8", available) // Some(zh-Hant)
严格解析器覆盖语言、书写系统、地区、variants、extensions 和 private-use 区段。Extlang 与 grandfathered 标签会被拒绝,而不是被猜测成其他区域。内置的 likely-subtag 表覆盖项目内置区域及常见语言。
let money = @number.NumberOptions::with_fraction_digits(2, 2) @number.format_decimal(1.0, locale, options=money) // 1,00 let plain = @number.NumberOptions::default().without_grouping() @number.format_decimal(1234567.0, locale, options=plain)
let en = @core.Locale::parse("en") let code = @number.CurrencyOptions::with_display(@number.Code) @number.format_currency(1234.5, "USD", en, currency_options=code) // USD 1,234.50 let accounting = @number.CurrencyOptions::default().with_accounting() @number.format_currency(-1234.5, "USD", en, currency_options=accounting) // ($1,234.50) @number.format_currency_with_pattern(1234.5, "USD", en, "¤¤#,##0.00") // USD 1,234.50
复数选择可以直接使用最终渲染出来的数字位数。因此 1 与 1.00 在英语中会选择 不同的复数类别:
1
1.00
let en = @core.Locale::parse("en") let rendered = @number.format_decimal_result(1.0, en, options=money) rendered.text // 1.00 rendered.integer_digits // 1 rendered.fraction_digits // 00 let rules = @plural.PluralRules::cached("en") rules.select_with_digits( rendered.integer_digits, rendered.fraction_digits, ) // other(v = 2)
core
data
decimal
pattern
number
plural
datetime
relative
examples/readme
cmd/main 会打印完整的跨区域格式化示例:
cmd/main
moon run cmd/main
examples/readme 是 README 代码片段的可执行版本。它会实际调用文档中的 API, 并由配套测试固定输出:
moon run examples/readme moon test examples/readme
生成代码已经提交到仓库,因此使用者不需要安装 Node.js。需要刷新数据时执行:
node tools/fetch-cldr.mjs # 下载锁定版本的 CLDR JSON 子集 node tools/gen-cldr.mjs # 重新生成并格式化 data/generated.mbt moon test
数据来源:unicode-org/cldr-json。
生成器会对货币数据进行去重:共享的 ISO 代码表和基于英语的符号表只生成一次, 每个区域只保存自己的符号、窄符号覆盖以及按货币代码排列的本地名称。紧凑记数表 为每个数量级保存一个默认模式,并仅在需要时保存复数覆盖。加入科学计数法和紧凑 记数数据后,data/generated.mbt 仍从 40,088 字节降至 37,021 字节,缩小 7.7%。 生成器最后会执行 moon fmt,保证重新生成的文件符合格式要求。
data/generated.mbt
moon fmt
moon check moon test moon test --target js moon test --target native moon coverage analyze -- -f summary node tools/gen-intl-fixtures.mjs --check
当前测试覆盖严格 Locale 解析、likely subtags、Accept-Language 协商、区域回退、 分组与小数位规则、负数子模式、ISO 4217 小数位、基数与序数复数选择、多区域日期 时间风格、数字与日期模式中的引号字面量、相对时间、科学计数法、紧凑记数以及共享 货币表覆盖。
.github/workflows/ci.yml 会在 wasm-gc、JavaScript 和 Native 三种后端运行 完整测试与可执行示例。独立的质量任务负责检查格式、校验已提交的一致性测试数据, 并输出 moon coverage 覆盖率摘要。
.github/workflows/ci.yml
wasm-gc
moon coverage
.github/workflows/publish.yml 会在推送 v* 标签后将模块发布到 MoonCakes。 首次发布前需要配置 MOONCAKES_USERNAME 与 MOONCAKES_TOKEN 两个仓库 Secrets。
.github/workflows/publish.yml
v*
MOONCAKES_USERNAME
MOONCAKES_TOKEN
conformance/intl_conformance_wbtest.mbt 包含 178 条由 Node.js Intl 生成的 静态白盒断言。因此 CI 运行时只需要 moon test,不需要 Node.js 参与格式化。 测试覆盖 6 个区域,包括十进制与负数、百分号、货币符号/代码/名称/窄符号、 accounting 模式、日期、时间、相对日/小时、科学计数法、紧凑记数、基数复数和 可见小数位。
conformance/intl_conformance_wbtest.mbt
moon test
重新生成或校验一致性数据:
node tools/gen-intl-fixtures.mjs node tools/gen-intl-fixtures.mjs --check
生成器会在文件头记录 Node 与 ICU 版本。日期时间比较会把 U+202F(CLDR 窄不换行 空格)和 U+0020(Node ICU 在这些模式中输出的普通空格)视为等价;其余空格, 包括货币间距,均按精确内容比较。
当前明确不包含时区渲染、非公历日历和非 latn 数字系统。时区字段可以通过 Reject 策略被显式检测,而不是静默丢弃。这些能力列在后续路线图中。
竞赛申报书与生态调研位于 docs/proposal.md。
docs/proposal.md
Apache-2.0。
版权所有:中国计算机学会技术支持:开源发展技术委员会 京ICP备13000930号-9 京公网安备 11010802047560号
moonl10n
纯 MoonBit 实现、基于 Unicode CLDR 数据的本地化格式化内核。
moonl10n把(locale, value, options)转换为符合区域习惯的字符串,不依赖 JavaScript 宿主的Intl。项目面向 WASM-GC、JS 与 Native 后端,格式化规则由库内 数据决定,不依赖运行时环境提供国际化能力。enzhdejafr为什么需要 moonl10n
MoonBit 现有的 i18n 工作主要解决消息目录(MF2)问题,数字、日期、货币和复数的 最终渲染仍然经常交给宿主
Intl。这种方式在浏览器里可以使用,但编译到 Native、 WASM-GC 或嵌入式环境后,宿主兜底不再存在,应用会突然失去格式化能力。moonl10n补上的正是这一层:当前能力
项目当前处于早期 alpha 阶段,核心链路已经实现并通过测试。
1.2K、1.2万、1,2 Mio.等短格式en、zh、de、ja、fr、arlatn数字系统z字段支持跳过、保留或报错策略快速开始
显式模式会保留字面空格,包括首尾空格。默认情况下,不支持的字段会被跳过;
format_pattern_with_policy和format_time_with_policy提供Skip、Preserve、Reject三种策略。Locale 解析与协商
try_parse执行严格校验,parse保持尽力解析的兼容行为:严格解析器覆盖语言、书写系统、地区、variants、extensions 和 private-use 区段。Extlang 与 grandfathered 标签会被拒绝,而不是被猜测成其他区域。内置的 likely-subtag 表覆盖项目内置区域及常见语言。
数字选项
货币显示与会计格式
复数与已渲染位数
复数选择可以直接使用最终渲染出来的数字位数。因此
1与1.00在英语中会选择 不同的复数类别:包结构
coredatadecimalpatternnumberpluraldatetimerelativeexamples/readme示例与演示
cmd/main会打印完整的跨区域格式化示例:examples/readme是 README 代码片段的可执行版本。它会实际调用文档中的 API, 并由配套测试固定输出:数据管线
生成代码已经提交到仓库,因此使用者不需要安装 Node.js。需要刷新数据时执行:
数据来源:unicode-org/cldr-json。
生成器会对货币数据进行去重:共享的 ISO 代码表和基于英语的符号表只生成一次, 每个区域只保存自己的符号、窄符号覆盖以及按货币代码排列的本地名称。紧凑记数表 为每个数量级保存一个默认模式,并仅在需要时保存复数覆盖。加入科学计数法和紧凑 记数数据后,
data/generated.mbt仍从 40,088 字节降至 37,021 字节,缩小 7.7%。 生成器最后会执行moon fmt,保证重新生成的文件符合格式要求。测试
当前测试覆盖严格 Locale 解析、likely subtags、Accept-Language 协商、区域回退、 分组与小数位规则、负数子模式、ISO 4217 小数位、基数与序数复数选择、多区域日期 时间风格、数字与日期模式中的引号字面量、相对时间、科学计数法、紧凑记数以及共享 货币表覆盖。
持续集成与发布
.github/workflows/ci.yml会在wasm-gc、JavaScript 和 Native 三种后端运行 完整测试与可执行示例。独立的质量任务负责检查格式、校验已提交的一致性测试数据, 并输出moon coverage覆盖率摘要。.github/workflows/publish.yml会在推送v*标签后将模块发布到 MoonCakes。 首次发布前需要配置MOONCAKES_USERNAME与MOONCAKES_TOKEN两个仓库 Secrets。一致性测试
conformance/intl_conformance_wbtest.mbt包含 178 条由 Node.jsIntl生成的 静态白盒断言。因此 CI 运行时只需要moon test,不需要 Node.js 参与格式化。 测试覆盖 6 个区域,包括十进制与负数、百分号、货币符号/代码/名称/窄符号、 accounting 模式、日期、时间、相对日/小时、科学计数法、紧凑记数、基数复数和 可见小数位。重新生成或校验一致性数据:
生成器会在文件头记录 Node 与 ICU 版本。日期时间比较会把 U+202F(CLDR 窄不换行 空格)和 U+0020(Node ICU 在这些模式中输出的普通空格)视为等价;其余空格, 包括货币间距,均按精确内容比较。
已知边界
当前明确不包含时区渲染、非公历日历和非
latn数字系统。时区字段可以通过Reject策略被显式检测,而不是静默丢弃。这些能力列在后续路线图中。路线图
Intl差分测试之外增加 ICU4X 差分测试。latn数字系统(阿拉伯-印度数字、天城文数字等)。项目申报书
竞赛申报书与生态调研位于
docs/proposal.md。许可证
Apache-2.0。