crate::ix!();
#[derive(Debug, Clone, Default, Getters, Setters)]
#[getset(get = "pub")]
pub struct BilingualStr {
original: String,
translated: String,
}
impl AddAssign<&BilingualStr> for BilingualStr {
#[inline]
fn add_assign(&mut self, other: &BilingualStr) {
trace!(
"BilingualStr::add_assign: self = ({}, {}), other = ({}, {})",
self.original,
self.translated,
other.original,
other.translated
);
self.original += &other.original;
self.translated += &other.translated;
}
}
impl BilingualStr {
#[inline]
pub fn empty(&self) -> bool {
self.original.is_empty()
}
#[inline]
pub fn clear(&mut self) {
trace!("BilingualStr::clear before = {:?}", self);
self.original.clear();
self.translated.clear();
debug!("BilingualStr::clear after = {:?}", self);
}
}
impl Add<&BilingualStr> for BilingualStr {
type Output = BilingualStr;
#[inline]
fn add(mut self, other: &BilingualStr) -> Self::Output {
self += other;
self
}
}
impl From<&str> for BilingualStr {
#[inline]
fn from(s: &str) -> Self {
untranslated(s)
}
}
#[inline]
pub fn untranslated(original: &str) -> BilingualStr {
trace!("untranslated: {}", original);
BilingualStr {
original: original.to_owned(),
translated: original.to_owned(),
}
}
pub mod tinyformat {
use super::BilingualStr;
#[macro_export]
macro_rules! bilingual_format {
($tmpl:expr $(, $arg:expr)* $(,)?) => {{
let bs: &BilingualStr = &$tmpl;
BilingualStr {
original: format!(bs.original.as_str() $(, $arg)*),
translated: format!(bs.translated.as_str() $(, $arg)*),
}
}};
}
pub fn format(bs: &BilingualStr, args_original: String, args_translated: String) -> BilingualStr {
BilingualStr {
original: args_original,
translated: args_translated,
}
}
}
pub type TranslationFn = fn(&str) -> String;
lazy_static! {
static ref G_TRANSLATION_FUN_DOES_NOT_ALIAS: RwLock<Option<TranslationFn>> = RwLock::new(None);
}
pub fn set_translation_fn(f: Option<TranslationFn>) {
trace!("set_translation_fn: {:?}", f.is_some());
let mut guard = G_TRANSLATION_FUN_DOES_NOT_ALIAS.write();
*guard = f;
}
pub fn bilingual_tr(psz: &str) -> BilingualStr {
let guard = G_TRANSLATION_FUN_DOES_NOT_ALIAS.read();
let translated = guard
.as_ref()
.map(|f| f(psz))
.unwrap_or_else(|| psz.to_owned());
trace!("_(): \"{}\" → \"{}\"", psz, translated);
BilingualStr {
original: psz.to_owned(),
translated,
}
}
#[cfg(test)]
mod tests_bilingual {
use super::*;
#[traced_test]
fn untranslated_roundtrip() {
let msg = BilingualStr::from("abc");
assert_eq!(msg.original(), "abc");
assert_eq!(msg.translated(), "abc");
assert!(msg.empty() == false);
}
#[traced_test]
fn clear_empties() {
let mut msg = BilingualStr::from("x");
msg.clear();
assert!(msg.empty());
assert_eq!(msg.translated(), "");
}
#[traced_test]
fn add_and_add_assign() {
let mut a = BilingualStr::from("foo");
let b = BilingualStr::from("bar");
a += &b;
assert_eq!(a.original(), "foobar");
assert_eq!(a.translated(), "foobar");
let c = BilingualStr::from("baz");
let d = a + &c;
assert_eq!(d.original(), "foobarbaz");
}
#[traced_test]
fn global_translation_function() {
set_translation_fn(Some(|s| format!("ES: {}", s)));
let msg = bilingual_tr("hello");
assert_eq!(msg.original(), "hello");
assert_eq!(msg.translated(), "ES: hello");
set_translation_fn(None);
}
}