# smokesignal API Reference ## Template Rendering System ### TemplateRenderer The `TemplateRenderer` is the core component of smokesignal's unified template rendering system. It provides automatic context enrichment with i18n, HTMX, and gender data. #### Basic Usage ```rust use smokesignal::http::template_renderer::TemplateRenderer; use smokesignal::http::macros::create_renderer; // Create renderer with all context let renderer = TemplateRenderer::new( template_engine, i18n_context, language, is_htmx ).with_gender_context(gender_context); // Render template let html = renderer.render("template_name", &context)?; ``` #### Constructor Methods ##### `new()` ```rust pub fn new( template_engine: &'a Environment<'a>, i18n_context: &'a I18nTemplateContext, language: Language, is_htmx: bool, ) -> Self ``` Creates a new TemplateRenderer with basic context. **Parameters:** - `template_engine`: minijinja Environment for template rendering - `i18n_context`: I18n context for translation support - `language`: Current user language (Language wrapper) - `is_htmx`: Whether this is an HTMX request ##### `with_gender_context()` ```rust pub fn with_gender_context(self, gender_context: Option<&'a GenderContext>) -> Self ``` Adds gender context for personalized content. **Parameters:** - `gender_context`: Optional gender context for gendered translations #### Rendering Methods ##### `render()` ```rust pub fn render(&self, template_name: &str, context: &impl Serialize) -> Result ``` Renders a template with automatic context enrichment. **Parameters:** - `template_name`: Name of template (without extension) - `context`: Template context data **Returns:** Rendered HTML string or error ##### `render_error()` ```rust pub fn render_error(&self, template_name: &str, context: &impl Serialize, error_context: Value) -> Result ``` Renders error templates with merged context. **Parameters:** - `template_name`: Error template name - `context`: Base template context - `error_context`: Additional error-specific context #### Context Enrichment The TemplateRenderer automatically adds the following to all template contexts: ```rust // Base context automatically added { "locale": "en-us", // Current language "is_htmx": false, // HTMX request detection "gender": "neutral", // User gender preference "tr": TranslationFunction, // Translation function "current_locale": LocaleFunction, // Current locale function "has_locale": HasLocaleFunction, // Locale availability check } ``` ### Template Selection Templates are automatically selected based on: 1. **Language**: `template.{locale}.html` 2. **Request Type**: - `.partial.html` for HTMX requests - `.bare.html` for bare requests - `.html` for full page requests 3. **Fallback**: Falls back to English if locale template missing #### Template Naming Convention ``` templates/ ├── home.en-us.html # Full English template ├── home.en-us.partial.html # HTMX partial ├── home.en-us.bare.html # Bare template (no layout) ├── home.fr-ca.html # Full French template ├── home.fr-ca.partial.html # HTMX partial (French) └── error.en-us.html # Error template ``` ### Macros #### `create_renderer!` ```rust create_renderer!(template_engine, i18n_context, language, is_htmx, gender_context) ``` Convenience macro for creating TemplateRenderer instances. #### `contextual_error!` ```rust contextual_error!(renderer, "error_template", error_context) ``` Renders error templates with context preservation. ### I18n Integration #### I18nTemplateContext Provides dynamic locale support for templates. ```rust use smokesignal::i18n::I18nTemplateContext; // Create context let i18n_context = I18nTemplateContext::new(loader); // Use in templates via TemplateRenderer let renderer = TemplateRenderer::new(engine, &i18n_context, language, is_htmx); ``` #### Translation Functions Templates have access to several i18n functions: ##### `tr(key, args)` ```html {{ tr("welcome-message") }} {{ tr("user-greeting", {"name": user.name}) }} {{ tr("welcome-user", {"name": user.name, "gender": user.gender}) }} ``` ##### `current_locale()` ```html ``` ##### `has_locale(locale)` ```html {% if has_locale("fr-ca") %} Français {% endif %} ``` ## Language and Locale Management ### Language Wrapper The `Language` type wraps `LanguageIdentifier` for consistent handling: ```rust use smokesignal::i18n::Language; // Create from identifier let language = Language(langid!("en-US")); // Access identifier let id = language.0; ``` ### Supported Languages ```rust use smokesignal::i18n::SUPPORTED_LANGUAGES; // Available languages const SUPPORTED_LANGUAGES: &[&str] = &["en-us", "fr-ca"]; ``` ### Language Detection ```rust use smokesignal::http::middleware_i18n::{ detect_language_from_headers, detect_language_from_cookie, language_matches_any }; // Detect from Accept-Language header let language = detect_language_from_headers(&headers)?; // Detect from cookie let language = detect_language_from_cookie(&cookie_value)?; // Check if language is supported let is_supported = language_matches_any(&language); ``` ## Error Handling ### TemplateError ```rust use smokesignal::http::template_renderer::TemplateError; pub enum TemplateError { RenderError(minijinja::Error), ContextError(String), } ``` ### Error Context Error templates receive merged context: ```rust // Base context + error context { "error_message": "Translation key failed", "error_code": "TEMPLATE_001", "debug_info": {...}, // ... plus all base template context } ``` ## HTMX Integration ### HTMX Detection ```rust use smokesignal::http::middleware_i18n::{HX_REQUEST, HX_TRIGGER}; // Check headers let is_htmx = headers.get(HX_REQUEST).is_some(); let trigger = headers.get(HX_TRIGGER); ``` ### Partial Rendering HTMX requests automatically use `.partial.html` templates: ```rust // Automatically selects: // - home.en-us.partial.html for HTMX // - home.en-us.html for full requests let html = renderer.render("home", &context)?; ``` ## Gender Context ### GenderContext ```rust use smokesignal::i18n::gender::{Gender, GenderContext}; // Create gender context let gender_context = GenderContext { user_gender: Some(Gender::Female), target_gender: None, }; // Use in renderer let renderer = renderer.with_gender_context(Some(&gender_context)); ``` ### Gender Values ```rust pub enum Gender { Male, // "male" Female, // "female" Neutral, // "neutral" } ``` Gender values are automatically available in templates as strings. ## Performance Considerations ### Compile-Time Optimizations - **Static Translation Loading**: All translations compiled into binary - **Template Validation**: Templates validated at compile time - **Zero Runtime I/O**: No file system access for translations ### Runtime Optimizations - **HTMX Header Caching**: Constants for faster header parsing - **Early Exit Language Detection**: Optimized language matching - **Context Reuse**: Efficient context composition ### Memory Usage - **Shared References**: Template engines and i18n contexts are shared - **Strategic Cloning**: Only clone when necessary for API compatibility - **Minimal Allocations**: Efficient string handling in translations ## Migration Guide ### From Old System The new system is backwards compatible, but for optimal performance: 1. **Replace direct template calls**: ```rust // Old template_engine.render("template", &context)?; // New let renderer = create_renderer!(engine, i18n, lang, htmx, gender); renderer.render("template", &context)?; ``` 2. **Use unified error handling**: ```rust // Old if let Err(e) = result { return render_error_template(&e); } // New contextual_error!(renderer, "error_template", error_context) ``` 3. **Leverage automatic context**: ```rust // Old let mut context = minijinja::context! { ... }; context.insert("locale", &locale); context.insert("is_htmx", is_htmx); // New - automatic renderer.render("template", &base_context)?; ``` ### Best Practices 1. **Use create_renderer! macro** for concise renderer creation 2. **Leverage automatic context** instead of manual context building 3. **Use contextual_error!** for consistent error handling 4. **Test with multiple locales** to ensure fallback behavior 5. **Validate gender variants** for French Canadian content ## Testing ### Unit Tests ```rust #[cfg(test)] mod tests { use super::*; #[test] fn test_template_rendering() { let renderer = create_test_renderer(); let html = renderer.render("test", &context!{}).unwrap(); assert!(html.contains("expected content")); } } ``` ### Integration Tests ```bash # Test entire i18n system cargo test i18n # Test template rendering cargo test template_renderer # Test specific language cargo test i18n -- fr_ca ```