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
| Sources | Winner |
|---|---|
--baseUrl flag, I18N_BASE_URL, providerBaseUrl in config | Flag, then environment, then config |
| Declared config vs. a value read out of a framework's own config file | Declared |
| A config in an app directory vs. one in the repository root | The nearest one, outright |
| Locale directory probing vs. a directory named in a framework config | The 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.jswrapped 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
1rather 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.
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.