euv_ui/hook/i18n/struct.rs
1use super::*;
2
3/// The aggregate i18n state.
4///
5/// Constructed once per app via `App::use_i18n()`, threaded
6/// through any code that needs to render translated text.
7/// Cheap to `Clone` (the internal signal is
8/// `Copy`-by-pointer).
9///
10/// # Storage
11///
12/// Messages are stored in a process-wide
13/// [`I18N_MESSAGES`] `OnceLock<RwLock<HashMap<...>>>` rather
14/// than a per-handle `Signal`. The translation table is
15/// not reactive on its own — only the `locale` field is —
16/// so wrapping the table in a signal forced every `t()`
17/// call to clone the entire `HashMap<String, HashMap<String,
18/// String>>`. Moving the table behind a `OnceLock` means:
19///
20/// - one allocation for the whole process (no per-handle
21/// `Signal::create(HashMap::new())`),
22/// - reads (`t`, `locale_count`, `active_message_count`)
23/// borrow through the read guard with zero clone,
24/// - writes (`add_messages`, `remove_locale`,
25/// `remove_message`) take the write guard once.
26///
27/// Process-wide translation table storage.
28///
29/// Backed by [`std::sync::OnceLock`] so the table is
30/// allocated lazily on first write/read and never torn
31/// down. Wrapped in a [`std::sync::RwLock`] because runtime
32/// mutation is supported — `add_messages`,
33/// `remove_locale`, and `remove_message` all write.
34///
35/// On WASM this is single-threaded so the lock is
36/// uncontended; on the native test target it serialises
37/// the rare concurrent test against itself without
38/// affecting functional correctness.
39///
40/// OPT 22 (tail): replaces the previous
41/// `Signal<HashMap<...>>` field on `I18n`. Every `t()` call
42/// used to clone the entire translation table; now the
43/// table lives behind this lock and `t()` borrows through
44/// the read guard.
45pub(crate) static I18N_MESSAGES: OnceLock<RwLock<HashMap<String, HashMap<String, String>>>> =
46 OnceLock::new();
47
48#[derive(Clone, Data, New)]
49pub struct I18n {
50 /// The currently-active locale tag. Setting this
51 /// via `set_locale` triggers a reactive update that
52 /// re-evaluates any reactive `t(...)` read.
53 pub(crate) locale: Signal<String>,
54 /// The locale to fall back to when a key is missing
55 /// in the active locale. Defaults to `"en"`.
56 pub(crate) fallback_locale: Signal<String>,
57}