synpad 0.1.0

A full-featured Matrix chat client built with Dioxus
use std::collections::HashMap;

/// Internationalization support.
pub struct I18n {
    locale: String,
    strings: HashMap<String, String>,
    is_rtl: bool,
}

/// Supported locales with metadata.
pub static SUPPORTED_LOCALES: &[(&str, &str, bool)] = &[
    ("en", "English", false),
    ("es", "Español", false),
    ("fr", "Français", false),
    ("de", "Deutsch", false),
    ("it", "Italiano", false),
    ("pt", "Português", false),
    ("ru", "Русский", false),
    ("zh", "中文", false),
    ("ja", "日本語", false),
    ("ko", "한국어", false),
    ("ar", "العربية", true),
    ("he", "עברית", true),
    ("fa", "فارسی", true),
    ("tr", "Türkçe", false),
    ("nl", "Nederlands", false),
    ("pl", "Polski", false),
    ("sv", "Svenska", false),
    ("fi", "Suomi", false),
    ("da", "Dansk", false),
    ("no", "Norsk", false),
    ("cs", "Čeština", false),
    ("hu", "Magyar", false),
    ("ro", "Română", false),
    ("el", "Ελληνικά", false),
    ("th", "ไทย", false),
    ("vi", "Tiếng Việt", false),
    ("uk", "Українська", false),
    ("bg", "Български", false),
    ("hr", "Hrvatski", false),
    ("sk", "Slovenčina", false),
    ("ca", "Català", false),
    ("id", "Bahasa Indonesia", false),
    ("ms", "Bahasa Melayu", false),
    ("hi", "हिन्दी", false),
    ("bn", "বাংলা", false),
    ("ta", "தமிழ்", false),
    ("te", "తెలుగు", false),
    ("mr", "मराठी", false),
    ("gu", "ગુજરાતી", false),
    ("kn", "ಕನ್ನಡ", false),
];

impl I18n {
    /// Create a new i18n instance with the given locale.
    pub fn new(locale: &str) -> Self {
        let strings = load_strings(locale);
        let is_rtl = SUPPORTED_LOCALES
            .iter()
            .find(|(code, _, _)| *code == locale)
            .map(|(_, _, rtl)| *rtl)
            .unwrap_or(false);
        Self {
            locale: locale.to_string(),
            strings,
            is_rtl,
        }
    }

    /// Translate a key, returning the key itself if no translation is found.
    pub fn t<'a>(&'a self, key: &'a str) -> &'a str {
        self.strings.get(key).map(|s| s.as_str()).unwrap_or(key)
    }

    /// Translate with interpolation parameters.
    pub fn t_with(&self, key: &str, params: &[(&str, &str)]) -> String {
        let mut result = self.t(key).to_string();
        for (name, value) in params {
            result = result.replace(&format!("{{{name}}}"), value);
        }
        result
    }

    /// Get the current locale.
    pub fn locale(&self) -> &str {
        &self.locale
    }

    /// Whether this locale is RTL.
    pub fn is_rtl(&self) -> bool {
        self.is_rtl
    }

    /// Get the CSS direction value.
    pub fn direction(&self) -> &str {
        if self.is_rtl { "rtl" } else { "ltr" }
    }
}

/// Load string translations for the given locale.
fn load_strings(locale: &str) -> HashMap<String, String> {
    let mut strings = HashMap::new();

    // English strings (default / fallback)
    load_english(&mut strings);

    // Load locale-specific overrides
    match locale {
        "es" => load_spanish(&mut strings),
        "fr" => load_french(&mut strings),
        "de" => load_german(&mut strings),
        "ja" => load_japanese(&mut strings),
        "zh" => load_chinese(&mut strings),
        "ar" => load_arabic(&mut strings),
        "ru" => load_russian(&mut strings),
        "ko" => load_korean(&mut strings),
        "pt" => load_portuguese(&mut strings),
        "it" => load_italian(&mut strings),
        _ => {} // Fall back to English
    }

    strings
}

fn load_english(s: &mut HashMap<String, String>) {
    s.insert("app.name".into(), "Netrix".into());
    s.insert("login.title".into(), "Sign In".into());
    s.insert("login.homeserver".into(), "Homeserver".into());
    s.insert("login.username".into(), "Username".into());
    s.insert("login.password".into(), "Password".into());
    s.insert("login.sign_in".into(), "Sign In".into());
    s.insert("login.signing_in".into(), "Signing in...".into());
    s.insert("login.sso".into(), "Sign in with SSO".into());
    s.insert("login.register_prompt".into(), "Don't have an account?".into());
    s.insert("login.register_link".into(), "Create account".into());
    s.insert("login.forgot_password".into(), "Forgot password?".into());
    s.insert("login.error.invalid_credentials".into(), "Invalid username or password".into());
    s.insert("login.error.server_unreachable".into(), "Could not reach the homeserver".into());
    s.insert("register.title".into(), "Create Account".into());
    s.insert("register.username".into(), "Username".into());
    s.insert("register.password".into(), "Password".into());
    s.insert("register.confirm_password".into(), "Confirm Password".into());
    s.insert("register.email".into(), "Email (optional)".into());
    s.insert("register.create_account".into(), "Create Account".into());
    s.insert("register.login_prompt".into(), "Already have an account?".into());
    s.insert("register.login_link".into(), "Sign in".into());
    s.insert("home.rooms".into(), "Rooms".into());
    s.insert("home.people".into(), "People".into());
    s.insert("home.favorites".into(), "Favorites".into());
    s.insert("home.search_placeholder".into(), "Search rooms...".into());
    s.insert("home.no_rooms".into(), "No rooms yet".into());
    s.insert("home.welcome".into(), "Welcome to Netrix".into());
    s.insert("room.send_placeholder".into(), "Send a message...".into());
    s.insert("room.send_button".into(), "Send".into());
    s.insert("room.typing_one".into(), "{user} is typing...".into());
    s.insert("room.typing_many".into(), "{count} people are typing...".into());
    s.insert("room.unread_messages".into(), "New messages".into());
    s.insert("room.members".into(), "Members".into());
    s.insert("room.files".into(), "Files".into());
    s.insert("room.pinned".into(), "Pinned Messages".into());
    s.insert("settings.title".into(), "Settings".into());
    s.insert("settings.general".into(), "General".into());
    s.insert("settings.appearance".into(), "Appearance".into());
    s.insert("settings.notifications".into(), "Notifications".into());
    s.insert("settings.security".into(), "Security".into());
    s.insert("settings.sessions".into(), "Sessions".into());
    s.insert("settings.theme".into(), "Theme".into());
    s.insert("settings.theme.light".into(), "Light".into());
    s.insert("settings.theme.dark".into(), "Dark".into());
    s.insert("settings.theme.system".into(), "System".into());
    s.insert("settings.logout".into(), "Sign Out".into());
    s.insert("settings.logout_confirm".into(), "Are you sure you want to sign out?".into());
    s.insert("common.cancel".into(), "Cancel".into());
    s.insert("common.confirm".into(), "Confirm".into());
    s.insert("common.save".into(), "Save".into());
    s.insert("common.close".into(), "Close".into());
    s.insert("common.loading".into(), "Loading...".into());
    s.insert("common.error".into(), "Error".into());
    s.insert("common.retry".into(), "Retry".into());
}

fn load_spanish(s: &mut HashMap<String, String>) {
    s.insert("login.title".into(), "Iniciar Sesión".into());
    s.insert("login.username".into(), "Nombre de usuario".into());
    s.insert("login.password".into(), "Contraseña".into());
    s.insert("login.sign_in".into(), "Iniciar Sesión".into());
    s.insert("login.signing_in".into(), "Iniciando sesión...".into());
    s.insert("login.register_prompt".into(), "¿No tienes una cuenta?".into());
    s.insert("login.register_link".into(), "Crear cuenta".into());
    s.insert("register.title".into(), "Crear Cuenta".into());
    s.insert("home.rooms".into(), "Salas".into());
    s.insert("home.people".into(), "Personas".into());
    s.insert("home.favorites".into(), "Favoritos".into());
    s.insert("home.search_placeholder".into(), "Buscar salas...".into());
    s.insert("room.send_placeholder".into(), "Enviar un mensaje...".into());
    s.insert("room.send_button".into(), "Enviar".into());
    s.insert("settings.title".into(), "Configuración".into());
    s.insert("common.cancel".into(), "Cancelar".into());
    s.insert("common.save".into(), "Guardar".into());
    s.insert("common.loading".into(), "Cargando...".into());
}

fn load_french(s: &mut HashMap<String, String>) {
    s.insert("login.title".into(), "Connexion".into());
    s.insert("login.username".into(), "Nom d'utilisateur".into());
    s.insert("login.password".into(), "Mot de passe".into());
    s.insert("login.sign_in".into(), "Se connecter".into());
    s.insert("login.signing_in".into(), "Connexion en cours...".into());
    s.insert("register.title".into(), "Créer un compte".into());
    s.insert("home.rooms".into(), "Salons".into());
    s.insert("home.people".into(), "Personnes".into());
    s.insert("home.favorites".into(), "Favoris".into());
    s.insert("room.send_placeholder".into(), "Envoyer un message...".into());
    s.insert("room.send_button".into(), "Envoyer".into());
    s.insert("settings.title".into(), "Paramètres".into());
    s.insert("common.cancel".into(), "Annuler".into());
    s.insert("common.save".into(), "Enregistrer".into());
}

fn load_german(s: &mut HashMap<String, String>) {
    s.insert("login.title".into(), "Anmelden".into());
    s.insert("login.username".into(), "Benutzername".into());
    s.insert("login.password".into(), "Passwort".into());
    s.insert("login.sign_in".into(), "Anmelden".into());
    s.insert("register.title".into(), "Konto erstellen".into());
    s.insert("home.rooms".into(), "Räume".into());
    s.insert("home.people".into(), "Personen".into());
    s.insert("room.send_placeholder".into(), "Nachricht senden...".into());
    s.insert("room.send_button".into(), "Senden".into());
    s.insert("settings.title".into(), "Einstellungen".into());
    s.insert("common.cancel".into(), "Abbrechen".into());
    s.insert("common.save".into(), "Speichern".into());
}

fn load_japanese(s: &mut HashMap<String, String>) {
    s.insert("login.title".into(), "ログイン".into());
    s.insert("login.username".into(), "ユーザー名".into());
    s.insert("login.password".into(), "パスワード".into());
    s.insert("login.sign_in".into(), "ログイン".into());
    s.insert("register.title".into(), "アカウント作成".into());
    s.insert("home.rooms".into(), "ルーム".into());
    s.insert("home.people".into(), "ピープル".into());
    s.insert("room.send_placeholder".into(), "メッセージを送信...".into());
    s.insert("room.send_button".into(), "送信".into());
    s.insert("settings.title".into(), "設定".into());
    s.insert("common.cancel".into(), "キャンセル".into());
    s.insert("common.save".into(), "保存".into());
}

fn load_chinese(s: &mut HashMap<String, String>) {
    s.insert("login.title".into(), "登录".into());
    s.insert("login.username".into(), "用户名".into());
    s.insert("login.password".into(), "密码".into());
    s.insert("login.sign_in".into(), "登录".into());
    s.insert("register.title".into(), "创建账户".into());
    s.insert("home.rooms".into(), "房间".into());
    s.insert("home.people".into(), "联系人".into());
    s.insert("room.send_placeholder".into(), "发送消息...".into());
    s.insert("room.send_button".into(), "发送".into());
    s.insert("settings.title".into(), "设置".into());
    s.insert("common.cancel".into(), "取消".into());
    s.insert("common.save".into(), "保存".into());
}

fn load_arabic(s: &mut HashMap<String, String>) {
    s.insert("login.title".into(), "تسجيل الدخول".into());
    s.insert("login.username".into(), "اسم المستخدم".into());
    s.insert("login.password".into(), "كلمة المرور".into());
    s.insert("login.sign_in".into(), "تسجيل الدخول".into());
    s.insert("register.title".into(), "إنشاء حساب".into());
    s.insert("home.rooms".into(), "الغرف".into());
    s.insert("home.people".into(), "الأشخاص".into());
    s.insert("room.send_placeholder".into(), "أرسل رسالة...".into());
    s.insert("room.send_button".into(), "إرسال".into());
    s.insert("settings.title".into(), "الإعدادات".into());
    s.insert("common.cancel".into(), "إلغاء".into());
    s.insert("common.save".into(), "حفظ".into());
}

fn load_russian(s: &mut HashMap<String, String>) {
    s.insert("login.title".into(), "Вход".into());
    s.insert("login.username".into(), "Имя пользователя".into());
    s.insert("login.password".into(), "Пароль".into());
    s.insert("login.sign_in".into(), "Войти".into());
    s.insert("register.title".into(), "Создать аккаунт".into());
    s.insert("home.rooms".into(), "Комнаты".into());
    s.insert("home.people".into(), "Люди".into());
    s.insert("room.send_placeholder".into(), "Отправить сообщение...".into());
    s.insert("room.send_button".into(), "Отправить".into());
    s.insert("settings.title".into(), "Настройки".into());
    s.insert("common.cancel".into(), "Отмена".into());
    s.insert("common.save".into(), "Сохранить".into());
}

fn load_korean(s: &mut HashMap<String, String>) {
    s.insert("login.title".into(), "로그인".into());
    s.insert("login.username".into(), "사용자 이름".into());
    s.insert("login.password".into(), "비밀번호".into());
    s.insert("login.sign_in".into(), "로그인".into());
    s.insert("register.title".into(), "계정 만들기".into());
    s.insert("home.rooms".into(), "".into());
    s.insert("room.send_placeholder".into(), "메시지 보내기...".into());
    s.insert("room.send_button".into(), "전송".into());
    s.insert("settings.title".into(), "설정".into());
    s.insert("common.cancel".into(), "취소".into());
    s.insert("common.save".into(), "저장".into());
}

fn load_portuguese(s: &mut HashMap<String, String>) {
    s.insert("login.title".into(), "Entrar".into());
    s.insert("login.username".into(), "Nome de usuário".into());
    s.insert("login.password".into(), "Senha".into());
    s.insert("login.sign_in".into(), "Entrar".into());
    s.insert("register.title".into(), "Criar Conta".into());
    s.insert("home.rooms".into(), "Salas".into());
    s.insert("room.send_placeholder".into(), "Enviar mensagem...".into());
    s.insert("room.send_button".into(), "Enviar".into());
    s.insert("settings.title".into(), "Configurações".into());
    s.insert("common.cancel".into(), "Cancelar".into());
    s.insert("common.save".into(), "Salvar".into());
}

fn load_italian(s: &mut HashMap<String, String>) {
    s.insert("login.title".into(), "Accedi".into());
    s.insert("login.username".into(), "Nome utente".into());
    s.insert("login.password".into(), "Password".into());
    s.insert("login.sign_in".into(), "Accedi".into());
    s.insert("register.title".into(), "Crea Account".into());
    s.insert("home.rooms".into(), "Stanze".into());
    s.insert("room.send_placeholder".into(), "Invia messaggio...".into());
    s.insert("room.send_button".into(), "Invia".into());
    s.insert("settings.title".into(), "Impostazioni".into());
    s.insert("common.cancel".into(), "Annulla".into());
    s.insert("common.save".into(), "Salva".into());
}

impl Default for I18n {
    fn default() -> Self {
        Self::new("en")
    }
}