翻译文件格式对比:JSON / PO / XLIFF / ARB
High Contrast
Dark Mode
Light Mode
Sepia
Forest
3 min read691 words

翻译文件格式对比:JSON / PO / XLIFF / ARB

核心问题:翻译文件应该用什么格式?各种格式有什么优缺点,何时选哪种?


真实场景

你的项目开始时用 JSON 存储翻译,但随着上线了 5 种语言,翻译团队抱怨 JSON 格式没有注释字段、不知道每条文字的上下文、专业翻译工具也不支持。是时候评估是否切换格式了。


四种主流格式对比

graph LR JSON["JSON\n开发友好\n生态最广"] --- PO["PO/POT\nGettext 标准\n翻译工具支持最好"] PO --- XLIFF["XLIFF\n企业级标准\nCAT 工具首选"] XLIFF --- ARB["ARB\nFlutter/Dart 专用\n带注释"]
特性 JSON PO/POT XLIFF ARB
文件大小
可读性 一般
注释/上下文
复数支持 需框架配合 ✅ 原生 ✅ 原生
翻译状态跟踪
CAT 工具支持 一般 ✅ 最好 ✅ 最好 一般
Web 框架集成 ✅ 最好 需转换 需转换 Flutter 原生
TMS 平台支持 有限

JSON 格式

基础结构

// locales/zh-CN.json
{
"common": {
"save": "保存",
"cancel": "取消",
"loading": "加载中..."
},
"product": {
"title": "商品详情",
"addToCart": "加入购物车",
"price": "¥{amount}",
"stockCount": "{count} 件库存"
}
}

优点: - 所有 JavaScript 框架原生支持 - 结构清晰,开发者友好 - 嵌套结构支持按模块组织

缺点: - 无法添加注释(JSON 规范不支持注释) - 没有翻译状态字段(已翻译/待翻译/已过期) - 专业翻译工具支持参差不齐

JSON5 / JSONC(带注释的 JSON)

// locales/zh-CN.jsonc(仅用于开发/文档,需构建时转换)
{
"product": {
// 商品名称,最大 100 字符
"title": "商品详情",
// 购物车按钮,商品有库存时显示
"addToCart": "加入购物车",
// {amount} 是货币金额,已格式化(如 ¥129.99)
"price": "价格:{amount}"
}
}

PO / POT 格式(GNU Gettext)

最古老也是支持最广泛的翻译格式,起源于 GNU gettext 项目。

文件结构

# zh-CN.po
# 语言:简体中文
# 翻译者:团队名称 <team@example.com>
msgid ""
msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: zh-CN\n"
"Plural-Forms: nplurals=1; plural=0;\n"
# 注释:翻译者备注
# 引用:src/components/Product.tsx:42
# 上下文:产品详情页的主标题
msgctxt "product-page"
msgid "Product Details"
msgstr "商品详情"
# 带占位符的字符串
# 引用:src/components/Cart.tsx:18
msgid "Add {productName} to cart"
msgstr "将 {productName} 加入购物车"
# 复数形式(英语)
msgid "{count} item"
msgid_plural "{count} items"
msgstr[0] "{count} 件商品"
# 模糊翻译(需要审查)
#, fuzzy
msgid "Checkout"
msgstr "结算"
# 未翻译(空字符串)
msgid "Order Summary"
msgstr ""

POT 文件(模板)

POT(PO Template)是未翻译的模板文件,从源代码提取,然后为每种语言创建 PO 文件:

# 从 Python 代码提取字符串,生成 messages.pot
xgettext --language=Python --keyword=_ --output=messages.pot src/*.py
# 为中文创建 PO 文件
msginit --input=messages.pot --locale=zh_CN --output=zh_CN.po
# 合并新字符串到已有 PO 文件
msgmerge --update zh_CN.po messages.pot
# PO 编译为二进制 MO 文件(用于运行时)
msgfmt zh_CN.po --output-file=zh_CN.mo

在 JavaScript 中使用 PO

npm install gettext.js
# 或将 PO 转换为 JSON:
npm install po2json
// 将 PO 转换为 JSON(构建时)
const po2json = require('po2json');
const fs = require('fs');
const po = fs.readFileSync('./locales/zh-CN.po', 'utf8');
const json = po2json.parse(po, { format: 'jed' });
fs.writeFileSync('./dist/locales/zh-CN.json', JSON.stringify(json));

XLIFF 格式(XML Localization Interchange File Format)

XLIFF 是 OASIS 组织制定的 XML 标准,广泛用于企业级翻译项目和 CAT 工具(Computer-Aided Translation)。

XLIFF 2.0 示例

<?xml version="1.0" encoding="UTF-8"?>
<xliff version="2.0"
xmlns="urn:oasis:names:tc:xliff:document:2.0"
srcLang="en-US"
trgLang="zh-CN">
<file id="product-page">
<unit id="product.title">
<!-- 翻译备注,供翻译者参考 -->
<notes>
<note category="context">商品详情页的主标题</note>
<note category="max-length">30</note>
</notes>
<segment state="translated">
<source>Product Details</source>
<target>商品详情</target>
</segment>
</unit>
<unit id="product.price">
<notes>
<note category="context">{amount} 是货币金额,格式化后的字符串(如 $19.99)</note>
</notes>
<segment state="translated">
<source>Price: {amount}</source>
<target>价格:{amount}</target>
</segment>
</unit>
<!-- 未翻译的条目 -->
<unit id="product.wishlist">
<segment state="initial">
<source>Add to Wishlist</source>
<target></target>
</segment>
</unit>
<!-- 需要审查的条目 -->
<unit id="product.share">
<segment state="reviewed">
<source>Share</source>
<target>分享</target>
</segment>
</unit>
</file>
</xliff>

XLIFF 状态值

状态 含义
initial 未翻译
translated 已翻译,待审查
reviewed 已审查
final 最终定稿

ARB 格式(Application Resource Bundle)

ARB 是 Flutter/Dart 官方使用的翻译格式,JSON 的超集,支持元数据注释。

// app_zh.arb
{
"@@locale": "zh",
"@@last_modified": "2026-03-22T00:00:00Z",
"appTitle": "我的应用",
"@appTitle": {
"description": "应用的主标题,显示在导航栏"
},
"productPrice": "价格:{price}",
"@productPrice": {
"description": "商品价格显示",
"placeholders": {
"price": {
"type": "String",
"example": "¥129.99"
}
}
},
"cartItemCount": "{count, plural, =0{购物车是空的} other{{count} 件商品}}",
"@cartItemCount": {
"description": "购物车商品数量",
"placeholders": {
"count": {
"type": "int"
}
}
}
}
// Flutter 使用
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
Text(AppLocalizations.of(context)!.productPrice(price: '¥129.99'));

格式选型决策树

flowchart TD A[选择翻译文件格式] --> B{使用的框架?} B -->|Flutter/Dart| C[ARB ✅] B -->|Python/PHP/Ruby| D[PO/Gettext ✅] B -->|React/Vue/Next.js| E{团队规模?} E -->|小型 < 3 人| F[JSON ✅] E -->|中型 3-10 人| G{使用 TMS 平台?} G -->|是| H{平台支持?} H -->|支持 XLIFF| I[XLIFF ✅] H -->|支持 JSON| J[JSON ✅] G -->|否| K[JSON 或 PO ✅] E -->|大型 > 10 人| L[XLIFF 或 PO ✅]

推荐方案

场景 推荐格式 理由
Next.js / Nuxt.js 中小项目 JSON 框架原生支持,开发效率最高
需要专业翻译团队 PO 或 XLIFF 翻译工具(OmegaT、SDL Trados)支持最好
使用 Crowdin/Lokalise TMS JSON 或 XLIFF 两者都支持,XLIFF 保留更多上下文
Flutter 应用 ARB Flutter 官方标准
跨平台(iOS/Android/Web) XLIFF iOS .strings/Android .xml 可互转

格式转换工具

# JSON → PO
npm install i18next-conv
i18next-conv -l zh-CN -s locales/zh-CN.json -t locales/zh-CN.po
# PO → JSON
i18next-conv -l zh-CN -s locales/zh-CN.po -t locales/zh-CN.json
# XLIFF → JSON(使用 xliff-simple-merge 或 xliff-conv)
npm install xliff
// 程序化转换 XLIFF → JSON
const xliff = require('xliff');
const xliffContent = fs.readFileSync('translations.xliff', 'utf8');
const result = await xliff.xliff2js(xliffContent, { /* options */ });
// result: { resources: { 'product-page': { 'product.title': { source: 'Product Details', target: '商品详情' } } } }

下一节:格式选好了,接下来解决翻译 key 的命名混乱问题——如何规划 namespace 结构,让翻译文件可维护。

命名空间规划与 key 命名规范