# i18n โ€” Internationalization Guide (Filipino) ๐ŸŒ **Languages:** ๐Ÿ‡บ๐Ÿ‡ธ [English](../../../../guides/I18N.md) ยท ๐Ÿ‡ธ๐Ÿ‡ฆ [ar](../../../ar/docs/guides/I18N.md) ยท ๐Ÿ‡ฆ๐Ÿ‡ฟ [az](../../../az/docs/guides/I18N.md) ยท ๐Ÿ‡ง๐Ÿ‡ฌ [bg](../../../bg/docs/guides/I18N.md) ยท ๐Ÿ‡ง๐Ÿ‡ฉ [bn](../../../bn/docs/guides/I18N.md) ยท ๐Ÿ‡จ๐Ÿ‡ฟ [cs](../../../cs/docs/guides/I18N.md) ยท ๐Ÿ‡ฉ๐Ÿ‡ฐ [da](../../../da/docs/guides/I18N.md) ยท ๐Ÿ‡ฉ๐Ÿ‡ช [de](../../../de/docs/guides/I18N.md) ยท ๐Ÿ‡ช๐Ÿ‡ธ [es](../../../es/docs/guides/I18N.md) ยท ๐Ÿ‡ฎ๐Ÿ‡ท [fa](../../../fa/docs/guides/I18N.md) ยท ๐Ÿ‡ซ๐Ÿ‡ฎ [fi](../../../fi/docs/guides/I18N.md) ยท ๐Ÿ‡ซ๐Ÿ‡ท [fr](../../../fr/docs/guides/I18N.md) ยท ๐Ÿ‡ฎ๐Ÿ‡ณ [gu](../../../gu/docs/guides/I18N.md) ยท ๐Ÿ‡ฎ๐Ÿ‡ฑ [he](../../../he/docs/guides/I18N.md) ยท ๐Ÿ‡ฎ๐Ÿ‡ณ [hi](../../../hi/docs/guides/I18N.md) ยท ๐Ÿ‡ญ๐Ÿ‡บ [hu](../../../hu/docs/guides/I18N.md) ยท ๐Ÿ‡ฎ๐Ÿ‡ฉ [id](../../../id/docs/guides/I18N.md) ยท ๐Ÿ‡ฎ๐Ÿ‡น [it](../../../it/docs/guides/I18N.md) ยท ๐Ÿ‡ฏ๐Ÿ‡ต [ja](../../../ja/docs/guides/I18N.md) ยท ๐Ÿ‡ฐ๐Ÿ‡ท [ko](../../../ko/docs/guides/I18N.md) ยท ๐Ÿ‡ฎ๐Ÿ‡ณ [mr](../../../mr/docs/guides/I18N.md) ยท ๐Ÿ‡ฒ๐Ÿ‡พ [ms](../../../ms/docs/guides/I18N.md) ยท ๐Ÿ‡ณ๐Ÿ‡ฑ [nl](../../../nl/docs/guides/I18N.md) ยท ๐Ÿ‡ณ๐Ÿ‡ด [no](../../../no/docs/guides/I18N.md) ยท ๐Ÿ‡ต๐Ÿ‡ฑ [pl](../../../pl/docs/guides/I18N.md) ยท ๐Ÿ‡ต๐Ÿ‡น [pt](../../../pt/docs/guides/I18N.md) ยท ๐Ÿ‡ง๐Ÿ‡ท [pt-BR](../../../pt-BR/docs/guides/I18N.md) ยท ๐Ÿ‡ท๐Ÿ‡ด [ro](../../../ro/docs/guides/I18N.md) ยท ๐Ÿ‡ท๐Ÿ‡บ [ru](../../../ru/docs/guides/I18N.md) ยท ๐Ÿ‡ธ๐Ÿ‡ฐ [sk](../../../sk/docs/guides/I18N.md) ยท ๐Ÿ‡ธ๐Ÿ‡ช [sv](../../../sv/docs/guides/I18N.md) ยท ๐Ÿ‡ฐ๐Ÿ‡ช [sw](../../../sw/docs/guides/I18N.md) ยท ๐Ÿ‡ฎ๐Ÿ‡ณ [ta](../../../ta/docs/guides/I18N.md) ยท ๐Ÿ‡ฎ๐Ÿ‡ณ [te](../../../te/docs/guides/I18N.md) ยท ๐Ÿ‡น๐Ÿ‡ญ [th](../../../th/docs/guides/I18N.md) ยท ๐Ÿ‡น๐Ÿ‡ท [tr](../../../tr/docs/guides/I18N.md) ยท ๐Ÿ‡บ๐Ÿ‡ฆ [uk-UA](../../../uk-UA/docs/guides/I18N.md) ยท ๐Ÿ‡ต๐Ÿ‡ฐ [ur](../../../ur/docs/guides/I18N.md) ยท ๐Ÿ‡ป๐Ÿ‡ณ [vi](../../../vi/docs/guides/I18N.md) ยท ๐Ÿ‡จ๐Ÿ‡ณ [zh-CN](../../../zh-CN/docs/guides/I18N.md) ยท ๐Ÿ‡น๐Ÿ‡ผ [zh-TW](../../../zh-TW/docs/guides/I18N.md) --- OmniRoute supports **30 languages** with full dashboard UI translation, translated documentation, and RTL support for Arabic and Hebrew. ## Quick Reference | Task | Command | | ---------------------- | --------------------------------------------------------------------------------------- | | Generate translations | `node scripts/i18n/generate-multilang.mjs messages` | | Translate docs (LLM) | `python3 scripts/i18n_autotranslate.py --api-url --api-key --model ` | | Validate a locale | `python3 scripts/validate_translation.py quick -l cs` | | Check code keys | `python3 scripts/check_translations.py` | | Generate QA report | `node scripts/i18n/generate-qa-checklist.mjs` | | Visual QA (Playwright) | `node scripts/i18n/run-visual-qa.mjs` | ## Arkitektura ### Source of Truth - **UI strings**: `src/i18n/messages/en.json` (English source, ~2800 keys) - **Locale files**: `src/i18n/messages/{locale}.json` (30 translations) - **Framework**: `next-intl` with cookie-based locale resolution - **Config**: `src/i18n/config.ts` โ€” defines all 30 locales, language names, flags ### Runtime Flow 1. User selects language โ†’ `NEXT_LOCALE` cookie set 2. `src/i18n/request.ts` resolves locale: cookie โ†’ `Accept-Language` header โ†’ fallback `en` 3. Dynamic import loads `messages/{locale}.json` 4. Components use `useTranslations("namespace")` and `t("key")` ### Supported Locales | Code | Language | RTL | Google Translate Code | | ------- | -------------------- | --- | --------------------- | | `ar` | ุงู„ุนุฑุจูŠุฉ | Yes | `ar` | | `bg` | ะ‘ัŠะปะณะฐั€ัะบะธ | No | `bg` | | `cs` | ฤŒeลกtina | No | `cs` | | `da` | Dansk | No | `da` | | `de` | Deutsch | No | `de` | | `es` | Espaรฑol | No | `es` | | `fi` | Suomi | No | `fi` | | `fr` | Franรงais | No | `fr` | | `he` | ืขื‘ืจื™ืช | Yes | `iw` | | `hi` | เคนเคฟเคจเฅเคฆเฅ€ | No | `hi` | | `hu` | Magyar | No | `hu` | | `id` | Bahasa Indonesia | No | `id` | | `it` | Italiano | No | `it` | | `ja` | ๆ—ฅๆœฌ่ชž | No | `ja` | | `ko` | ํ•œ๊ตญ์–ด | No | `ko` | | `ms` | Bahasa Melayu | No | `ms` | | `nl` | Nederlands | No | `nl` | | `no` | Norsk | No | `no` | | `phi` | Filipino | No | `tl` | | `pl` | Polski | No | `pl` | | `pt` | Portuguรชs (Portugal) | No | `pt` | | `pt-BR` | Portuguรชs (Brasil) | No | `pt` | | `ro` | Romรขnฤƒ | No | `ro` | | `ru` | ะ ัƒััะบะธะน | No | `ru` | | `sk` | Slovenฤina | No | `sk` | | `sv` | Svenska | No | `sv` | | `th` | เน„เธ—เธข | No | `th` | | `tr` | Tรผrkรงe | No | `tr` | | `uk-UA` | ะฃะบั€ะฐั—ะฝััŒะบะฐ | No | `uk` | | `vi` | Tiแบฟng Viแป‡t | No | `vi` | | `zh-CN` | ไธญๆ–‡ (็ฎ€ไฝ“) | No | `zh-CN` | ## Adding a New Language ### 1. Register the Locale Edit `src/i18n/config.ts`: ```ts // Add to LOCALES array "xx", // Add to LANGUAGES array { code: "xx", label: "XX", name: "Language Name", flag: "๐Ÿณ๏ธ" }, ``` ### 2. Add to Generator Edit `scripts/i18n/generate-multilang.mjs` โ€” add entry to `LOCALE_SPECS`: ```js { code: "xx", googleTl: "xx", label: "XX", flag: "๐Ÿณ๏ธ", languageName: "Language Name", readmeName: "Language Name", docsName: "Language Name", }, ``` ### 3. Generate Initial Translation ```bash node scripts/i18n/generate-multilang.mjs messages ``` This creates `src/i18n/messages/xx.json` auto-translated from `en.json` via Google Translate. ### 4. Review & Fix Auto-Translations Auto-translations are a starting point. Review manually for: - Technical accuracy - Context-appropriate terminology - Proper handling of placeholders (`{count}`, `{value}`, etc.) ### 5. Validate ```bash python3 scripts/validate_translation.py quick -l xx python3 scripts/validate_translation.py diff common -l xx ``` ### 6. Generate Translated Documentation ```bash node scripts/i18n/generate-multilang.mjs docs ``` ## Auto-Translation Pipeline ### generate-multilang.mjs (Google Translate) **Primary auto-translation engine** โ€” uses Google Translate free API to generate translations for UI strings, READMEs, and documentation. ```bash node scripts/i18n/generate-multilang.mjs [messages|readme|docs|all] ``` | Mode | What it does | | ---------- | ----------------------------------------------------------------------------- | | `messages` | Translates missing keys in `src/i18n/messages/{locale}.json` from `en.json` | | `readme` | Translates `README.md` into all locales as `README.{code}.md` in project root | | `docs` | Translates `DOC_SOURCE_FILES` into `docs/i18n/{locale}/{docName}` | | `all` | Runs all three modes | **Features:** - **Text protection**: Masks code blocks (` ``` `), inline code (`` ` ``), markdown links/images (`[text](url)`), HTML tags, tables, and ICU placeholders (`{count}`, `{value}`, `{total}`, etc.) before translation, then restores them - **Chunked batching**: Joins multiple strings with `__OMNIROUTE_I18N_SEPARATOR__` delimiters to minimize API calls (max 1800 chars per request) - **In-memory cache**: Avoids redundant API calls for repeated strings within a session - **Retry logic**: Exponential backoff (up to 5 attempts with 300ms ร— attempt delay) for 429/5xx errors - **Timeout**: 20 seconds per request - **Skip existing**: If target file already exists, it is NOT overwritten **Important behaviors:** - `docs/i18n/README.md` is **regenerated** each run โ€” it's an auto-generated index of all docs - Root `README.{code}.md` files are only created if they don't exist (skips locales in `EXISTING_README_CODES`) - Language bars (`๐ŸŒ **Languages:** ...`) are automatically inserted/updated in all translated docs ### i18n_autotranslate.py (LLM-based) **Secondary translator** โ€” uses any OpenAI-compatible LLM API (including OmniRoute itself) to translate existing `docs/i18n/` markdown files. Best for polishing or re-translating docs with better quality than Google Translate. ```bash python3 scripts/i18n_autotranslate.py \ --api-url http://localhost:20128/v1 \ --api-key sk-your-key \ --model gpt-4o ``` **Features:** - Scans `docs/i18n/` markdown files for English paragraphs - Skips code blocks, tables, and already-translated content - Sends paragraphs to LLM with technical translation system prompt - Supports all 30 languages ## Validation & QA ### validate_translation.py **Translation validator** โ€” compares any locale JSON against `en.json` and reports issues. ```bash # Quick check (counts only) python3 scripts/validate_translation.py quick -l cs # Output: # Missing: 0 # Untranslated: 0 # Ignored (UNTRANSLATABLE_KEYS): 236 # Detailed diff by category python3 scripts/validate_translation.py diff common -l cs python3 scripts/validate_translation.py diff settings -l cs # Export to CSV python3 scripts/validate_translation.py csv -l cs > report.csv # Export to Markdown python3 scripts/validate_translation.py md -l cs > report.md # Full report (default) python3 scripts/validate_translation.py -l cs ``` **Detects:** - **Missing keys** โ€” keys in `en.json` but not in locale file - **Extra keys** โ€” keys in locale file but not in `en.json` - **Untranslated keys** โ€” keys where locale value equals English source (excluding allowlist) - **Placeholder mismatches** โ€” ICU placeholders that don't match between source and translation **Exit codes:** | Code | Meaning | | ---- | ---------------------------- | | 0 | OK | | 1 | Generic error | | 2 | Missing strings (hard error) | | 3 | Untranslated warning (soft) | **Environment:** Set `TRANSLATION_LANG=cs` or use `-l cs` flag. ### check_translations.py **Code-to-JSON key checker** โ€” scans `src/**/*.tsx` and `src/**/*.ts` for `useTranslations()` calls and verifies all referenced keys exist in `en.json`. ```bash # Basic check python3 scripts/check_translations.py # Verbose output python3 scripts/check_translations.py --verbose # Auto-fix (adds missing keys to en.json) python3 scripts/check_translations.py --fix ``` ### generate-qa-checklist.mjs **Static analysis QA** โ€” scans Next.js page files for i18n risk metrics and generates a Markdown report. ```bash node scripts/i18n/generate-qa-checklist.mjs ``` **Checks:** - Fixed-width class usage (overflow risk) - Directional left/right classes (RTL risk) - Clipping-prone patterns - Locale parity (missing/extra keys vs `en.json`) - README language selector bars in priority locales (`es`, `fr`, `de`, `ja`, `ar`) **Output:** `docs/reports/i18n-qa-checklist-{date}.md` ### run-visual-qa.mjs **Visual QA via Playwright** โ€” takes screenshots of all dashboard routes in multiple locales and viewports, then evaluates page health. ```bash # Default: es, fr, de, ja, ar on localhost:20128 node scripts/i18n/run-visual-qa.mjs # Custom base URL and locales QA_BASE_URL=http://staging.example.com QA_LOCALES=de,fr node scripts/i18n/run-visual-qa.mjs # Custom routes QA_ROUTES=/dashboard/settings,/dashboard/providers node scripts/i18n/run-visual-qa.mjs ``` **Detects:** - Text overflow - Element clipping - RTL layout mismatches **Output:** `docs/reports/i18n-visual-qa-{date}.md` + JSON report ## Managing Untranslatable Keys ### untranslatable-keys.json **File:** `scripts/i18n/untranslatable-keys.json` Allowlist of keys that should remain identical to English source. Used by `validate_translation.py` to avoid false-positive "untranslated" warnings. ```json { "description": "Keys that should remain untranslated...", "keys": [ "common.model", "common.oauth", "health.cpu", ... ] } ``` **What belongs here:** - Brand/product names: `landing.brandName`, `common.social-github` - Technical terms/acronyms: `health.cpu`, `mcpDashboard.pid`, `settings.ai` - ICU/format strings: `apiManager.modelsCount`, `health.millisecondsShort` - Placeholder values: `providers.openaiBaseUrlPlaceholder`, `cliTools.baseUrlPlaceholder` - Protocol names: `common.http`, `common.oauth`, `providers.oauth2Label` - Navigation sections: `sidebar.primarySection`, `sidebar.cliSection` **To add a key:** Edit the `keys` array in `scripts/i18n/untranslatable-keys.json` and re-run validation. ## CI Integration ### GitHub Actions (`.github/workflows/ci.yml`) The CI pipeline validates all locales on every push and PR: 1. **`i18n-matrix` job** โ€” dynamically discovers all locale files (excluding `en.json`) 2. **`i18n` job** โ€” runs `validate_translation.py quick -l ''` for each locale in parallel 3. **`ci-summary` job** โ€” aggregates results into a dashboard summary ```yaml # i18n-matrix: discovers languages LANGS=$(ls src/i18n/messages/*.json | xargs -n1 basename | sed 's/.json$//' | grep -v '^en$') # i18n: validates each language python3 scripts/validate_translation.py quick -l '${{ matrix.lang }}' ``` **Dashboard output:** ``` ## ๐ŸŒ Translations | Metric | Value | |--------|------| | Languages checked | 30 | | Total untranslated | 0 | โœ… All translations complete ``` ## File Structure ``` src/i18n/ โ”œโ”€โ”€ config.ts # Locale definitions (30 locales, RTL config) โ”œโ”€โ”€ request.ts # Runtime locale resolution โ””โ”€โ”€ messages/ โ”œโ”€โ”€ en.json # Source of truth (~2800 keys) โ”œโ”€โ”€ cs.json # Czech translation โ”œโ”€โ”€ de.json # German translation โ””โ”€โ”€ ... # 30 locale files total scripts/ โ”œโ”€โ”€ i18n/ โ”‚ โ”œโ”€โ”€ generate-multilang.mjs # Auto-translation engine (Google Translate, 888 lines) โ”‚ โ”œโ”€โ”€ generate-qa-checklist.mjs # Static analysis QA โ”‚ โ”œโ”€โ”€ run-visual-qa.mjs # Playwright visual QA โ”‚ โ””โ”€โ”€ untranslatable-keys.json # Allowlist for validation (236 keys) โ”œโ”€โ”€ validate_translation.py # Translation validator โ”œโ”€โ”€ check_translations.py # Code-to-JSON key checker โ””โ”€โ”€ i18n_autotranslate.py # LLM-based doc translator .github/workflows/ โ””โ”€โ”€ ci.yml # i18n validation in CI matrix docs/ โ”œโ”€โ”€ I18N.md # This file โ€” i18n toolchain documentation โ”œโ”€โ”€ i18n/ โ”‚ โ”œโ”€โ”€ README.md # Auto-generated language index โ”‚ โ”œโ”€โ”€ cs/ # Czech docs โ”‚ โ”‚ โ””โ”€โ”€ docs/ โ”‚ โ”‚ โ”œโ”€โ”€ I18N.md # Czech translation of this file โ”‚ โ”‚ โ””โ”€โ”€ ... โ”‚ โ”œโ”€โ”€ de/ # German docs โ”‚ โ””โ”€โ”€ ... # 30 locale directories โ””โ”€โ”€ reports/ โ”œโ”€โ”€ i18n-qa-checklist-*.md # Static analysis reports โ””โ”€โ”€ i18n-visual-qa-*.md # Visual QA reports ``` ## Best Practices ### When Editing Translations 1. **Always edit `en.json` first** โ€” it's the source of truth 2. **Run `generate-multilang.mjs messages`** to propagate new keys to all locales 3. **Review auto-translations** โ€” Google Translate is a starting point, not final 4. **Validate before committing** โ€” `python3 scripts/validate_translation.py quick -l ` 5. **Update `untranslatable-keys.json`** if a key should remain in English ### Placeholder Safety - ICU placeholders (`{count}`, `{value}`, `{total}`, `{seconds}`) must be preserved exactly - Plural formats (`{count, plural, one {# model} other {# models}}`) must maintain structure - The validator detects placeholder mismatches automatically ### Adding New Translation Keys in Code ```tsx // Use namespaced keys const t = useTranslations("settings"); t("cacheSettings"); // maps to settings.cacheSettings in JSON // Run check_translations.py to verify keys exist python3 scripts/check_translations.py --verbose ``` ### RTL Considerations - Arabic (`ar`) and Hebrew (`he`) are RTL locales - Avoid hardcoded `left`/`right` CSS โ€” use `start`/`end` logical properties - Visual QA catches RTL layout mismatches via `run-visual-qa.mjs` ## Known Issues & History ### `in.json` โ†’ `hi.json` Fix The generator originally used `code: "in"` (deprecated Google Translate code) for Hindi instead of the correct ISO 639-1 `hi`. This created an orphaned `in.json` duplicate of `hi.json`. Fixed by changing `code: "in"` to `code: "hi"` in `generate-multilang.mjs` and removing the orphaned file. ### `docs/i18n/README.md` Is Auto-Generated The `docs/i18n/README.md` file is completely regenerated by `generate-multilang.mjs docs`. Any manual edits will be lost. Use `docs/guides/I18N.md` (this file) for hand-written documentation that should persist. ### External Untranslatable Keys List The `untranslatable-keys.json` allowlist was moved from an inline Python set in `validate_translation.py` to an external JSON file for easier maintenance. The validator loads it at runtime. ### `generate-multilang.mjs` Hindi Code Fix The generator originally used `code: "in"` (deprecated Google Translate code) for Hindi instead of the correct ISO 639-1 `hi`. This was introduced in upstream commit `952b0b22c` by `diegosouzapw`. Fixed by changing `code: "in"` to `code: "hi"` in the `LOCALE_SPECS` array and removing the orphaned `in.json` file. ### `validate_translation.py` Ignored Count Output The `quick` check now displays the count of ignored keys from `untranslatable-keys.json`: ``` Missing: 0 Untranslated: 0 Ignored (UNTRANSLATABLE_KEYS): 236 ```