# How to Set Up and Manage i18n with 42 Locales Using next-intl in OmniRoute

> Learn to set up and manage i18n with 42 locales using next-intl in OmniRoute. Generate type-safe locale constants from a single JSON config for zero-code language additions.

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

---

**OmniRoute provides a complete next-intl integration that generates type-safe locale constants from a single JSON configuration file, enabling zero-code additions of new languages.**

Managing internationalization across 42 locales requires a maintainable, type-safe architecture. OmniRoute solves this by centralizing all locale metadata in [`config/i18n.json`](https://github.com/diegosouzapw/OmniRoute/blob/main/config/i18n.json) and deriving TypeScript constants automatically. This guide walks through the complete setup based on the actual source code implementation in the `diegosouzapw/OmniRoute` repository.

## OmniRoute's i18n Architecture

The system separates configuration from implementation across four layers:

| Layer | File | Purpose |
|-------|------|---------|
| **Catalogue** | [`config/i18n.json`](https://github.com/diegosouzapw/OmniRoute/blob/main/config/i18n.json) | Source of truth for 42 locale definitions |
| **Types** | [`src/i18n/config.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/i18n/config.ts) | Generated constants: `LOCALES`, `DEFAULT_LOCALE`, `RTL_LOCALES` |
| **Server** | [`src/i18n/request.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/i18n/request.ts) | `getRequestConfig` for next-intl server integration |
| **Client** | [`src/app/layout.tsx`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/app/layout.tsx) | `NextIntlClientProvider` wrapping the application |

This architecture ensures that modifying [`config/i18n.json`](https://github.com/diegosouzapw/OmniRoute/blob/main/config/i18n.json) automatically updates all type-safe references throughout the codebase on the next build.

## Configuring the Locale Catalogue

The 42-locale list lives in a single JSON file without hard-coded TypeScript equivalents.

### config/i18n.json Structure

```json
{
  "default": "en",
  "rtl": ["ar", "he", "fa"],
  "locales": [
    {
      "code": "en",
      "label": "English",
      "name": "English",
      "native": "English",
      "english": "English",
      "flag": "🇺🇸"
    },
    {
      "code": "pt",
      "label": "Portuguese",
      "name": "Português",
      "native": "Português",
      "english": "Portuguese",
      "flag": "🇧🇷"
    }
    // ... 40 additional locales
  ]
}

```

Fields include:
- `code`: ISO locale identifier used in URLs and APIs
- `label`: Display name in the current locale
- `native`: Autonym (name in the language itself)
- `flag`: Emoji flag for UI selectors

## Generating Type-Safe Locale Constants

In [`src/i18n/config.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/i18n/config.ts), OmniRoute transforms the JSON catalogue into compile-time constants:

```typescript
import config from '../../config/i18n.json';

export const LOCALES = config.locales.map(l => l.code);
// Type: string[] — derived at build time

export const DEFAULT_LOCALE = config.default;
// Type: string

export const RTL_LOCALES = config.rtl;
// Type: string[]

export const LOCALE_COOKIE = 'NEXT_LOCALE';
// Cookie name used by next-intl

```

Using `config.locales.map(l => l.code)` at module initialization guarantees that `LOCALES` (line 26 in [`src/i18n/config.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/i18n/config.ts)) always reflects the current JSON state. No manual synchronization required.

## Setting Up the Server-Side Request Loader

The [`src/i18n/request.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/i18n/request.ts) file implements next-intl's `getRequestConfig` to resolve locales per incoming request:

```typescript
import { getRequestConfig } from 'next-intl/server';
import { LOCALES, DEFAULT_LOCALE } from './config';

export default getRequestConfig({
  locales: LOCALES,
  defaultLocale: DEFAULT_LOCALE,
  timeZone: 'UTC',
  now: new Date()
});

```

This default export is consumed by Next.js App Router's i18n routing. The configuration:
- Validates incoming locales against the 42-entry `LOCALES` array
- Falls back to `DEFAULT_LOCALE` for unmatched paths
- Enables automatic message loading from `src/i18n/messages/`

## Integrating the Client Provider

Root layout setup in [`src/app/layout.tsx`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/app/layout.tsx) (lines 4-5) establishes the client-side context:

```typescript
import { NextIntlClientProvider } from 'next-intl';
import { getMessages, getLocale } from 'next-intl/server';

export default async function RootLayout({
  children
}: {
  children: React.ReactNode
}) {
  const locale = await getLocale();
  const messages = await getMessages();

  return (
    <html lang={locale}>
      <body>
        <NextIntlClientProvider 
          locale={locale} 
          messages={messages}
          timeZone="UTC"
        >
          {children}
        </NextIntlClientProvider>
      </body>
    </html>
  );
}

```

`getLocale()` respects the priority order: URL segment → `NEXT_LOCALE` cookie → `Accept-Language` header → `DEFAULT_LOCALE`. `getMessages()` automatically loads the matching JSON file from `src/i18n/messages/{locale}.json`.

## Adding a New Locale (Zero-Code Workflow)

Expanding beyond 42 locales requires only JSON edits:

1. **Update [`config/i18n.json`](https://github.com/diegosouzapw/OmniRoute/blob/main/config/i18n.json)** with the new locale object:

```json
{
  "code": "ko",
  "label": "Korean",
  "name": "Korean",
  "native": "한국어",
  "english": "Korean",
  "flag": "🇰🇷"
}

```

2. **Create translation file** at [`src/i18n/messages/ko.json`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/i18n/messages/ko.json):

```json
{
  "metadata": {
    "title": "OmniRoute - 글로벌 물류 플랫폼"
  },
  "navigation": {
    "dashboard": "대시보드",
    "shipments": "배송"
  }
}

```

3. **Rebuild**: `npm run build`

The `LOCALES` constant automatically includes `"ko"`; TypeScript compilation validates all references; the UI instantly supports Korean selection.

## Consuming Locales in Components

Access the generated constants and next-intl hooks in React components:

```tsx
'use client';

import { useLocale, useTranslations } from 'next-intl';
import { LOCALES } from '@/i18n/config';

export default function LanguageSwitcher() {
  const t = useTranslations('language-selector');
  const currentLocale = useLocale();

  return (
    <select 
      value={currentLocale}
      onChange={(e) => {
        // Locale change logic
        document.cookie = `NEXT_LOCALE=${e.target.value}; path=/`;
        window.location.href = `/${e.target.value}`;
      }}
    >
      {LOCALES.map((code) => (
        <option key={code} value={code}>
          {t(`locale.${code}`)}
        </option>
      ))}
    </select>
  );
}

```

`LOCALES` from [`src/i18n/config.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/i18n/config.ts) line 26 provides the complete 42-item array. The `t()` function references nested keys in the translation files.

## Managing Translations at Scale

For 42 locales, OmniRoute organizes messages by feature domain:

```

src/i18n/messages/
├── en.json      (source reference)
├── pt.json
├── es.json
├── ...
└── _build/      (optional generated files)

```

Keep English as the authoritative source, then synchronize secondary locales via CI pipelines or translation management platforms. The flat JSON structure allows partial translations—missing keys gracefully fall back through next-intl's default behavior.

## Summary

- **Single source of truth**: [`config/i18n.json`](https://github.com/diegosouzapw/OmniRoute/blob/main/config/i18n.json) defines all 42 locales; no scattered hard-coding
- **Type-safe generation**: [`src/i18n/config.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/i18n/config.ts) builds `LOCALES`, `DEFAULT_LOCALE`, and `RTL_LOCALES` at build time
- **Server integration**: [`src/i18n/request.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/i18n/request.ts) provides `getRequestConfig` for App Router compatibility
- **Client hydration**: `NextIntlClientProvider` in [`src/app/layout.tsx`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/app/layout.tsx) supplies hooks globally
- **Zero-code scaling**: Add locales by editing JSON and creating translation files; TypeScript updates automatically

## Frequently Asked Questions

### Where does OmniRoute store the list of supported locales?

The 42 locales are defined in [`config/i18n.json`](https://github.com/diegosouzapw/OmniRoute/blob/main/config/i18n.json). This JSON file is the only location requiring manual edits when adding or removing languages. The [`src/i18n/config.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/i18n/config.ts) module imports this file and exports `LOCALES` as a generated string array derived from `config.locales.map(l => l.code)`.

### How does next-intl determine which locale to use for a request?

The resolution order implemented in `getLocale()` follows: URL path segment first, then the `NEXT_LOCALE` cookie, then the `Accept-Language` header, and finally `DEFAULT_LOCALE` from configuration. This logic executes in [`src/i18n/request.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/i18n/request.ts) through next-intl's `getRequestConfig` implementation.

### Do I need to modify TypeScript code when adding a new language?

No. Adding a locale requires only updating [`config/i18n.json`](https://github.com/diegosouzapw/OmniRoute/blob/main/config/i18n.json) and creating the corresponding `src/i18n/messages/{code}.json` file. Running `npm run build` regenerates type-safe constants automatically. The `LOCALES` export in [`src/i18n/config.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/i18n/config.ts) updates without code changes.

### How does OmniRoute handle right-to-left languages?

The `rtl` array in [`config/i18n.json`](https://github.com/diegosouzapw/OmniRoute/blob/main/config/i18n.json) specifies which locales require RTL layout. [`src/i18n/config.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/i18n/config.ts) exports this as `RTL_LOCALES`. Components can detect RTL mode by checking `RTL_LOCALES.includes(currentLocale)` and applying directional styles accordingly.