Fork i18n + search + filtering- v0.2

Phase 2 i18n Template Integration Guide#

This document explains how to integrate the newly completed Phase 2 i18n template functions into the Smokesignal application.

✅ Completed Features#

Phase 2 implementation provides the following MiniJinja template functions:

Core Translation Functions#

  1. t(key, **args) - Main translation function

    {{ t('welcome') }}
    {{ t('hello-user', name='Alice') }}
    
  2. tl(locale, key, **args) - Translation with explicit locale

    {{ tl('es-ES', 'welcome') }}
    {{ tl('fr-FR', 'hello-user', name='Bob') }}
    
  3. plural(count, key, **args) - Pluralization helper

    {{ plural(1, 'item-count', item='book') }}
    {{ plural(5, 'item-count', item='books') }}
    

Utility Functions#

  1. current_locale() - Get current locale

    Current language: {{ current_locale() }}
    
  2. has_locale(locale) - Check locale availability

    {% if has_locale('es-ES') %}
      <a href="/es">Español</a>
    {% endif %}
    
  3. format_number(number, style?) - Number formatting

    {{ format_number(1234.56) }}
    {{ format_number(total, 'currency') }}
    
  4. format_date(date, format?) - Date formatting (placeholder)

    {{ format_date(event.date, 'short') }}
    

🔧 Integration Instructions#

Step 1: Update Template Environment Setup#

Modify the template environment builders in src/http/templates.rs:

use crate::i18n::template_helpers::{register_i18n_functions, I18nTemplateContext};

// In reload_env module:
pub fn build_env(http_external: &str, version: &str, i18n_context: I18nTemplateContext) -> AutoReloader {
    // ... existing code ...
    AutoReloader::new(move |notifier| {
        let mut env = Environment::new();
        // ... existing setup ...
        
        // Register i18n functions
        register_i18n_functions(&mut env, i18n_context.clone());
        
        Ok(env)
    })
}

// In embed_env module:
pub fn build_env(http_external: String, version: String, i18n_context: I18nTemplateContext) -> Environment<'static> {
    let mut env = Environment::new();
    // ... existing setup ...
    
    // Register i18n functions
    register_i18n_functions(&mut env, i18n_context);
    
    env
}

Step 2: Create I18nTemplateContext in Application Setup#

In your application setup (likely in main.rs or wherever templates are initialized):

use std::sync::Arc;
use unic_langid::LanguageIdentifier;
use crate::i18n::{create_supported_languages, Locales};
use crate::i18n::template_helpers::I18nTemplateContext;

// Initialize i18n system
let languages = create_supported_languages();
let locales = Arc::new(Locales::new(languages.clone()));

// Create template context
let i18n_context = I18nTemplateContext::new(
    locales,
    LanguageIdentifier::from_str("en-US").unwrap(), // Current locale (from request)
    LanguageIdentifier::from_str("en-US").unwrap(), // Fallback locale
);

// Pass to template environment builders
let template_env = build_env(http_external, version, i18n_context);

Step 3: Request-Level Locale Context#

For per-request locale handling, you'll need to create the I18nTemplateContext based on the user's locale preference:

// In your request handlers
fn get_user_locale(request: &Request) -> LanguageIdentifier {
    // Extract from Accept-Language header, user settings, etc.
    // Fallback to default
    LanguageIdentifier::from_str("en-US").unwrap()
}

// Create context per request
let user_locale = get_user_locale(&request);
let i18n_context = I18nTemplateContext::new(
    locales.clone(),
    user_locale,
    LanguageIdentifier::from_str("en-US").unwrap(),
);

📝 Template Migration Examples#

Before (Hardcoded Strings)#

<h1>Welcome to Smokesignal</h1>
<p>You have 5 new messages</p>

After (i18n Functions)#

<h1>{{ t('welcome-title') }}</h1>
<p>{{ plural(message_count, 'new-messages') }}</p>

Conditional Locale Content#

{% if has_locale('es-ES') %}
  <a href="/set-locale/es-ES">Español</a>
{% endif %}

<p>{{ t('current-language') }}: {{ current_locale() }}</p>

Number and Date Formatting#

<span class="price">{{ format_number(price, 'currency') }}</span>
<time>{{ format_date(event.date, 'medium') }}</time>

🧪 Testing#

The template functions include comprehensive tests in src/i18n/template_helpers.rs. Run tests with:

cargo test i18n::template_helpers

Example test patterns:

  • Function registration verification
  • Locale detection and switching
  • Argument passing and conversion
  • Error handling for missing translations

📋 Next Steps (Phase 3)#

  1. Gender-aware translations - Add support for gendered message variants
  2. ICU message formatting - Enhanced pluralization and number formatting
  3. Real-time locale switching - Dynamic template re-rendering
  4. Translation management - Admin interface for managing translations
  5. Performance optimization - Caching and lazy loading of translations

🔍 Implementation Details#

Architecture#

  • Template Functions: Registered with MiniJinja environment
  • Context Management: Per-request locale handling via I18nTemplateContext
  • Value Conversion: Automatic conversion between MiniJinja and Fluent types
  • Error Handling: Graceful fallbacks for missing translations
  • Type Safety: Full Rust type safety with proper error propagation

Key Files#

  • src/i18n/template_helpers.rs - Template function implementations
  • src/i18n/mod.rs - Core i18n module with enhanced APIs
  • src/i18n/errors.rs - Extended error handling
  • examples/i18n_template_example.rs - Usage examples