use std::rc::Rc; use crate::assets::AssetProvider; // a string which optionally has translation key in it // it will hopefully support dynamic language changing soon // for now it's just a simple string container #[derive(Debug, Default, Clone)] pub struct Translation { pub text: Rc, pub translated: bool, // if true, `text` is a translation key } impl PartialEq for Translation { fn eq(&self, other: &Self) -> bool { *self.text == *other.text && self.translated == other.translated } } impl Translation { pub fn generate(&self, i18n: &mut I18n) -> Rc { if self.translated { i18n.translate(&self.text) } else { self.text.clone() } } pub fn from_raw_text(text: &str) -> Self { Self { text: Rc::from(text), translated: false, } } pub fn from_translation_key(translated: &str) -> Self { Self { text: Rc::from(translated), translated: true, } } } pub struct I18n { json_root_translated: serde_json::Value, // any language json_root_fallback: serde_json::Value, // english } fn find_translation<'a>(translation: &str, mut val: &'a serde_json::Value) -> Option<&'a str> { for part in translation.split('.') { if let Some(sub) = val.get(part) { val = sub; } } val.as_str() } fn guess_lang() -> String { if let Ok(lang) = std::env::var("LANG") { if let Some((first, _)) = lang.split_once('_') { String::from(first) } else { lang } } else { log::warn!("LANG is not set, defaulting to \"en\"."); String::from("en") } } impl I18n { pub fn new(provider: &mut Box) -> anyhow::Result { let mut lang = guess_lang(); log::info!("Guessed system language: {lang}"); match lang.as_str() { "en" | "pl" | "it" | "ja" | "es" | "de" => {} _ => { log::warn!("Unsupported language \"{}\", defaulting to \"en\".", lang.as_str()); lang = String::from("en"); } } let data_english = provider.load_from_path("lang/en.json")?; let data_translated = provider.load_from_path(&format!("lang/{lang}.json"))?; let json_root_fallback = serde_json::from_str(str::from_utf8(&data_english)?)?; let json_root_translated = serde_json::from_str(str::from_utf8(&data_translated)?)?; Ok(Self { json_root_translated, json_root_fallback, }) } pub fn translate(&mut self, translation_key: &str) -> Rc { if let Some(translated) = find_translation(translation_key, &self.json_root_translated) { return Rc::from(translated); } if let Some(translated_fallback) = find_translation(translation_key, &self.json_root_fallback) { log::warn!("missing translation for key \"{translation_key}\", using \"en\" instead"); return Rc::from(translated_fallback); } log::error!("missing translation for key \"{translation_key}\""); Rc::from(translation_key) // show translation key as a fallback } pub fn translate_and_replace(&mut self, translation_key: &str, to_replace: (&str, &str)) -> String { let translated = self.translate(translation_key); translated.replace(to_replace.0, to_replace.1) } }