EmDash 的管理界面使用 Lingui 进行消息提取,使用 Lunaria 跟踪翻译进度,支持国际化翻译。所有翻译存放在 PO(gettext)文件中——每个语言对应一个文件。
翻译状态
查看翻译仪表板了解所有语言的当前进度。
谁可以翻译
翻译必须由母语使用者或流利使用者提交。我们不接受机器生成的翻译。如果你使用 AI 工具辅助翻译,必须逐条人工审核每个字符串,并在实际界面中测试翻译效果(参见下方的测试你的翻译)。
我们宁可某个字符串没有翻译,也不要一个错误的翻译。错误的翻译比显示英文回退更糟糕——它会直接误导用户。
文件结构
翻译目录位于 packages/admin/src/locales/:
packages/admin/src/locales/
├── en/
│ └── messages.po # English (source)
├── de/
│ └── messages.po # German
└── ...
每个 .po 文件包含 msgid/msgstr 对。msgid 是英文源文本;msgstr 是你的翻译。空的 msgstr 表示”尚未翻译”——Lingui 将在运行时回退到英文。
翻译字符串
-
查看翻译仪表板,了解哪些内容需要翻译。检查未合并的 PR,避免重复劳动。
-
Fork 仓库并创建分支:
git checkout -b i18n/de -
打开你所在语言的 PO 文件(例如
packages/admin/src/locales/de/messages.po)。 -
填写翻译。 每个条目的格式如下:
#: packages/admin/src/components/LoginPage.tsx:304 msgid "Sign in with Passkey" msgstr ""填写
msgstr:#: packages/admin/src/components/LoginPage.tsx:304 msgid "Sign in with Passkey" msgstr "Mit Passkey anmelden" -
测试你的翻译(见下文)。
-
提交 PR 到
main分支。标题格式:i18n(de): add/update German translations。
应该翻译什么
- 每个条目的
msgstr值。
不应翻译什么
msgid值——这些是查找键。- 插值占位符如
{error}、{email}、{label}——保持原样。 - XML 风格标签如
<0>、</0>——这些用于包裹交互元素(链接、按钮)。保留标签,翻译标签之间的文本。 - 以
#:开头的注释——这些是 Lingui 添加的源码引用。
插值和标签
部分字符串包含占位符和标签:
msgid "Authentication error: {error}"
msgstr "Authentifizierungsfehler: {error}"
msgid "Don't have an account? <0>Sign up</0>"
msgstr "Noch kein Konto? <0>Registrieren</0>"
msgid "If an account exists for <0>{email}</0>, we've sent a sign-in link."
msgstr "Falls ein Konto für <0>{email}</0> existiert, haben wir einen Anmeldelink gesendet."
占位符({error}、{email})在运行时会被替换为动态值。标签(<0>...</0>)用于包裹 React 组件。两者在翻译中必须与源文本完全一致——相同的名称、相同的嵌套结构。
测试你的翻译
-
编译并运行示例项目:
pnpm run locale:compile pnpm build pnpm --filter emdash-demo dev -
切换语言,在管理界面的设置页面中验证你的翻译在上下文中是否正确。
伪语言
EmDash 内置了一个伪语言,它会将所有被包裹的字符串转换为带重音的相似字符——"Dashboard" 变成 "Ðàšĥƀöàřð",以此类推。在伪语言激活时,如果某个字符串仍然显示为正常英文,说明它缺少 t..“ 包裹,或者来自目录之外。
要启用伪语言,在示例项目目录的 .env 文件中添加以下内容:
EMDASH_PSEUDO_LOCALE=1
然后重启开发服务器。伪语言会在登录页面和设置页面的语言选择器中显示为 Pseudo。切换到该语言即可一目了然地发现未包裹的字符串。
添加新语言
如果你的语言还没有 PO 文件:
-
将语言添加到
packages/admin/src/locales/locales.ts:export const LOCALES: LocaleDefinition[] = [ { code: "en", label: "English", enabled: true }, { code: "de", label: "Deutsch", enabled: true }, // ... { code: "ja", label: "日本語", enabled: false }, // add yours ];这是唯一的数据源——
lingui.config.ts、lunaria.config.ts和管理界面运行时都从此文件获取语言列表。除非你的翻译覆盖率达到 100%,否则请将enabled设为false——翻译达到足够覆盖率后我们会启用它。 -
运行提取以生成空的 PO 文件:
pnpm run locale:extract这会创建
packages/admin/src/locales/{your-locale}/messages.po,其中包含所有待翻译的字符串。 -
翻译并测试,按照上述步骤操作。
翻译标准
准确性
翻译应忠实地反映英文源文本,达到母语使用者的水平。不要添加、删除或重新解读含义。如果源字符串含义不明确,请查看 #: 注释中的源文件位置——阅读组件代码以理解上下文。
一致性
在你的语言中使用一致的术语。如果你在一处将”collection”翻译为”Sammlung”,不要在另一处切换为”Kollektion”。如果你的语言已有翻译,请在开始之前通读已有的 PO 文件,以匹配已建立的术语体系。
语气
管理界面使用直接、专业的语气。在你的语言中也保持这种风格——避免过于正式或过于随意的表达。
AI 辅助翻译
你可以使用 AI 工具起草翻译,但:
- 你必须亲自审核每一个字符串。AI 工具会产生只有流利使用者才能察觉的细微错误——语域不当、表达不自然、技术术语不正确。
- 你必须在运行中的管理界面中测试结果。AI 工具无法感知布局约束或界面上下文。
- 在 PR 描述中说明是否使用了 AI。
- 明显未经审核的机器翻译 PR 将被关闭。
部分翻译
欢迎提交部分翻译。你不需要在一个 PR 中翻译所有字符串——任何进展都有帮助。未翻译的字符串将在运行时回退到英文。