4个稳定版本
新版本 3.1.1 | 2024年8月9日 |
---|---|
3.0.0 | 2024年1月19日 |
2.4.0 |
|
2.3.0 | 2023年11月14日 |
145 在 国际化(i18n) 中排名
每月212次下载
100KB
1.5K SLoC
Rust I18n
🎯 让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
库。 (自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 的最小示例可以在 这里 找到。
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!
] 方法,在 MacBook Pro (2023, 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
依赖关系
~10–21MB
~299K SLoC