# OmniRoute i18n System: How It Supports 42 Locales and the Translation Workflow

> Discover how OmniRoute's i18n system efficiently manages 42 locales using data-driven config deep-merge fallbacks browser detection and Next-Intl integration for seamless localization.

- Repository: [Diego Rodrigues de Sa e Souza/OmniRoute](https://github.com/diegosouzapw/OmniRoute)
- Tags: how-to-guide
- Published: 2026-08-04

---

**OmniRoute's internationalization system supports 42 locales through a data-driven configuration in [`config/i18n.json`](https://github.com/diegosouzapw/OmniRoute/blob/main/config/i18n.json), with a translation workflow that uses deep-merge fallbacks, browser detection, and Next-Intl integration to serve localized content without hard-coding locale lists.**

The OmniRoute repository (diegosouzapw/OmniRoute) implements a fully-typed, data-driven internationalization layer capable of serving UI translations across 42 distinct language locales. Unlike systems that hard-code supported languages in multiple files, OmniRoute centralizes locale definitions in a single JSON configuration, enabling automatic propagation of changes throughout the TypeScript codebase. This architecture ensures that adding or removing a locale requires updates to only one file, while the runtime handles fallback chains, missing key detection, and right-to-left layout switching automatically.

## Core Architecture: Four Modules Powering 42 Locales

The OmniRoute i18n system relies on four tightly integrated modules that separate configuration, type safety, request resolution, and browser detection.

### config/i18n.json as the Source of Truth

The file [`config/i18n.json`](https://github.com/diegosouzapw/OmniRoute/blob/main/config/i18n.json) serves as the master source of truth, containing 42 entries that define each locale's code, label, name, native representation, English description, and flag icon. This JSON file also specifies the `default` locale and lists all `rtl` (right-to-left) languages. By maintaining a single canonical list, the system guarantees consistency across the application without requiring scattered updates when locales change.

### src/i18n/config.ts Type Safety Layer

The [`src/i18n/config.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/i18n/config.ts) module acts as a typed adapter that imports the JSON configuration and exposes four critical constants: `LOCALES` (a `readonly string[]`), `DEFAULT_LOCALE`, `RTL_LOCALES`, and `LANGUAGES`. Because this module never hard-codes the locale list, any modification to [`config/i18n.json`](https://github.com/diegosouzapw/OmniRoute/blob/main/config/i18n.json) immediately reflects in the TypeScript type system. This prevents runtime errors and ensures that the rest of the codebase always references the current, accurate list of 42 supported locales.

### src/i18n/request.ts Request Resolution

The [`src/i18n/request.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/i18n/request.ts) file implements the Next-Intl request-level resolver through the `getRequestConfig` pattern. This module performs five critical operations: (a) reading the locale from the `NEXT_LOCALE` cookie or the `x-locale` HTTP header, (b) validating the locale against the supported list and falling back to the default if invalid, (c) dynamically importing the corresponding message bundle from `./messages/${locale}.json`, (d) executing a two-stage deep-merge algorithm to fill missing keys from English (`en`), and (e) returning the final `{ locale, messages }` object for page rendering.

### src/i18n/detectBrowserLocale.ts Browser Detection

The [`src/i18n/detectBrowserLocale.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/i18n/detectBrowserLocale.ts) utility provides a pure function with no DOM dependencies, enabling reuse on both client and server. It accepts an array of language strings (typically `navigator.languages`) and applies a three-tier matching strategy: exact match (e.g., `en-US` → `en`), special-case mapping for Hong Kong and Macao (`zh-HK` / `zh-MO` → `zh-TW`), and prefix matching (e.g., `fr-CA` → `fr`). The function returns the first supported locale or `null`, allowing the application to auto-select a language before the user explicitly sets a preference cookie.

## Step-by-Step Translation Workflow

The translation workflow operates through a deterministic pipeline that handles locale detection, message loading, and graceful degradation when translations are incomplete.

### Bootstrapping the Locale List

During the build process, the script `scripts/i18n/run-translation.mjs` reads [`config/i18n.json`](https://github.com/diegosouzapw/OmniRoute/blob/main/config/i18n.json) to validate the locale structure. The [`src/i18n/config.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/i18n/config.ts) module then re-exports the array as `LOCALES`, making the list available to routing logic, middleware, and UI components. This single-source approach guarantees that adding a locale to the JSON file automatically enables it throughout the application without code changes.

### Detecting the User's Preferred Locale

When a request arrives without a `NEXT_LOCALE` cookie, the system checks for the `x-locale` header. If neither exists, the client-side code can invoke `detectBrowserLocale(navigator.languages, LOCALES)` to determine the best match based on the user's browser preferences. Once identified, the chosen locale is stored in the `NEXT_LOCALE` cookie with a one-year max-age, ensuring subsequent requests use the cached value.

### Loading and Merging Message Bundles

The [`src/i18n/request.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/i18n/request.ts) module dynamically imports JSON files from `src/i18n/messages/` based on the resolved locale. Each file contains plain key-value maps representing translation strings for that language. The system implements a two-stage fallback merge: first, it performs a deep merge of the target locale with the generic English (`en`) bundle to ensure every key exists; second, it performs a shallow top-level merge to add any new namespaces that exist only in English, allowing non-English locales to display English strings for newly added features until translations are provided.

### Handling Missing Translations with Placeholders

The translation sync script tags untranslated strings with the `__MISSING__:` sentinel prefix. Within [`src/i18n/request.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/i18n/request.ts), the `deepMergeFallback()` function (lines 20-27) treats these placeholders as missing values, causing the fallback English translation to win during the merge process. This mechanism prevents empty strings or broken UI elements when translation coverage is incomplete.

### RTL Language Support

The [`src/i18n/config.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/i18n/config.ts) module exports `RTL_LOCALES` derived from the `rtl` array in [`config/i18n.json`](https://github.com/diegosouzapw/OmniRoute/blob/main/config/i18n.json). This list includes Arabic, Persian, Hebrew, and Urdu among the 42 locales. UI components consume this constant to conditionally apply right-to-left layout direction, ensuring proper text rendering and component alignment for supported languages.

## Implementing i18n in Practice

### Detecting Browser Locale on First Visit

Use the `detectBrowserLocale` utility to automatically select a language when a user first lands on the site:

```typescript
import { detectBrowserLocale } from '@/i18n/detectBrowserLocale';
import { LOCALES } from '@/i18n/config';

const userLangs = navigator.languages; // e.g., ['pt-BR', 'en-US']
const chosen = detectBrowserLocale(userLangs, LOCALES) ?? 'en';

document.cookie = `NEXT_LOCALE=${chosen}; path=/; max-age=31536000`;

```

### Using Translations in React Components

Access localized strings through the Next-Intl hooks after the request configuration has loaded the messages:

```tsx
import { useTranslations } from 'next-intl';

export function Header() {
  const t = useTranslations('header');
  
  return (
    <header>
      <h1>{t('title')}</h1>
      <p>{t('subtitle')}</p>
    </header>
  );
}

```

### Adding a New Locale to the System

To extend support beyond the current 42 locales:

1. Append a new entry to [`config/i18n.json`](https://github.com/diegosouzapw/OmniRoute/blob/main/config/i18n.json) with the locale code, label, name, and flag properties.
2. Execute `npm run i18n:sync` to generate `src/i18n/messages/<code>.json` populated with `__MISSING__:` placeholders.
3. Provide translations for each key; the runtime will automatically serve the new locale without modifying TypeScript code.

## Summary

- **Single source of truth**: The [`config/i18n.json`](https://github.com/diegosouzapw/OmniRoute/blob/main/config/i18n.json) file centrally defines all 42 supported locales, preventing drift between configuration and code.
- **Type-safe propagation**: [`src/i18n/config.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/i18n/config.ts) exposes locale arrays as readonly constants, ensuring TypeScript catches invalid locale references at compile time.
- **Intelligent fallback**: The request resolver in [`src/i18n/request.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/i18n/request.ts) deep-merges missing keys from English and handles `__MISSING__:` placeholders to prevent UI breakage.
- **Browser-aware detection**: The pure `detectBrowserLocale` function implements exact, special-case, and prefix matching to select the optimal locale from `navigator.languages`.
- **Zero-code locale addition**: New locales require only JSON configuration and message file creation, with the build pipeline and type system adapting automatically.

## Frequently Asked Questions

### How does OmniRoute handle missing translations for supported locales?

When a translation key is missing from a locale's message bundle, the `deepMergeFallback()` function in [`src/i18n/request.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/i18n/request.ts) detects the `__MISSING__:` placeholder sentinel and replaces it with the corresponding English value. This two-stage deep-merge process ensures that users always see content in English rather than empty strings or error states when translations are incomplete.

### Can the locale detection work on the server side for initial page loads?

Yes, the `detectBrowserLocale` function is designed as a pure utility without DOM dependencies, allowing it to run in server-side rendering contexts. However, for initial loads without a `NEXT_LOCALE` cookie, the system typically relies on the `x-locale` header or falls back to the default locale, with client-side detection available to set the cookie for subsequent requests.

### What is the process for adding a new language to the 42 existing locales?

To add a new language, developers append the locale metadata to [`config/i18n.json`](https://github.com/diegosouzapw/OmniRoute/blob/main/config/i18n.json), run `npm run i18n:sync` to generate a placeholder message file in `src/i18n/messages/`, and then provide the actual translations. The [`src/i18n/config.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/i18n/config.ts) adapter automatically picks up the new locale, making it available throughout the application without modifying any TypeScript code or routing logic.

### How does OmniRoute determine text direction for right-to-left languages?

The system reads the `rtl` array from [`config/i18n.json`](https://github.com/diegosouzapw/OmniRoute/blob/main/config/i18n.json) and exposes it as `RTL_LOCALES` in [`src/i18n/config.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/i18n/config.ts). UI components check this list to conditionally apply RTL layout direction. This covers Arabic, Persian, Hebrew, Urdu, and any other right-to-left languages defined in the configuration, ensuring proper text rendering across all 42 locales.