Skip to main content

i18n Config

Multi-language support via next-intl.

File: config/i18n.config.ts

Off by default. When enabled, the site routes users to /<locale>/... URLs and loads translations from messages/{locale}.json.

Two switches

Enable both i18n: true in features.config.ts and enabled: true in i18n.config.ts. The feature flag gates the middleware; the config flag gates the routing logic.

Setup

1. Enable the flag

config/features.config.ts
export const featuresConfig = {
  // ...
  i18n: true,
};

2. Configure locales

config/i18n.config.ts
export const i18nConfig = {
  enabled: true,                   // master switch
  defaultLocale: 'en' as const,
  locales: ['en', 'ru', 'es'] as const,
  localeCookie: 'NEXT_LOCALE',
  localeDetection: true,           // auto-detect from Accept-Language
};
FieldWhat it does
enabledMaster switch. false = zero routing impact.
defaultLocaleFallback when a translation is missing.
localesAll supported languages. Each needs a messages/{code}.json.
localeCookieCookie name for saving the user's choice.
localeDetectionAuto-detect language from browser headers on first visit.

3. Add translation files

messages/
├── en.json     ← required, the baseline
├── ru.json
└── es.json

Each file has the same structure — copy en.json and translate the strings.

AI Prompt· Generate a translation file

Generate messages/{locale-code, e.g. "ru" or "es"}.json by translating every key and value in messages/en.json to {target language, e.g. "Russian" or "Spanish"}.

Rules:

  • Preserve the exact JSON structure, keys, and nesting.
  • Translate only the string values — never the keys.
  • Keep \{placeholders\} in curly braces intact (those are next-intl variables).
  • For brand-specific terms like "DirectoryKit", decide: translate if it reads naturally localized, otherwise keep English.
  • Match the tone of the English source (friendly/professional, not overly formal).

Don't touch messages/en.json.

4. The language switcher

Already in the header when i18n is on. Users can change language at any time.

Language switcher in the site header

How URLs work

  • en is the default — URL stays clean: /, /pricing, /submit
  • Other locales get a prefix/ru/, /ru/pricing, /ru/submit
  • All links inside the app use next-intl's Link helper — it auto-prefixes.

What gets translated

  • Every string in messages/{locale}.json
  • Email templates (when using translation keys)
  • Validation error messages (when using keys)

What doesn't get auto-translated:

  • User-generated content (project names, descriptions, comments)
  • The blog (content/blog/*.mdx — write per-locale articles manually)
  • Legal pages (Terms, Privacy) — translate manually in messages/

Keep i18n off if you don't need it

enabled: false means zero routing or rendering overhead. Leave it off until you really have translated content — enabling it adds complexity to everything from links to SEO.

See also