euv-ui 0.18.2

Reusable UI component library for the euv framework, providing buttons, cards, modals, inputs, and more.
Documentation
use super::*;

/// Implements [`HookContextI18nExt`] for [`HookContext`].
impl HookContextI18nExt for HookContext {
    /// Returns a fresh [`I18n`] bound to the current component scope.
    ///
    /// # Returns
    ///
    /// - `I18n` - A `I18n` value.
    fn i18n() -> I18n {
        HookContext::use_hook(|| {
            I18n::new(
                Signal::create(String::from("en")),
                Signal::create(String::from("en")),
                Signal::create(HashMap::new()),
            )
        })
    }
}

/// Inherent implementation of [`I18n`].
impl I18n {
    /// Sets the active locale to `locale`. Triggers a
    /// reactive update so any reactive `t(key)` read
    /// re-evaluates.
    ///
    /// Named `change_locale` (not `set_locale`) to avoid
    /// colliding with the `set_locale` getter generated by
    /// `#[derive(Data)]` on the struct field.
    ///
    /// # Arguments
    ///
    /// - `&str` - Shared reference to a `str`.
    pub fn change_locale(&self, locale: &str) {
        self.get_locale().set(locale.to_string());
    }

    /// Sets the fallback locale. Trigger a reactive
    /// update for any `t(key)` whose key is missing in
    /// the active locale — they may now resolve to a
    /// different fallback value.
    ///
    /// Named `change_fallback_locale` (not
    /// `set_fallback_locale`) for the same reason as
    /// `change_locale`.
    ///
    /// # Arguments
    ///
    /// - `&str` - Shared reference to a `str`.
    pub fn change_fallback_locale(&self, locale: &str) {
        self.get_fallback_locale().set(locale.to_string());
    }

    /// Adds a batch of `(key, message)` entries to the
    /// translation table for `locale`. Existing entries
    /// for that locale are overwritten (last-write-wins).
    ///
    /// # Arguments
    ///
    /// - `&str` - Shared reference to a `str`.
    /// - `&[MessageEntry]` - Shared reference to a `[MessageEntry]`.
    pub fn add_messages(&self, locale: &str, entries: &[MessageEntry]) {
        let mut table: HashMap<String, HashMap<String, String>> = self.get_messages().get();
        let entry_map: &mut HashMap<String, String> = table.entry(locale.to_string()).or_default();
        for (key, value) in entries {
            entry_map.insert((*key).to_string(), (*value).to_string());
        }
        self.get_messages().set(table);
    }

    /// Removes every entry for `locale`. After this
    /// call, `t(key)` for any key will skip this locale
    /// in its lookup chain.
    ///
    /// # Arguments
    ///
    /// - `&str` - Shared reference to a `str`.
    pub fn remove_locale(&self, locale: &str) {
        let mut table: HashMap<String, HashMap<String, String>> = self.get_messages().get();
        table.remove(locale);
        self.get_messages().set(table);
    }

    /// Removes a single message from a locale. After this
    /// call, `t(key)` for this key in this locale will
    /// fall back to `fallback_locale`.
    ///
    /// # Arguments
    ///
    /// - `&str` - Shared reference to a `str`.
    /// - `&str` - Shared reference to a `str`.
    pub fn remove_message(&self, locale: &str, key: &str) {
        let mut table: HashMap<String, HashMap<String, String>> = self.get_messages().get();
        if let Some(entry_map) = table.get_mut(locale) {
            entry_map.remove(key);
        }
        self.get_messages().set(table);
    }

    /// Translates `key` to a string under the active
    /// locale, falling back to `fallback_locale` if the
    /// active locale has no entry. Returns `key` itself
    /// if neither locale has an entry (debug-friendly).
    ///
    /// This is the reactive read — calling it inside a
    /// render closure subscribes that closure to locale
    /// changes (and to any subsequent edits to the
    /// messages map for the active or fallback locale).
    ///
    /// # Arguments
    ///
    /// - `&str` - Shared reference to a `str`.
    ///
    /// # Returns
    ///
    /// - `String` - A `String` value.
    pub fn t(&self, key: &str) -> String {
        let active: String = self.get_locale().get();
        let fallback: String = self.get_fallback_locale().get();
        let table: HashMap<String, HashMap<String, String>> = self.get_messages().get();
        if let Some(message) = table
            .get(&active)
            .and_then(|m: &HashMap<String, String>| m.get(key))
        {
            return message.clone();
        }
        if let Some(message) = table
            .get(&fallback)
            .and_then(|m: &HashMap<String, String>| m.get(key))
        {
            return message.clone();
        }
        key.to_string()
    }

    /// Translates `key` and substitutes `{name}`-style
    /// placeholders from `vars`.
    ///
    /// Placeholders that are present in `vars` are
    /// replaced with their corresponding value.
    /// Placeholders that are missing from `vars` are left
    /// as the literal `{name}` token — matching the
    /// i18next default behavior. No escaping is
    /// supported; add it when a real use case shows up.
    ///
    /// # Arguments
    ///
    /// - `&str` - Shared reference to a `str`.
    /// - `&HashMap<&'static str, &'static str>` - Shared reference to a `HashMap<&'static str, &'static str>`.
    ///
    /// # Returns
    ///
    /// - `String` - A `String` value.
    pub fn t_with(&self, key: &str, vars: &HashMap<&'static str, &'static str>) -> String {
        let template: String = self.t(key);
        interpolate(&template, vars)
    }

    /// Returns the number of locales currently registered
    /// (i.e. the number of distinct keys in the messages
    /// map's outer level).
    ///
    /// # Returns
    ///
    /// - `usize` - Count of registered locales.
    pub fn locale_count(&self) -> usize {
        self.get_messages().get().len()
    }

    /// Returns the number of messages registered for the
    /// active locale.
    ///
    /// # Returns
    ///
    /// - `usize` - Count of currently-registered messages.
    pub fn active_message_count(&self) -> usize {
        let active: String = self.get_locale().get();
        self.get_messages()
            .get()
            .get(&active)
            .map(|m: &HashMap<String, String>| m.len())
            .unwrap_or_default()
    }
}