use async_trait::async_trait; use axum::{Extension, Router as AxumRouter}; use fluent_templates::{ArcLoader, FluentLoader}; use loco_rs::{ app::{AppContext, Initializer}, controller::views::{engines, ViewEngine}, Error, Result, }; use std::collections::HashMap; use tracing::info; const I18N_DIR: &str = "assets/i18n"; // Kept outside `I18N_DIR`: fluent-templates >=0.13 scans top-level *.ftl files // in that dir as locales, and "shared" parses as a langid, so a shared.ftl // living there would be loaded twice and fail with a duplicate-resource error. const I18N_SHARED: &str = "assets/i18n_shared/shared.ftl"; #[allow(clippy::module_name_repetitions)] pub struct ViewEngineInitializer; #[async_trait] impl Initializer for ViewEngineInitializer { fn name(&self) -> String { "view-engine".to_string() } async fn after_routes(&self, router: AxumRouter, _ctx: &AppContext) -> Result { // Load locales only if present; `t` is registered conditionally below so // the single post-process closure covers both cases. let locales = if std::path::Path::new(I18N_DIR).exists() { let arc = std::sync::Arc::new( ArcLoader::builder(&I18N_DIR, unic_langid::langid!("sk")) .shared_resources(Some(&[I18N_SHARED.into()])) .customize(|bundle| bundle.set_use_isolating(false)) .build() .map_err(|e| Error::string(&e.to_string()))?, ); info!("locales loaded"); Some(arc) } else { None }; let tera_engine = engines::TeraView::build()?.post_process(move |tera| { if let Some(arc) = &locales { tera.register_function("t", FluentLoader::new(arc.clone())); } // `csrf_token()`: the in-flight request's CSRF token (bound by // `shared::csrf::protect`), rendered into `` and // `ui::csrf_field()`. Inlined so its `tera::Error` return is inferred // from `register_function` — we never name a `tera` type, keeping it // off our direct deps and pinned to loco's. tera.register_function("csrf_token", |_args: &HashMap| { Ok(serde_json::Value::String( crate::shared::csrf::current_token().unwrap_or_default(), )) }); Ok(()) })?; Ok(router.layer(Extension(ViewEngine::from(tera_engine)))) } }