1use serde_json::Value;
2use std::collections::HashMap;
3use teaql_tool_core::{Result, TeaQLToolError};
4
5#[derive(Debug, Clone)]
6pub struct I18nTool {
7 translations: HashMap<String, HashMap<String, String>>,
8 default_locale: String,
9}
10
11impl I18nTool {
12 pub fn new() -> Self {
13 Self {
14 translations: HashMap::new(),
15 default_locale: "en".to_string(),
16 }
17 }
18
19 pub fn set_default_locale(&mut self, locale: &str) {
20 self.default_locale = locale.to_string();
21 }
22
23 pub fn add(&mut self, locale: &str, key: &str, value: &str) {
24 self.translations
25 .entry(locale.to_string())
26 .or_default()
27 .insert(key.to_string(), value.to_string());
28 }
29
30 pub fn load_json(&mut self, locale: &str, json_str: &str) -> Result<()> {
33 let v: Value = serde_json::from_str(json_str)
34 .map_err(|e| TeaQLToolError::ParseError(format!("Invalid JSON for i18n: {}", e)))?;
35
36 self.flatten_and_add(locale, "", &v);
37 Ok(())
38 }
39
40 fn flatten_and_add(&mut self, locale: &str, prefix: &str, value: &Value) {
41 match value {
42 Value::Object(map) => {
43 for (k, v) in map {
44 let new_prefix = if prefix.is_empty() {
45 k.clone()
46 } else {
47 format!("{}.{}", prefix, k)
48 };
49 self.flatten_and_add(locale, &new_prefix, v);
50 }
51 }
52 Value::String(s) => {
53 self.add(locale, prefix, s);
54 }
55 Value::Number(n) => {
56 self.add(locale, prefix, &n.to_string());
57 }
58 Value::Bool(b) => {
59 self.add(locale, prefix, &b.to_string());
60 }
61 _ => {}
62 }
63 }
64
65 pub fn t(&self, locale: &str, key: &str) -> String {
68 if let Some(map) = self.translations.get(locale) {
69 if let Some(val) = map.get(key) {
70 return val.clone();
71 }
72 }
73
74 if locale != self.default_locale {
75 if let Some(map) = self.translations.get(&self.default_locale) {
76 if let Some(val) = map.get(key) {
77 return val.clone();
78 }
79 }
80 }
81
82 key.to_string()
83 }
84
85 pub fn tf(&self, locale: &str, key: &str, args: &[(&str, &str)]) -> String {
87 let mut text = self.t(locale, key);
88 for (k, v) in args {
89 let placeholder = format!("{{{}}}", k);
90 text = text.replace(&placeholder, v);
91 }
92 text
93 }
94}
95
96impl Default for I18nTool {
97 fn default() -> Self {
98 Self::new()
99 }
100}