Configuration

Declared vs. Derived Configuration

The kit works from two kinds of configuration: declared config, which you write for the kit, and derived config, which it reads out of files your framework already owns.

The Two Kinds

Declared config is everything the tool cannot see for itself: project context, a glossary, per-locale notes, which locales are protected. It lives in i18n-kit.config.ts or .i18n-mcp.json — see where configuration lives. The configuration field reference lists every field you can declare and which of those files accepts it.

Derived config is what your project already states somewhere: which locales exist, which one is the reference, where the locale files are, and which layers — scoped locale directories, whether that is a Nuxt layer, a workspace package, a shared UI library or an app in a monorepo — contribute to an app.

The kit reads the following, in each case executing the file rather than pattern-matching it:

SetupRead fromGives
Nuxt with @the-i18n-kit/nuxt.nuxt/i18n-kit.json, published during the buildLocales, default locale, fallback locale, layer directories
next-intlsrc/i18n/routing.tsdefineRouting({ ... })locales, defaultLocale
next-translatei18n.jslocales, defaultLocale
Next.js Pages Routernext.config.{ts,js,mjs} — the i18n blocklocales, defaultLocale
Vue with @intlify/unplugin-vue-i18nvite.config.{ts,js} — the plugin's includeLocale directories

The Next.js sources are tried in that order, and when none of them yields anything the adapter falls back to probing a fixed list of candidate directories.

A framework config is only ever executed while resolving a project, never while detecting one.

Declared Beats Derived

Where both have an opinion, the value you declared wins. Pinning the reference locale regardless of what any framework config says:

i18n-kit.config.ts
import { defineI18nKitConfig } from '@the-i18n-kit/cli/config'

export default defineI18nKitConfig({
  defaultLocale: 'en',
})

The resolution order for defaultLocale in the React adapter is declared value, then the framework config, then the first discovered locale — so declaring it also removes the last of those, which is a guess about directory order.

Nuxt inverts the ownership for the three keys it can answer itself. @the-i18n-kit/nuxt rejects locales, localeDirs and defaultLocale in .i18n-mcp.json with a build error, because Nuxt resolves them and a second hand-written copy is a source of truth with no tiebreak.

When a Config Cannot Be Read

A framework config that cannot be executed is a warning, and the command continues on the directory probing described above. A real next.config.js wrapped in withNextIntl(...) or withSentryConfig(...) needs its plugins installed and its environment present, and often has neither in CI:

 WARN  Could not read /app/src/i18n/routing.ts: Cannot find module 'some-missing-pkg'.
Falling back to directory detection — declare the value in i18n-kit.config.ts to be sure of it.

The same holds for the Nuxt artifact: absent, malformed, written by a version this CLI does not understand, or older than the nuxt.config.ts it describes — each is a warning and a fallback to loading the app through Nuxt.

A kit config that cannot be loaded fails the command, naming the file. Nothing else reads it, so there is no fallback that preserves its meaning:

 ERROR  Failed to load /app/i18n-kit.config.ts: Cannot find module './glossary.js'

A config that loads but is wrong — an unknown key, a misspelled field, or anything other than a default export — fails validation and exits 1 rather than proceeding with a partial config.

Limits

  • Derived config is read per project directory. It cannot tell you why a value is what it is beyond the log line naming the file it came from — run with debug logging when a locale set surprises you.
  • Only the sources in the table are read. A project declaring locales anywhere else falls back to directory probing, and declaring the value yourself is the fix.
  • The Vue reader takes the plugin's include option. A project that configures the plugin dynamically, or from a value the recorder cannot observe, gets directory probing instead.
  • Deriving a value never rewrites your files. The kit reads them; it does not offer to normalize them.
Copyright © 2026