返回

国际化文件检查

开发工具

正在载入

正在加载工具

工具代码会在打开时按需载入,请稍候。

本工具的全部运算都发生在你的浏览器中,输入内容不会发送到任何服务器。

关于这个工具

发布翻译文案前,将参考语言与目标语言文件进行结构检查。工具保留嵌套对象路径、空容器和 JSON 类型,在 JSON Pointer 位置报告缺失键、多余键与不匹配。可选纯文本、花括号占位符或明确受限的 ICU-flat 语法,不渲染或执行任何模板。翻译措辞和基本类型的具体值不参与差异比较。结果用于辅助结构审核,不是翻译质量评分,也不能证明本地化框架会接受所有消息。

常见用途

  • 合并翻译文件后,找出缺失的菜单标签、目标文件中多余的旧键,以及误从字符串变成对象的字段。
  • 检查译文是否保留应用代码需要的命名或位置参数,同时避免把正常的语言措辞差异当成错误。
  • 依据两种语言各自的规则检查扁平基数或序数复数消息,并向本地化审核者提供带位置的 JSON 问题报告。

使用方法

  1. 1.粘贴或打开参考与目标 UTF-8 JSON 文件,两侧根值必须为对象。先使用纯文本,或明确选择花括号、ICU-flat;使用 ICU-flat 时填写各自真实的语言标签。
  2. 2.除非文件格式适合按索引比较,否则保留默认的拒绝数组选项。运行检查后按来源、问题种类和分页查看错误与警告。缺失子树会分别报告容器及其每个后代路径。
  3. 3.检查全部问题后复制或下载 JSON 报告。筛选和分页不缩减导出;编辑输入或比较设置会使旧结果失效。触及报告上限时需缩小检查范围后重新运行。

可复现的国际化检查示例

纯文本忽略措辞和具体值

reference:
{"greeting":"Hello {name}","count":1,"enabled":true,"empty":{}}

target:
{"greeting":"こんにちは {broken","count":99,"enabled":false,"empty":{}}

options:
{"mode":"plain","beforeLocale":"en","afterLocale":"ja","arrays":"reject"}
{"valid":true,"issues":[]}

两侧译文、数字和布尔值不同,但类型一致。纯文本模式有意忽略未闭合花括号,因此此处的精简问题摘要为空。

缺失、多余路径及类型变化

reference:
{"menu":{"home":"Home","gone":"Old"},"count":1,"empty":{}}

target:
{"menu":{"home":"首页","extra":"New"},"count":"1","empty":[]}

options:
{"mode":"plain","beforeLocale":"en","afterLocale":"ja","arrays":"indices"}
{"valid":true,"issues":[{"code":"missing-key","path":"/menu/gone","side":"before","severity":"error"},{"code":"type-mismatch","path":"/count","side":"after","severity":"error"},{"code":"type-mismatch","path":"/empty","side":"after","severity":"error"},{"code":"extra-key","path":"/menu/extra","side":"after","severity":"error"}]}

目标缺少 /menu/gone,并新增 /menu/extra。数字变为字符串,空对象变为空数组;这里显式选择按索引处理,允许检查该数组。

缺失子树包含容器路径

reference:
{"group":{"title":"Title"},"empty":{}}

target:
{}

options:
{"mode":"plain","beforeLocale":"en","afterLocale":"ja","arrays":"reject"}
{"valid":true,"issues":[{"code":"missing-key","path":"/group","side":"before","severity":"error"},{"code":"missing-key","path":"/group/title","side":"before","severity":"error"},{"code":"missing-key","path":"/empty","side":"before","severity":"error"}]}

参考侧 group、其中的 title 和独立空对象均占据一个 JSON 路径。三者在目标侧都不存在;空容器不会因扁平化而消失。

JSON Pointer 转义与特殊键名

reference:
{"a/b":{"~name":"x"},"a.b":"x","__proto__":"text","constructor":{}}

target:
{"a/b":{},"a.b":"y","__proto__":"译文","constructor":{}}

options:
{"mode":"plain","beforeLocale":"en","afterLocale":"ja","arrays":"reject"}
{"valid":true,"issues":[{"code":"missing-key","path":"/a~1b/~0name","side":"before","severity":"error"}]}

缺少的是 a/b 内的 ~name,定位为 /a~1b/~0name。字面键 a.b、__proto__ 和 constructor 仍分别作为普通数据处理。

拒绝转义解码后重复键名

reference:
{"title":"One","ti\u0074le":"Two"}

target:
{}

options:
{"mode":"plain","beforeLocale":"en","afterLocale":"ja","arrays":"reject"}
{"valid":false,"issues":[{"code":"duplicate-key","path":"/title","side":"before","severity":"error"}]}

title 与 ti\u0074le 解码为相同名称。参考输入被拒绝,不会悄悄采用最后一个值;无法继续比较或导出报告。

默认策略拒绝两侧的数组

reference:
{"steps":["A","B"]}

target:
{"steps":["一"]}

options:
{"mode":"plain","beforeLocale":"en","afterLocale":"ja","arrays":"reject"}
{"valid":false,"issues":[{"code":"arrays-rejected","path":"/steps","side":"before","severity":"error"},{"code":"arrays-rejected","path":"/steps","side":"after","severity":"error"}]}

两份文件都在 /steps 处包含数组。默认拒绝策略在键比较前分别诊断两侧,不会把数组元素假装成对象键。

显式按从零开始的索引比较

reference:
{"steps":["A","B"]}

target:
{"steps":["一"]}

options:
{"mode":"plain","beforeLocale":"en","afterLocale":"ja","arrays":"indices"}
{"valid":true,"issues":[{"code":"missing-key","path":"/steps/1","side":"before","severity":"error"}]}

启用索引模式后,第一项虽然措辞不同,但均为字符串。参考侧第二项在目标中不存在,报告位置为 /steps/1。

双花括号字面量与参数集合

reference:
{"label":"{{literal}} {name} {0} {name}"}

target:
{"label":"{0} {name} {{文字}}"}

options:
{"mode":"braces","beforeLocale":"en","afterLocale":"ja","arrays":"reject"}
{"valid":true,"issues":[]}

双花括号属于字面文本。两条消息都使用 name 与 0,改变顺序或重复 name 不改变参数集合,因此没有不匹配。

参数名称改变会产生不匹配

reference:
{"label":"Hello {name}, item {0}"}

target:
{"label":"{user} さん、項目 {0}"}

options:
{"mode":"braces","beforeLocale":"en","afterLocale":"ja","arrays":"reject"}
{"valid":true,"issues":[{"code":"placeholder-mismatch","path":"/label","side":"after","severity":"error"}]}

目标保留位置参数 0,但将 name 改成 user。不匹配定位在目标字符串;译文本身的正常措辞变化不影响结果。

消息语法错误仍有可用诊断

reference:
{"label":"Hello {name"}

target:
{"label":"こんにちは {name}"}

options:
{"mode":"braces","beforeLocale":"en","afterLocale":"ja","arrays":"reject"}
{"valid":true,"issues":[{"code":"message-syntax","path":"/label","side":"before","severity":"error"}]}

JSON 合法,但参考字符串缺少右花括号。valid 仍为 true,因为输入可用于检查;message-syntax 错误仍需修正。

英语与日语使用各自复数类别

reference:
{"cart":"{count, plural, one {# item} other {# items}}"}

target:
{"cart":"{count, plural, other {# 個}}"}

options:
{"mode":"icu-flat","beforeLocale":"en","afterLocale":"ja","arrays":"reject"}
{"valid":true,"issues":[]}

英语提供 one 和 other,日语仅使用 other。所需参数与各自语言的类别都适用,因此不要求两种语言具有相同分支集合。

覆盖不足与不可达类别只警告

reference:
{"cart":"{count, plural, other {# items}}"}

target:
{"cart":"{count, plural, one {# 個} other {# 個}}"}

options:
{"mode":"icu-flat","beforeLocale":"en","afterLocale":"ja","arrays":"reject"}
{"valid":true,"issues":[{"code":"category-coverage","path":"/cart","side":"before","severity":"warning"},{"code":"category-unreachable","path":"/cart","side":"after","severity":"warning"}]}

英语未明确提供 one 分支,因此有覆盖警告。日语基数规则使用 other,却额外写了 one,该分支得到不可达类别警告。

分支参数与精确整数选择器

reference:
{"cart":"{count, plural, =0 {None} one {# {unit}} other {# {unit}}}"}

target:
{"cart":"{count, plural, other {# 個}}"}

options:
{"mode":"icu-flat","beforeLocale":"en","afterLocale":"ja","arrays":"reject"}
{"valid":true,"issues":[{"code":"plural-placeholder-mismatch","path":"/cart","side":"after","severity":"error"},{"code":"branch-placeholder-mismatch","path":"/cart","side":"after","severity":"error"},{"code":"exact-selector-missing","path":"/cart","side":"after","severity":"warning"}]}

日语 other 分支丢失参考 other 中的 unit,产生错误;参考 =0 选择器也被移除,另给警告,它不是语言规则强制要求的类别。 还会比较全部复数分支的参数并集,因此丢失一个参数可能同时产生复数整体与单个分支两条错误。

不支持的 ICU 结构及缺少 other

reference:
{"nested":"{n, plural, one {{m, plural, other {x}}} other {x}}","format":"{price, number}","required":"{n, plural, one {x}}"}

target:
{"nested":"{n, plural, other {#}}","format":"{price}","required":"{n, plural, other {#}}"}

options:
{"mode":"icu-flat","beforeLocale":"en","afterLocale":"ja","arrays":"reject"}
{"valid":true,"issues":[{"code":"unsupported-nested","path":"/nested","side":"before","severity":"error"},{"code":"unsupported-formatter","path":"/format","side":"before","severity":"error"},{"code":"missing-other","path":"/required","side":"before","severity":"error"}]}

参考侧分别包含嵌套复数、number 格式器和没有 other 的复数。每个问题定位到对应字符串,不会渲染、执行或默默接受这些消息。

国际化审核常见误区

  • 把 i18next 风格的 {{name}} 当成本工具花括号模式的参数:在这里它是字面文本。
  • 以为每种语言都必须具有英语的全部复数类别。
  • 用点号路径理解实际含点、斜杠或波浪号的键,忽略 JSON Pointer 转义。
  • 对已重排元素的数组启用按索引比较;工具不会按元素身份配对。
  • 把 valid: true 理解为没有错误,或把当前筛选画面误当成导出的全部问题。
  • 分享报告前没有检查私密键路径和参数名;虽然不含翻译值,名称仍可能暴露项目信息。

限制与说明

  • 仅接受根值为对象的严格 JSON,不支持 JSONC 注释、尾逗号、YAML、JavaScript 模块或自动修复。任意层级解码后重复的键、不成对 Unicode 代理项均被拒绝。忽略对象键顺序,键名区分大小写且不做 Unicode 规范化。JSON Pointer 将 ~ 转为 ~0、/ 转为 ~1,含点键不会与嵌套路径混淆;__proto__、constructor 作为普通数据键处理。默认拒绝数组,也可明确选择从 0 开始逐索引比较。空对象、空数组和 null 保留不同类型。
  • 纯文本模式忽略所有消息语法。花括号模式识别 {name} 或位置参数 {0},参数名为 ASCII [A-Za-z_][A-Za-z0-9_]* 或纯数字;{{ 和 }} 表示字面花括号。未配对的单花括号会报错。比较参数集合,允许调整顺序或重复使用同一参数;不比较具体值、翻译措辞或参数重复次数。
  • ICU-flat 支持简单参数、单层 plural 和 selectordinal,选择器可用命名类别或 =0 等非负整数。分支内允许简单参数及 # 文本。嵌套选择器、select、number/date 等格式器、offset、同一参数重复定义复数,以及 ICU 单引号转义均不支持并会诊断。每个复数必须有 other。工具只检查声明语法,不格式化或求值消息,不等同于完整 ICU MessageFormat。 复数分支内的 # 计为使用控制参数,例如由 count 控制时与 {count} 视为同一参数;复数外的 # 是字面文本。 ICU 模式下,数字参数名必须为 0–32767 的规范整数且不得有前导零;复数控制参数名也遵守此限制。
  • 复数类别来自当前运行环境针对两侧语言及基数/序数类型的 Intl.PluralRules。缺少可选类别或提供该语言不可达的类别只产生警告,不要求跨语言类别完全一致;精确整数选择器的差异也只警告。目标分支与参考侧同名选择器比较参数,若不存在则与参考 other 比较。回退与覆盖检查是静态近似,不能判断语言质量或实际渲染行为。 还会比较全部复数分支的参数并集,因此丢失一个参数可能同时产生复数整体与单个分支两条错误。 精确 =N 分支可直接写固定数字,因此在此类分支及其参数并集比较中排除控制参数,其他参数名仍会检查。
  • 每侧最多 262,144 个 UTF-8 字节、32 层深度与 10,000 个 JSON 节点;单条消息的参数/分支预算为 1,000 个标记。最多保留 1,000 条问题,JSON 报告最多 1 MiB(1,048,576 字节)。报告截断或超限时禁止作为完整报告导出。无效 JSON 阻止比较和导出;JSON 可用但消息有错误时仍可导出诊断。全部本地处理,不上传、不联网查询、不持久化、不执行表达式。报告不含翻译值,但保留键路径和参数名,这些名称仍可能属于私密信息。 每个 JSON Pointer 最多 4,096 个 UTF-8 字节,全部路径合计最多 1 MiB;单条诊断详情超过 512 个 UTF-16 码元也会标记报告截断并禁止导出。

常见问题

为什么译文和数字改变后没有问题?

工具只比较路径、JSON 类型和所选消息语法。不同语言的字符串措辞本来就会不同;数字和布尔值也只检查类型,不比较或转换具体值。需要比较实际值时,请使用 JSON Diff。

日语需要和英语有相同的复数分支吗?

不需要。英语与日语支持的基数类别可能不同,工具按两侧各自的 Intl.PluralRules 检查,并要求 other。省略可选类别会给出覆盖警告,因为 other 可以承接;这不自动等于消息无效。

应选择哪种占位符模式?

不确定消息方言时,用纯文本做结构检查。只有符合所述 {name}/{0} 及双花括号字面转义规则时才选择花括号;ICU-flat 也仅用于页面列明的子集。框架专用插值、完整 ICU 嵌套与自定义格式器需要使用对应框架的校验工具。

检查成功或报告 valid 为 true 代表什么?

valid 表示 JSON 和选项可供检查,不代表没有错误,应继续查看问题及严重程度。无问题仅代表通过本工具覆盖的结构和语法规则,不保证翻译准确、覆盖全部应用代码用法,或与生产环境的语言数据版本行为一致。

相关工具