use super::{Lang, Msg};
use std::{
env, fmt,
str::FromStr,
sync::{LazyLock, RwLock},
};
static LANG: LazyLock<RwLock<Lang>> =
LazyLock::new(|| RwLock::new(get_lang_from_system_locale().unwrap_or_default()));
#[macro_export]
macro_rules! msg {
($x:ident) => {
$crate::Msg::$x.as_str()
};
}
impl fmt::Display for Msg {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.as_str())
}
}
impl Msg {
pub fn as_str(self) -> &'static str {
match Self::lang() {
Lang::Rus => self.as_str_rus(),
_ => self.as_str_eng(),
}
}
pub fn lang() -> Lang {
LANG.read().map(|guard| *guard).unwrap_or_default()
}
pub fn set_lang(lang: Lang) -> Result<(), String> {
if lang == Self::lang() {
Ok(())
} else {
LANG.write()
.map(|mut l| *l = lang)
.map_err(|e| e.to_string())
}
}
}
impl FromStr for Lang {
type Err = String;
fn from_str(s: &str) -> Result<Lang, Self::Err> {
get_lang_from_str(s).ok_or_else(|| Msg::NotFoundAValidLangCode.to_string())
}
}
fn get_lang_from_system_locale() -> Option<Lang> {
let locale = env::var("LC_ALL")
.ok()
.or_else(|| env::var("LANG").ok())
.or_else(|| env::var("LC_MESSAGES").ok())
.or_else(|| get_system_locale_fallback())?;
return get_lang_from_str(&locale);
fn get_system_locale_fallback() -> Option<String> {
#[cfg(windows)]
{
use std::ffi::OsString;
use std::os::windows::ffi::OsStringExt;
use winapi::um::winnls::GetUserDefaultLocaleName;
const LOCALE_NAME_MAX_LENGTH: usize = 85;
let mut buffer = [0u16; LOCALE_NAME_MAX_LENGTH];
let len = unsafe {
GetUserDefaultLocaleName(buffer.as_mut_ptr(), LOCALE_NAME_MAX_LENGTH as i32)
};
if len > 0 {
let os_str = OsString::from_wide(&buffer[..(len as usize - 1)]);
os_str.into_string().ok()
} else {
None
}
}
#[cfg(unix)]
{
None
}
}
}
fn get_lang_from_str(s: &str) -> Option<Lang> {
fn str_to_ascii_words(s: &str) -> impl Iterator<Item = &[u8]> {
s.as_bytes()
.split(|b| !b.is_ascii_alphabetic())
.filter(|w| !w.is_empty())
}
let mut buf = [0u8; 3];
str_to_ascii_words(s).find_map(|word| {
let len = word.len();
if !matches!(len, 2 | 3) {
return None;
}
for (dst, src) in buf[..len].iter_mut().zip(word) {
*dst = src.to_ascii_lowercase();
}
let lang = match &buf[..len] {
b"en" | b"eng" => Lang::Eng,
b"ru" | b"rus" => Lang::Rus,
_ => return None,
};
Some(lang)
})
}