use std::sync::Arc;
use indexmap::IndexMap;
pub trait Lang: Send + Sync {
fn has(&self, name: &str) -> bool;
fn get(&self, name: &str) -> String;
}
#[derive(Debug, Clone, Default)]
pub struct SimpleLang {
pack: IndexMap<String, String>,
}
impl SimpleLang {
pub fn new() -> Self {
Self::default()
}
pub fn set(mut self, name: &str, value: &str) -> Self {
self.pack.insert(name.to_lowercase(), value.to_string());
self
}
pub fn extend(mut self, entries: IndexMap<String, String>) -> Self {
for (k, v) in entries {
self.pack.insert(k.to_lowercase(), v);
}
self
}
}
impl Lang for SimpleLang {
fn has(&self, name: &str) -> bool {
self.pack.contains_key(&name.to_lowercase())
}
fn get(&self, name: &str) -> String {
self.pack
.get(&name.to_lowercase())
.cloned()
.unwrap_or_else(|| name.to_string())
}
}
pub fn translate_msg(msg: &str, lang: Option<&Arc<dyn Lang>>) -> String {
let Some(lang) = lang else {
return msg.to_string();
};
if let Some(stripped) = msg.strip_prefix("{%").and_then(|s| s.strip_suffix('}')) {
if !stripped.is_empty() {
return lang.get(stripped);
}
}
if lang.has(msg) {
return lang.get(msg);
}
msg.to_string()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_simple_lang_new_empty() {
let lang = SimpleLang::new();
assert!(!lang.has("any"));
assert_eq!(lang.get("any"), "any");
}
#[test]
fn test_simple_lang_set_and_get() {
let lang = SimpleLang::new().set("require", "必须填写");
assert!(lang.has("require"));
assert_eq!(lang.get("require"), "必须填写");
}
#[test]
fn test_simple_lang_case_insensitive() {
let lang = SimpleLang::new().set("Require", "必须填写");
assert!(lang.has("require"));
assert!(lang.has("REQUIRE"));
assert!(lang.has("Require"));
assert_eq!(lang.get("REQUIRE"), "必须填写");
}
#[test]
fn test_simple_lang_not_found_returns_name() {
let lang = SimpleLang::new().set("require", "必须填写");
assert_eq!(lang.get("not_found"), "not_found");
}
#[test]
fn test_simple_lang_chained_set() {
let lang = SimpleLang::new()
.set("require", "必须填写")
.set("email", "邮箱格式错误");
assert_eq!(lang.get("require"), "必须填写");
assert_eq!(lang.get("email"), "邮箱格式错误");
}
#[test]
fn test_simple_lang_extend() {
let mut entries = IndexMap::new();
entries.insert("require".to_string(), "必须".to_string());
entries.insert("email".to_string(), "邮箱错误".to_string());
let lang = SimpleLang::new().extend(entries);
assert_eq!(lang.get("require"), "必须");
assert_eq!(lang.get("email"), "邮箱错误");
}
#[test]
fn test_simple_lang_override() {
let lang = SimpleLang::new()
.set("require", "旧值")
.set("require", "新值");
assert_eq!(lang.get("require"), "新值");
}
#[test]
fn test_translate_msg_none_lang_returns_original() {
let result = translate_msg("hello world", None);
assert_eq!(result, "hello world");
}
#[test]
fn test_translate_msg_percent_var_syntax() {
let lang: Arc<dyn Lang> = Arc::new(SimpleLang::new().set("hello", "你好"));
let result = translate_msg("{%hello}", Some(&lang));
assert_eq!(result, "你好");
}
#[test]
fn test_translate_msg_percent_var_not_found_returns_key() {
let lang: Arc<dyn Lang> = Arc::new(SimpleLang::new());
let result = translate_msg("{%not_found}", Some(&lang));
assert_eq!(result, "not_found");
}
#[test]
fn test_translate_msg_lang_has_check() {
let lang: Arc<dyn Lang> = Arc::new(SimpleLang::new().set("require", "必须填写"));
let result = translate_msg("require", Some(&lang));
assert_eq!(result, "必须填写");
}
#[test]
fn test_translate_msg_no_match_returns_original() {
let lang: Arc<dyn Lang> = Arc::new(SimpleLang::new().set("require", "必须填写"));
let result = translate_msg("custom message", Some(&lang));
assert_eq!(result, "custom message");
}
#[test]
fn test_translate_msg_percent_var_priority_over_has() {
let lang: Arc<dyn Lang> = Arc::new(
SimpleLang::new()
.set("require", "直接翻译")
.set("custom_key", "key翻译"),
);
let result = translate_msg("{%custom_key}", Some(&lang));
assert_eq!(result, "key翻译");
}
#[test]
fn test_translate_msg_empty_percent_var() {
let lang: Arc<dyn Lang> = Arc::new(SimpleLang::new());
let result = translate_msg("{}", Some(&lang));
assert_eq!(result, "{}");
}
#[test]
fn test_translate_msg_only_prefix() {
let lang: Arc<dyn Lang> = Arc::new(SimpleLang::new());
let result = translate_msg("{%", Some(&lang));
assert_eq!(result, "{%");
}
#[test]
fn test_translate_msg_percent_var_with_special_chars() {
let lang: Arc<dyn Lang> = Arc::new(SimpleLang::new().set("foo.bar", "点号键"));
let result = translate_msg("{%foo.bar}", Some(&lang));
assert_eq!(result, "点号键");
}
#[test]
fn test_php_behavior_lang_case_insensitive() {
let lang: Arc<dyn Lang> = Arc::new(SimpleLang::new().set("Hello", "你好"));
assert!(lang.has("hello"));
assert!(lang.has("HELLO"));
assert_eq!(lang.get("hello"), "你好");
assert_eq!(lang.get("HELLO"), "你好");
}
#[test]
fn test_php_behavior_lang_get_returns_name_when_not_found() {
let lang: Arc<dyn Lang> = Arc::new(SimpleLang::new());
assert_eq!(lang.get("nonexistent"), "nonexistent");
assert_eq!(lang.get("Some Key"), "Some Key");
}
#[test]
fn test_php_behavior_translate_percent_var_extracts_key() {
let lang: Arc<dyn Lang> = Arc::new(SimpleLang::new().set("my_key", "翻译值"));
let result = translate_msg("{%my_key}", Some(&lang));
assert_eq!(result, "翻译值");
}
#[test]
fn test_php_behavior_translate_has_check_falls_back_to_original() {
let lang: Arc<dyn Lang> = Arc::new(SimpleLang::new());
let result = translate_msg("plain message", Some(&lang));
assert_eq!(result, "plain message");
}
#[test]
fn test_php_behavior_translate_priority_percent_over_has() {
let lang: Arc<dyn Lang> = Arc::new(
SimpleLang::new()
.set("{%key}", "整体键值") .set("key", "提取键值"),
);
let result = translate_msg("{%key}", Some(&lang));
assert_eq!(result, "提取键值");
}
}