Server data from the Official MCP Registry
MCP server for managing i18n translations in Nuxt projects.
MCP server for managing i18n translations in Nuxt projects.
Valid MCP server (2 strong, 2 medium validity signals). 3 known CVEs in dependencies (0 critical, 3 high severity) ⚠️ Package registry links to a different repository than scanned source. Imported from the Official MCP Registry. 1 finding(s) downgraded by scanner intelligence.
5 files analyzed · 4 issues found
Security scores are indicators to help you make informed decisions, not guarantees. Always review permissions before connecting any MCP server.
This plugin requests these system permissions. Most are normal for its category.
Add this to your MCP configuration file:
{
"mcpServers": {
"io-github-fabkho-nuxt-i18n-mcp": {
"args": [
"-y",
"nuxt-i18n-mcp"
],
"command": "npx"
}
}
}From the project's GitHub README.
An MCP server that gives your AI agent full control over your app's translations — without dumping entire locale files into context.
Point it at any project with JSON or PHP locale files and your agent can read, write, search, rename, and remove translation keys across all locales and layers. It auto-detects your framework (or reads explicit config), discovers monorepo structures, and handles the file I/O so the agent never has to parse JSON or PHP arrays manually.
Managing translations with an AI agent sounds simple until you have 30 locales, 6 layers, and 4,000 keys. Pasting locale files into chat doesn't scale. This server exposes 15 purpose-built tools that let the agent work surgically — touching only the keys it needs.
translate_missing batches keys to an LLM via MCP sampling, writes results back, and shows a progress bar. 1,000+ keys across 13 locales in minutes, not hours.add-language prompt walks your agent through config updates, file scaffolding, and bulk translation end-to-end.{placeholders} / @:linked refs are validated before writing..i18n-mcp.json with your glossary, tone rules, and per-locale notes. The agent uses them in every translation request.Migrating from
nuxt-i18n-mcp? The old package name still works — bothnpx the-i18n-mcpandnpx nuxt-i18n-mcppoint to the same server. No breaking changes.
| Framework | Locale Format | Auto-Detection | Config Source |
|---|---|---|---|
| Nuxt (v3+) | JSON | nuxt.config.ts with @nuxtjs/i18n | @nuxt/kit (optional peer dep) |
| Laravel (9+) | PHP arrays | artisan, composer.json, lang/ directory | Built-in |
| Generic (any JS/PHP project) | JSON or PHP arrays | localeDirs + defaultLocale in .i18n-mcp.json | Built-in |
The server detects your framework automatically based on project structure. For projects that aren't Nuxt or Laravel (React, Vue, Next.js, Symfony, WordPress, etc.), add localeDirs and defaultLocale to your .i18n-mcp.json and all 15 tools work immediately:
{
"defaultLocale": "en",
"localeDirs": ["src/locales"]
}
You can also force a specific adapter via "framework": "generic", "framework": "laravel", or "framework": "nuxt" in .i18n-mcp.json.
No install needed — your MCP host runs the server via npx.
Add to .vscode/mcp.json:
{
"servers": {
"the-i18n-mcp": {
"type": "stdio",
"command": "npx",
"args": ["the-i18n-mcp@latest"]
}
}
}
Add to .zed/settings.json:
{
"context_servers": {
"the-i18n-mcp": {
"command": "npx",
"args": ["the-i18n-mcp@latest"]
}
}
}
Add to claude_desktop_config.json:
{
"mcpServers": {
"the-i18n-mcp": {
"command": "npx",
"args": ["the-i18n-mcp@latest"]
}
}
}
That's it — no configuration needed. The server auto-detects your project structure, locales, and layers. Just ask:
"Add a 'save changes' button translation in all locales"
"Find and fix all missing translations in the admin layer"
"Rename
common.actions.deletetocommon.actions.removeacross all locales""Add Swedish as a new language and translate everything"
1. detect_i18n_config → understand project structure, locales, layers
2. list_locale_dirs → see available layers with file counts and key namespaces
3. get_missing_translations → find gaps between reference locale and targets
4. add_translations → add new keys (requires layer param)
translate_missing → auto-translate missing keys via MCP sampling
5. find_orphan_keys → find keys not referenced in source code
cleanup_unused_translations → remove orphan keys in one step
Always call detect_i18n_config first — all other tools depend on the detected config.
Use the add-language prompt for a guided workflow, or do it manually:
1. Add the locale to your framework config (nuxt.config.ts or config/app.php)
2. scaffold_locale → create empty locale files with key structure from default locale
3. translate_missing → auto-translate all keys from the reference locale
4. get_missing_translations → verify zero gaps remain
scaffold_locale copies every key from your default locale and sets all values to "". This gives translate_missing the complete key set to work with.
Every write tool requires a layer parameter (e.g., "root", "app-admin", "lang"). Use list_locale_dirs to discover available layers.
| Tool | Description |
|---|---|
detect_i18n_config | Auto-detects framework (Nuxt or Laravel), returns locales, layers, directories, and project config. Call first. |
list_locale_dirs | Lists locale directories grouped by layer, with file counts and top-level key namespaces |
get_translations | Reads values for dot-path keys from a locale/layer. Use "*" as locale for all locales |
add_translations | Adds new keys to a layer across locales. Fails if key already exists. Supports dryRun |
update_translations | Updates existing keys in a layer. Fails if key doesn't exist. Supports dryRun |
remove_translations | Removes keys from ALL locale files in a layer. Supports dryRun |
rename_translation_key | Renames/moves a key across all locales in a layer. Conflict detection + dryRun |
get_missing_translations | Finds keys present in reference locale but missing/empty in targets. "" counts as missing |
find_empty_translations | Finds keys with empty string values. Checks each locale independently |
search_translations | Searches by key pattern or value substring across layers and locales |
translate_missing | Auto-translates via MCP sampling (batches of 50 keys by default), or returns context for inline translation when sampling is unavailable. Supports batchSize override. Each locale writes to its own file — parallel calls targeting different locales are safe |
find_orphan_keys | Finds keys not referenced in source code. Keys overlapping unresolved dynamic patterns are reported separately as uncertain. Scans Vue/TS for Nuxt, Blade/PHP for Laravel |
scan_code_usage | Shows where keys are used — file paths, line numbers, call patterns |
cleanup_unused_translations | Finds orphan keys + removes them in one step. Dry-run by default (dryRun: true) — pass dryRun: false to actually delete. Uncertain keys (overlapping dynamic patterns) are excluded from removal and listed separately |
scaffold_locale | Creates empty locale files for new languages. Copies key structure from default locale with all values set to "". Supports JSON (Nuxt) and PHP (Laravel) |
| Prompt | Description |
|---|---|
add-feature-translations | Guided workflow for adding translations when building a new feature. Accepts optional layer and namespace |
fix-missing-translations | Find and fix all missing translations across the project. Accepts optional layer |
add-language | Add a new language end-to-end: update framework config, scaffold locale files, translate all keys, and verify. Requires language (e.g., "Swedish", "sv") |
| Resource | Description |
|---|---|
i18n:///{layer}/{locale} | Read a locale's translations for a specific layer (e.g., i18n:///root/en-US) |
nuxt.config.ts with @nuxtjs/i18n via @nuxt/kitprojectDir@nuxt/kit as a peer dependency (already present in Nuxt projects).vue, .ts, .tsx, .js, .jsx, .mjs, .mts for $t(), t(), this.$t(), $te(), this.$te()artisan, composer.json, or lang/ directorylang/ (Laravel 9+) and resources/lang/ (legacy) layoutsreturn ['key' => 'value'];).blade.php and .php for __(), trans(), trans_choice(), Lang::get(), @lang():placeholder syntax (not {placeholder}) — reflect this in your translationPrompt and exampleslocaleDirs and defaultLocale in .i18n-mcp.jsonen.json → "en", de/ → "de")en.json), directory-per-locale JSON (en/common.json), or directory-per-locale PHP (en/messages.php)"locales" array{
"defaultLocale": "en",
"localeDirs": [
{ "path": "packages/ui/locales", "layer": "ui" },
{ "path": "packages/app/locales", "layer": "app" }
]
}
"default" as the layer name"framework": "generic" needed (though it works as an explicit override)The server discovers all Nuxt apps with i18n configuration under the given projectDir. Pass the monorepo root and it walks the directory tree, finds every nuxt.config.ts with i18n settings, loads each app via @nuxt/kit, and merges the results. Each app's locale directories become separate layers.
monorepo/
├── apps/
│ ├── shop/ ← discovered, becomes "shop" layer
│ │ └── nuxt.config.ts (has i18n)
│ └── admin/ ← discovered, becomes "admin" layer
│ └── nuxt.config.ts (has i18n)
├── packages/
│ └── shared/ ← skipped, no nuxt.config with i18n
└── package.json
Flat layouts work too — app-shop/ and app-admin/ at the project root are discovered the same way. Discovery stops descending into a directory once it finds a nuxt.config — nested Nuxt layers are loaded by @nuxt/kit automatically.
find_orphan_keys and cleanup_unused_translations use a multi-strategy approach to determine whether a translation key is referenced in source code. A key is only reported as an orphan if none of the strategies find a match.
Scans for explicit i18n function calls on a single line:
| Framework | Patterns |
|---|---|
| Nuxt/Vue | $t('key'), t('key'), this.$t('key'), $te('key'), this.$te('key') |
| Laravel | __('key'), trans('key'), trans_choice('key', n), Lang::get('key'), @lang('key') |
Backtick literals without interpolation are promoted to static matches: t(`common.actions.save`) is treated the same as t('common.actions.save').
Extracts all quoted strings containing at least one dot from source files (e.g., 'common.actions.save'), regardless of whether they appear inside a t() call. These are intersected with known translation keys — if a key appears as a dotted string anywhere in the codebase, it's considered used.
This catches keys referenced in data structures, config objects, or passed as variables:
// All detected via bare string matching — no t() call needed
const columns = [{ label: 'common.actions.save', i18n: true }]
const key = 'pages.dashboard.title'
Scans for template literals with ${...} interpolation inside t() / $t() calls. Converts them to regex patterns and matches against all known keys:
t(`common.metrics.${metric}`) // → matches common.metrics.revenue, common.metrics.bookings, etc.
t(`${prefix}.items.${id}.label`) // → matches shop.items.42.label, admin.items.abc.label, etc.
Extracts all template literals containing at least one dot and ${...} interpolation from source files, regardless of t() context. Like bare string matching, these are optimistically treated as potential i18n patterns and matched against known keys.
This catches dynamic keys that are split across lines by formatters like Prettier, or used outside direct t() calls:
this.$t(
`common.components.plans.trialPeriod.${interval}`
)
const keyPattern = `pages.${section}.title`
Keys matching glob patterns in orphanScan.ignorePatterns (from .i18n-mcp.json) are excluded:
{
"orphanScan": {
"root": {
"ignorePatterns": ["common.datetime.**", "common.countries.*"]
}
}
}
** matches any number of dot-separated segments* matches exactly one segmentWhen the scanner encounters a dynamic translation call it cannot fully resolve (e.g., __("integrations.${$type}.description")), it includes an unresolvedKeyWarnings array in the output. Each warning shows the source location and suggests an ignorePatterns entry:
{
"expression": "`integrations.${_}.description`",
"file": "app/Integrations/AbstractManifest.php",
"line": 17,
"callee": "__",
"suggestedIgnorePattern": "integrations.**"
}
Orphan keys whose prefix matches an unresolved warning are moved to a separate uncertainKeys section and excluded from deletion by cleanup_unused_translations. This prevents false-positive removals. You can review the uncertain keys and ask the agent to remove them explicitly if they are truly unused.
The scanner automatically determines the correct scan scope for each layer using a consumer graph. For each layer, it identifies all apps that consume it (via Nuxt's _layers) and scans their source directories. Shared layers consumed by multiple apps are checked against all consumers' code — no manual configuration needed.
Orphan detection has been validated against real production codebases:
| Structure | Details | Keys | Orphans | Uncertain | False Positives |
|---|---|---|---|---|---|
| Nuxt monorepo | 7 apps, 30 locales, nested layers | 7,032 | 1,057 | 0 | 0 (all 124 checked) |
| Laravel API | 31 locales, PHP array files | 2,086 | 98 | 26 | 0 (all 124 checked) |
The consumer graph correctly scopes shared layers — a root layer used by 7 apps is scanned against all 7 app directories. App-specific layers are scanned against their own directory plus any parent layers they depend on.
Keys constructed via t('prefix.' + var + '.suffix') will be incorrectly reported as orphans. Only template literals and PHP double-quoted string interpolation are supported.
Mitigation: Enable ESLint's built-in prefer-template rule to auto-fix concatenation to template literals across your codebase:
{ "prefer-template": "error" }
If you need a rule scoped specifically to i18n calls (t(), $t(), $te()), no existing plugin covers this — you'll need a custom ESLint rule:
// eslint-rules/no-i18n-concat.js
module.exports = {
meta: {
type: 'suggestion',
docs: { description: 'Disallow string concatenation in i18n calls; use template literals.' },
messages: { noConcat: 'Use a template literal instead of string concatenation in i18n calls.' },
},
create(context) {
const fns = new Set(['t', '$t', '$te', '$tc', 'tc']);
return {
CallExpression(node) {
const c = node.callee;
const name = c.type === 'Identifier' ? c.name : c.type === 'MemberExpression' ? c.property.name : null;
if (!name || !fns.has(name)) return;
const arg = node.arguments[0];
if (arg?.type === 'BinaryExpression' && arg.operator === '+') {
context.report({ node: arg, messageId: 'noConcat' });
}
},
};
},
};
The scanner does not trace data flow across variable assignments. If a translation key prefix is stored in a variable and the suffix is appended on a separate line, the children will appear as orphans:
// Not detected — $key is assigned on one line, used on another
$key = 'notifications.subscriptions.charged';
__("{$key}.message"); // notifications.subscriptions.charged.message appears orphaned
Mitigation: Add affected key prefixes to ignorePatterns.
Lang::get('parent.key') returns the entire subtree as an array, implicitly marking all children as used. The scanner detects parent.key as used but does not mark parent.key.child1, parent.key.child2, etc.
// Scanner sees 'passport.scopes' as used, but not its children
$scopes = Lang::get('passport.scopes');
Mitigation: Add "passport.scopes.**" to ignorePatterns.
Each ${variable} or $variable interpolation matches exactly one dot-separated segment ([^.]+). Keys where a single variable spans multiple segments (e.g., exceptions.$this->code where $this->code = access_control.duplicate_resource) will not match correctly.
Mitigation: Add the parent namespace to ignorePatterns (e.g., "exceptions.**").
translate_missing uses MCP sampling — the host picks which LLM fulfills the request. The server sends modelPreferences hinting toward fast, cheap models since translation is high-volume and doesn't require frontier reasoning.
Recommended host: VS Code. VS Code has the most complete MCP sampling implementation — it supports
temperature,modelPreferences,systemPrompthoisting, and lets you restrict which models the server can use per-server via the UI. Other hosts vary in their sampling support: some ignoremodelPreferencesor don't expose model selection. Iftranslate_missingbehaves unexpectedly (wrong model, no temperature control), your host's sampling implementation is likely the bottleneck.
How batching works: each batch sends up to 50 keys (configurable via batchSize) to the host LLM. Locales are processed sequentially within a single call — but since each locale writes to its own file, you can call translate_missing once per locale in parallel for faster throughput. A progress bar tracks completion across all locales and batches.
Built-in defaults (used when no samplingPreferences in .i18n-mcp.json):
| Priority | Value | Rationale |
|---|---|---|
hints | ["flash", "haiku", "gpt-4o-mini"] | Fastest models across providers (substring match) |
speedPriority | 0.9 | Users watch a progress bar — latency matters |
costPriority | 0.8 | Hundreds of batches add up |
intelligencePriority | 0.3 | Translation needs quality, not reasoning |
Override via samplingPreferences in .i18n-mcp.json if needed (e.g., to prefer a stronger model for nuanced locales).
Controlling the model in VS Code:
the-i18n-mcp → Configure Model AccessTip: For large translation runs (1,000+ keys), restricting to a fast model like Gemini 2.5 Flash significantly reduces wall-clock time. A batch of 50 keys typically completes in 10–20s with Flash. You can increase
batchSizeup to 200 for fewer round trips, but larger batches risk hitting the host's request timeout. For maximum throughput, calltranslate_missingonce per locale in parallel — each locale writes to its own file, so concurrent calls are safe.
A single translate_missing call processes locales sequentially. On a project with many locales and layers, this adds up. The fastest approach is to split locales across parallel calls and tackle layers in order of size.
Step 1 — Start with your largest layer. Split the target locales across 3–4 parallel translate_missing calls:
Call 1: layer "app-admin", targetLocales ["bg-BG", "da-DK", "el-GR"]
Call 2: layer "app-admin", targetLocales ["et-EE", "fi-FI", "ga-IE"]
Call 3: layer "app-admin", targetLocales ["hr-HR", "lt-LT", "lv-LV"]
Call 4: layer "app-admin", targetLocales ["mt-MT", "sk-SK", "sl-SI", "sv-SE"]
Each call runs independently — different locales, different files, no conflicts.
Step 2 — Move to smaller layers. Once the large layer finishes, translate the remaining layers. Small layers can run concurrently since each layer writes to its own directory:
Call 1: layer "app-shop", targetLocales [all 13 locales]
Call 2: layer "app-panels", targetLocales [all 13 locales]
Step 3 — Verify. Run get_missing_translations across all layers to confirm zero gaps.
This pattern lets you translate thousands of keys across dozens of locales in minutes instead of waiting for one sequential run.
Optionally drop a .i18n-mcp.json at your project root to give the agent project-specific context. Everything is optional — the server passes them to the agent, which interprets the natural-language rules. The server walks up from projectDir to find the nearest config file (like ESLint or tsconfig resolution).
For IDE autocompletion, point to the schema:
{
"$schema": "node_modules/the-i18n-mcp/schema.json"
}
| Field | Purpose |
|---|---|
framework | Force framework detection: "nuxt", "laravel", or "generic". Normally auto-detected from project structure |
context | Free-form project background (business domain, user base, brand voice) |
layerRules | Rules for which layer a new key belongs to, with natural-language when conditions |
glossary | Term dictionary for consistent translations |
translationPrompt | System prompt prepended to all translation requests |
localeNotes | Per-locale instructions — terminology constraints, formality, regional conventions. Keys must match your locale codes exactly (case-sensitive) |
examples | Few-shot translation examples demonstrating project style |
orphanScan | Per-layer config for orphan detection: ignorePatterns (glob patterns for keys to skip). Keys are layer names from list_locale_dirs. The scanner automatically determines scan scope via the consumer graph. |
reportOutput | true for default .i18n-reports/ dir, or a string for a custom path. Diagnostic tools write full output to disk and return only a summary in the MCP response |
samplingPreferences | Override model preferences for translate_missing sampling. See Model Selection |
localeDirs | Locale directories for the generic adapter. Array of path strings or { path, layer } objects. Required (with defaultLocale) to activate the generic adapter |
defaultLocale | Default locale code. Required (with localeDirs) to activate the generic adapter |
locales | Explicit list of locale codes to operate on. If absent, auto-discovered from files on disk |
{
"$schema": "node_modules/the-i18n-mcp/schema.json",
"context": "B2B SaaS booking platform. Professional but approachable tone.",
"layerRules": [
{
"layer": "shared",
"description": "Shared translations: common.actions.*, common.messages.*",
"when": "The key is generic enough to be used in multiple apps"
},
{
"layer": "app-admin",
"description": "Admin dashboard translations",
"when": "The key is only relevant to admin functionality"
}
],
"glossary": {
"Buchung": "Booking (never 'Reservation')",
"Ressource": "Resource (a bookable entity like a room, desk, or person)",
"Termin": "Appointment"
},
"translationPrompt": "Use professional but approachable tone. Preserve all {placeholders} and @:linked references exactly. Keep translations concise — button labels should be 1-2 words.",
"localeNotes": {
"de": "Informal German (du). Standard business tone.",
"de-formal": "Formal German (Sie). Used by enterprise customers.",
"en-us": "American English. Default reference locale.",
"nl": "Dutch (je/jij). Informal. Resource = 'Resource' (NEVER 'Middel'). Booking = 'Boeking'.",
"fr": "French. Use inclusive writing where practical."
},
"examples": [
{
"key": "common.actions.save",
"de": "Speichern",
"en-us": "Save",
"note": "Concise, imperative"
},
{
"key": "bookings.status.checked_in",
"de": "Eingecheckt",
"en-us": "Checked in",
"note": "Past participle, not imperative"
}
],
"orphanScan": {
"shared": {
"ignorePatterns": ["common.datetime.**", "common.countries.*"]
},
"app-admin": {
"ignorePatterns": ["admin.legacy.*"]
}
},
"reportOutput": true,
"samplingPreferences": {
"hints": ["sonnet", "gpt-4o"],
"intelligencePriority": 0.7,
"speedPriority": 0.5,
"costPriority": 0.3
}
}
See playground/nuxt/.i18n-mcp.json for a working example.
find_hardcoded_strings — detect user-facing strings not wrapped in translation callsmove_translations — move keys between layers (e.g., promote to shared)flatJson: true in vue-i18n configtrans_choicepnpm build # Build via tsdown → dist/index.js
pnpm test # Run all tests
pnpm test:perf # Run performance benchmarks
pnpm lint # ESLint
pnpm typecheck # tsc --noEmit
pnpm start # Start the server on stdio
pnpm inspect # Open MCP Inspector for manual testing
Set DEBUG=1 to enable verbose logging to stderr.
Be the first to review this server!
by Modelcontextprotocol · Developer Tools
Read, search, and manipulate Git repositories programmatically
by Toleno · Developer Tools
Toleno Network MCP Server — Manage your Toleno mining account with Claude AI using natural language.
by mcp-marketplace · Developer Tools
Create, build, and publish Python MCP servers to PyPI — conversationally.
by Microsoft · Content & Media
Convert files (PDF, Word, Excel, images, audio) to Markdown for LLM consumption
by mcp-marketplace · Developer Tools
Scaffold, build, and publish TypeScript MCP servers to npm — conversationally
by mcp-marketplace · Finance
Free stock data and market news for any MCP-compatible AI assistant.