//! UI language (Tim t1004u): English (default) or Dutch. //! //! Source strings are Dutch; [`t`] translates any displayed text at the UI //! boundary through [`crate::i18n_table::TABLE`]. Entries may be format //! templates whose `{…}` holes are matched and translated recursively. use std::collections::HashMap; use std::path::Path; use std::sync::atomic::{AtomicU8, Ordering}; use std::sync::{Mutex, OnceLock}; use crate::i18n_table::TABLE; #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] pub enum Lang { #[default] En, Nl, } impl Lang { pub const ALL: [Lang; 2] = [Lang::En, Lang::Nl]; pub fn code(self) -> &'static str { match self { Lang::En => "en", Lang::Nl => "nl", } } pub fn from_code(code: &str) -> Option { Self::ALL.into_iter().find(|l| l.code() == code) } /// Name of the language in that language. pub fn native_name(self) -> &'static str { match self { Lang::En => "English", Lang::Nl => "Nederlands", } } } static LANG: AtomicU8 = AtomicU8::new(0); pub fn lang() -> Lang { if LANG.load(Ordering::Relaxed) == 1 { Lang::Nl } else { Lang::En } } pub fn set_lang(lang: Lang) { LANG.store(u8::from(lang == Lang::Nl), Ordering::Relaxed); } /// Display text in the current language. pub fn t(text: impl AsRef) -> String { tr(lang(), text.as_ref()) } /// Display text in `lang`. pub fn tr(lang: Lang, text: &str) -> String { match lang { Lang::Nl => text.to_string(), Lang::En => to_english(text), } } struct Template { /// Literal parts; holes sit between consecutive parts. parts: Vec, holes: Vec, english: &'static str, weight: usize, } struct Tables { exact: HashMap<&'static str, &'static str>, templates: Vec