// Internationalisation: in-game texts live in lang/*.lang files (key = text). // English is the reference language and the fallback for missing keys. // To add a language, create lang/.lang and add it to SOURCES. use std::cell::RefCell; use std::collections::HashMap; const SOURCES: &[(&str, &str)] = &[("en", include_str!("../lang/en.lang")), ("de", include_str!("../lang/de.lang"))]; struct I18n { langs: Vec<(String, HashMap)>, current: usize, } fn parse(src: &str) -> HashMap { let mut m = HashMap::new(); for line in src.lines() { let line = line.trim(); if line.is_empty() || line.starts_with('#') { continue; } if let Some((k, v)) = line.split_once('=') { m.insert(k.trim().to_string(), v.trim().to_string()); } } m } thread_local! { static I18N: RefCell = RefCell::new(I18n { langs: SOURCES.iter().map(|(code, src)| (code.to_string(), parse(src))).collect(), current: 0, }); } // Available languages: (code, display name in that language) pub fn languages() -> Vec<(String, String)> { I18N.with(|i| { i.borrow().langs.iter().map(|(c, m)| (c.clone(), m.get("lang.name").cloned().unwrap_or_else(|| c.clone()))).collect() }) } pub fn set_language(code: &str) -> bool { I18N.with(|i| { let mut i = i.borrow_mut(); if let Some(k) = i.langs.iter().position(|(c, _)| c == code) { i.current = k; true } else { false } }) } #[allow(dead_code)] // used by the settings menu pub fn language() -> String { I18N.with(|i| { let i = i.borrow(); i.langs[i.current].0.clone() }) } // Translate a key (fallback: English, then the key itself) pub fn tr(key: &str) -> String { I18N.with(|i| { let i = i.borrow(); i.langs[i.current].1.get(key).or_else(|| i.langs[0].1.get(key)).cloned().unwrap_or_else(|| key.to_string()) }) } // Translate with arguments. Arguments starting with '@' are keys and get translated too. pub fn trf(key: &str, args: &[String]) -> String { let mut s = tr(key); for (n, a) in args.iter().enumerate() { let v = if let Some(k) = a.strip_prefix('@') { tr(k) } else { a.clone() }; s = s.replace(&format!("{{{}}}", n), &v); } s } // Translate a key in a specific language (used by the language selection dialog) pub fn tr_in(code: &str, key: &str) -> String { I18N.with(|i| { let i = i.borrow(); i.langs.iter().find(|(c, _)| c == code).and_then(|(_, m)| m.get(key)).cloned().unwrap_or_else(|| key.to_string()) }) }