uzor 1.3.3

Core UI engine — geometry, interaction, input state
//! Generic i18n mechanism. No concrete languages or strings live here —
//! the consumer crate defines its Language enum, keys and translation tables
//! and implements `Translate` for its key types.

use std::sync::atomic::{AtomicU8, Ordering};

/// Generic translation contract. `lang_index` is the consumer's language
/// position (0 = primary/fallback). uzor does not know how many languages
/// exist or what they are.
pub trait Translate: Copy {
    /// Return the translated string for `lang_index`.
    /// Implementors MUST fall back to index 0 when a cell is empty.
    fn translate(self, lang_index: usize) -> &'static str;
}

static LANG_INDEX: AtomicU8 = AtomicU8::new(0);

/// Current language as an opaque index (consumer maps it to its enum).
#[inline]
pub fn current_lang_index() -> usize {
    LANG_INDEX.load(Ordering::Relaxed) as usize
}

/// Set current language by opaque index.
#[inline]
pub fn set_lang_index(idx: u8) {
    LANG_INDEX.store(idx, Ordering::Relaxed);
}

/// Translate a key using the current global language index.
#[inline]
pub fn t<K: Translate>(key: K) -> &'static str {
    key.translate(current_lang_index())
}

#[cfg(test)]
mod tests {
    use super::*;

    #[derive(Clone, Copy)]
    enum TestKey {
        Hello,
        Bye,
    }

    // Simulated 2-language table: [En, Ru]
    static HELLO_ROW: [&str; 2] = ["Hello", "Привет"];
    static BYE_ROW: [&str; 2] = ["Bye", ""];  // empty cell — must fall back to [0]

    impl Translate for TestKey {
        fn translate(self, lang_index: usize) -> &'static str {
            let row: &[&str] = match self {
                TestKey::Hello => &HELLO_ROW,
                TestKey::Bye => &BYE_ROW,
            };
            let s = row[lang_index.min(row.len() - 1)];
            if s.is_empty() { row[0] } else { s }
        }
    }

    #[test]
    fn test_translate_index_0() {
        set_lang_index(0);
        assert_eq!(t(TestKey::Hello), "Hello");
        assert_eq!(t(TestKey::Bye), "Bye");
    }

    #[test]
    fn test_translate_index_1() {
        set_lang_index(1);
        assert_eq!(t(TestKey::Hello), "Привет");
    }

    #[test]
    fn test_fallback_empty_cell() {
        // BYE_ROW[1] is empty — must fall back to BYE_ROW[0]
        set_lang_index(1);
        assert_eq!(t(TestKey::Bye), "Bye");
        // Reset
        set_lang_index(0);
    }

    #[test]
    fn test_set_and_get_index() {
        set_lang_index(3);
        assert_eq!(current_lang_index(), 3);
        set_lang_index(0);
    }
}