Rust I18n 是一个用于从一组(YAML、JSON 或 TOML)映射文件中加载本地化文本的 crate。这些映射在编译时被转换为 Rust 程序可读的数据,然后可以通过简单地调用提供的 [t!] 宏来加载本地化文本。
与其他 I18n 库不同,Rust I18n 的目标是提供一个简单易用的 API。
该 crate 的 API 灵感来源于 ruby-i18n 和 Rails I18n。
特性
- 编译时生成代码,将翻译内容包含到二进制文件中。
- 全局 [
t!] 宏,可在任何地方加载本地化文本。 - 使用 YAML(默认)、JSON 或 TOML 格式映射本地化文本,并支持多个文件合并。
cargo i18n命令行工具,用于检查并将未翻译的文本提取到 YAML 文件中。- 支持将所有本地化文本放在一个文件中,或按语言环境拆分为不同的文件。
- 支持为缺失的翻译指定回退语言环境链。
- 支持自动查找语言地区的回退语言环境。例如,如果
zh-CN不可用,它将回退到zh。 - 支持短哈希键,以优化内存使用和查找速度。(自 v3.1.0 起)
- 支持在 [
t!] 中使用格式化变量,并支持使用std::fmt语法的格式化变量。(自 v3.1.0 起) - 支持通过
log-miss-tr功能以警告级别记录缺失的翻译,该功能需要logcrate。(自 v3.1.0 起)
使用方法
在 Cargo.toml 中添加 crate 依赖并设置 I18n 配置:
toml
[dependencies]
rust-i18n = "3"
在 lib.rs 或 main.rs 中加载宏并初始化翻译:
rust,compile_fail,no_run
// 加载 I18n 宏,允许你在任何地方使用 `t!` 宏。
#[macro_use]
extern crate rust_i18n;
// 为当前 crate 初始化翻译。
// 如果 `Cargo.toml` 中存在 `[package.metadata.i18n]` 配置,这将加载该配置。
// 或者,你可以通过 `i18n!` 传递参数来覆盖它。
i18n!("locales");
// 配置将缺失的翻译回退到 "en" 语言环境。
// 使用 `fallback` 选项设置回退语言环境。
//
i18n!("locales", fallback = "en");
// 或者设置多个具有优先级的回退语言环境。
//
i18n!("locales", fallback = ["en", "es"]);
// 对于长字符串字面量,使用短哈希键作为标识符,
// 以优化内存使用和查找速度。
// 密钥生成算法为 `${Prefix}${Base62(SipHash13("msg"))}`。
i18n!("locales", minify_key = true);
//
// 或者,你可以自定义短哈希键的密钥长度、前缀和阈值。
i18n!("locales",
minify_key = true,
minify_key_len = 12,
minify_key_prefix = "t_",
minify_key_thresh = 64
);
// 现在,如果消息长度超过 64,`t!` 宏将自动为它生成一个带有 "t_" 前缀的 12 字节短哈希键,
// 否则,它将使用原始消息。
// 如果没有参数,则使用 Cargo.toml 中的配置或默认配置。
i18n!();
或者你也可以直接导入使用:
rust,no_run
// 当你想要使用 `t!` 宏时,必须在每个文件中导入。
use rust_i18n::t;
rust_i18n::i18n!("locales");
fn main() {
// 使用手动提供的键 `hello` 查找字符串字面量 `Hello` 的翻译。
println!("{}", t!("hello"));
// 使用 `available_locales!` 方法获取所有可用的语言环境。
println!("{:?}", rust_i18n::available_locales!());
}
语言环境文件
你可以使用 _version 键来指定语言环境文件的版本(此版本是语言环境文件的版本,而不是 rust-i18n 的版本),默认值为 1。
rust-i18n 支持两种风格的配置文件,并且这些版本将始终保持。
_version: 1- 将每个语言环境拆分为不同的文件,当你的项目想要拆分翻译工作时,这很有用。_version: 2- 将所有本地化文本放在同一个文件中,这样可以方便地通过 AI(例如:GitHub Copilot)快速翻译。当你编写原始文本时,只需按 Enter 键,AI 就会为你建议其他语言的翻译文本。
你可以根据需要选择。
将本地化文本拆分到不同文件中
_version: 1
你也可以将每种语言拆分为不同的文件,并且可以选择(YAML、JSON、TOML),例如:en.json:
bash
.
├── Cargo.lock
├── Cargo.toml
├── locales
│ ├── zh-CN.yml
│ ├── en.yml
└── src
│ └── main.rs
yml
_version: 1
hello: "Hello world"
messages.hello: "Hello, %{name}"
t_4Cct6Q289b12SkvF47dXIx: "Hello, %{name}"
或者使用 JSON 或 TOML 格式,只需将文件重命名为 en.json 或 en.toml,内容如下:
json
{
"_version": 1,
"hello": "Hello world",
"messages.hello": "Hello, %{name}",
"t_4Cct6Q289b12SkvF47dXIx": "Hello, %{name}"
}
toml
hello = "Hello world"
t_4Cct6Q289b12SkvF47dXIx = "Hello, %{name}"
[messages]
hello = "Hello, %{name}"
所有本地化文本放在一个文件中
_version: 2
确保所有本地化文件(包含本地化映射)都位于项目根目录的 locales/ 文件夹中:
bash
.
├── Cargo.lock
├── Cargo.toml
├── locales
│ ├── app.yml
│ ├── some-module.yml
└── src
│ └── main.rs
└── sub_app
│ └── locales
│ │ └── app.yml
│ └── src
│ │ └── main.rs
│ └── Cargo.toml
在本地化文件中,指定本地化键及其对应的值,例如,在 app.yml 中:
yml
_version: 2
hello:
en: Hello world
zh-CN: 你好世界
messages.hello:
en: Hello, %{name}
zh-CN: 你好,%{name}
# 使用 `minify_key=true, minify_key_thresh=10` 生成短哈希键
t_4Cct6Q289b12SkvF47dXIx:
en: Hello, %{name}
zh-CN: 你好,%{name}
当你使用 GitHub Copilot 时,这很有用,你写完第一个翻译文本后,Copilot 会自动为你生成其他语言环境的翻译。

在 Rust 中获取本地化字符串
从该 crate 中将 [t!] 宏导入当前作用域:
rust,no_run
use rust_i18n::t;
然后,在需要本地化字符串的地方直接使用它:
rust,no_run
# macro_rules! t {
# ($($all_tokens:tt)*) => {}
# }
# fn main() {
// use rust_i18n::t;
t!("hello");
// => "Hello world"
t!("hello", locale = "zh-CN");
// => "你好世界"
t!("messages.hello", name = "world");
// => "Hello, world"
t!("messages.hello", "name" => "world");
// => "Hello, world"
t!("messages.hello", locale = "zh-CN", name = "Jason", count = 2);
// => "你好,Jason (2)"
t!("messages.hello", locale = "zh-CN", "name" => "Jason", "count" => 3 + 2);
// => "你好,Jason (5)"
t!("Hello, %{name}, you serial number is: %{sn}", name = "Jason", sn = 123 : {:08});
// => "Hello, Jason, you serial number is: 000000123"
# }
当前语言环境
你可以使用 rust_i18n::set_locale() 在运行时设置全局语言环境,这样你就不需要在每次调用 [t!] 时指定语言环境。
rust
rust_i18n::set_locale("zh-CN");
let locale = rust_i18n::locale();
assert_eq!(&*locale, "zh-CN");
扩展后端
自 v2.0.0 起,rust-i18n 支持扩展后端,以便自定义你的翻译实现。
例如,你可以使用 HTTP API 从远程服务器加载翻译:
rust,no_run
# pub mod reqwest {
# pub mod blocking {
# pub struct Response;
# impl Response {
# pub fn text(&self) -> Result<String, Box<dyn std::error::Error>> { todo!() }
# }
# pub fn get(_url: &str) -> Result<Response, Box<dyn std::error::Error>> { todo!() }
# }
# }
# use std::collections::HashMap;
# use std::borrow::Cow;
use rust_i18n::Backend;
pub struct RemoteI18n {
trs: HashMap<String, HashMap<String, String>>,
}
impl RemoteI18n {
fn new() -> Self {
// 从远程 URL 获取翻译
let response = reqwest::blocking::get("https://your-host.com/assets/locales.yml").unwrap();
let trs = serde_yaml::from_str::<HashMap<String, HashMap<String, String>>>(&response.text().unwrap()).unwrap();
return Self {
trs
};
}
}
impl Backend for RemoteI18n {
fn available_locales(&self) -> Vec<Cow<'_, str>> {
return self.trs.keys().map(|k| Cow::from(k.as_str())).collect();
}
fn translate(&self, locale: &str, key: &str) -> Option<Cow<'_, str>> {
// 在这里编写你自己的查找逻辑。
// 例如,从数据库加载
return self.trs.get(locale)?.get(key).map(|k| Cow::from(k.as_str()));
}
fn messages_for_locale(&self, locale: &str) -> Option<Vec<(Cow<'_, str>, Cow<'_, str>)>> {
None
}
}
现在你可以通过扩展你自己的后端来初始化 rust_i18n:
rust,no_run
# use std::borrow::Cow;
# struct RemoteI18n;
# impl RemoteI18n {
# fn new() -> Self { todo!() }
# }
# impl rust_i18n::Backend for RemoteI18n {
# fn available_locales(&self) -> Vec<std::borrow::Cow<'_, str>> { todo!() }
# fn translate(&self, locale: &str, key: &str) -> Option<std::borrow::Cow<'_, str>> { todo!() }
fn messages_for_locale(&self, locale: &str) -> Option<Vec<(Cow<'_, str>, Cow<'_, str>)>> { todo!() }
# }
rust_i18n::i18n!("locales", backend = RemoteI18n::new());
这也会从 ./locales 路径加载本地翻译,但你自己的 RemoteI18n 优先级更高。
现在你调用 [t!] 将首先从你自己的后端查找翻译,如果找不到,则会从本地文件中查找。
示例
一个使用 rust-i18n 的最小示例可以在这里找到。
I18n Ally
I18n Ally 是一个 VS Code 扩展,可以帮助你翻译 Rust 项目。
你可以将 i18n-ally-custom-framework.yml 添加到你的项目 .vscode 目录中,然后使用 I18n Ally 可以解析 t! 宏,从而在 VS Code 编辑器中显示翻译文本。
提取器
实验性功能
我们提供了一个 cargo i18n 命令行工具,帮助你从源代码中提取未翻译的文本,然后写入 YAML 文件。
目前仅输出 YAML,并使用
_version: 2格式。
你可以通过 cargo install rust-i18n-cli 安装它,然后你就获得了 cargo i18n 命令。
bash
$ cargo install rust-i18n-cli
提取器配置
💡 注意:Cargo.toml 中的 package.metadata.i18n 配置部分仅适用于 cargo i18n 命令,如果你不使用该命令,则不需要此配置。
toml
[package.metadata.i18n]
# 你的应用程序可用的语言环境,默认值:["en"]。
# available-locales = ["en", "zh-CN"]
# 默认语言环境,默认值:"en"。
# default-locale = "en"
# 你的翻译 YAML 文件的路径,默认值:"locales"。
# 此配置用于让 `cargo i18n` 命令行工具知道在哪里找到你的翻译。
# 你必须保持此路径与传递给 `rust_i18n::i18n!` 方法的路径一致。
# load-path = "locales"
Rust I18n 提供了一个 i18n 二进制文件,帮助你从源代码中提取未翻译的文本,然后写入 YAML 文件。
bash
$ cargo install rust-i18n-cli
# 现在你有了 `cargo i18n` 命令
之后,未翻译的文本将被提取并保存到 locales/TODO.en.yml 文件中。
你还可以使用 --locale 选项指定语言环境:
bash
$ cd your_project_root_directory
$ cargo i18n
Checking [en] and generating untranslated texts...
Found 1 new texts need to translate.
----------------------------------------
Writing to TODO.en.yml
Checking [fr] and generating untranslated texts...
Found 11 new texts need to translate.
----------------------------------------
Writing to TODO.fr.yml
Checking [zh-CN] and generating untranslated texts...
All thing done.
Checking [zh-HK] and generating untranslated texts...
Found 11 new texts need to translate.
----------------------------------------
Writing to TODO.zh-HK.yml
运行 cargo i18n -h 查看详细信息。
bash
$ cargo i18n -h
cargo-i18n 3.1.0
---------------------------------------
Rust I18n 命令,帮助你从源代码中提取所有未翻译的文本。
它将遍历源目录中的所有 Rust 文件,并提取所有使用 `t!` 宏的未翻译文本。然后它将生成一个 YAML 文件并与现有翻译合并。
https://github.com/longbridge/rust-i18n
用法:cargo i18n [OPTIONS] [-- <SOURCE>]
参数:
[SOURCE]
从源代码中提取所有未翻译的 I18n 文本
[默认值:./]
选项:
-t, --translate <TEXT>...
手动向本地化文件添加翻译。
这对于 `t!` 宏中的非字面量值很有用。
例如,如果你的代码中有 `t!(format!("Hello, {}!", "world"))`,
你可以使用 `-t "Hello, world!"` 为其添加翻译,
或者使用 `-t "Hello, world! => Hola, world!"` 提供翻译后的消息。
注意:键和值前后的空白将被修剪。
-h, --help
打印帮助信息(使用 '-h' 查看摘要)
-V, --version
打印版本
调试代码生成过程
可以使用 RUST_I18N_DEBUG 环境变量在编译时打印一些调试信息。
bash
$ RUST_I18N_DEBUG=1 cargo build
基准测试
对 [t!] 方法进行基准测试,结果在 MacBook Pro(2023,Apple M3)上:
bash
t 时间:[32.637 ns 33.139 ns 33.613 ns]
t_with_locale 时间:[24.616 ns 24.812 ns 25.071 ns]
t_with_args 时间:[128.70 ns 128.97 ns 129.24 ns]
t_with_args (字符串) 时间:[129.48 ns 130.08 ns 130.76 ns]
t_with_args (多个) 时间:[370.28 ns 374.46 ns 380.56 ns]
t_with_threads 时间:[38.619 ns 39.506 ns 40.419 ns]
t_lorem_ipsum 时间:[33.867 ns 34.286 ns 34.751 ns]
结果 101 ns (0.0001 ms) 意味着如果有 10K 个翻译文本,将花费 1ms。
使用案例
以下是一些使用 rust-i18n 的热门项目:
- gpui-component - ~10K star
- hyperswitch - 40K star
- Seelen-UI - ~16K star
- EasyTier - ~10K star
- trippy - 6.5K star
- fresh - 5.3K star