diff --git a/components/config/src/config.rs b/components/config/src/config.rs index 0a1e1e2..18dd2ec 100644 --- a/components/config/src/config.rs +++ b/components/config/src/config.rs @@ -8,6 +8,7 @@ use toml; use toml::Value as Toml; use errors::Result; +use errors::Error; use highlighting::THEME_SET; use theme::Theme; use utils::fs::read_file_with_error; @@ -83,6 +84,8 @@ impl Default for Taxonomy { } } +type TranslateTerm = HashMap; + #[derive(Clone, Debug, Serialize, Deserialize)] #[serde(default)] pub struct Config { @@ -100,8 +103,12 @@ pub struct Config { pub default_language: String, /// The list of supported languages outside of the default one pub languages: Vec, + /// Languages list and translated strings - pub translations: HashMap, + /// + /// The `String` key of `HashMap` is a language name, the value should be toml crate `Table` + /// with String key representing term and value another `String` representing its translation. + pub translations: HashMap, /// Whether to highlight all code blocks found in markdown files. Defaults to false pub highlight_code: bool, @@ -299,6 +306,16 @@ impl Config { // and this operation can be expensive. self.highlight_code = false; } + + pub fn get_translation>(&self, lang: S, key: S) -> Result { + let terms = self.translations.get(lang.as_ref()).ok_or_else(|| { + Error::msg(format!("Translation for language '{}' is missing", lang.as_ref())) + })?; + + terms.get(key.as_ref()).ok_or_else(|| { + Error::msg(format!("Translation key '{}' for language '{}' is missing", key.as_ref(), lang.as_ref())) + }).map(|term| term.to_string()) + } } impl Default for Config { @@ -447,9 +464,7 @@ a_value = 10 assert_eq!(extra["a_value"].as_integer().unwrap(), 10); } - #[test] - fn can_use_language_configuration() { - let config = r#" + const CONFIG_TRANSLATION: &str = r#" base_url = "https://remplace-par-ton-url.fr" default_language = "fr" @@ -459,14 +474,38 @@ title = "Un titre" [translations.en] title = "A title" - "#; - let config = Config::parse(config); + #[test] + fn can_use_language_configuration() { + let config = Config::parse(CONFIG_TRANSLATION); assert!(config.is_ok()); let translations = config.unwrap().translations; - assert_eq!(translations["fr"]["title"].as_str().unwrap(), "Un titre"); - assert_eq!(translations["en"]["title"].as_str().unwrap(), "A title"); + assert_eq!(translations["fr"]["title"].as_str(), "Un titre"); + assert_eq!(translations["en"]["title"].as_str(), "A title"); + } + + #[test] + fn can_use_present_translation() { + let config = Config::parse(CONFIG_TRANSLATION).unwrap(); + assert_eq!(config.get_translation("fr", "title").unwrap(), "Un titre"); + assert_eq!(config.get_translation("en", "title").unwrap(), "A title"); + } + + #[test] + fn error_on_absent_translation_lang() { + let config = Config::parse(CONFIG_TRANSLATION).unwrap(); + let error = config.get_translation("absent", "key").unwrap_err(); + + assert_eq!("Translation for language 'absent' is missing", format!("{}", error)); + } + + #[test] + fn error_on_absent_translation_key() { + let config = Config::parse(CONFIG_TRANSLATION).unwrap(); + let error = config.get_translation("en", "absent").unwrap_err(); + + assert_eq!("Translation key 'absent' for language 'en' is missing", format!("{}", error)); } #[test] diff --git a/components/templates/src/global_fns/mod.rs b/components/templates/src/global_fns/mod.rs index a57126b..8ae7d07 100644 --- a/components/templates/src/global_fns/mod.rs +++ b/components/templates/src/global_fns/mod.rs @@ -33,8 +33,12 @@ impl TeraFn for Trans { let key = required_arg!(String, args.get("key"), "`trans` requires a `key` argument."); let lang = optional_arg!(String, args.get("lang"), "`trans`: `lang` must be a string.") .unwrap_or_else(|| self.config.default_language.clone()); - let translations = &self.config.translations[lang.as_str()]; - Ok(to_value(&translations[key.as_str()]).unwrap()) + + let term = self.config.get_translation(lang, key).map_err(|e| { + Error::chain("Failed to retreive term translation", e) + })?; + + Ok(to_value(term).unwrap()) } } @@ -505,9 +509,8 @@ mod tests { assert!(static_fn.call(&args).is_err()); } - #[test] - fn can_translate_a_string() { - let trans_config = r#" + + const TRANS_CONFIG: &str = r#" base_url = "https://remplace-par-ton-url.fr" default_language = "fr" @@ -517,10 +520,11 @@ title = "Un titre" [translations.en] title = "A title" - "#; - let config = Config::parse(trans_config).unwrap(); + #[test] + fn can_translate_a_string() { + let config = Config::parse(TRANS_CONFIG).unwrap(); let static_fn = Trans::new(config); let mut args = HashMap::new(); @@ -533,4 +537,26 @@ title = "A title" args.insert("lang".to_string(), to_value("fr").unwrap()); assert_eq!(static_fn.call(&args).unwrap(), "Un titre"); } + + #[test] + fn error_on_absent_translation_lang() { + let mut args = HashMap::new(); + args.insert("lang".to_string(), to_value("absent").unwrap()); + args.insert("key".to_string(), to_value("title").unwrap()); + + let config = Config::parse(TRANS_CONFIG).unwrap(); + let error = Trans::new(config).call(&args).unwrap_err(); + assert_eq!("Failed to retreive term translation", format!("{}", error)); + } + + #[test] + fn error_on_absent_translation_key() { + let mut args = HashMap::new(); + args.insert("lang".to_string(), to_value("en").unwrap()); + args.insert("key".to_string(), to_value("absent").unwrap()); + + let config = Config::parse(TRANS_CONFIG).unwrap(); + let error = Trans::new(config).call(&args).unwrap_err(); + assert_eq!("Failed to retreive term translation", format!("{}", error)); + } }