# i18n — Internationalization Guide (हिन्दी (IN))

🌐 **Languages:** 🇺🇸 [English](../../../../docs/I18N.md) · 🇪🇸 [es](../../es/docs/I18N.md) · 🇫🇷 [fr](../../fr/docs/I18N.md) · 🇩🇪 [de](../../de/docs/I18N.md) · 🇮🇹 [it](../../it/docs/I18N.md) · 🇷🇺 [ru](../../ru/docs/I18N.md) · 🇨🇳 [zh-CN](../../zh-CN/docs/I18N.md) · 🇯🇵 [ja](../../ja/docs/I18N.md) · 🇰🇷 [ko](../../ko/docs/I18N.md) · 🇸🇦 [ar](../../ar/docs/I18N.md) · 🇮🇳 [hi](../../hi/docs/I18N.md) · 🇮🇳 [in](../../in/docs/I18N.md) · 🇹🇭 [th](../../th/docs/I18N.md) · 🇻🇳 [vi](../../vi/docs/I18N.md) · 🇮🇩 [id](../../id/docs/I18N.md) · 🇲🇾 [ms](../../ms/docs/I18N.md) · 🇳🇱 [nl](../../nl/docs/I18N.md) · 🇵🇱 [pl](../../pl/docs/I18N.md) · 🇸🇪 [sv](../../sv/docs/I18N.md) · 🇳🇴 [no](../../no/docs/I18N.md) · 🇩🇰 [da](../../da/docs/I18N.md) · 🇫🇮 [fi](../../fi/docs/I18N.md) · 🇵🇹 [pt](../../pt/docs/I18N.md) · 🇷🇴 [ro](../../ro/docs/I18N.md) · 🇭🇺 [hu](../../hu/docs/I18N.md) · 🇧🇬 [bg](../../bg/docs/I18N.md) · 🇸🇰 [sk](../../sk/docs/I18N.md) · 🇺🇦 [uk-UA](../../uk-UA/docs/I18N.md) · 🇮🇱 [he](../../he/docs/I18N.md) · 🇵🇭 [phi](../../phi/docs/I18N.md) · 🇧🇷 [pt-BR](../../pt-BR/docs/I18N.md) · 🇨🇿 [cs](../../cs/docs/I18N.md) · 🇹🇷 [tr](../../tr/docs/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 <url> --api-key <key> --model <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`                                                   |

## आर्किटेक्चर

![Incremental hash-based i18n pipeline](./diagrams/exported/i18n-flow.svg)

> Source: [diagrams/i18n-flow.mmd](./diagrams/i18n-flow.mmd)

### 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 '<lang>'` 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 <lang>`
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
```
