loon-embed 0.5.0

I18n for Rust, support embed YAML, JSON.
Documentation
use std::collections::HashMap;

use super::key::Key;
use super::opts::Opts;

/// Container for translation messages
#[derive(Debug)]
pub struct Dictionary {
    pub(crate) inner: HashMap<String, serde_json::Value>,
    pub default_locale: String,
}

impl Default for Dictionary {
    fn default() -> Self {
        Self {
            inner: HashMap::new(),
            default_locale: "en".into(),
        }
    }
}

impl Dictionary {
    /// Get the translated message.
    pub fn translate<'a, K: Into<Key<'a>>, I: Into<Opts<'a>>>(
        &self,
        key: K,
        opts: I,
    ) -> super::err::Result<String> {
        let opts = opts.into();

        let mut key = key.into();

        let alt_key;

        match opts.count {
            Some(0) => {
                alt_key = key.chain(["zero"].as_ref());
                key = alt_key;
            }
            Some(1) => {
                alt_key = key.chain(["one"].as_ref());
                key = alt_key;
            }
            Some(_) => {
                alt_key = key.chain(["other"].as_ref());
                key = alt_key;
            }
            _ => {}
        }

        let locale = opts.locale.unwrap_or_else(|| &self.default_locale);

        let localized = self.inner.get(locale).ok_or_else(|| {
            crate::err::Error::UnknownLocale(String::from(locale).into_boxed_str())
        })?;

        let entry = |key: Key| {
            key.find(localized)
                .and_then(|val| val.as_str())
                .map(String::from)
                .ok_or_else(|| crate::err::Error::UnknownKey(key.to_string().into_boxed_str()))
        };

        let value = match entry(key) {
            Ok(value) => value,
            Err(e) => match opts.default_key {
                Some(default_key) => {
                    return entry(default_key);
                }
                _ => {
                    return Err(e);
                }
            },
        };

        match opts.vars {
            Some(vars) => Ok(strfmt::strfmt(&value, &vars)?),
            None => Ok(value),
        }
    }

    /// Shortcut for `translate`.
    pub fn t<'a, K: Into<Key<'a>>, I: Into<Opts<'a>>>(
        &self,
        key: K,
        opts: I,
    ) -> crate::err::Result<String> {
        self.translate(key, opts)
    }
}

#[cfg(test)]
mod tests {

    use crate::opts::{Count, DefaultKey, Opts, Var};
    use rust_embed::RustEmbed;

    #[derive(RustEmbed)]
    #[folder = "examples/locales/"]
    struct Asset;

    #[test]
    fn it_works() {
        let mut dict = crate::Config::default()
            .with_embed::<Asset>()
            .finish()
            .unwrap();

        assert_eq!(
            dict.t(&["greeting"], None).unwrap(),
            String::from("Hello, World!")
        );

        assert_eq!(
            dict.t("missed", DefaultKey("missing.default")).unwrap(),
            String::from("Sorry, that translation doesn't exist.")
        );

        assert_eq!(
            dict.t(&["custom", "greeting"], Var("name", "Jacob"))
                .unwrap(),
            String::from("Hello, Jacob!!!")
        );

        assert_eq!(
            dict.t("messages", Opts::default().count(1)).unwrap(),
            String::from("You have one message.")
        );

        assert_eq!(
            dict.t("messages", Opts::default().count(0)).unwrap(),
            String::from("You have no messages.")
        );

        assert_eq!(
            dict.t("messages", Count(200)).unwrap(),
            String::from("You have 200 messages.")
        );

        assert!(dict.t("message.x", ()).is_err());

        assert_eq!(
            dict.t(
                "a.very.nested.message",
                (
                    Var("name", "you"),
                    Var("message", "\"a very nested message\"")
                )
            )
            .unwrap(),
            String::from("Hello, you. Your message is: \"a very nested message\"")
        );

        dict.default_locale = String::from("de");

        assert_eq!(
            dict.t("greeting", None).unwrap(),
            String::from("Hallo Welt!")
        );
    }
}