# 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.