Skip to main content

euv_ui/hook/i18n/
impl.rs

1use super::*;
2
3/// Implements [`HookContextI18nExt`] for [`HookContext`].
4impl HookContextI18nExt for HookContext {
5    /// Returns a fresh [`I18n`] bound to the current component scope.
6    ///
7    /// # Returns
8    ///
9    /// - `I18n` - A `I18n` value.
10    fn i18n() -> I18n {
11        HookContext::use_hook(|| {
12            I18n::new(
13                Signal::create(String::from("en")),
14                Signal::create(String::from("en")),
15                Signal::create(HashMap::new()),
16            )
17        })
18    }
19}
20
21/// Inherent implementation of [`I18n`].
22impl I18n {
23    /// Sets the active locale to `locale`. Triggers a
24    /// reactive update so any reactive `t(key)` read
25    /// re-evaluates.
26    ///
27    /// Named `change_locale` (not `set_locale`) to avoid
28    /// colliding with the `set_locale` getter generated by
29    /// `#[derive(Data)]` on the struct field.
30    ///
31    /// # Arguments
32    ///
33    /// - `&str` - Shared reference to a `str`.
34    pub fn change_locale(&self, locale: &str) {
35        self.get_locale().set(locale.to_string());
36    }
37
38    /// Sets the fallback locale. Trigger a reactive
39    /// update for any `t(key)` whose key is missing in
40    /// the active locale — they may now resolve to a
41    /// different fallback value.
42    ///
43    /// Named `change_fallback_locale` (not
44    /// `set_fallback_locale`) for the same reason as
45    /// `change_locale`.
46    ///
47    /// # Arguments
48    ///
49    /// - `&str` - Shared reference to a `str`.
50    pub fn change_fallback_locale(&self, locale: &str) {
51        self.get_fallback_locale().set(locale.to_string());
52    }
53
54    /// Adds a batch of `(key, message)` entries to the
55    /// translation table for `locale`. Existing entries
56    /// for that locale are overwritten (last-write-wins).
57    ///
58    /// # Arguments
59    ///
60    /// - `&str` - Shared reference to a `str`.
61    /// - `&[MessageEntry]` - Shared reference to a `[MessageEntry]`.
62    pub fn add_messages(&self, locale: &str, entries: &[MessageEntry]) {
63        let mut table: HashMap<String, HashMap<String, String>> = self.get_messages().get();
64        let entry_map: &mut HashMap<String, String> = table.entry(locale.to_string()).or_default();
65        for (key, value) in entries {
66            entry_map.insert((*key).to_string(), (*value).to_string());
67        }
68        self.get_messages().set(table);
69    }
70
71    /// Removes every entry for `locale`. After this
72    /// call, `t(key)` for any key will skip this locale
73    /// in its lookup chain.
74    ///
75    /// # Arguments
76    ///
77    /// - `&str` - Shared reference to a `str`.
78    pub fn remove_locale(&self, locale: &str) {
79        let mut table: HashMap<String, HashMap<String, String>> = self.get_messages().get();
80        table.remove(locale);
81        self.get_messages().set(table);
82    }
83
84    /// Removes a single message from a locale. After this
85    /// call, `t(key)` for this key in this locale will
86    /// fall back to `fallback_locale`.
87    ///
88    /// # Arguments
89    ///
90    /// - `&str` - Shared reference to a `str`.
91    /// - `&str` - Shared reference to a `str`.
92    pub fn remove_message(&self, locale: &str, key: &str) {
93        let mut table: HashMap<String, HashMap<String, String>> = self.get_messages().get();
94        if let Some(entry_map) = table.get_mut(locale) {
95            entry_map.remove(key);
96        }
97        self.get_messages().set(table);
98    }
99
100    /// Translates `key` to a string under the active
101    /// locale, falling back to `fallback_locale` if the
102    /// active locale has no entry. Returns `key` itself
103    /// if neither locale has an entry (debug-friendly).
104    ///
105    /// This is the reactive read — calling it inside a
106    /// render closure subscribes that closure to locale
107    /// changes (and to any subsequent edits to the
108    /// messages map for the active or fallback locale).
109    ///
110    /// # Arguments
111    ///
112    /// - `&str` - Shared reference to a `str`.
113    ///
114    /// # Returns
115    ///
116    /// - `String` - A `String` value.
117    pub fn t(&self, key: &str) -> String {
118        let active: String = self.get_locale().get();
119        let fallback: String = self.get_fallback_locale().get();
120        let table: HashMap<String, HashMap<String, String>> = self.get_messages().get();
121        if let Some(message) = table
122            .get(&active)
123            .and_then(|m: &HashMap<String, String>| m.get(key))
124        {
125            return message.clone();
126        }
127        if let Some(message) = table
128            .get(&fallback)
129            .and_then(|m: &HashMap<String, String>| m.get(key))
130        {
131            return message.clone();
132        }
133        key.to_string()
134    }
135
136    /// Translates `key` and substitutes `{name}`-style
137    /// placeholders from `vars`.
138    ///
139    /// Placeholders that are present in `vars` are
140    /// replaced with their corresponding value.
141    /// Placeholders that are missing from `vars` are left
142    /// as the literal `{name}` token — matching the
143    /// i18next default behavior. No escaping is
144    /// supported; add it when a real use case shows up.
145    ///
146    /// # Arguments
147    ///
148    /// - `&str` - Shared reference to a `str`.
149    /// - `&HashMap<&'static str, &'static str>` - Shared reference to a `HashMap<&'static str, &'static str>`.
150    ///
151    /// # Returns
152    ///
153    /// - `String` - A `String` value.
154    pub fn t_with(&self, key: &str, vars: &HashMap<&'static str, &'static str>) -> String {
155        let template: String = self.t(key);
156        interpolate(&template, vars)
157    }
158
159    /// Returns the number of locales currently registered
160    /// (i.e. the number of distinct keys in the messages
161    /// map's outer level).
162    ///
163    /// # Returns
164    ///
165    /// - `usize` - Count of registered locales.
166    pub fn locale_count(&self) -> usize {
167        self.get_messages().get().len()
168    }
169
170    /// Returns the number of messages registered for the
171    /// active locale.
172    ///
173    /// # Returns
174    ///
175    /// - `usize` - Count of currently-registered messages.
176    pub fn active_message_count(&self) -> usize {
177        let active: String = self.get_locale().get();
178        self.get_messages()
179            .get()
180            .get(&active)
181            .map(|m: &HashMap<String, String>| m.len())
182            .unwrap_or_default()
183    }
184}