98 lines
3.3 KiB
Rust
98 lines
3.3 KiB
Rust
extern crate self as i18n;
|
||
|
||
use std::{collections::HashMap, sync::OnceLock};
|
||
|
||
use message_ids::MessageId;
|
||
use strum::IntoEnumIterator;
|
||
|
||
pub mod message_ids;
|
||
|
||
const I18N_FILE: &str = "i18n.csv";
|
||
static I18N: OnceLock<HashMap<&'static str, HashMap<&'static str, &'static str>>> = OnceLock::new();
|
||
|
||
fn init() -> HashMap<&'static str, HashMap<&'static str, &'static str>> {
|
||
let mut i18n_map = HashMap::new();
|
||
let mut i18n_id_map = HashMap::new();
|
||
let mut rdr = csv::Reader::from_path(I18N_FILE).expect("读取多语言文件失败");
|
||
let headers = rdr.headers().expect("解析多语言文件失败");
|
||
let header_length = headers.len();
|
||
for i in 1..header_length {
|
||
let lang_id = headers.get(i).unwrap();
|
||
i18n_id_map.insert(i, lang_id.to_string());
|
||
}
|
||
|
||
for result in rdr.deserialize() {
|
||
let record: Vec<String> = result.expect("解析多语言文件失败");
|
||
let column_length = record.len();
|
||
let message_id: &'static str = Box::leak(Box::new(record[0].clone()));
|
||
for i in 1..column_length {
|
||
let lang_id: &'static str = Box::leak(Box::new(i18n_id_map.get(&i).unwrap().clone()));
|
||
let message: &'static str = Box::leak(Box::new(record[i].clone()));
|
||
i18n_map
|
||
.entry(lang_id)
|
||
.or_insert_with(HashMap::new)
|
||
.insert(message_id, message);
|
||
}
|
||
}
|
||
|
||
// 检查多语言文件是否完整
|
||
for (lang_id, lang_map) in i18n_map.iter() {
|
||
MessageId::iter().for_each(|message_id| {
|
||
if !lang_map.contains_key(message_id.as_ref()) {
|
||
tracing::error!("多语言文件缺失消息:{},语言:{}", message_id, lang_id);
|
||
}
|
||
});
|
||
}
|
||
i18n_map
|
||
}
|
||
|
||
fn init_i18n() -> &'static HashMap<&'static str, HashMap<&'static str, &'static str>> {
|
||
I18N.get_or_init(init)
|
||
}
|
||
|
||
/// 获取语言模板
|
||
pub fn lang(lang_id: &str, message_id: MessageId) -> &'static str {
|
||
init_i18n()
|
||
.get(lang_id)
|
||
.and_then(|map| map.get(message_id.as_ref()))
|
||
.unwrap_or_else(|| &"UNKNOWN")
|
||
}
|
||
|
||
/// 将模板消息转换为最终消息,支持模板占位符
|
||
pub fn convert_message_with_params(message_template: &'static str, args: Vec<&str>) -> String {
|
||
if message_template.is_empty() {
|
||
return String::new();
|
||
}
|
||
|
||
if message_template.contains("{}") {
|
||
let message_split_array: Vec<&str> = message_template.split("{}").collect();
|
||
let capacity = message_template.len() + args.iter().map(|s| s.len()).sum::<usize>();
|
||
let mut message = String::with_capacity(capacity);
|
||
|
||
message_split_array.iter().zip(args.iter().chain(std::iter::once(&""))).for_each(|(part, arg)| {
|
||
message.push_str(part);
|
||
message.push_str(arg);
|
||
});
|
||
|
||
message
|
||
} else {
|
||
String::from(message_template)
|
||
}
|
||
}
|
||
|
||
#[macro_export]
|
||
macro_rules! message {
|
||
// 通过lang id和message id获取消息
|
||
($lang_id:expr, $message_id:expr) => {
|
||
i18n::lang($lang_id, $message_id)
|
||
};
|
||
// 通过lang id和message id获取消息,并将参数格式化到消息中
|
||
($lang_id:expr, $message_id:expr, $($arg:expr),*) => {
|
||
{
|
||
let message_template: &'static str = i18n::lang($lang_id, $message_id);
|
||
let args = vec![$($arg),*];
|
||
i18n::convert_message_with_params(message_template, args)
|
||
}
|
||
};
|
||
}
|