use std::sync::OnceLock;
const VARIABLES: [&str; 4] = ["PORTAKI_LANG", "LC_ALL", "LC_MESSAGES", "LANG"];
pub fn french() -> bool {
static FRENCH: OnceLock<bool> = OnceLock::new();
!crate::ui::json()
&& *FRENCH.get_or_init(|| {
let variables: &[&str] = if cfg!(test) {
&VARIABLES[..1]
} else {
&VARIABLES
};
is_french(variables.iter().map(|key| std::env::var(key).ok()))
})
}
fn is_french(values: impl Iterator<Item = Option<String>>) -> bool {
values
.flatten()
.find(|value| !value.trim().is_empty())
.is_some_and(|value| value.trim().to_ascii_lowercase().starts_with("fr"))
}
#[macro_export]
macro_rules! tr {
($en:literal, $fr:literal $(, $arg:expr)* $(,)?) => {
if $crate::lang::french() {
format!($fr $(, $arg)*)
} else {
format!($en $(, $arg)*)
}
};
}
#[cfg(test)]
mod tests {
use super::*;
fn decide(values: &[Option<&str>]) -> bool {
is_french(values.iter().map(|value| value.map(str::to_string)))
}
#[test]
fn the_first_variable_set_decides() {
assert!(decide(&[None, None, None, Some("fr_FR.UTF-8")]));
assert!(!decide(&[
None,
Some("en_US.UTF-8"),
None,
Some("fr_FR.UTF-8")
]));
assert!(decide(&[Some("fr"), Some("en_US.UTF-8"), None, None]));
assert!(!decide(&[Some("en"), None, None, Some("fr_FR.UTF-8")]));
assert!(decide(&[None, None, Some("fr_CA"), Some("C")]));
}
#[test]
fn an_empty_variable_is_skipped() {
assert!(decide(&[Some(""), Some(" "), None, Some("fr_BE.UTF-8")]));
}
#[test]
fn anything_else_is_english() {
assert!(!decide(&[None, None, None, Some("C")]));
assert!(!decide(&[None, None, None, Some("de_DE.UTF-8")]));
assert!(!decide(&[None, None, None, None]));
}
}