hippox 0.2.0

The most reliable AI agent.🦛
Documentation
use once_cell::sync::Lazy;
use serde::Deserialize;
use std::collections::HashMap;
use std::sync::RwLock;

#[derive(Debug, Deserialize)]
struct Translations {
    app: HashMap<String, String>,
    skill: HashMap<String, String>,
    error: HashMap<String, String>,
    prompt: HashMap<String, String>,
}

static CURRENT_LANG: Lazy<RwLock<String>> = Lazy::new(|| RwLock::new("en".to_string()));
static TRANSLATIONS: Lazy<RwLock<HashMap<String, Translations>>> =
    Lazy::new(|| RwLock::new(HashMap::new()));

const EN_TOML: &str = include_str!("./i18n/en.toml");
const ZH_TOML: &str = include_str!("./i18n/zh.toml");

pub fn init() {
    load_translations();
    let lang = std::env::var("HIPPO_LANG").unwrap_or_else(|_| "en".to_string());
    set_language(&lang);
}

fn load_translations() {
    let mut translations = HashMap::new();
    if let Ok(trans) = toml::from_str(EN_TOML) {
        translations.insert("en".to_string(), trans);
    }
    if let Ok(trans) = toml::from_str(ZH_TOML) {
        translations.insert("zh".to_string(), trans);
    }
    let mut store = TRANSLATIONS.write().unwrap();
    *store = translations;
}

pub fn set_language(lang: &str) {
    let mut current = CURRENT_LANG.write().unwrap();
    *current = lang.to_string();
}

pub fn t(key: &str) -> String {
    let lang = get_language();
    let translations = TRANSLATIONS.read().unwrap();
    if let Some(trans) = translations.get(&lang) {
        if let Some(value) = get_value(trans, key) {
            return value;
        }
    }
    if let Some(trans) = translations.get("en") {
        if let Some(value) = get_value(trans, key) {
            return value;
        }
    }
    key.to_string()
}

pub fn t_with_args(key: &str, args: &[String]) -> String {
    let mut result = t(key);
    for (i, arg) in args.iter().enumerate() {
        result = result.replace(&format!("{{{}}}", i), arg);
    }
    result
}

fn get_value(trans: &Translations, key: &str) -> Option<String> {
    let parts: Vec<&str> = key.split('.').collect();
    if parts.len() != 2 {
        return None;
    }
    match parts[0] {
        "app" => trans.app.get(parts[1]).cloned(),
        "skill" => trans.skill.get(parts[1]).cloned(),
        "error" => trans.error.get(parts[1]).cloned(),
        "prompt" => trans.prompt.get(parts[1]).cloned(),
        _ => None,
    }
}

fn get_language() -> String {
    CURRENT_LANG.read().unwrap().clone()
}

#[macro_export]
macro_rules! t {
    ($key:expr) => {
        $crate::i18n::t($key)
    };
    ($key:expr, $($arg:expr),*) => {{
        let args = vec![$($arg.to_string()),*];
        $crate::i18n::t_with_args($key, &args)
    }};
}