# Smokesignal Template Migration Guidelines This document provides step-by-step guidance for migrating existing hardcoded templates to use the new i18n system with on-demand translation functions. ## Migration Overview Migrate from hardcoded strings in templates to Fluent-based translations using template functions. This migration eliminates pre-rendered translation HashMaps and improves HTMX performance. ## Migration Strategy ### Phase 1: Template Analysis & Key Extraction #### 1.1 Inventory Existing Strings ```bash # Find all hardcoded strings in templates find templates/ -name "*.html" -exec grep -Hn '"[^"]*"' {} \; > strings_inventory.txt find templates/ -name "*.html" -exec grep -Hn "'[^']*'" {} \; >> strings_inventory.txt # Categorize by domain for organized migration grep -E "(button|btn|submit|save|edit|delete|cancel)" strings_inventory.txt > actions.txt grep -E "(error|fail|invalid|required)" strings_inventory.txt > errors.txt grep -E "(title|heading|h1|h2|h3)" strings_inventory.txt > headings.txt grep -E "(label|placeholder|hint)" strings_inventory.txt > forms.txt ``` #### 1.2 Create Translation Key Naming Convention ``` # Pattern: domain-purpose[-variant] save-changes # Basic action edit-profile # Specific action validation-required # Error message profile-title # Page heading enter-name-placeholder # Form guidance welcome-message-feminine # Gender variant ``` ### Phase 2: Fluent File Creation #### 2.1 Organize by Category ```ftl # i18n/en-us/actions.ftl save-changes = Save Changes edit-profile = Edit Profile delete-item = Delete cancel-action = Cancel follow-user = Follow unfollow-user = Unfollow # i18n/en-us/errors.ftl validation-required = This field is required validation-email = Please enter a valid email validation-minlength = Must be at least {$min} characters form-submit-error = Unable to submit form profile-not-found = Profile not found # i18n/en-us/ui.ftl profile-title = Profile member-since = Member since events-created = Events Created welcome-message = Welcome search-placeholder = Search... # i18n/fr-ca/actions.ftl save-changes = Enregistrer les modifications edit-profile = Modifier le profil delete-item = Supprimer cancel-action = Annuler follow-user = Suivre unfollow-user = Ne plus suivre ``` #### 2.2 Gender-Aware Translations ```ftl # English (gender-neutral by default) welcome-message = Welcome profile-greeting = Hello there # French Canadian (gender variants) welcome-message = Bienvenue welcome-message-feminine = Bienvenue welcome-message-masculine = Bienvenu welcome-message-neutral = Bienvenue profile-greeting = Bonjour profile-greeting-feminine = Bonjour madame profile-greeting-masculine = Bonjour monsieur profile-greeting-neutral = Bonjour ``` ### Phase 3: Template Function Integration #### 3.1 Replace Simple Strings ```html

Profile

Member since {{ profile.created_at }}

{{ t(key="profile-title", locale=locale) }}

{{ t(key="member-since", locale=locale) }} {{ profile.created_at }}

``` #### 3.2 Add Gender-Aware Translations ```html

Welcome, {{ user.name }}!

{{ tg(key="welcome-message", locale=locale, gender=user_gender) }}, {{ user.name }}!

``` #### 3.3 Handle Parameterized Messages ```html

You have {{ event_count }} events

{{ tc(key="events-count", locale=locale, count=event_count) }}

``` ### Phase 4: HTMX-Specific Migration #### 4.1 Form Templates with Language Propagation ```html
``` #### 4.2 Error Message Templates ```html
Invalid email address
{{ t(key="validation-email", locale=locale) }}
``` ### Phase 5: Template Hierarchy Migration #### 5.1 Base Template Updates ```html {{ t(key="site-title", locale=locale) }} {% include 'nav.html' %} {% block content %}{% endblock %} {% include 'footer.html' %} ``` #### 5.2 Partial Templates for HTMX ```html

{{ t(key="edit-profile-title", locale=locale) }}

{% if errors %}
{% for error in errors %}

{{ t(key=error.key, locale=locale) }}

{% endfor %}
{% endif %}
``` ## Migration Tools & Automation ### Automated String Replacement Script ```bash #!/bin/bash # migrate_template.sh TEMPLATE_FILE=$1 BACKUP_FILE="${TEMPLATE_FILE}.bak" # Create backup cp "$TEMPLATE_FILE" "$BACKUP_FILE" # Replace common patterns sed -i 's/"Save Changes"/{{ t(key="save-changes", locale=locale) }}/g' "$TEMPLATE_FILE" sed -i 's/"Edit Profile"/{{ t(key="edit-profile", locale=locale) }}/g' "$TEMPLATE_FILE" sed -i 's/"Delete"/{{ t(key="delete-item", locale=locale) }}/g' "$TEMPLATE_FILE" sed -i 's/"Cancel"/{{ t(key="cancel-action", locale=locale) }}/g' "$TEMPLATE_FILE" # Handle form labels sed -i 's/"Display Name"/{{ t(key="display-name", locale=locale) }}/g' "$TEMPLATE_FILE" sed -i 's/"Email"/{{ t(key="email", locale=locale) }}/g' "$TEMPLATE_FILE" echo "Migrated $TEMPLATE_FILE (backup: $BACKUP_FILE)" ``` ### Translation Key Validator ```rust // tools/validate_keys.rs use std::collections::HashSet; use regex::Regex; fn extract_translation_keys_from_templates() -> HashSet { let re = Regex::new(r#"\{\{\s*t\w*\(key="([^"]+)""#).unwrap(); // Extract all translation keys from templates // Return set of used keys } fn load_fluent_keys() -> HashSet { // Load all keys from .ftl files // Return set of available keys } #[test] fn test_all_translation_keys_exist() { let used_keys = extract_translation_keys_from_templates(); let available_keys = load_fluent_keys(); for key in &used_keys { assert!( available_keys.contains(key), "Missing translation key: {} (used in templates)", key ); } println!("✅ All {} translation keys validated", used_keys.len()); } ``` ## Migration Validation ### Template Syntax Validation ```bash # Validate template syntax after migration find templates/ -name "*.html" -exec python3 -c " import sys import re def validate_template(file_path): with open(file_path, 'r') as f: content = f.read() # Check for proper function calls pattern = r'\{\{\s*t[gc]?\(key=[\"'\''][^\"\']+[\"'\''][^}]*\)\s*\}\}' matches = re.findall(pattern, content) # Check for missing locale parameter missing_locale = re.findall(r'\{\{\s*t[gc]?\([^}]*\)\s*\}\}', content) print(f'File: {file_path}') print(f' Translation calls: {len(matches)}') if missing_locale: print(f' ⚠️ Potential missing locale: {len(missing_locale)}') validate_template(sys.argv[1]) " {} \; ``` ### Performance Comparison ```rust // Compare before/after performance #[cfg(test)] mod migration_performance_tests { #[test] fn benchmark_old_vs_new_rendering() { // Test pre-rendered HashMap approach vs on-demand functions let start = std::time::Instant::now(); // Old approach: pre-render all translations let _old_result = render_with_prerendered_translations(); let old_duration = start.elapsed(); let start = std::time::Instant::now(); // New approach: on-demand translation functions let _new_result = render_with_template_functions(); let new_duration = start.elapsed(); println!("Old approach: {:?}", old_duration); println!("New approach: {:?}", new_duration); // Expect significant improvement assert!(new_duration < old_duration * 3 / 4); } } ``` ## Migration Checklist ### Per Template - [ ] Backup original template - [ ] Extract all hardcoded strings - [ ] Create corresponding Fluent keys - [ ] Replace strings with template functions - [ ] Add HTMX language headers if applicable - [ ] Test rendering in both languages - [ ] Validate gender variants (if applicable) - [ ] Performance test HTMX interactions ### Per Handler - [ ] Remove pre-rendered translation HashMap - [ ] Use minimal template context - [ ] Ensure Language extractor is used - [ ] Add proper error handling for missing keys - [ ] Test with HTMX requests ### Project-Wide - [ ] All templates migrated - [ ] All Fluent files complete - [ ] Translation key validator passes - [ ] HTMX language propagation working - [ ] Performance benchmarks improved - [ ] Documentation updated ## Common Migration Patterns ### Form Validation Messages ```html {% if field_errors %}
{% for error in field_errors %} {{ t(key=error.translation_key, locale=locale, args=error.args) }} {% endfor %}
{% endif %} ``` ### Conditional Gender Messages ```html {% if user_gender == "feminine" %} {{ tg(key="welcome-message", locale=locale, gender="feminine") }} {% elif user_gender == "masculine" %} {{ tg(key="welcome-message", locale=locale, gender="masculine") }} {% else %} {{ tg(key="welcome-message", locale=locale, gender="neutral") }} {% endif %} ``` ### Count-Based Messages ```html

{{ tc(key="events-created", locale=locale, count=profile.event_count) }}

{{ tc(key="followers-count", locale=locale, count=profile.followers) }}

``` This migration approach ensures a smooth transition from hardcoded strings to a flexible, performance-optimized i18n system while maintaining HTMX compatibility.