mod key;
mod locale;
pub use key::Key;
pub use locale::Locale;
mod i18n;
pub use i18n::I18n;
use {ahash::AHashMap, parking_lot::RwLock, std::sync::Arc};
lazy_static::lazy_static! {
static ref CURRENT: Arc<RwLock<Option<I18n>>> = Arc::new(RwLock::new(None));
static ref DICTIONARY: Arc<RwLock<AHashMap<Locale, I18n>>> = Arc::new(RwLock::new(AHashMap::default()));
}
#[macro_export]
macro_rules! i18n {
($key:expr) => {
bi18n::i18n_current($key)
};
($key:expr, $locale:expr) => {
bi18n::i18n_with_locale($key, $locale)
};
}
#[macro_export]
macro_rules! try_i18n {
($key:expr) => {
bi18n::try_i18n_current($key)
};
($key:expr, $locale:expr) => {
bi18n::try_i18n_with_locale($key, $locale)
};
}
pub fn i18n_current(key: impl Into<Key>) -> String {
try_i18n_current(key).unwrap()
}
pub fn try_i18n_current(key: impl Into<Key>) -> Option<String> {
CURRENT.read().as_ref()?.try_get(&key.into()).cloned()
}
pub fn i18n_with_locale(key: impl Into<Key>, locale: impl Into<Locale>) -> String {
try_i18n_with_locale(key, locale).unwrap()
}
pub fn try_i18n_with_locale(key: impl Into<Key>, locale: impl Into<Locale>) -> Option<String> {
let locale = locale.into();
let key = key.into();
if let Some(ref current) = *CURRENT.read() {
if current.locale() == &locale {
return current.try_get(&key).cloned();
}
}
DICTIONARY
.read()
.iter()
.filter(|(l, _)| *l == &locale)
.flat_map(|(_, i)| i.try_get(&key))
.next()
.cloned()
}
pub fn set(i18n: I18n) {
if let Some(old) = CURRENT.write().take() {
DICTIONARY.write().insert(old.locale().clone(), old);
}
*CURRENT.write() = Some(i18n)
}
pub fn add(i18n: I18n) {
if CURRENT.read().is_none() {
*CURRENT.write() = Some(i18n);
return;
}
DICTIONARY.write().insert(i18n.locale().clone(), i18n);
}
pub fn free() {
*CURRENT.write() = None;
DICTIONARY.write().clear();
}
pub fn try_get_locale() -> Option<Locale> {
Some(CURRENT.read().as_ref()?.locale().clone())
}
pub struct LocaleNotFound;
pub fn set_language(locale: impl Into<Locale>) -> Result<(), LocaleNotFound> {
let locale = locale.into();
if let Some(ref current) = *CURRENT.read() {
if current.locale() == &locale {
return Ok(());
}
}
if let Some(new) = DICTIONARY.write().remove(&locale) {
set(new);
return Ok(())
}
Err(LocaleNotFound)
}