3 个版本
0.1.2 | 2021年11月7日 |
---|---|
0.1.1 | 2021年11月6日 |
0.1.0 | 2021年10月31日 |
#150 in 国际化 (i18n)
44KB
555 行
简单国际化
依赖
基础
基本使用假设您在项目中不使用任何功能。
[dependencies]
sorrow-i18n = "0.1.0"
功能
incl_dir
一个简单的功能,用于将静态文件加载到项目中。
宏
如果使用 incl_dir
功能,添加了用于静态内核初始化的宏 init!
和 init_dir!
。初始化后,在任何地方使用宏: i18n!
文档
用法
基础用法与静态文件
首先在项目中添加依赖 sorrow-i18n
。文件结构的一个典型视图是 FileStructure
。数据方案 - scheme/locale-scheme.json
。因此,我们将创建实际的最小工作文件
kind: I18N
locale: EN
description: test en
data:
name: "Test"
其次,第二个本地化
kind: I18N
locale: RU
description: test ru
data:
name: "Тест"
现在是我们创建的代码库,我们将之前创建的文件放在其中,例如,在 locale/
。
use sorrow_i18n::{GetData, InternationalCore};
let core = InternationalCore::new("locale/");
创建核心后,我们可以获取我们的本地化并与之交互。
let eu = core.get_by_locale("EN")?;
let ru = core.get_by_locale("RU")?;
OR
let eu = core.get_by_locale("EN")?;
let ru = core.get_by_locale("RU")?;
这些方法之间有什么区别?首先,当调用 get_by_locale
方法时,返回可变数据的引用,而在 get_by_locale
的情况下,你只是获取数据的引用(在两种情况下,你都在与包装器一起工作,它不能从外部更改)。此外,在第一种方法中,如果你的本地化在程序执行期间可以更改,那么你会收到最新的数据,但会有阻塞的开销。另外,在第二种方法中,如果你的文件是静态的,被加载到项目中,并且不能更改,那么在这种情况下,你只需与 HashMap
一起工作。
但是,我们如何跟踪或选择文件跟踪策略?我们将如何获取这些更新?提供商将帮助我们完成这项工作!默认情况下,我们为您提供了几个此类提供商,具体如下
StaticFileProvider
- 静态文件。它没有被监控。如果文件结构中没有指定提供商,这是默认选项FileProvider
- 文件的动态监控器。如果未指定提供商,默认为StaticFileProvider
。问题仍然是,如何选择此提供商?很简单,下面是一个显式指定提供商的示例
kind: I18N
locale: RU
description: test ru
provider: FileProvider
data:
name: "Тест"
OR
kind: I18N
locale: RU
description: test ru
provider: StaticFileProvider
data:
name: "Тест"
您可以在下面了解更多有关提供商的信息。
最后,我们得到了我们的区域设置,接下来要得到我们想要的!即:data.name
。
assert_eq!("Test", eu.get("data.name")?);
assert_eq!("Тест", ru.get("data.name")?);
如您所见,一切都很简单,也有类似的默认值获取方法(这将与您请求的键相同)
assert_eq!("Test", eu.get_or_default("data.name"));
assert_eq!("Тест", ru.get_or_default("data.name"));
assert_eq!("keykey", eu.get_or_default("keykey"));
您可以在examples/*
中看到更多示例
提供者
如我们之前所述,提供者负责数据更新策略。它的主要方法是watch。
pub trait WatchProvider {
/// The main observer method that is called to observe the state.
fn watch(&mut self) -> Result<(), Error>;
/// Setter for data reference.
fn set_data(&mut self, data: Arc<RwLock<HashMap<String, String>>>) -> Result<(), Error>;
}
StaticFileProvider
它是观察数据变化的。对于静态观察者,我们有一个空方法,因为我们不需要观察。
impl WatchProvider for StaticFileProvider {
fn watch(&mut self) -> Result<(), Error> {
Ok(())
}
fn set_data(&mut self, _data: Arc<RwLock<HashMap<String, String>>>) -> Result<(), Error> {
Ok(())
}
}
FileProvider
但是,与FileProvider相比,并没有复杂多少。使用notify
库不断监视文件的状态。唯一跟踪的事件是文件更改。
let event = result.map_err(|e| Error::WatchError { message: e.to_string() }).unwrap();
if event.kind.is_modify() {
...
}
每次我们更改文件,我们都会对我们的数据进行锁定,但首先我们需要加载更新的文件本身(以验证结构)。实际上,以这种方式阻塞是非常非常困难的。
// Validation file
let structure = load_struct(&path.clone()).unwrap();
// Lock data and clear
let mut w_holder = holder.write().unwrap();
w_holder.clear();
// Clone internal state.
let l_holder = structure.messages.write().unwrap().clone();
w_holder.extend(l_holder);
自定义提供者
在某些情况下是必要的,例如,首先加载项目区域设置,然后保持与数据库或其他数据源的连接,以不断更新数据本身。为此,我们可以创建自己的数据提供者!最简单和说明性的示例在examples/custom_provider.rs
中。
好吧,现在,一点一点地,首先,让我们创建一个简单的结构来监视我们的数据。
pub struct CustomProvider {
data: Arc<RwLock<HashMap<String, String>>>,
}
我们将为它实现我们的提供者
impl WatchProvider for CustomProvider {
fn watch(&mut self) -> Result<(), sorrow_i18n::Error> {
println!("Accepted custom provider");
let data = self.data.write();
let mut un = data.unwrap();
// Print all current data
un.iter().for_each(|kv| {
println!("Key: {}, Value: {}", kv.0, kv.1);
});
// Add new key
un.insert("Hello".to_string(), "World".to_string());
// Print all data, current data has been contains key "Hello"
un.iter().for_each(|kv| {
println!("Key: {}, Value: {}", kv.0, kv.1);
});
Ok(())
}
fn set_data(&mut self, data: Arc<RwLock<HashMap<String, String>>>) -> Result<(), Error> {
self.data = data;
println!("Data has been set");
Ok(())
}
}
只剩下一点,将我们的数据提供者添加到任何区域。
let mut core = InternationalCore::new("locale/");
core.add_provider("EN", Box::new(CustomProvider::new()))?;
如果存在这样的区域,将执行以下操作
holder
-> 获取当前数据provider
->set_data(current_data_in_holder)
provider
->watch()
宏使用
添加依赖项
[dependencies]
sorrow-i18n = { version = "0.1.0", features = ["macro"] }
初始设置
有两种初始化方法(取决于是否包含incl_dir
)。
init_i18n!
- 允许您初始化i18n核心的宏。(InternationalCore
)- 使用示例:
init_i18n!("my_locale_folder");
- 使用示例:
init_i18n_static_dir!
- 与此相同,但仅针对incl_dir
功能。- 使用示例:
const PROJECT_DIR: Dir = include_dir!("resources/en_ru"); init_i18n_static_dir!(PROJECT_DIR);
- 使用示例:
用法
交互的主要宏将是i18n!
。第一个参数是区域,第二个参数是键。如果找不到区域或键,则返回键本身而不是值。
使用示例
let manifest = format!("{}{}", env!("CARGO_MANIFEST_DIR"), "/resources/en_ru");
// Init i18n core
init_i18n!(manifest);
// Getting data.name key by ru locale
let test = i18n!("RU", "data.name");
println!("test: {}", &*test);
assert_eq!("Тест", &*test);
找不到区域或键的情况
let not_found_data = i18n!("RANDOM_LOCALE_NAME", "data.not_found_me");
println!("data not found: {}", &*not_found_data);
assert_eq!("data.not_found_me", &*not_found_data);
incl_dir的使用
添加依赖项
[dependencies]
sorrow-i18n = { version = "0.1.0", features = ["incl_dir"] }
初始设置和使用
const PROJECT_DIR: Dir = include_dir!("resources/en_ru");
let core = InternationalCore::from(PROJECT_DIR);
依赖项
~3–13MB
~146K SLoC