40 个版本 (21 个稳定版)
新 3.1.2 | 2024 年 8 月 20 日 |
---|---|
3.0.1 | 2024 年 1 月 23 日 |
2.4.0 |
|
2.3.0 | 2023 年 11 月 14 日 |
0.5.1 | 2021 年 12 月 10 日 |
#7 in 国际化 (i18n)
每月 15,891 次下载
在 16 个包中使用 (15 个直接使用)
79KB
1K SLoC
Rust I18n
🎯 让国际化变得简单!
Rust I18n 是一个用于从一组 (YAML、JSON 或 TOML) 映射文件加载本地化文本的包。这些映射在编译时转换为 Rust 程序可读的数据,然后可以通过调用提供的 [t!
] 宏来简单地加载本地化文本。
与其他 I18n 库不同,Rust I18n 的目标是提供简单易用的 API。
此包的 API 受 ruby-i18n 和 Rails I18n 的启发。
特性
- 编译时代码生成,将翻译包含到二进制文件中。
- 全局 [
t!
] 宏,用于在所有地方加载本地化文本。 - 使用 YAML(默认)、JSON 或 TOML 格式映射本地化文本,并支持多文件合并。
cargo i18n
命令行工具,用于检查和提取未翻译文本到 YAML 文件。- 支持在一个文件中包含所有本地化文本,或按区域拆分到不同的文件中。
- 支持指定缺失翻译的回退区域链。
- 支持自动查找回退区域的语言区域。例如,如果
zh-CN
不可用,它将回退到zh
。 (自 v2.4.0) - 支持短哈希键以优化内存使用和查找速度。 (自 v3.1.0)
- 支持在 [
t!
] 中使用格式变量,并支持使用std::fmt
语法进行格式变量。 (自 v3.1.0) - 支持在警告级别使用
log-miss-tr
功能记录缺失的翻译,该功能需要log
crate。 (自 v3.1.0 版本起)
用法
在 Cargo.toml 中添加 crate 依赖并设置 I18n 配置
[dependencies]
rust-i18n = "3"
在 lib.rs
或 main.rs
中加载宏并初始化翻译
// Load I18n macro, for allow you use `t!` macro in anywhere.
#[macro_use]
extern crate rust_i18n;
// Init translations for current crate.
// This will load Configuration using the `[package.metadata.i18n]` section in `Cargo.toml` if exists.
// Or you can pass arguments by `i18n!` to override it.
i18n!("locales");
// Config fallback missing translations to "en" locale.
// Use `fallback` option to set fallback locale.
//
i18n!("locales", fallback = "en");
// Or more than one fallback with priority.
//
i18n!("locales", fallback = ["en", "es"]);
// Use a short hashed key as an identifier for long string literals
// to optimize memory usage and lookup speed.
// The key generation algorithm is `${Prefix}${Base62(SipHash13("msg"))}`.
i18n!("locales", minify_key = true);
//
// Alternatively, you can customize the key length, prefix,
// and threshold for the short hashed key.
i18n!("locales",
minify_key = true,
minify_key_len = 12,
minify_key_prefix = "t_",
minify_key_thresh = 64
);
// Now, if the message length exceeds 64, the `t!` macro will automatically generate
// a 12-byte short hashed key with a "t_" prefix for it, if not, it will use the original.
// If no any argument, use config from Cargo.toml or default.
i18n!();
或者您可以直接导入使用
// You must import in each files when you wants use `t!` macro.
use rust_i18n::t;
rust_i18n::i18n!("locales");
fn main() {
// Find the translation for the string literal `Hello` using the manually provided key `hello`.
println!("{}", t!("hello"));
// Use `available_locales!` method to get all available locales.
println!("{:?}", rust_i18n::available_locales!());
}
地区文件
您可以使用 _version
键来指定地区文件的版本(此版本是地区文件的版本,而不是 rust-i18n 的版本),默认值为 1
。
rust-i18n 支持两种配置文件风格,这些版本将始终保留。
_version: 1
- 将每个地区拆分为不同的文件,当您的项目需要拆分翻译工作时很有用。_verison: 2
- 将所有本地化文本放入同一文件中,使用 AI(例如:GitHub Copilot)快速翻译非常容易。当您编写原始文本时,只需按 Enter 键,然后 AI 将为您建议其他语言的翻译文本。
您可以根据自己的喜好选择。
将本地化文本拆分为不同的文件
_version: 1
您也可以将每种语言拆分为不同的文件,您可以选择 YAML、JSON 或 TOML 格式,例如:en.json
.
├── Cargo.lock
├── Cargo.toml
├── locales
│ ├── zh-CN.yml
│ ├── en.yml
└── src
│ └── main.rs
_version: 1
hello: "Hello world"
messages.hello: "Hello, %{name}"
t_4Cct6Q289b12SkvF47dXIx: "Hello, %{name}"
或者使用 JSON 或 TOML 格式,只需将文件重命名为 en.json
或 en.toml
,内容如下
{
"_version": 1,
"hello": "Hello world",
"messages.hello": "Hello, %{name}",
"t_4Cct6Q289b12SkvF47dXIx": "Hello, %{name}"
}
hello = "Hello world"
t_4Cct6Q289b12SkvF47dXIx = "Hello, %{name}"
[messages]
hello = "Hello, %{name}"
在一个文件中包含所有本地化文本
_version: 2
确保所有包含本地化映射的本地化文件都位于项目根目录下的 locales/
文件夹中
.
├── 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
中
_version: 2
hello:
en: Hello world
zh-CN: 你好世界
messages.hello:
en: Hello, %{name}
zh-CN: 你好,%{name}
# Generate short hashed keys using `minify_key=true, minify_key_thresh=10`
t_4Cct6Q289b12SkvF47dXIx:
en: Hello, %{name}
zh-CN: 你好,%{name}
当您使用 GitHub Copilot 时,这非常有用。在您编写第一个翻译文本后,Copilot 将自动为您生成其他语言的翻译。
在 Rust 中获取本地化字符串
将此 crate 中的 [t!
] 宏导入到当前作用域中
use rust_i18n::t;
然后,在需要本地化字符串的地方简单地使用它
# 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_i18n::set_locale("zh-CN");
let locale = rust_i18n::locale();
assert_eq!(&*locale, "zh-CN");
扩展后端
自 v2.0.0 版本起,rust-i18n 支持扩展后端以自定义您的翻译实现。
例如,您可以使用 HTTP API 从远程服务器加载翻译
# 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 rust_i18n::Backend;
pub struct RemoteI18n {
trs: HashMap<String, HashMap<String, String>>,
}
impl RemoteI18n {
fn new() -> Self {
// fetch translations from remote URL
let response = reqwest::blocking::get("https://your-host.com/assets/locales.yml").unwrap();
let trs = serde_yml::from_str::<HashMap<String, HashMap<String, String>>>(&response.text().unwrap()).unwrap();
return Self {
trs
};
}
}
impl Backend for RemoteI18n {
fn available_locales(&self) -> Vec<&str> {
return self.trs.keys().map(|k| k.as_str()).collect();
}
fn translate(&self, locale: &str, key: &str) -> Option<&str> {
// Write your own lookup logic here.
// For example load from database
return self.trs.get(locale)?.get(key).map(|k| k.as_str());
}
}
现在您可以通过扩展自己的后端来初始化 rust_i18n
# struct RemoteI18n;
# impl RemoteI18n {
# fn new() -> Self { todo!() }
# }
# impl rust_i18n::Backend for RemoteI18n {
# fn available_locales(&self) -> Vec<&str> { todo!() }
# fn translate(&self, locale: &str, key: &str) -> Option<&str> { todo!() }
# }
rust_i18n::i18n!("locales", backend = RemoteI18n::new());
这也会从 ./locales 路径加载本地翻译,但您的 RemoteI18n
将具有优先权。
现在您调用 [t!
] 将首先从您自己的后端查找翻译,如果未找到,则从本地文件中查找。
示例
可以在这里找到使用 rust-i18n 的最小示例 here。
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
命令。
$ cargo install rust-i18n-cli
提取器配置
💡 注意:在Cargo.toml中的package.metadata.i18n
配置部分仅适用于cargo i18n
命令,如果您不使用它,则不需要此配置。
[package.metadata.i18n]
# The available locales for your application, default: ["en"].
# available-locales = ["en", "zh-CN"]
# The default locale, default: "en".
# default-locale = "en"
# Path for your translations YAML file, default: "locales".
# This config for let `cargo i18n` command line tool know where to find your translations.
# You must keep this path same as the one you pass to method `rust_i18n::i18n!`.
# load-path = "locales"
Rust I18n提供了一个i18n
二进制文件,帮助您从源代码中提取未翻译的文本,并将其写入YAML文件。
$ cargo install rust-i18n-cli
# Now you have `cargo i18n` command
之后,未翻译的文本将被提取并保存到locales/TODO.en.yml
文件中。
您也可以通过使用--locale
选项来指定特定的区域设置
$ 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
以查看详细信息。
$ cargo i18n -h
cargo-i18n 3.1.0
---------------------------------------
Rust I18n command to help you extract all untranslated texts from source code.
It will iterate all Rust files in the source directory and extract all untranslated texts that used `t!` macro. Then it will generate a YAML file and merge with the existing translations.
https://github.com/longbridgeapp/rust-i18n
Usage: cargo i18n [OPTIONS] [-- <SOURCE>]
Arguments:
[SOURCE]
Extract all untranslated I18n texts from source code
[default: ./]
Options:
-t, --translate <TEXT>...
Manually add a translation to the localization file.
This is useful for non-literal values in the `t!` macro.
For example, if you have `t!(format!("Hello, {}!", "world"))` in your code,
you can add a translation for it using `-t "Hello, world!"`,
or provide a translated message using `-t "Hello, world! => Hola, world!"`.
NOTE: The whitespace before and after the key and value will be trimmed.
-h, --help
Print help (see a summary with '-h')
-V, --version
Print version
代码生成过程的调试
当在编译时生成代码时,可以使用RUST_I18N_DEBUG
环境变量来打印一些调试信息。
$ RUST_I18N_DEBUG=1 cargo build
基准测试
基准测试 [t!
] 方法,结果在2023年MacBook Pro (Apple M3)
t time: [32.637 ns 33.139 ns 33.613 ns]
t_with_locale time: [24.616 ns 24.812 ns 25.071 ns]
t_with_args time: [128.70 ns 128.97 ns 129.24 ns]
t_with_args (str) time: [129.48 ns 130.08 ns 130.76 ns]
t_with_args (many) time: [370.28 ns 374.46 ns 380.56 ns]
t_with_threads time: [38.619 ns 39.506 ns 40.419 ns]
t_lorem_ipsum time: [33.867 ns 34.286 ns 34.751 ns]
结果101 ns (0.0001 ms)
表示如果有10K
翻译文本,它将花费1ms
。
许可证
MIT
依赖关系
~9–20MB
~292K SLoC