i18n: add template override mechanism
This commit is contained in:
parent
069d332123
commit
6236d3bf40
43
src/mail.rs
43
src/mail.rs
|
@ -1,4 +1,4 @@
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use failure;
|
use failure;
|
||||||
use handlebars::Handlebars;
|
use handlebars::Handlebars;
|
||||||
|
@ -10,10 +10,9 @@ use uuid::Uuid;
|
||||||
use crate::counters;
|
use crate::counters;
|
||||||
|
|
||||||
use rocket_i18n::I18n;
|
use rocket_i18n::I18n;
|
||||||
use gettext_macros::include_i18n;
|
|
||||||
use gettext_macros::i18n;
|
use gettext_macros::i18n;
|
||||||
|
|
||||||
use crate::i18n::I18NHelper;
|
use crate::template_helpers;
|
||||||
|
|
||||||
use crate::database::types::Email;
|
use crate::database::types::Email;
|
||||||
use crate::Result;
|
use crate::Result;
|
||||||
|
@ -73,7 +72,7 @@ impl Service {
|
||||||
|
|
||||||
fn new(from: String, base_uri: String, template_dir: PathBuf, transport: Transport)
|
fn new(from: String, base_uri: String, template_dir: PathBuf, transport: Transport)
|
||||||
-> Result<Self> {
|
-> Result<Self> {
|
||||||
let templates = load_handlebars(template_dir)?;
|
let templates = template_helpers::load_handlebars(template_dir)?;
|
||||||
let domain =
|
let domain =
|
||||||
url::Url::parse(&base_uri)
|
url::Url::parse(&base_uri)
|
||||||
?.host_str().ok_or_else(|| failure::err_msg("No host in base-URI"))
|
?.host_str().ok_or_else(|| failure::err_msg("No host in base-URI"))
|
||||||
|
@ -167,7 +166,7 @@ impl Service {
|
||||||
&self,
|
&self,
|
||||||
template: &str,
|
template: &str,
|
||||||
locale: &str,
|
locale: &str,
|
||||||
ctx: impl Serialize + Clone
|
ctx: impl Serialize
|
||||||
) -> Result<(String, String)> {
|
) -> Result<(String, String)> {
|
||||||
let html = self.templates.render(&format!("{}/{}.htm", locale, template), &ctx)
|
let html = self.templates.render(&format!("{}/{}.htm", locale, template), &ctx)
|
||||||
.or_else(|_| self.templates.render(&format!("{}.htm", template), &ctx))
|
.or_else(|_| self.templates.render(&format!("{}.htm", template), &ctx))
|
||||||
|
@ -185,7 +184,7 @@ impl Service {
|
||||||
subject: &str,
|
subject: &str,
|
||||||
template: &str,
|
template: &str,
|
||||||
locale: &str,
|
locale: &str,
|
||||||
ctx: impl Serialize + Clone
|
ctx: impl Serialize
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let (html, txt) = self.render_template(template, locale, ctx)?;
|
let (html, txt) = self.render_template(template, locale, ctx)?;
|
||||||
|
|
||||||
|
@ -221,35 +220,3 @@ impl Service {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_handlebars(template_dir: PathBuf) -> Result<Handlebars> {
|
|
||||||
let mut handlebars = Handlebars::new();
|
|
||||||
|
|
||||||
let i18ns = include_i18n!();
|
|
||||||
let i18n_helper = I18NHelper::new(i18ns);
|
|
||||||
handlebars.register_helper("text", Box::new(i18n_helper));
|
|
||||||
|
|
||||||
let mut glob_path = template_dir.join("**").join("*");
|
|
||||||
glob_path.set_extension("hbs");
|
|
||||||
let glob_path = glob_path.to_str().expect("valid glob path string");
|
|
||||||
|
|
||||||
for path in glob::glob(glob_path).unwrap().flatten() {
|
|
||||||
let template_name = remove_extension(path.strip_prefix(&template_dir)?);
|
|
||||||
handlebars.register_template_file(&template_name.to_string_lossy(), &path)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(handlebars)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn remove_extension<P: AsRef<Path>>(path: P) -> PathBuf {
|
|
||||||
let path = path.as_ref();
|
|
||||||
let stem = match path.file_stem() {
|
|
||||||
Some(stem) => stem,
|
|
||||||
None => return path.to_path_buf()
|
|
||||||
};
|
|
||||||
|
|
||||||
match path.parent() {
|
|
||||||
Some(parent) => parent.join(stem),
|
|
||||||
None => PathBuf::from(stem)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
|
@ -52,6 +52,7 @@ mod rate_limiter;
|
||||||
mod dump;
|
mod dump;
|
||||||
mod counters;
|
mod counters;
|
||||||
mod i18n;
|
mod i18n;
|
||||||
|
mod template_helpers;
|
||||||
mod gettext_strings;
|
mod gettext_strings;
|
||||||
mod web;
|
mod web;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,82 @@
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
|
use handlebars::Handlebars;
|
||||||
|
|
||||||
|
use gettext_macros::include_i18n;
|
||||||
|
|
||||||
|
use crate::Result;
|
||||||
|
use crate::i18n::I18NHelper;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct TemplateOverrides(String, HashSet<(String)>);
|
||||||
|
|
||||||
|
impl TemplateOverrides {
|
||||||
|
pub fn load(template_path: &Path, localized_dir: &str) -> Result<Self> {
|
||||||
|
load_localized_template_names(template_path, localized_dir)
|
||||||
|
.map(|vec| Self(localized_dir.to_owned(), vec))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_template_override(&self, lang: &str, tmpl: &str) -> Option<String> {
|
||||||
|
let template_name = format!("{}/{}/{}", self.0, lang, tmpl);
|
||||||
|
if self.1.contains(&template_name) {
|
||||||
|
println!("{}", &template_name);
|
||||||
|
Some(template_name)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_localized_template_names(template_path: &Path, localized_dir: &str) -> Result<HashSet<(String)>> {
|
||||||
|
let language_glob = template_path.join(localized_dir).join("*");
|
||||||
|
glob::glob(language_glob.to_str().expect("valid glob path string"))
|
||||||
|
.unwrap()
|
||||||
|
.flatten()
|
||||||
|
.flat_map(|language_path| {
|
||||||
|
let mut template_glob = language_path.join("**").join("*");
|
||||||
|
template_glob.set_extension("hbs");
|
||||||
|
glob::glob(template_glob.to_str().expect("valid glob path string"))
|
||||||
|
.unwrap()
|
||||||
|
.flatten()
|
||||||
|
.map(move |path| {
|
||||||
|
// TODO this is a hack
|
||||||
|
let template_name = remove_extension(remove_extension(path.strip_prefix(&template_path)?));
|
||||||
|
Ok(template_name.to_string_lossy().into_owned())
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load_handlebars(template_dir: PathBuf) -> Result<Handlebars> {
|
||||||
|
let mut handlebars = Handlebars::new();
|
||||||
|
|
||||||
|
let i18ns = include_i18n!();
|
||||||
|
let i18n_helper = I18NHelper::new(i18ns);
|
||||||
|
handlebars.register_helper("text", Box::new(i18n_helper));
|
||||||
|
|
||||||
|
let mut glob_path = template_dir.join("**").join("*");
|
||||||
|
glob_path.set_extension("hbs");
|
||||||
|
let glob_path = glob_path.to_str().expect("valid glob path string");
|
||||||
|
|
||||||
|
for path in glob::glob(glob_path).unwrap().flatten() {
|
||||||
|
let template_name = remove_extension(path.strip_prefix(&template_dir)?);
|
||||||
|
handlebars.register_template_file(&template_name.to_string_lossy(), &path)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(handlebars)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remove_extension<P: AsRef<Path>>(path: P) -> PathBuf {
|
||||||
|
let path = path.as_ref();
|
||||||
|
let stem = match path.file_stem() {
|
||||||
|
Some(stem) => stem,
|
||||||
|
None => return path.to_path_buf()
|
||||||
|
};
|
||||||
|
|
||||||
|
match path.parent() {
|
||||||
|
Some(parent) => parent.join(stem),
|
||||||
|
None => PathBuf::from(stem)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ use std::path::PathBuf;
|
||||||
use crate::mail;
|
use crate::mail;
|
||||||
use crate::tokens;
|
use crate::tokens;
|
||||||
use crate::counters;
|
use crate::counters;
|
||||||
|
use crate::template_helpers::TemplateOverrides;
|
||||||
use crate::i18n::I18NHelper;
|
use crate::i18n::I18NHelper;
|
||||||
use crate::rate_limiter::RateLimiter;
|
use crate::rate_limiter::RateLimiter;
|
||||||
|
|
||||||
|
@ -48,9 +49,16 @@ impl Responder<'static> for HagridTemplate {
|
||||||
fn respond_to(self, req: &rocket::Request) -> std::result::Result<Response<'static>, Status> {
|
fn respond_to(self, req: &rocket::Request) -> std::result::Result<Response<'static>, Status> {
|
||||||
let HagridTemplate(tmpl, ctx) = self;
|
let HagridTemplate(tmpl, ctx) = self;
|
||||||
let i18n: I18n = req.guard().expect("Error parsing language");
|
let i18n: I18n = req.guard().expect("Error parsing language");
|
||||||
|
let template_overrides: rocket::State<TemplateOverrides> = req.guard().expect("TemplateOverrides must be in managed state");
|
||||||
|
let template_override = template_overrides.get_template_override(i18n.lang, tmpl);
|
||||||
let origin: RequestOrigin = req.guard().expect("Error determining request origin");
|
let origin: RequestOrigin = req.guard().expect("Error determining request origin");
|
||||||
let layout_context = templates::HagridLayout::new(ctx, i18n, origin);
|
let layout_context = templates::HagridLayout::new(ctx, i18n, origin);
|
||||||
Template::render(tmpl, layout_context).respond_to(req)
|
|
||||||
|
if let Some(template_override) = template_override {
|
||||||
|
Template::render(template_override, layout_context)
|
||||||
|
} else {
|
||||||
|
Template::render(tmpl, layout_context)
|
||||||
|
}.respond_to(req)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -410,6 +418,8 @@ fn rocket_factory(mut rocket: rocket::Rocket) -> Result<rocket::Rocket> {
|
||||||
let mail_service = configure_mail_service(rocket.config())?;
|
let mail_service = configure_mail_service(rocket.config())?;
|
||||||
let rate_limiter = configure_rate_limiter(rocket.config())?;
|
let rate_limiter = configure_rate_limiter(rocket.config())?;
|
||||||
let maintenance_mode = configure_maintenance_mode(rocket.config())?;
|
let maintenance_mode = configure_maintenance_mode(rocket.config())?;
|
||||||
|
let localized_template_list = configure_localized_template_list(rocket.config())?;
|
||||||
|
println!("{:?}", localized_template_list);
|
||||||
|
|
||||||
let prometheus = configure_prometheus(rocket.config());
|
let prometheus = configure_prometheus(rocket.config());
|
||||||
|
|
||||||
|
@ -427,6 +437,7 @@ fn rocket_factory(mut rocket: rocket::Rocket) -> Result<rocket::Rocket> {
|
||||||
.manage(mail_service)
|
.manage(mail_service)
|
||||||
.manage(db_service)
|
.manage(db_service)
|
||||||
.manage(rate_limiter)
|
.manage(rate_limiter)
|
||||||
|
.manage(localized_template_list)
|
||||||
.mount("/", routes);
|
.mount("/", routes);
|
||||||
|
|
||||||
if let Some(prometheus) = prometheus {
|
if let Some(prometheus) = prometheus {
|
||||||
|
@ -514,6 +525,11 @@ fn configure_rate_limiter(config: &Config) -> Result<RateLimiter> {
|
||||||
Ok(RateLimiter::new(timeout_secs))
|
Ok(RateLimiter::new(timeout_secs))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn configure_localized_template_list(config: &Config) -> Result<TemplateOverrides> {
|
||||||
|
let template_dir: PathBuf = config.get_str("template_dir")?.into();
|
||||||
|
TemplateOverrides::load(&template_dir, "localized")
|
||||||
|
}
|
||||||
|
|
||||||
fn configure_maintenance_mode(config: &Config) -> Result<MaintenanceMode> {
|
fn configure_maintenance_mode(config: &Config) -> Result<MaintenanceMode> {
|
||||||
let maintenance_file: PathBuf = config.get_str("maintenance_file")
|
let maintenance_file: PathBuf = config.get_str("maintenance_file")
|
||||||
.unwrap_or("maintenance").into();
|
.unwrap_or("maintenance").into();
|
||||||
|
|
Loading…
Reference in New Issue