use derive_more::From; use rocket_dyn_templates::tera::{self, Value}; use serde::Serialize; use std::{ collections::HashMap, fs::{self, File}, io::BufReader, process::Command, }; use gettext::Catalog; #[derive(From, Debug)] pub enum LoadCatalogsError { Io(std::io::Error), Parse(gettext::Error), MissingDefaultLanguage, } pub const DEFAULT: &str = "ja"; type Catalogs = HashMap; pub fn load_catalogs() -> Result<(Catalogs, Vec), LoadCatalogsError> { let mut catalogs = HashMap::new(); for file in fs::read_dir("i18n")? { let file = file?; let path = file.path(); if !file.file_type()?.is_file() || !path .extension() .map(|extension| extension.eq("po")) .unwrap_or(false) { continue; } let language_code = path .file_stem() .expect("Invalid translation file name") .to_string_lossy() .to_string(); let po_file_path = path; let mo_file_path = format!("i18n/{}.mo", language_code); Command::new("msgfmt") .arg(&po_file_path) .arg("-o") .arg(&mo_file_path) .output()?; catalogs.insert( language_code, Catalog::parse(BufReader::new(File::open(mo_file_path)?))?, ); } if !catalogs.contains_key(DEFAULT) { return Err(LoadCatalogsError::MissingDefaultLanguage); } let langs = catalogs .iter() .map(|(code, catalog)| LangCode { code: code.clone(), name: catalog.gettext("lang").to_owned(), }) .collect(); Ok((catalogs, langs)) } pub fn i18n_filter( value: &Value, args: &HashMap, catalogs: &Catalogs, ) -> tera::Result { let key = value .as_str() .ok_or_else(|| tera::Error::msg("The translation key must be a string"))?; let langs = args .get("lang") .map(|value| value.as_array()) .flatten() .map(|array| { let mut langs = Vec::with_capacity(array.len()); for lang in array { langs.push(lang.as_str().unwrap()); } langs }) .unwrap_or_else(|| vec![DEFAULT]); for lang in langs { if let Some(catalog) = catalogs.get(lang) { return Ok(Value::String(catalog.gettext(key).to_owned())); } } panic!("Missing catalog"); } #[derive(Serialize)] pub struct LangCode { pub code: String, pub name: String, } pub fn langs_filter( _value: &Value, _args: &HashMap, langs: &Vec, ) -> tera::Result { Ok(serde_json::to_value(langs).unwrap()) }