Configuration

Precedence and Locale References

The Same Key in Both Files

Declare one key in both config files and the kit stops and names them rather than picking a winner:

 ERROR  defaultLocale is declared in both
  /app/i18n-kit.config.ts
  /app/.i18n-mcp.json
Neither wins — a reader cannot tell which one took effect. Delete the duplicate from one of them.

The command exits 1 without doing any work, and the same message comes back as an error object carrying the code CONFIG_ERROR. Both files may exist: what is rejected is the same key in each, ignoring the JSON-only $schema. glossary in one and protectedLocales in the other is fine.

The check is per key, not per subkey — glossary in both files is an error even when the two objects hold different terms. It does not cross directories: a config in an app directory means the root one is never read at all.

Where Precedence Is Resolved

SourcesWinner
--baseUrl flag, I18N_BASE_URL, providerBaseUrl in configFlag, then environment, then config
Declared config vs. a value read out of a framework's own config fileDeclared
A config in an app directory vs. one in the repository rootThe nearest one, outright
Locale directory probing vs. a directory named in a framework configThe framework config

Blank values count as unset in the base URL chain, so an unset shell variable expanding to an empty string does not shadow the config below it.

Declared beats derived. Where your config and your framework's config both have an opinion, yours wins — declaring defaultLocale also removes the last fallback, which is a guess about directory order. Nuxt inverts this for the three keys it answers itself: @the-i18n-kit/nuxt rejects locales, localeDirs and defaultLocale in a hand-written kit config with a build error.

When a Config Cannot Be Read

  • A framework config that cannot be executed is a warning. The command continues on directory probing and tells you to declare the value to be sure of it. A next.config.js wrapped in a plugin that is not installed is the common case in CI.
  • 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. A config that loads but is invalid — an unknown key, a non-object default export — exits 1 rather than proceeding with half of it.

Referring to a Locale

Anywhere a locale is named — --referenceLocale, --targetLocales, protectedLocales, the keys of a write payload — you may use the locale's code, its language tag, or its file name with extension. They are tried in that order.

Prefer the code. Codes are unique by construction; language tags are not. Two German locales can both carry language: "de-DE", and which one de-DE resolves to depends on the order of your framework's locale array. A file name needs its extension — de-DE-formal.json, not de-DE-formal.

A reference matching nothing is reported, not dropped:

{
  "written": ["common.save"],
  "filesWritten": 2,
  "unresolvedLocales": [
    { "ref": "de-DE-formal", "keys": ["common.save"], "suggestion": "Did you mean \"de-formal\"?" }
  ]
}

unresolvedLocales is the only reliable signal that a write did less than you asked. The key still appears in written, because the other locales succeeded, and filesWritten is short by one. A reference matching several locales is reported under ambiguousLocales. Both fields are absent when every reference resolves uniquely.

A protectedLocales entry that resolves to nothing protects nothing, and the run that machine-translates the locale you meant to protect looks exactly like a successful one. On Nuxt, the module fails the build on that; elsewhere, check the codes in discover.
Copyright © 2026