406 lines
11 KiB
Markdown
Raw Permalink Normal View History

2026-01-12 03:31:00 +00:00
# Advanced i18n Example - Multi-Locale Production Workflows
**Production-grade internationalization with 9+ locales, advanced translation features, and RTL support**
This example demonstrates building truly global applications with complex translation scenarios: pluralization rules, gender-aware strings, number/date formatting, fallback chains, and right-to-left layout support.
## Overview
Basic i18n: English + Spanish + French
Advanced i18n: 9 locales with complex rules, fallbacks, and region-specific formatting
**Key features:**
- **9 complete locales** - Every form fully translated
- **Advanced Fluent features** - Plurals, gender, context-aware strings
- **Fallback chains** - Portuguese Brazil → Portuguese → Spanish → English
- **RTL support** - Arabic with proper text direction
- **Locale-specific formatting** - Currency ($1,234.56 vs 1.234,56€), dates, numbers
- **Production workflow** - Translation maintenance, coverage tracking, testing
## Locales Included
| Locale | Region | Features | Currency | Plurals |
|--------|--------|----------|----------|---------|
| **en-US** | United States | Base reference | $ USD | 2 (1 vs other) |
| **en-GB** | United Kingdom | British English, £ | £ GBP | 2 (1 vs other) |
| **es-ES** | Spain | Gender agreement, € | € EUR | 2 (1 vs other) |
| **es-MX** | Mexico | Gender agreement, $ | $ MXN | 2 (1 vs other) |
| **pt-BR** | Brazil | Fallback: pt → es | R$ BRL | 2 (1 vs other) |
| **pt-PT** | Portugal | Fallback: pt → es | € EUR | 2 (1 vs other) |
| **fr-FR** | France | Gender agreement, € | € EUR | 2 (0/1 vs other) |
| **ja-JP** | Japan | No plurals, no gender | ¥ JPY | 1 (no plurals) |
| **ar-SA** | Saudi Arabia | RTL text direction, complex plurals | ﷼ SAR | 6 (0, 1, 2, 3-10, 11-99, 100+) |
## Directory Structure
```
17-advanced-i18n/
├── README.md (This file)
├── checkout-form.toml (Example form)
├── test-locales.sh (Test all 9 locales)
├── translations-status.md (Coverage matrix)
├── i18n-config.toml (Fallback chains)
└── locales/
├── en-US/main.ftl (English US - reference)
├── en-GB/main.ftl (English UK)
├── es-ES/main.ftl (Spanish Spain)
├── es-MX/main.ftl (Spanish Mexico)
├── pt-BR/main.ftl (Portuguese Brazil)
├── pt-PT/main.ftl (Portuguese Portugal)
├── fr-FR/main.ftl (French)
├── ja-JP/main.ftl (Japanese)
└── ar-SA/main.ftl (Arabic - RTL)
```
## Example Form: E-Commerce Checkout
`checkout-form.toml` demonstrates real-world translation challenges:
```toml
[[sections]]
title = "Order Summary"
[[sections.elements]]
name = "item_count"
type = "display"
label = "{ $count ->
[one] You have 1 item in your cart
*[other] You have { $count } items in your cart
}"
[[sections.elements]]
name = "subtotal"
type = "display"
label = "Subtotal: { $amount -number }"
[[sections.elements]]
name = "tax"
type = "display"
label = "Tax: { $amount -currency }"
[[sections.elements]]
name = "delivery_date"
type = "display"
label = "Estimated delivery: { $date -date }"
```
Each locale renders this form correctly:
- **English:** "You have 5 items" | "Subtotal: $1,234.56" | "Estimated delivery: Tuesday, January 15, 2025"
- **French:** "Vous avez 5 articles" | "Total: 1.234,56 €" | "Livraison estimée: mardi 15 janvier 2025"
- **Arabic:** "لديك 5 عناصر" (RTL) | "الإجمالي: 1.234,56 ﷼" | "التسليم المتوقع: الثلاثاء 15 يناير 2025"
## Running Examples
### Test All Locales
```bash
# Run form in each locale (9 iterations)
./test-locales.sh
# Output shows how form renders in each language
```
### Test Specific Locale
```bash
# English US (default)
cargo run -p typedialog-tui -- checkout-form.toml
# English UK
LANG=en_GB cargo run -p typedialog-tui -- checkout-form.toml
# Spanish Spain
LANG=es_ES cargo run -p typedialog-tui -- checkout-form.toml
# Arabic (RTL)
LANG=ar_SA cargo run -p typedialog-tui -- checkout-form.toml
# French
LANG=fr_FR cargo run -p typedialog-tui -- checkout-form.toml
# Japanese
LANG=ja_JP cargo run -p typedialog-tui -- checkout-form.toml
# Portuguese Brazil (tests fallback chain)
LANG=pt_BR cargo run -p typedialog-tui -- checkout-form.toml
```
### Check Translation Status
```bash
# See which strings are translated in each locale
cat translations-status.md
```
## Fluent Features Demonstrated
### 1. Pluralization
**English (2 plural forms):**
```fluent
items-in-cart =
{ $count ->
[one] You have 1 item
*[other] You have { $count } items
}
```
**French (special rule for 0 and 1):**
```fluent
articles-panier =
{ $count ->
[0] Vous n'avez pas d'articles
[1] Vous avez 1 article
*[other] Vous avez { $count } articles
}
```
**Arabic (6 plural forms):**
```fluent
items-in-cart =
{ $count ->
[0] لا توجد عناصر
[1] عنصر واحد
[2] عنصرين
[3] { $count } عناصر
[4] { $count } عنصر
*[other] { $count } عنصر
}
```
### 2. Gender Agreement
**Spanish (nouns have gender):**
```fluent
# -gender attribute determines which form to use
guest-welcome = { $name, $gender ->
[m] Bienvenido, { $name }
[f] Bienvenida, { $name }
*[other] Bienvenido, { $name }
}
```
**French:**
```fluent
thank-you = { $gender ->
[m] Merci, Monsieur
[f] Merci, Madame
*[other] Merci
}
```
### 3. Number Formatting
```fluent
# Numbers format per locale automatically
order-total = Total: { $amount -number }
# Output:
# en-US: Total: 1,234.56
# fr-FR: Total: 1.234,56
# de-DE: Total: 1.234,56
# ar-SA: Total: ١٬٢٣٤٫٥٦
```
### 4. Date Formatting
```fluent
# Dates format per locale
ship-date = Ships on { $date -date }
# Output:
# en-US: Ships on 1/15/2025
# fr-FR: Ships on 15/01/2025
# ja-JP: Ships on 2025年1月15日
# ar-SA: Ships on ١٥ يناير ٢٠٢٥
```
### 5. Fallback Chains
**Portuguese Brazil → Portuguese → Spanish → English**
```toml
# i18n-config.toml
fallback_chains = [
"pt-BR → pt → es → en-US",
"pt-PT → pt → es → en-US",
"es-MX → es → en-US",
]
```
If a string is missing from `pt-BR/main.ftl`:
1. Look in `pt-PT/main.ftl`
2. Look in `es/main.ftl`
3. Look in `en-US/main.ftl` (fallback to English)
## RTL (Right-to-Left) Support
**Arabic (ar-SA):**
```ftl
# Direction attribute tells UI to render RTL
welcome = مرحبا بك
.direction = rtl
# Numerals use Arabic-Indic numerals
order-number = رقم الطلب: { $number }
```
TypeDialog automatically:
- Reverses text direction
- Handles number formatting (Arabic numerals: ٠١٢٣٤٥٦٧٨٩)
- Mirrors UI layout
## Translation Maintenance
### Workflow
1. **Add new string in en-US:**
```ftl
new-feature = This is a new feature
```
2. **Mark as untranslated in other locales:**
```ftl
new-feature = [NEEDS TRANSLATION]
```
3. **Update status tracking:**
```bash
./translations-status.md # Shows incomplete translations
```
4. **Translate and remove marker:**
```ftl
new-feature = Ceci est une nouvelle fonctionnalité
```
5. **Verify coverage:**
```bash
grep -r "NEEDS TRANSLATION" locales/
```
### Coverage Matrix
`translations-status.md` shows:
```markdown
| Locale | Complete | Partial | Missing |
|--------|----------|---------|---------|
| en-US | 100% | 0% | 0% |
| en-GB | 95% | 5% | 0% |
| es-ES | 90% | 10% | 0% |
| ... | ... | ... | ... |
```
## Testing Translations Programmatically
```bash
# Validate all Fluent files parse correctly
for locale in locales/*/; do
nickel check "$locale/main.ftl" || echo "Error in $locale"
done
# Test that all form keys are translated in all locales
./test-locales.sh --validate
# Run form and verify output in each locale
for locale in en-US en-GB es-ES pt-BR ar-SA ja-JP; do
echo "Testing $locale..."
LANG="$locale" cargo run -p typedialog -- checkout-form.toml --format json | jq . > /dev/null
done
```
## Key Learning Points
### 1. Pluralization is Language-Specific
- English: 2 forms (singular 1 vs plural)
- Polish: 4 forms (1, 2-4, 5-21, 22+)
- Arabic: 6 forms (0, 1, 2, 3-10, 11-99, 100+)
- Japanese: 1 form (no plurals)
**Cannot** use a one-size-fits-all plural function!
### 2. Gender Agreement Requires Context
Spanish/French/German nouns have gender, affecting:
- Articles (el/la/lo, le/la/l', der/die/das)
- Adjectives (rojo/roja, rouge/rouge, roter/rote)
- Past participles (comido/comida, mangé/mangée)
**Cannot** just translate strings in isolation!
### 3. Number Formatting Varies
- US: 1,234.56 (comma thousands, period decimal)
- Europe: 1.234,56 (period thousands, comma decimal)
- India: 12,34,567 (special grouping)
- Arabic: Uses Arabic-Indic numerals (٠١٢٣٤)
**Cannot** hardcode number formatting!
### 4. Date Formatting Varies
- en-US: 1/15/2025
- en-GB: 15/01/2025
- fr-FR: 15 janvier 2025
- ja-JP: 2025年1月15日
**Cannot** use ISO format everywhere!
### 5. RTL Requires Special Handling
Right-to-left languages (Arabic, Hebrew, Persian):
- Text flows right to left
- Numbers still read left to right
- UI layout must be mirrored
**Cannot** ignore RTL just because it's a minority use case!
## Real-World Best Practices
1. **Always use Fluent** for anything beyond trivial translations
2. **Test with native speakers** - Automated tests miss context
3. **Plan for fallbacks** - Incomplete translations should not crash
4. **Track coverage** - Know which locales are complete
5. **Separate translation work** - Translators shouldn't need to understand code
6. **Use context** - Provide translator notes for ambiguous strings
7. **Test all locales** - Every deployment should verify each language
8. **Monitor in production** - Users will report issues you missed
## Troubleshooting
**"String appears in English instead of locale"**
- Check if fallback chain applies (pt-BR → pt → es → en-US)
- Verify LANG environment variable is set correctly
- Check Fluent file for syntax errors
**"Numbers don't format correctly"**
- Ensure you're using `-number` function in Fluent
- Check locale data is available in your system
- Test with: `date +%x` to verify locale
**"Arabic text appears reversed"**
- Add `.direction = rtl` attribute in Fluent
- Check UI component supports RTL
- Verify numerals format with Arabic-Indic style
**"Plural forms incorrect"**
- Verify plural rules for language (not just guess English 2-form)
- Check documentation for language's plural rules
- Test with edge cases: 0, 1, 2, 21, 100, 1000
## Next Steps
1. **Add more locales** - Use existing locales as templates
2. **Extract all UI strings** - Audit your forms for hardcoded text
3. **Set up translation platform** - Crowdin, Weblate, or similar
4. **Automate testing** - Run `test-locales.sh` in CI/CD
5. **Monitor translations** - Track completion and quality
6. **Build management UI** - For non-developers to manage translations
---
**Key Takeaway:** Internationalization is not just translation - it's localizing numbers, dates, plurals, and gender agreement while handling RTL and fallback chains. Use Fluent for complex features and always test with native speakers.