use handlebars::{ Context, Handlebars, Helper, HelperDef, HelperResult, Output, RenderContext, RenderError }; use std::io; pub struct I18NHelper { catalogs: Vec<(&'static str, gettext::Catalog)>, } impl I18NHelper { pub fn new(catalogs: Vec<(&'static str, gettext::Catalog)>) -> Self { Self { catalogs } } pub fn get_catalog( &self, lang: &str, ) -> &gettext::Catalog { let (_, ref catalog) = self.catalogs .iter() .find(|(candidate, _)| *candidate == lang) .unwrap_or_else(|| self.catalogs.get(0).unwrap()); catalog } // Traverse the fallback chain, pub fn lookup<'a>( &'a self, lang: &str, text_id: &'a str, // args: Option<&HashMap<&str, FluentValue>>, ) -> &'a str { let catalog = self.get_catalog(lang); catalog.gettext(text_id) // format!("Unknown localization {}", text_id) } } #[derive(Default)] struct StringOutput { pub s: String, } impl Output for StringOutput { fn write(&mut self, seg: &str) -> Result<(), io::Error> { self.s.push_str(seg); Ok(()) } } impl HelperDef for I18NHelper { fn call<'reg: 'rc, 'rc>( &self, h: &Helper<'reg, 'rc>, reg: &'reg Handlebars, context: &'rc Context, rcx: &mut RenderContext<'reg>, out: &mut dyn Output, ) -> HelperResult { let id = if let Some(id) = h.param(0) { id } else { return Err(RenderError::new( "{{text}} must have at least one parameter", )); }; let id = if let Some(id) = id.value().as_str() { id } else { return Err(RenderError::new("{{text}} takes an identifier parameter")); }; let rerender = h .param(1) .and_then(|p| p .path() .map(|v| v == "rerender") ).unwrap_or(false); let lang = context .data() .get("lang") .expect("Language not set in context") .as_str() .expect("Language must be string"); let response = self.lookup(lang, &id); if rerender { let data = rcx.evaluate(context, ".", false).unwrap(); let response = reg.render_template(&response, data) .map_err(RenderError::with)?; out.write(&response).map_err(RenderError::with)?; } else { out.write(&response).map_err(RenderError::with)?; } Ok(()) } }