国际化
我们是一个拥有全球用户的应用程序。为了帮助用户使用他们自己的语言来使用 Metabase,我们已将所有字符串标记为国际化 (i18n)。
快速指南
如果您需要添加新字符串(请谨慎添加),请执行以下操作
- 在前端使用
t
和jt
ES6 模板字面量标记字符串(详情请参阅 https://ttag.js.org/)
const someString = t`Hello ${name}!`;
const someJSX = <div>{jt`Hello ${name}`}</div>;
并在后端使用 trs
(使用站点语言)或 tru
(使用当前用户语言)
(trs "Hello {0}!" name)
翻译错误或缺失字符串
如果您发现您的语言的字符串不正确或缺失,请访问我们的 Crowdin 项目并在此处提交您的修正。
后端翻译指南
Metabase 支持多种语言的翻译。权威列表可以在 resources/locales.clj
中找到。
Metabase 方面
Metabase 关注两种不同的本地化:翻译为服务器的语言环境和翻译为用户的语言环境。其主要区别在于:这会记录在服务器上还是通过网络发送回用户。
要翻译服务器字符串,请使用 metabase.util.i18n/trs
;要翻译用户语言环境,请使用类似的 metabase.util.i18n/tru
。可以理解为 tr-server
和 tr-user
。
工作原理
从宏观层面来看,待翻译字符串被视为源字符串到本地化字符串映射中的查找键。此翻译字符串的使用方式如下:
;; from source of `translate` in `metabase.util.i18n`
(.format (MessageFormat. looked-up-string) (to-array args))
其余大部分是簿记工作。这使用 java.text.MessageFormat 类来拼接格式参数。
函数 trs
和 tru
分别创建 SiteLocalizedString
和 UserLocalizedString
两个记录的实例,并重写 toString
方法。此方法将查找当前语言环境(根据需要为用户或站点),将要翻译的字符串查找为关联的翻译字符串,然后调用 MessageFormat
上的 .format
方法。
从源字符串到翻译字符串的映射
我们构建过程中的一个步骤是为我们支持的每个语言环境创建一个从源字符串到翻译字符串的 edn 文件。这些文件位于 resources/i18n
中。如果您没有这些文件,可以运行 bin/build-translation-resources
来生成它们。
我们有许多贡献者帮助我们维护一个包含多种语言翻译字符串的语料库。我们使用 Crowdin 来维护一个权威列表。我们从其中导出 .po
文件,这些文件本质上是从源字符串到翻译字符串的字典。作为构建过程的一部分,我们将这些文件格式化为 edn 文件,即每个语言环境的源字符串到翻译字符串的映射。
格式参数
除了字符串字面量,我们还希望翻译中间拼接了参数的字符串。我们使用前面提到的 java.text.MessageFormat 类中的语法。这些是零索引参数,形式为 {0}
、{1}
。
例如,
(trs "{0} accepted their {1} invite" (:common_name new-user) (app-name-trs))
(tru "{0}th percentile of {1}" p (aggregation-arg-display-name inner-query arg))
(tru "{0} driver does not support foreign keys." driver/*driver*)
转义
每种字符串语言都需要一个转义字符。由于 {0}
是要拼接的参数,那么如何在字符串中放入字面量 “{0}” 呢?撇号(apostrophe)扮演了这个角色,并在 MessageFormat 的 Javadoc 中有描述。
不过,这有一个不幸的副作用。由于撇号在日常用语中非常常见(尤其是在法语中),我们经常会遇到将转义字符用作字符串的常规部分而不是转义字符的情况。格式字符串需要使用双撇号,例如 (deferred-tru "SAML attribute for the user''s email address")
来转义撇号。
法语中有很多翻译字符串错误地使用了单撇号(例如 “l’URL” 而不是 “l’‘URL”)。我们在 bin/i18n/src/i18n/create_artifacts/backend.clj
中对此进行了手动修复,我们尝试识别这些不是转义字符的撇号,并将其替换为双引号。
阅读其他 Metabase 版本的文档。