# 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
Save Changes
Profile
Member since {{ profile.created_at }}
{{ t(key="save-changes", locale=locale) }}
{{ 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
```
## 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.