@the-i18n-kit/cli 4.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/README.md +368 -0
  2. package/dist/_shared-Dv1EW2gy.js +248 -0
  3. package/dist/_shared-Dv1EW2gy.js.map +1 -0
  4. package/dist/add-BvXrJkqZ.js +40 -0
  5. package/dist/add-BvXrJkqZ.js.map +1 -0
  6. package/dist/bin-Cs-dvpEC.d.ts +1 -0
  7. package/dist/bin.js +11 -0
  8. package/dist/bin.js.map +1 -0
  9. package/dist/check-9dcNfHtC.js +49 -0
  10. package/dist/check-9dcNfHtC.js.map +1 -0
  11. package/dist/cli-CiX4llY1.js +82 -0
  12. package/dist/cli-CiX4llY1.js.map +1 -0
  13. package/dist/config/framework/stubs/next-intl-routing-DEwC7Bbk.d.ts +19 -0
  14. package/dist/config/framework/stubs/next-intl-routing-DEwC7Bbk.d.ts.map +1 -0
  15. package/dist/config/framework/stubs/next-intl-routing.js +22 -0
  16. package/dist/config/framework/stubs/next-intl-routing.js.map +1 -0
  17. package/dist/config/framework/stubs/unplugin-vue-i18n-D9yaDzRk.d.ts +25 -0
  18. package/dist/config/framework/stubs/unplugin-vue-i18n-D9yaDzRk.d.ts.map +1 -0
  19. package/dist/config/framework/stubs/unplugin-vue-i18n.js +26 -0
  20. package/dist/config/framework/stubs/unplugin-vue-i18n.js.map +1 -0
  21. package/dist/define-config-4ubtfvUy.js +14 -0
  22. package/dist/define-config-4ubtfvUy.js.map +1 -0
  23. package/dist/define-config-Dhg13592.d.ts +142 -0
  24. package/dist/define-config-Dhg13592.d.ts.map +1 -0
  25. package/dist/define-config-lQrIpPSK.d.ts +2 -0
  26. package/dist/define-config.d.ts +1 -0
  27. package/dist/define-config.js +2 -0
  28. package/dist/detect-Dr2skxeL.js +17 -0
  29. package/dist/detect-Dr2skxeL.js.map +1 -0
  30. package/dist/empty-p0wct8Hk.js +36 -0
  31. package/dist/empty-p0wct8Hk.js.map +1 -0
  32. package/dist/errors-coI1dhw1.js +33 -0
  33. package/dist/errors-coI1dhw1.js.map +1 -0
  34. package/dist/find-duplicates-DuI8niF7.js +31 -0
  35. package/dist/find-duplicates-DuI8niF7.js.map +1 -0
  36. package/dist/get-BskDC2wT.js +41 -0
  37. package/dist/get-BskDC2wT.js.map +1 -0
  38. package/dist/index-BtSA6-iT.d.ts +1130 -0
  39. package/dist/index-BtSA6-iT.d.ts.map +1 -0
  40. package/dist/index.d.ts +1 -0
  41. package/dist/index.js +6 -0
  42. package/dist/init-D7r6iX3q.js +33 -0
  43. package/dist/init-D7r6iX3q.js.map +1 -0
  44. package/dist/list-dirs-BHk38JXZ.js +17 -0
  45. package/dist/list-dirs-BHk38JXZ.js.map +1 -0
  46. package/dist/missing-0gSZKTgt.js +51 -0
  47. package/dist/missing-0gSZKTgt.js.map +1 -0
  48. package/dist/operations-BCJd7in3.js +6464 -0
  49. package/dist/operations-BCJd7in3.js.map +1 -0
  50. package/dist/php-reader-DcgOAhTw.js +2 -0
  51. package/dist/php-reader-DuZ0Hyl_.js +32 -0
  52. package/dist/php-reader-DuZ0Hyl_.js.map +1 -0
  53. package/dist/providers-BAwPjeCy.js +482 -0
  54. package/dist/providers-BAwPjeCy.js.map +1 -0
  55. package/dist/remove-DR_mRiMA.js +41 -0
  56. package/dist/remove-DR_mRiMA.js.map +1 -0
  57. package/dist/remove-orphans-CcA3XQHJ.js +57 -0
  58. package/dist/remove-orphans-CcA3XQHJ.js.map +1 -0
  59. package/dist/rename-CWLfThC1.js +45 -0
  60. package/dist/rename-CWLfThC1.js.map +1 -0
  61. package/dist/scaffold-BahXKbo9.js +39 -0
  62. package/dist/scaffold-BahXKbo9.js.map +1 -0
  63. package/dist/scan-CTxs3kl7.js +31 -0
  64. package/dist/scan-CTxs3kl7.js.map +1 -0
  65. package/dist/search-BonEgmSt.js +49 -0
  66. package/dist/search-BonEgmSt.js.map +1 -0
  67. package/dist/status-C_5Khw14.js +45 -0
  68. package/dist/status-C_5Khw14.js.map +1 -0
  69. package/dist/stdout-guard-sJHFWVem.js +20 -0
  70. package/dist/stdout-guard-sJHFWVem.js.map +1 -0
  71. package/dist/translate-CnXvBi5O.js +92 -0
  72. package/dist/translate-CnXvBi5O.js.map +1 -0
  73. package/dist/translate-key-DwO9XZMc.js +73 -0
  74. package/dist/translate-key-DwO9XZMc.js.map +1 -0
  75. package/dist/update-HR8MvPlI.js +40 -0
  76. package/dist/update-HR8MvPlI.js.map +1 -0
  77. package/dist/write-CAD9xQDy.js +47 -0
  78. package/dist/write-CAD9xQDy.js.map +1 -0
  79. package/package.json +86 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"operations-BCJd7in3.js","names":["NUXT_CONFIG_FILES","SKIP_DIRS","discoverLocales","COMMON_LOCALE_DIRS","computeScore","findLocaleDir","discoverLocales","NEXT_CONFIG_FILES","COMMON_LOCALE_DIRS"],"sources":["../src/core/shared.ts","../src/core/report.ts","../src/adapters/registry.ts","../src/config/discovery.ts","../src/config/nuxt-loader.ts","../src/config/locale-override.ts","../src/config/fallback-locale.ts","../src/adapters/nuxt/layer-dedup.ts","../src/config/artifact.ts","../src/adapters/nuxt/merge-apps.ts","../src/adapters/nuxt/index.ts","../src/adapters/laravel/index.ts","../src/adapters/generic/index.ts","../src/config/framework/execute.ts","../src/config/framework/vue.ts","../src/adapters/shared.ts","../src/adapters/vue/index.ts","../src/config/framework/next.ts","../src/adapters/react/index.ts","../src/config/detector.ts","../src/config/layer-graph.ts","../src/io/read-cache.ts","../src/io/json-reader.ts","../src/io/key-operations.ts","../src/io/atomic-write.ts","../src/io/json-writer.ts","../src/io/php-writer.ts","../src/io/locale-io.ts","../src/io/locale-data.ts","../src/core/ops-translate.ts","../src/core/ops-read.ts","../src/scanner/patterns.ts","../src/scanner/code-scanner.ts","../src/tools/scaffold-locale.ts","../src/core/ops-write.ts","../src/core/ops-init.ts","../src/core/ops-status.ts","../src/core/codequality.ts","../src/core/ops-orphans.ts","../src/core/ops-duplicates.ts","../src/core/ops-check.ts"],"sourcesContent":["/**\n * Shared locale/layer lookup helpers used across the core operation modules.\n */\n\nimport type { I18nConfig, LocaleDefinition, LocaleDir } from '../config/types.js'\nimport { ToolError } from '../utils/errors.js'\n\nimport type { LocaleRefInfo } from './types.js'\n\n/**\n * Look up a locale directory by layer name and throw LAYER_NOT_FOUND with fuzzy\n * matching hints if the name is not found.\n */\nexport function findLayerOrThrow(config: I18nConfig, layer: string): LocaleDir {\n const localeDir = config.localeDirs.find(d => d.layer === layer)\n if (!localeDir) {\n const available = config.localeDirs.map(d => d.layer).join(', ')\n const layerRule = config.projectConfig?.layerRules?.find(r => r.layer === layer)\n const fuzzyHint = layerRule\n ? ` Note: \"${layer}\" matches a layerRules entry in .i18n-mcp.json but is not an internal layer name.`\n : ''\n throw new ToolError(\n `Layer not found: \"${layer}\". Available: ${available}.${fuzzyHint} Use list_locale_dirs to see all layers.`,\n 'LAYER_NOT_FOUND',\n )\n }\n return localeDir\n}\n\n/**\n * Look up a layer that will be written to: same as findLayerOrThrow, but also\n * rejects alias layers (writes must target the source layer).\n */\nexport function findWritableLayerOrThrow(config: I18nConfig, layer: string): LocaleDir {\n const localeDir = findLayerOrThrow(config, layer)\n if (localeDir.aliasOf) {\n throw new ToolError(`Layer \"${layer}\" is an alias of \"${localeDir.aliasOf}\". Modify the source layer \"${localeDir.aliasOf}\" instead.`, 'LAYER_IS_ALIAS')\n }\n return localeDir\n}\n\n/**\n * Resolve the reference locale (requested or project default) and throw\n * REFERENCE_LOCALE_NOT_FOUND when it does not exist.\n */\nexport function findReferenceLocaleOrThrow(config: I18nConfig, requested?: string): LocaleDefinition {\n const refCode = requested ?? config.defaultLocale\n const refLocale = findLocaleImpl(config, refCode)\n if (!refLocale) {\n throw new ToolError(`Reference locale not found: \"${refCode}\". Available: ${config.locales.map(l => l.code).join(', ')}. Pass a valid locale code as referenceLocale, or omit it to use the project default.`, 'REFERENCE_LOCALE_NOT_FOUND')\n }\n return refLocale\n}\n\n/**\n * The locale directories an operation should scan: one named layer, or every\n * non-alias layer. Alias layers are skipped because they point at another\n * layer's files, so scanning both would count the same keys twice.\n *\n * Throws rather than returning empty: an operation with nothing to scan has no\n * meaningful result, and a mistyped layer name should say so.\n */\nexport function resolveLayersToScan(config: I18nConfig, layer?: string): LocaleDir[] {\n const layers = layer\n ? config.localeDirs.filter(d => d.layer === layer)\n : config.localeDirs.filter(d => !d.aliasOf)\n\n if (layers.length === 0) {\n // Surfaces the better \"layer not found, here are the valid ones\" error.\n if (layer) findLayerOrThrow(config, layer)\n throw new ToolError(\n 'No locale directories found. Run detect_i18n_config to verify the project setup.',\n 'LAYER_NOT_FOUND',\n )\n }\n return layers\n}\n\nexport function localeRefInfo(locale: LocaleDefinition): LocaleRefInfo {\n return {\n code: locale.code,\n ...(locale.language ? { language: locale.language } : {}),\n ...(locale.file ? { file: locale.file } : {}),\n ...(locale.name ? { name: locale.name } : {}),\n }\n}\n\nfunction localeAcceptedRefs(locale: LocaleDefinition): string[] {\n // Deduped: code and language are frequently the same string (a generic\n // adapter derives both from the filename), and \"de\" or \"de\" or \"de.json\"\n // reads like a bug in the suggestion rather than one locale's three refs.\n return [...new Set([locale.code, locale.language, locale.file].filter(Boolean) as string[])]\n}\n\nfunction formatLocaleChoices(locales: LocaleDefinition[]): string {\n return locales\n .map(locale => `- code: ${locale.code}${locale.language ? `, language: ${locale.language}` : ''}${locale.file ? `, file: ${locale.file}` : ''}${locale.name ? `, name: ${locale.name}` : ''}`)\n .join('\\n')\n}\n\nconst stripJson = (ref: string) => (ref.endsWith('.json') ? ref.slice(0, -5) : ref)\n\n/**\n * How well one of a locale's refs matches the requested one. Higher wins.\n * Ranking matters rather than first-match: for \"de-DE-formal\", the informal\n * `de` locale matches by containment (its language is `de-DE`) while the\n * formal one matches exactly once `.json` is stripped from its file name.\n * First-match order returned `de`, pointing the caller at the wrong locale —\n * following that hint would write formal German into the informal file (#301).\n */\nfunction suggestionScore(locale: LocaleDefinition, normalized: string): number {\n let best = 0\n for (const ref of localeAcceptedRefs(locale)) {\n const candidate = stripJson(ref)\n if (candidate === normalized) return 3\n if (candidate.toLowerCase() === normalized.toLowerCase()) best = Math.max(best, 2)\n else if (candidate.includes(normalized) || normalized.includes(candidate)) best = Math.max(best, 1)\n }\n return best\n}\n\nexport function findLocaleSuggestion(config: I18nConfig, localeRef: string): string {\n const normalized = stripJson(localeRef)\n\n let suggestion: LocaleDefinition | undefined\n let bestScore = 0\n for (const locale of config.locales) {\n const score = suggestionScore(locale, normalized)\n if (score > bestScore) {\n bestScore = score\n suggestion = locale\n }\n }\n if (!suggestion) return ''\n\n const refs = localeAcceptedRefs(suggestion).map(ref => `\"${ref}\"`).join(' or ')\n return ` Did you mean ${refs}?`\n}\n\n/** Fields a locale ref may match, in resolution precedence order. */\nconst LOCALE_MATCH_FIELDS = ['code', 'language', 'file'] as const\nexport type LocaleMatchField = (typeof LOCALE_MATCH_FIELDS)[number]\n\nexport interface LocaleRefAmbiguity {\n ref: string\n /** The field that matched more than one locale. */\n matchedBy: LocaleMatchField\n /** Codes of every locale the ref matched, in config order. */\n candidates: string[]\n /** The one that was used — the first candidate. */\n resolvedTo: string\n}\n\nexport interface LocaleRefResolution {\n locale?: LocaleDefinition\n ambiguity?: LocaleRefAmbiguity\n}\n\n/**\n * Resolve a locale ref with deliberate precedence: an exact `code` outranks a\n * `language` tag, which outranks a `file` name.\n *\n * Codes are unique by construction; language tags are not — two locales can\n * both declare `de-DE` (an informal and a formal German, say). Without\n * precedence, a locale's unique code could be shadowed by a different locale's\n * language tag purely through config ordering. Within one field a ref that\n * still matches several locales is reported as ambiguous rather than silently\n * resolved to whichever came first, because that choice depends on array order\n * and can change under the caller without warning (#301).\n */\nexport function resolveLocaleRef(config: I18nConfig, localeRef: string): LocaleRefResolution {\n for (const field of LOCALE_MATCH_FIELDS) {\n const matches = config.locales.filter(locale => locale[field] === localeRef)\n const [first] = matches\n if (!first) continue\n if (matches.length === 1) return { locale: first }\n return {\n locale: first,\n ambiguity: {\n ref: localeRef,\n matchedBy: field,\n candidates: matches.map(m => m.code),\n resolvedTo: first.code,\n },\n }\n }\n return {}\n}\n\nexport function findLocaleImpl(config: I18nConfig, localeRef: string) {\n return resolveLocaleRef(config, localeRef).locale\n}\n\n/**\n * Resolve the reference locale for scan operations: the requested ref or the\n * project default. Throws LOCALE_NOT_FOUND listing the available codes.\n */\nexport function resolveReferenceLocale(\n config: I18nConfig,\n requested?: string,\n): { localeCode: string; localeDef: LocaleDefinition } {\n const localeCode = requested ?? config.defaultLocale\n const localeDef = findLocaleImpl(config, localeCode)\n if (!localeDef) {\n throw new ToolError(\n `Locale not found: \"${localeCode}\". Available: ${config.locales.map(l => l.code).join(', ')}`,\n 'LOCALE_NOT_FOUND',\n )\n }\n return { localeCode, localeDef }\n}\n\nexport function findLocaleOrThrow(config: I18nConfig, localeRef: string): LocaleDefinition {\n const locale = findLocaleImpl(config, localeRef)\n if (!locale) {\n throw new ToolError(\n `Locale not found: \"${localeRef}\".${findLocaleSuggestion(config, localeRef)}\\nAvailable locales:\\n${formatLocaleChoices(config.locales)}`,\n 'LOCALE_NOT_FOUND',\n )\n }\n return locale\n}\n","/**\n * Report-file plumbing: path validation and resolution for tools that\n * support writing their output to a JSON report file.\n */\n\nimport { resolve, relative } from 'node:path'\n\nimport type { I18nConfig } from '../config/types.js'\nimport { ToolError } from '../utils/errors.js'\n\nconst DEFAULT_REPORT_DIR = '.i18n-reports'\n\nexport function validateReportPath(baseDir: string, absPath: string): void {\n const normalizedBase = resolve(baseDir)\n const normalizedPath = resolve(absPath)\n const rel = relative(normalizedBase, normalizedPath)\n if (rel.startsWith('..') || rel === '') {\n throw new ToolError(\n `Report path \"${absPath}\" resolves outside the project directory. Path must stay within \"${normalizedBase}\".`,\n 'INVALID_REPORT_PATH',\n )\n }\n}\n\n/**\n * Resolve a caller-supplied outputFile against the project dir. Relative\n * paths must anchor to `dir`, not the process cwd — otherwise\n * `--output-file report.json --project-dir /x` run from elsewhere can never\n * pass the in-project guard. Absolute paths pass through unchanged; either\n * way the result must stay within the project dir.\n */\nexport function resolveOutputFile(\n dir: string,\n outputFile: string | undefined,\n): string | undefined {\n if (!outputFile) return undefined\n const absPath = resolve(dir, outputFile)\n validateReportPath(dir, absPath)\n return absPath\n}\n\nexport function resolveReportFilePath(\n config: I18nConfig,\n dir: string,\n toolName: string,\n): string | undefined {\n const reportOutput = config.projectConfig?.reportOutput\n if (!reportOutput) return undefined\n const relDir = reportOutput === true ? DEFAULT_REPORT_DIR : reportOutput\n const absPath = resolve(dir, relDir, `${toolName}.json`)\n validateReportPath(dir, absPath)\n return absPath\n}\n","import type { FrameworkAdapter } from './types'\nimport { ConfigError, toErrorMessage } from '../utils/errors'\nimport { log } from '../utils/logger'\n\nconst adapters: FrameworkAdapter[] = []\n\nexport function registerAdapter(adapter: FrameworkAdapter): void {\n adapters.push(adapter)\n}\n\nexport function resetRegistry(): void {\n adapters.length = 0\n}\n\nexport interface FrameworkMatch {\n adapter: FrameworkAdapter\n /** Detection score. 0 when the adapter was forced by hint rather than detected. */\n confidence: number\n /** Every adapter that scored above zero, best first — context for `init`. */\n runnersUp: Array<{ name: string; confidence: number }>\n}\n\n/**\n * Detect the framework and report how confidently. `detectFramework` returns\n * only the winner; `init` needs the score to tell a user why Nuxt was chosen\n * over generic, and a bare directory needs to be distinguishable from a\n * confident match rather than surfacing as a thrown error.\n */\nexport async function detectFrameworkMatch(\n projectDir: string,\n hint?: string,\n): Promise<FrameworkMatch | undefined> {\n if (adapters.length === 0) {\n throw new ConfigError('No framework adapters registered.')\n }\n\n if (hint) {\n const match = adapters.find(a => a.name === hint)\n if (!match) return undefined\n return { adapter: match, confidence: 0, runnersUp: [] }\n }\n\n const scores = await Promise.all(\n adapters.map(async (adapter) => {\n try {\n return { adapter, confidence: await adapter.detect(projectDir) }\n }\n catch (error) {\n log.warn(`Adapter '${adapter.name}' detection failed: ${toErrorMessage(error)}`)\n return { adapter, confidence: 0 }\n }\n }),\n )\n\n const ranked = scores.filter(s => s.confidence > 0).sort((a, b) => b.confidence - a.confidence)\n const [best] = ranked\n if (!best) return undefined\n\n return {\n adapter: best.adapter,\n confidence: best.confidence,\n runnersUp: ranked.slice(1).map(s => ({ name: s.adapter.name, confidence: s.confidence })),\n }\n}\n\nexport async function detectFramework(\n projectDir: string,\n hint?: string,\n): Promise<FrameworkAdapter> {\n const match = await detectFrameworkMatch(projectDir, hint)\n if (match) return match.adapter\n\n if (hint) {\n throw new ConfigError(\n `Framework hint \"${hint}\" does not match any registered adapter. `\n + `Available: ${adapters.map(a => a.name).join(', ')}`,\n )\n }\n throw new ConfigError(\n `No framework detected for ${projectDir}. `\n + `Registered adapters: ${adapters.map(a => a.name).join(', ')}`,\n )\n}\n","import { existsSync, statSync, realpathSync } from 'node:fs'\nimport { readdir, readFile } from 'node:fs/promises'\nimport { resolve, relative } from 'node:path'\n\nconst NUXT_CONFIG_FILES = ['nuxt.config.ts', 'nuxt.config.js', 'nuxt.config.mjs'] as const\n\nconst SKIP_DIRS = new Set([\n 'node_modules', '.nuxt', '.output', '.git', 'dist', '.cache',\n])\n\nconst MAX_DISCOVERY_DEPTH = 4\n\nexport function canonicalPath(dir: string): string {\n try {\n return realpathSync(resolve(dir))\n }\n catch {\n return resolve(dir)\n }\n}\n\nexport function findNuxtConfig(dir: string): string | null {\n for (const name of NUXT_CONFIG_FILES) {\n if (existsSync(resolve(dir, name))) {\n return name\n }\n }\n return null\n}\n\nasync function hasI18nConfig(configPath: string): Promise<boolean> {\n try {\n const content = await readFile(configPath, 'utf-8')\n return /\\bi18n\\b/.test(content)\n }\n catch {\n return false\n }\n}\n\n/**\n * Discover all Nuxt apps with i18n configuration under a root directory.\n * Scans recursively up to MAX_DISCOVERY_DEPTH levels deep,\n * skipping common non-project directories.\n *\n * Returns sorted absolute paths to directories that:\n * 1. Contain a nuxt.config.{ts,js,mjs} file\n * 2. The config file references i18n\n */\nexport async function discoverNuxtApps(rootDir: string): Promise<string[]> {\n const apps: string[] = []\n await scanForApps(rootDir, 0, apps, true)\n const root = resolve(rootDir)\n apps.sort((a, b) => relative(root, a).localeCompare(relative(root, b)))\n return apps\n}\n\nasync function scanForApps(dir: string, depth: number, results: string[], isRoot = false): Promise<void> {\n if (depth > MAX_DISCOVERY_DEPTH) return\n\n const configFile = findNuxtConfig(dir)\n if (configFile) {\n const configPath = resolve(dir, configFile)\n if (await hasI18nConfig(configPath)) {\n results.push(dir)\n }\n if (!isRoot) return\n }\n\n let entries: string[]\n try {\n entries = await readdir(dir)\n }\n catch {\n return\n }\n\n for (const entry of entries) {\n if (entry.startsWith('.') || SKIP_DIRS.has(entry)) continue\n const childDir = resolve(dir, entry)\n try {\n const stat = statSync(childDir)\n if (!stat.isDirectory()) continue\n }\n catch {\n continue\n }\n await scanForApps(childDir, depth + 1, results)\n }\n}\n\n/**\n * Derive a human-friendly layer name from its root directory.\n * Uses the discovery root as the reference point for naming.\n *\n * When `usedNames` is provided, disambiguates collisions by progressively\n * including parent path segments (e.g., `admin` → `apps/admin`).\n */\nexport function deriveLayerName(\n layerRootDir: string,\n discoveryRoot: string,\n usedNames?: Set<string>,\n): string {\n const rel = relative(discoveryRoot, layerRootDir)\n if (rel === '' || rel === '.') {\n return 'root'\n }\n\n const posixRel = rel.replace(/\\\\/g, '/')\n\n if (!posixRel.startsWith('..')) {\n const segments = posixRel.split('/')\n let candidate = segments.pop() ?? posixRel\n\n if (usedNames) {\n for (let i = segments.length - 1; i >= 0; i--) {\n if (!usedNames.has(candidate)) break\n candidate = `${segments[i]}/${candidate}`\n }\n }\n\n return candidate\n }\n\n const parts = layerRootDir.replace(/\\\\/g, '/').split('/')\n let candidate = parts.pop() ?? layerRootDir\n\n if (usedNames) {\n for (let i = parts.length - 1; i >= 0; i--) {\n if (!usedNames.has(candidate)) break\n candidate = `${parts[i]}/${candidate}`\n }\n }\n\n return candidate\n}\n","import { log } from '../utils/logger'\n\ntype NuxtKit = typeof import('@nuxt/kit')\n\n/** Import `id` resolved against the project's node_modules rather than our own. */\nasync function importModuleFrom(id: string, rootDir: string): Promise<unknown> {\n const { createRequire } = await import('node:module')\n const require = createRequire(rootDir + '/')\n const resolved = require.resolve(id)\n return import(resolved)\n}\n\n/**\n * Load @nuxt/kit from the project's own node_modules.\n * Falls back to trying common Nuxt package names.\n */\nexport async function loadKit(rootDir: string): Promise<NuxtKit> {\n try {\n const kit = await importModuleFrom('@nuxt/kit', rootDir) as NuxtKit\n log.debug(`Loaded @nuxt/kit from project: ${rootDir}`)\n return kit\n } catch (e: unknown) {\n const message = String(e)\n if (message.includes(\"Cannot find module '@nuxt/kit'\")) {\n throw new Error(\n '@nuxt/kit is required for Nuxt projects but was not found. '\n + 'Install it with: npm install --save-dev @nuxt/kit\\n'\n + 'Laravel projects do not need @nuxt/kit — if this is a Laravel project, '\n + 'check that your .i18n-mcp.json has \"framework\": \"laravel\" or that '\n + 'your project structure is detected correctly.',\n )\n }\n throw e\n }\n}\n","import type { LocaleDefinition } from './types'\nimport { log } from '../utils/logger'\n\n/**\n * Apply the optional `locales` override from `.i18n-mcp.json` on top of the\n * locales auto-discovered by a framework adapter.\n *\n * Semantics:\n * - If `projectLocales` is unset/empty → return `discovered` unchanged.\n * - Else → restrict to the configured codes, preserving the order from\n * `projectLocales` and keeping the metadata (file, language, name) from\n * the discovered entries. Configured codes with no matching discovered\n * entry get a minimal `{code, language: code}` fallback and a warning.\n */\nexport function applyLocaleOverride(\n discovered: LocaleDefinition[],\n projectLocales: string[] | undefined,\n): LocaleDefinition[] {\n if (!projectLocales || projectLocales.length === 0) {\n return discovered\n }\n\n const byCode = new Map(discovered.map(l => [l.code, l]))\n const missing: string[] = []\n\n const result = projectLocales.map((code) => {\n const found = byCode.get(code)\n if (found) return found\n missing.push(code)\n return { code, language: code }\n })\n\n if (missing.length > 0) {\n log.warn(\n `Configured locales not found in framework auto-detection: ${missing.join(', ')}. `\n + 'Translations for these codes may fail to write — make sure the locale files exist.',\n )\n }\n\n const dropped = discovered.filter(l => !projectLocales.includes(l.code))\n if (dropped.length > 0) {\n log.debug(\n `Locale override active: dropped ${dropped.length} auto-detected locale(s) `\n + `(${dropped.map(l => l.code).join(', ')})`,\n )\n }\n\n return result\n}\n","/**\n * Normalise `@nuxtjs/i18n`'s `fallbackLocale` — a string, an array, or a\n * per-locale map — into the single map shape the rest of the CLI reads.\n *\n * Shared by both paths on purpose. The Nuxt module publishes this value exactly\n * as Nuxt resolved it rather than normalising it itself: two implementations of\n * the same normalisation, in two packages that do not depend on each other, is\n * precisely the drift the module exists to remove.\n */\nexport function normalizeFallbackLocale(\n fallback: unknown,\n defaultLocale: string,\n): Record<string, string[]> {\n if (typeof fallback === 'string') {\n return { default: [fallback] }\n }\n\n if (Array.isArray(fallback)) {\n return { default: (fallback as unknown[]).map(String) }\n }\n\n if (fallback && typeof fallback === 'object') {\n const result: Record<string, string[]> = {}\n for (const [key, value] of Object.entries(fallback as Record<string, unknown>)) {\n if (Array.isArray(value)) {\n result[key] = value.map(String)\n }\n else if (typeof value === 'string') {\n result[key] = [value]\n }\n }\n return result\n }\n\n return { default: [defaultLocale] }\n}\n","import { sep } from 'node:path'\nimport type { LocaleDir } from '../../config/types'\nimport { log } from '../../utils/logger'\n\nexport interface LayerRef {\n layer: string\n layerRootDir: string\n}\n\nexport interface OwnershipResult {\n owner: string\n alias: string\n}\n\nfunction isAncestorOf(ancestorDir: string, childPath: string): boolean {\n const normalized = ancestorDir.endsWith(sep) ? ancestorDir : ancestorDir + sep\n return childPath.startsWith(normalized) || childPath === ancestorDir\n}\n\nfunction ancestorDepth(ancestorDir: string, childPath: string): number {\n if (!isAncestorOf(ancestorDir, childPath)) return -1\n return ancestorDir.split(sep).filter(Boolean).length\n}\n\nexport function resolveLayerOwnership(\n existing: LayerRef,\n incoming: LayerRef,\n localeDirPath: string,\n): OwnershipResult {\n const existingIsAncestor = isAncestorOf(existing.layerRootDir, localeDirPath)\n const incomingIsAncestor = isAncestorOf(incoming.layerRootDir, localeDirPath)\n\n if (existingIsAncestor && !incomingIsAncestor) {\n return { owner: existing.layer, alias: incoming.layer }\n }\n\n if (incomingIsAncestor && !existingIsAncestor) {\n return { owner: incoming.layer, alias: existing.layer }\n }\n\n if (existingIsAncestor && incomingIsAncestor) {\n const existingDepth = ancestorDepth(existing.layerRootDir, localeDirPath)\n const incomingDepth = ancestorDepth(incoming.layerRootDir, localeDirPath)\n if (incomingDepth > existingDepth) {\n return { owner: incoming.layer, alias: existing.layer }\n }\n return { owner: existing.layer, alias: incoming.layer }\n }\n\n return { owner: existing.layer, alias: incoming.layer }\n}\n\n/**\n * Claim a locale directory for a layer, deduplicating by real path.\n *\n * Mutates `dirs` and `claims`:\n * - First claim for `realPath`: the incoming dir is pushed and recorded as owner.\n * - Same path already claimed and the incoming layer wins ownership\n * (see `resolveLayerOwnership`): the incoming dir replaces the previous\n * owner in place, and the previous owner is re-pushed demoted to an alias.\n * - Same path already claimed and the existing layer keeps ownership: the\n * incoming dir is pushed as an alias of the owner (unless it *is* the owner).\n * - If the recorded owner cannot be found as a non-alias entry (it was itself\n * demoted to an alias by an earlier takeover on another path), the incoming\n * dir is still pushed as an alias — a claim never silently drops a dir.\n */\nexport function claimLocaleDir(\n dirs: LocaleDir[],\n claims: Map<string, LayerRef>,\n incoming: LocaleDir,\n realPath: string,\n): void {\n const existing = claims.get(realPath)\n\n if (!existing) {\n claims.set(realPath, { layer: incoming.layer, layerRootDir: incoming.layerRootDir })\n dirs.push({ ...incoming })\n return\n }\n\n const { owner, alias } = resolveLayerOwnership(\n { layer: existing.layer, layerRootDir: existing.layerRootDir },\n { layer: incoming.layer, layerRootDir: incoming.layerRootDir },\n realPath,\n )\n\n if (owner !== existing.layer) {\n const ownerIndex = dirs.findIndex(d => d.layer === existing.layer && !d.aliasOf)\n const previousOwner = ownerIndex !== -1 ? dirs[ownerIndex] : undefined\n if (previousOwner) {\n const promoted = { ...incoming }\n delete promoted.aliasOf\n dirs[ownerIndex] = promoted\n dirs.push({ ...previousOwner, aliasOf: owner })\n claims.set(realPath, { layer: incoming.layer, layerRootDir: incoming.layerRootDir })\n log.debug(`Layer '${alias}' is alias of '${owner}' (ancestor-based ownership, same path: ${incoming.path})`)\n }\n else {\n // Recorded owner is itself an alias — keep the incoming dir as an alias\n // instead of dropping it.\n dirs.push({ ...incoming, aliasOf: existing.layer })\n log.debug(`Layer '${incoming.layer}' is alias of '${existing.layer}' (owner already aliased, same path: ${incoming.path})`)\n }\n }\n else if (incoming.layer !== existing.layer) {\n dirs.push({ ...incoming, aliasOf: owner })\n log.debug(`Layer '${alias}' is alias of '${owner}' (same path: ${incoming.path})`)\n }\n}\n","import { existsSync, statSync } from 'node:fs'\nimport { readFile, realpath } from 'node:fs/promises'\nimport { resolve } from 'node:path'\nimport { z } from 'zod'\nimport type { AppInfo, I18nConfig, LocaleDefinition, LocaleDir, ProjectConfig } from './types.js'\nimport { deriveLayerName, findNuxtConfig } from './discovery.js'\nimport { applyLocaleOverride } from './locale-override.js'\nimport { normalizeFallbackLocale } from './fallback-locale.js'\nimport { claimLocaleDir } from '../adapters/nuxt/layer-dedup.js'\nimport { log } from '../utils/logger.js'\n\n/** Where @the-i18n-kit/nuxt publishes what Nuxt resolved, relative to an app. */\nconst ARTIFACT_PATH = '.nuxt/i18n-kit.json'\n\nconst localeSchema = z.object({\n code: z.string().min(1),\n language: z.string(),\n file: z.string().min(1),\n name: z.string().optional(),\n})\n\nconst layerSchema = z.object({\n rootDir: z.string().min(1),\n localeDir: z.string().min(1).optional(),\n})\n\n/**\n * Authoring policy declared in nuxt.config.ts. Deliberately permissive: the\n * shape is owned by ProjectConfig's own schema, which validates it once the two\n * sources are merged. Re-stating it here would be a second definition to keep\n * in step.\n */\nconst policySchema = z.record(z.string(), z.unknown())\n\nconst artifactSchema = z.object({\n version: z.literal(1),\n generator: z.string(),\n appDir: z.string().min(1),\n defaultLocale: z.string().min(1),\n // As Nuxt resolved it, in any of the three shapes @nuxtjs/i18n accepts.\n // Normalising it here rather than in the module keeps one implementation.\n fallbackLocale: z.union([\n z.string(),\n z.array(z.string()),\n z.record(z.string(), z.union([z.string(), z.array(z.string())])),\n z.null(),\n ]),\n localeFileFormat: z.literal('json'),\n locales: z.array(localeSchema).min(1),\n layers: z.array(layerSchema).min(1),\n policy: policySchema.optional(),\n})\n\nexport type I18nKitArtifact = z.infer<typeof artifactSchema>\n\n/**\n * Read an app's artifact, or null when there isn't a usable one.\n *\n * Never throws: every failure here — absent, malformed, a version this CLI\n * does not know, or older than the config it claims to describe — means\n * falling back to loading the app through Nuxt, which is what happens today\n * anyway. A module that cannot be trusted must not be able to break a project\n * that worked without it.\n */\nexport async function readArtifact(appDir: string): Promise<I18nKitArtifact | null> {\n const path = resolve(appDir, ARTIFACT_PATH)\n if (!existsSync(path)) return null\n\n let parsed: unknown\n try {\n parsed = JSON.parse(await readFile(path, 'utf-8'))\n }\n catch (error) {\n log.warn(`Ignoring ${path}: ${error instanceof Error ? error.message : String(error)}`)\n return null\n }\n\n const result = artifactSchema.safeParse(parsed)\n if (!result.success) {\n const [issue] = result.error.issues\n const where = issue?.path.join('.') || '(root)'\n log.warn(`Ignoring ${path}: not a shape this CLI understands (${where}: ${issue?.message}).`)\n return null\n }\n\n // Staleness is checked after parsing so the layers can be checked too: a\n // layer's own config decides that layer's locales, and an app config that\n // has not changed says nothing about the layers it extends.\n const changed = staleAgainst(result.data, path)\n if (changed) {\n log.warn(\n `Ignoring ${path}: ${changed} has changed since it was generated. `\n + 'Run `nuxt prepare` to refresh it — falling back to loading the app.',\n )\n return null\n }\n\n log.info(`Using the layer graph published by ${result.data.generator}`)\n return result.data\n}\n\n/**\n * An artifact older than any config it describes is a lie about the current\n * project, and a quiet one. Cheaper to distrust it than to explain later why a\n * renamed locale never took effect.\n *\n * Returns the config that outdates it, for the message; undefined when the\n * artifact is current, when no config can be found, or when a stat fails —\n * unreadable timestamps are not evidence of staleness.\n */\nfunction staleAgainst(artifact: I18nKitArtifact, artifactPath: string): string | undefined {\n let artifactMtime: number\n try {\n artifactMtime = statSync(artifactPath).mtimeMs\n }\n catch {\n return undefined\n }\n\n const dirs = [artifact.appDir, ...artifact.layers.map(l => l.rootDir)]\n\n for (const dir of new Set(dirs)) {\n const configFile = findNuxtConfig(dir)\n if (!configFile) continue\n\n const configPath = resolve(dir, configFile)\n try {\n if (statSync(configPath).mtimeMs > artifactMtime) return configPath\n }\n catch {\n continue\n }\n }\n\n return undefined\n}\n\n/**\n * Turn one app's artifact into the config shape every command consumes.\n *\n * Layer *names* are derived here rather than read from the artifact: they\n * depend on the directory the CLI was pointed at, which the module cannot\n * know. Deriving them in one place is what keeps the artifact path and the\n * fallback path producing the same names for the same project.\n */\nexport async function artifactToConfig(\n artifact: I18nKitArtifact,\n appDir: string,\n discoveryRoot: string,\n projectConfig: ProjectConfig | null,\n): Promise<I18nConfig> {\n const merged = mergePolicy(artifact.policy, projectConfig)\n const localeDirs: LocaleDir[] = []\n const claimed = new Map<string, { layer: string, layerRootDir: string }>()\n const usedLayerNames = new Set<string>()\n\n for (const layer of artifact.layers) {\n // Named before the locale-dir check, and unconditionally: a layer without\n // translations still consumes its name, so that layers with translations\n // get the same names they would on the fallback path.\n const layerName = deriveLayerName(layer.rootDir, discoveryRoot, usedLayerNames)\n usedLayerNames.add(layerName)\n\n if (!layer.localeDir) continue\n\n const realDir = await realpath(layer.localeDir).catch(() => layer.localeDir!)\n claimLocaleDir(\n localeDirs,\n claimed,\n { path: layer.localeDir, layer: layerName, layerRootDir: layer.rootDir },\n realDir,\n )\n }\n\n const layerRootDirs = [...new Set(artifact.layers.map(l => l.rootDir))]\n if (layerRootDirs.length === 0) layerRootDirs.push(appDir)\n\n const namesForApp = new Set<string>()\n const app: AppInfo = {\n name: deriveLayerName(appDir, discoveryRoot, new Set()),\n rootDir: appDir,\n layers: artifact.layers.map(l => deriveLayerName(l.rootDir, discoveryRoot, namesForApp)),\n }\n\n const locales: LocaleDefinition[] = artifact.locales.map(l => ({\n code: l.code,\n language: l.language,\n file: l.file,\n ...(l.name === undefined ? {} : { name: l.name }),\n }))\n\n return {\n rootDir: appDir,\n defaultLocale: artifact.defaultLocale,\n fallbackLocale: normalizeFallbackLocale(artifact.fallbackLocale, artifact.defaultLocale),\n locales: applyLocaleOverride(locales, merged?.locales),\n localeDirs,\n layerRootDirs,\n projectConfig: merged,\n apps: [app],\n }\n}\n\n/**\n * Fold the policy declared in nuxt.config.ts into the policy read from\n * .i18n-mcp.json.\n *\n * The file wins on a collision, which should never happen: the module fails the\n * build when a key is declared in both. This is the belt to that braces — an\n * artifact generated before the check existed, or by a newer module, cannot\n * quietly override a value someone can see in their repository.\n */\nfunction mergePolicy(\n policy: Record<string, unknown> | undefined,\n projectConfig: ProjectConfig | null,\n): ProjectConfig | undefined {\n if (!policy || Object.keys(policy).length === 0) return projectConfig ?? undefined\n\n const declared = Object.keys(policy).filter(key => (projectConfig as Record<string, unknown> | null)?.[key] !== undefined)\n if (declared.length > 0) {\n log.warn(\n `Ignoring ${declared.join(', ')} from the Nuxt artifact: also declared in .i18n-mcp.json, which wins.`,\n )\n }\n\n return { ...policy, ...projectConfig } as ProjectConfig\n}\n","import { realpath } from 'node:fs/promises'\nimport type { AppInfo, I18nConfig, LocaleDefinition, LocaleDir } from '../../config/types'\nimport { deriveLayerName } from '../../config/discovery'\nimport { claimLocaleDir } from './layer-dedup'\n\n/**\n * Folds several Nuxt apps into one project config.\n *\n * A monorepo's apps overlap: they extend the same root layer, sometimes alias\n * each other's locale directories, and each reports the full locale table its\n * layers declare. Merging them is therefore four separate reconciliations —\n * directories claimed once by real path, locales deduplicated by code, layer\n * roots kept unique, and app→layer edges rewritten to whatever the directory\n * ended up being called. Keeping that state in one place stops the caller from\n * having to hold all four in its head at once.\n */\nexport class AppMerger {\n readonly localeDirs: LocaleDir[] = []\n readonly locales: LocaleDefinition[] = []\n readonly layerRootDirs: string[] = []\n readonly apps: AppInfo[] = []\n\n defaultLocale = 'en'\n fallbackLocale: Record<string, string[]> = { default: ['en'] }\n\n /**\n * Authoring policy contributed by the apps themselves — each app's artifact\n * may carry what its nuxt.config declared. Accumulated first-wins, then\n * overlaid by .i18n-mcp.json, which is the source a reader can see without\n * building.\n */\n private readonly policy: Record<string, unknown> = {}\n\n private readonly claimedPaths = new Map<string, { layer: string, layerRootDir: string }>()\n private readonly seenLocaleCodes = new Set<string>()\n private readonly usedLayerNames = new Set<string>()\n\n async add(appConfig: I18nConfig, discoveryRoot: string): Promise<void> {\n // The first app to be merged sets the project defaults. Checked against the\n // directories rather than a flag, so an app that contributed none does not\n // get to claim them.\n if (this.localeDirs.length === 0) {\n this.defaultLocale = appConfig.defaultLocale\n this.fallbackLocale = appConfig.fallbackLocale\n }\n\n this.collectPolicy(appConfig)\n const renamedLayers = await this.claimDirs(appConfig, discoveryRoot)\n this.collectLocales(appConfig)\n this.collectLayerRoots(appConfig)\n this.collectApps(appConfig, renamedLayers)\n }\n\n toConfig(discoveryRoot: string, projectConfig: I18nConfig['projectConfig'], locales: LocaleDefinition[]): I18nConfig {\n const merged = Object.keys(this.policy).length > 0\n ? { ...this.policy, ...projectConfig } as I18nConfig['projectConfig']\n : projectConfig\n\n return {\n rootDir: discoveryRoot,\n defaultLocale: this.defaultLocale,\n fallbackLocale: this.fallbackLocale,\n locales,\n localeDirs: this.localeDirs,\n layerRootDirs: this.layerRootDirs,\n projectConfig: merged,\n apps: this.apps,\n }\n }\n\n /**\n * Claim each of this app's locale directories, renaming a layer whose name is\n * already taken by a different directory. Returns the renames, because the\n * app's own layer list still refers to the old names.\n */\n private async claimDirs(appConfig: I18nConfig, discoveryRoot: string): Promise<Map<string, string>> {\n const renamed = new Map<string, string>()\n\n for (const dir of appConfig.localeDirs) {\n const realPath = await realpath(dir.path).catch(() => dir.path)\n\n // An already-claimed path keeps the name its first claimant gave it;\n // claimLocaleDir records this app as an alias of that layer.\n let claim = dir\n if (!this.claimedPaths.has(realPath)) {\n const layer = this.uniqueLayerName(dir, discoveryRoot)\n if (layer !== dir.layer) renamed.set(dir.layer, layer)\n this.usedLayerNames.add(layer)\n claim = { ...dir, layer }\n }\n\n claimLocaleDir(this.localeDirs, this.claimedPaths, claim, realPath)\n }\n\n return renamed\n }\n\n private uniqueLayerName(dir: LocaleDir, discoveryRoot: string): string {\n if (!this.usedLayerNames.has(dir.layer)) return dir.layer\n return deriveLayerName(dir.layerRootDir, discoveryRoot, this.usedLayerNames)\n }\n\n private collectPolicy(appConfig: I18nConfig): void {\n for (const [key, value] of Object.entries(appConfig.projectConfig ?? {})) {\n if (value !== undefined && this.policy[key] === undefined) this.policy[key] = value\n }\n }\n\n private collectLocales(appConfig: I18nConfig): void {\n for (const locale of appConfig.locales) {\n if (this.seenLocaleCodes.has(locale.code)) continue\n this.seenLocaleCodes.add(locale.code)\n this.locales.push(locale)\n }\n }\n\n private collectLayerRoots(appConfig: I18nConfig): void {\n for (const rootDir of appConfig.layerRootDirs) {\n if (!this.layerRootDirs.includes(rootDir)) this.layerRootDirs.push(rootDir)\n }\n }\n\n private collectApps(appConfig: I18nConfig, renamedLayers: Map<string, string>): void {\n for (const app of appConfig.apps) {\n this.apps.push({ ...app, layers: app.layers.map(name => renamedLayers.get(name) ?? name) })\n }\n }\n}\n","import { existsSync } from 'node:fs'\nimport { readdir, readFile, realpath } from 'node:fs/promises'\nimport { resolve, relative } from 'node:path'\nimport type { FrameworkAdapter, LocaleFileFormat } from '../types'\nimport type { I18nConfig, LocaleDefinition, LocaleDir, AppInfo } from '../../config/types'\nimport { findNuxtConfig, discoverNuxtApps, deriveLayerName } from '../../config/discovery'\nimport { loadKit } from '../../config/nuxt-loader'\nimport { loadProjectConfig } from '../../config/project-config'\nimport { applyLocaleOverride } from '../../config/locale-override'\nimport { normalizeFallbackLocale } from '../../config/fallback-locale'\nimport { artifactToConfig, readArtifact } from '../../config/artifact'\nimport { log } from '../../utils/logger'\nimport { ConfigError, toErrorMessage } from '../../utils/errors'\nimport { claimLocaleDir } from './layer-dedup'\nimport { AppMerger } from './merge-apps'\n\nexport class NuxtAdapter implements FrameworkAdapter {\n readonly name = 'nuxt'\n readonly label = 'Nuxt'\n readonly localeFileFormat: LocaleFileFormat = 'json'\n\n async detect(projectDir: string): Promise<number> {\n const configFile = findNuxtConfig(projectDir)\n if (configFile) {\n try {\n const content = await readFile(resolve(projectDir, configFile), 'utf-8')\n if (/\\bi18n\\b/.test(content)) return 2\n }\n catch {\n // Fall through to child app scan\n }\n\n // Root config exists but has no i18n — check child apps\n const appDirs = await discoverNuxtApps(projectDir)\n return appDirs.length > 0 ? 2 : 1\n }\n\n const appDirs = await discoverNuxtApps(projectDir)\n return appDirs.length > 0 ? 2 : 0\n }\n\n async resolve(projectDir: string): Promise<I18nConfig> {\n const appDirs = await discoverNuxtApps(projectDir)\n\n if (findNuxtConfig(projectDir) && !appDirs.includes(projectDir)) {\n appDirs.unshift(projectDir)\n }\n\n if (appDirs.length === 0) {\n throw new ConfigError(\n `No Nuxt apps with i18n configuration found under ${projectDir}. `\n + 'Make sure your Nuxt apps have a nuxt.config.ts with i18n configured.',\n )\n }\n\n const [soleAppDir] = appDirs\n if (appDirs.length === 1 && soleAppDir !== undefined) {\n const config = await loadApp(soleAppDir, projectDir)\n if (soleAppDir !== projectDir) {\n config.rootDir = projectDir\n const rootApp = config.apps[0]\n if (rootApp) {\n rootApp.rootDir = projectDir\n }\n }\n log.info(`Detected ${config.locales.length} locales, ${config.localeDirs.length} locale directories`)\n return config\n }\n\n log.info(`Discovered ${appDirs.length} Nuxt app(s) with i18n: ${appDirs.map(d => relative(projectDir, d) || '.').join(', ')}`)\n const config = await loadAndMergeApps(appDirs, projectDir)\n log.info(`Detected ${config.locales.length} locales, ${config.localeDirs.length} locale directories from ${appDirs.length} app(s)`)\n return config\n }\n}\n\n/**\n * One app's config, preferring what @the-i18n-kit/nuxt published from inside\n * the build over reconstructing it from outside. The artifact is additive: any\n * reason not to trust it falls through to loading the app exactly as before,\n * so a project without the module — or with a stale one — behaves as it always\n * has.\n */\nasync function loadApp(appDir: string, discoveryRoot: string): Promise<I18nConfig> {\n const artifact = await readArtifact(appDir)\n if (!artifact) return loadSingleApp(appDir, discoveryRoot)\n\n const config = await artifactToConfig(artifact, appDir, discoveryRoot, await loadProjectConfig(discoveryRoot))\n\n // An artifact describing no locale directory leaves nothing to read, which\n // the fallback path reports as a ConfigError naming what is missing. Silently\n // returning an empty config instead would make installing the module turn a\n // clear error into no output at all.\n if (config.localeDirs.length === 0) {\n log.warn(`The artifact for ${appDir} describes no locale directories — loading the app instead.`)\n return loadSingleApp(appDir, discoveryRoot)\n }\n\n return config\n}\n\nasync function loadSingleApp(appDir: string, discoveryRoot: string): Promise<I18nConfig> {\n const kit = await loadKit(appDir)\n\n let nuxt: Awaited<ReturnType<typeof kit.loadNuxt>>\n\n try {\n nuxt = await kit.loadNuxt({\n cwd: appDir,\n dotenv: { cwd: appDir },\n overrides: {\n logLevel: 'silent' as const,\n vite: { clearScreen: false },\n },\n })\n }\n catch (_error) {\n log.warn(`Initial loadNuxt failed for ${appDir}, retrying with ready:false...`)\n try {\n nuxt = await kit.loadNuxt({\n cwd: appDir,\n dotenv: { cwd: appDir },\n ready: false,\n overrides: {\n logLevel: 'silent' as const,\n vite: { clearScreen: false },\n },\n })\n }\n catch (retryError) {\n throw new ConfigError(\n `Failed to load Nuxt config from ${appDir}: ${retryError instanceof Error ? retryError.message : String(retryError)}`,\n )\n }\n }\n\n try {\n return await extractI18nConfig(nuxt as unknown as { options: Record<string, unknown> }, appDir, discoveryRoot)\n }\n finally {\n await nuxt.close()\n }\n}\n\nasync function loadAndMergeApps(appDirs: string[], discoveryRoot: string): Promise<I18nConfig> {\n const projectConfig = await loadProjectConfig(discoveryRoot)\n const merger = new AppMerger()\n\n for (const appDir of appDirs) {\n log.info(`Loading Nuxt app: ${relative(discoveryRoot, appDir) || '.'}`)\n\n let appConfig: I18nConfig\n try {\n appConfig = await loadApp(appDir, discoveryRoot)\n }\n catch (error) {\n // One unloadable app must not take the others with it: a monorepo where\n // a single app fails to build still has translations worth managing.\n log.warn(`Failed to load app at ${appDir}: ${toErrorMessage(error)}`)\n continue\n }\n\n await merger.add(appConfig, discoveryRoot)\n }\n\n if (merger.localeDirs.length === 0) {\n throw new ConfigError(\n `No locale directories found in any Nuxt app under ${discoveryRoot}. `\n + 'Make sure your Nuxt apps have i18n/locales/ directories with JSON files.',\n )\n }\n\n return merger.toConfig(\n discoveryRoot,\n projectConfig ?? undefined,\n applyLocaleOverride(merger.locales, projectConfig?.locales),\n )\n}\n\nasync function extractI18nConfig(\n nuxt: { options: Record<string, unknown> },\n appDir: string,\n discoveryRoot: string,\n): Promise<I18nConfig> {\n const projectConfig = await loadProjectConfig(discoveryRoot)\n\n const nuxtOptions = nuxt.options as Record<string, unknown>\n const i18nOptions = nuxtOptions.i18n as Record<string, unknown> | undefined\n const layers = (nuxtOptions._layers ?? []) as Array<{\n config: {\n rootDir: string\n i18n?: Record<string, unknown>\n }\n }>\n\n if (!i18nOptions) {\n throw new ConfigError(\n `No i18n configuration found in nuxt.config at ${appDir}. Make sure @nuxtjs/i18n is configured.`,\n )\n }\n\n const defaultLocale = (i18nOptions.defaultLocale as string) ?? 'en'\n log.debug(`Default locale: ${defaultLocale}`)\n\n const rawLocales = (i18nOptions.locales ?? []) as Array<Record<string, unknown>>\n const locales: LocaleDefinition[] = rawLocales\n .filter(l => typeof l === 'object' && l !== null)\n .map(l => ({\n code: String(l.code ?? ''),\n language: String(l.language ?? l.iso ?? ''),\n file: String(l.file ?? ''),\n name: l.name ? String(l.name) : undefined,\n }))\n .filter(l => l.code && l.file)\n\n if (locales.length === 0) {\n throw new ConfigError(\n 'No locales found in i18n configuration. Make sure locales are defined with code and file properties.',\n )\n }\n\n const fallbackLocale = normalizeFallbackLocale(i18nOptions.fallbackLocale, defaultLocale)\n const localeDirs = await discoverLocaleDirs(layers, i18nOptions, discoveryRoot)\n\n const layerRootDirs = [...new Set(layers.map(l => l.config.rootDir))]\n if (layerRootDirs.length === 0) {\n layerRootDirs.push(appDir)\n }\n\n const usedLayerNamesForApp = new Set<string>()\n const appLayerNames = layers.map(l => deriveLayerName(l.config.rootDir, discoveryRoot, usedLayerNamesForApp))\n const appName = deriveLayerName(appDir, discoveryRoot, new Set())\n const appInfo: AppInfo = { name: appName, rootDir: appDir, layers: appLayerNames }\n\n return {\n rootDir: appDir,\n defaultLocale,\n fallbackLocale,\n locales: applyLocaleOverride(locales, projectConfig?.locales),\n localeDirs,\n layerRootDirs,\n projectConfig: projectConfig ?? undefined,\n apps: [appInfo],\n }\n}\n\nasync function discoverLocaleDirs(\n layers: Array<{ config: { rootDir: string; i18n?: Record<string, unknown> } }>,\n i18nOptions: Record<string, unknown>,\n discoveryRoot: string,\n): Promise<LocaleDir[]> {\n const dirs: LocaleDir[] = []\n const resolvedPaths = new Map<string, { layer: string, layerRootDir: string }>()\n const usedLayerNames = new Set<string>()\n\n for (const layer of layers) {\n const layerRootDir = layer.config.rootDir\n const layerName = deriveLayerName(layerRootDir, discoveryRoot, usedLayerNames)\n usedLayerNames.add(layerName)\n const layerI18n = layer.config.i18n ?? i18nOptions\n\n const langDir = (layerI18n.langDir as string) ?? 'locales'\n const restructureDir = (layerI18n.restructureDir as string) ?? 'i18n'\n const resolvedDir = resolve(layerRootDir, restructureDir, langDir)\n\n if (!existsSync(resolvedDir)) {\n log.debug(`Locale dir not found for layer '${layerName}': ${resolvedDir}`)\n continue\n }\n\n const realDir = await realpath(resolvedDir).catch(() => resolvedDir)\n\n if (!resolvedPaths.has(realDir)) {\n const files = await readdir(resolvedDir)\n const jsonFiles = files.filter(f => f.endsWith('.json'))\n if (jsonFiles.length === 0) {\n log.debug(`No JSON files in locale dir for layer '${layerName}': ${resolvedDir}`)\n continue\n }\n log.debug(`Found locale dir for layer '${layerName}': ${resolvedDir} (${jsonFiles.length} files)`)\n }\n\n claimLocaleDir(dirs, resolvedPaths, { path: resolvedDir, layer: layerName, layerRootDir }, realDir)\n }\n\n if (dirs.length === 0) {\n throw new ConfigError(\n 'No locale directories found. Make sure your Nuxt layers have i18n/locales/ directories with JSON files.',\n )\n }\n\n return dirs\n}\n","import { existsSync, readdirSync } from 'node:fs'\nimport { readdir, readFile } from 'node:fs/promises'\nimport { join } from 'node:path'\nimport type { FrameworkAdapter, LocaleFileFormat } from '../types'\nimport type { I18nConfig, LocaleDefinition, LocaleDir } from '../../config/types'\nimport { loadProjectConfig } from '../../config/project-config'\nimport { applyLocaleOverride } from '../../config/locale-override'\nimport { log } from '../../utils/logger'\nimport { ConfigError } from '../../utils/errors'\n\nexport class LaravelAdapter implements FrameworkAdapter {\n readonly name = 'laravel'\n readonly label = 'Laravel'\n readonly localeFileFormat: LocaleFileFormat = 'php-array'\n\n async detect(projectDir: string): Promise<number> {\n let score = 0\n\n if (existsSync(join(projectDir, 'artisan'))) {\n score += 2\n }\n\n try {\n const raw = await readFile(join(projectDir, 'composer.json'), 'utf-8')\n const composer = JSON.parse(raw) as Record<string, unknown>\n const require = composer.require as Record<string, unknown> | undefined\n if (require && 'laravel/framework' in require) {\n score += 2\n }\n }\n catch {\n // composer.json missing or malformed — skip\n }\n\n // Weak signal: lang/ with PHP subdirectories or JSON files\n const langDir = findLangDir(projectDir)\n if (langDir) {\n const hasLocaleFiles = await hasLocaleContent(langDir)\n if (hasLocaleFiles) score += 1\n }\n\n return score\n }\n\n async resolve(projectDir: string): Promise<I18nConfig> {\n const projectConfig = await loadProjectConfig(projectDir)\n\n const langDir = findLangDir(projectDir)\n if (!langDir) {\n throw new ConfigError(\n `No lang/ or resources/lang/ directory found in ${projectDir}. `\n + 'Make sure your Laravel project has a locale directory.',\n )\n }\n\n const format = resolveFormat(langDir, projectConfig?.localeFileFormat)\n\n const localeCodes = await discoverLocaleCodes(langDir, format)\n if (localeCodes.length === 0) {\n throw new ConfigError(\n getFormatError(langDir, format),\n )\n }\n\n const { defaultLocale, fallbackLocale, configLocales } = await extractLocaleConfig(projectDir)\n\n const allCodes = mergeLocaleCodes(localeCodes, configLocales)\n\n const locales: LocaleDefinition[] = applyLocaleOverride(\n allCodes.map(code => ({\n code,\n language: code,\n ...(format === 'json' ? { file: `${code}.json` } : {}),\n })),\n projectConfig?.locales,\n )\n\n const localeDirs: LocaleDir[] = [{\n path: langDir,\n layer: 'root',\n layerRootDir: projectDir,\n }]\n\n log.info(\n `Discovered ${locales.length} locale(s) in ${langDir} (${format}): `\n + `${allCodes.join(', ')}`,\n )\n\n return {\n rootDir: projectDir,\n defaultLocale,\n fallbackLocale,\n locales,\n localeDirs,\n layerRootDirs: [projectDir],\n projectConfig: projectConfig ?? undefined,\n localeFileFormat: format,\n apps: [{ name: 'root', rootDir: projectDir, layers: ['root'] }],\n }\n }\n}\n\n// ─── Format detection ───────────────────────────────────────────\n\nfunction resolveFormat(\n langDir: string,\n override?: LocaleFileFormat,\n): LocaleFileFormat {\n if (override) return override\n\n const hasJson = existsSync(join(langDir, 'en.json'))\n || existsSync(join(langDir, 'de.json'))\n || existsSync(join(langDir, 'fr.json'))\n const hasPhp = hasPhpSubdirsSync(langDir)\n\n // When both exist, prefer php-array (backward compatible)\n if (hasPhp) return 'php-array'\n if (hasJson) return 'json'\n\n return 'php-array' // default\n}\n\nfunction hasPhpSubdirsSync(langDir: string): boolean {\n try {\n const entries = readdirSync(langDir, { withFileTypes: true })\n for (const entry of entries) {\n if (!entry.isDirectory() || entry.name === 'vendor') continue\n const subEntries = readdirSync(join(langDir, entry.name))\n if (subEntries.some(f => f.endsWith('.php'))) return true\n }\n }\n catch { /* can't read */ }\n return false\n}\n\nfunction getFormatError(langDir: string, format: LocaleFileFormat): string {\n if (format === 'json') {\n return `No JSON locale files found in ${langDir}. Expected files like lang/en.json, lang/de.json.`\n }\n return `No locale subdirectories found in ${langDir}. Expected directories like lang/en/, lang/de/.`\n}\n\n// ─── Locale discovery ───────────────────────────────────────────\n\nasync function hasLocaleContent(langDir: string): Promise<boolean> {\n // Check for JSON files (flat layout)\n const entries = await readdir(langDir).catch(() => [] as string[])\n if (entries.some(f => f.endsWith('.json'))) return true\n\n // Check for PHP subdirectories (namespaced layout)\n return hasPhpContent(langDir)\n}\n\nasync function discoverLocaleCodes(\n langDir: string,\n format: LocaleFileFormat,\n): Promise<string[]> {\n if (format === 'json') {\n return discoverJsonLocales(langDir)\n }\n return findLocaleSubdirs(langDir)\n}\n\nasync function discoverJsonLocales(langDir: string): Promise<string[]> {\n const entries = await readdir(langDir).catch(() => [] as string[])\n return entries\n .filter(f => f.endsWith('.json'))\n .map(f => f.replace(/\\.json$/, ''))\n .sort()\n}\n\nasync function hasPhpContent(langDir: string): Promise<boolean> {\n const subdirs = await findLocaleSubdirs(langDir)\n return subdirs.length > 0\n}\n\n/**\n * Laravel 9+ uses root-level `lang/`, older versions use `resources/lang/`.\n */\nfunction findLangDir(projectDir: string): string | null {\n const candidates = [\n join(projectDir, 'lang'),\n join(projectDir, 'resources', 'lang'),\n ]\n for (const candidate of candidates) {\n if (existsSync(candidate)) {\n return candidate\n }\n }\n return null\n}\n\nasync function findLocaleSubdirs(langDir: string): Promise<string[]> {\n let entries: import('node:fs').Dirent[]\n try {\n entries = await readdir(langDir, { withFileTypes: true })\n }\n catch {\n return []\n }\n\n const locales: string[] = []\n for (const entry of entries) {\n if (!entry.isDirectory() || entry.name === 'vendor') continue\n const files = await readdir(join(langDir, entry.name)).catch(() => [] as string[])\n if (files.some(f => f.endsWith('.php'))) {\n locales.push(entry.name)\n }\n }\n\n return locales.sort()\n}\n\n// ─── Config parsing ─────────────────────────────────────────────\n\n/**\n * Extract default and fallback locale from config/app.php.\n */\nasync function extractLocaleConfig(projectDir: string): Promise<{\n defaultLocale: string\n fallbackLocale: Record<string, string[]>\n configLocales: string[]\n}> {\n const configPath = join(projectDir, 'config', 'app.php')\n\n let content: string\n try {\n content = await readFile(configPath, 'utf-8')\n }\n catch {\n log.debug('No config/app.php found, using defaults')\n return {\n defaultLocale: 'en',\n fallbackLocale: { default: ['en'] },\n configLocales: [],\n }\n }\n\n const defaultLocale = extractPhpConfigValue(content, 'locale') ?? 'en'\n const fallbackValue = extractPhpConfigValue(content, 'fallback_locale') ?? defaultLocale\n const configLocales = extractPhpArrayValue(content, 'locales')\n\n log.debug(`Extracted locale config: locale=${defaultLocale}, fallback=${fallbackValue}, config locales=${configLocales.length}`)\n\n return {\n defaultLocale,\n fallbackLocale: { default: [fallbackValue] },\n configLocales,\n }\n}\n\nfunction extractPhpConfigValue(content: string, key: string): string | null {\n const envPattern = new RegExp(\n `['\"]${key}['\"]\\\\s*=>\\\\s*env\\\\s*\\\\(\\\\s*['\"][^'\"]*['\"]\\\\s*,\\\\s*['\"]([^'\"]+)['\"]\\\\s*\\\\)`,\n )\n const envMatch = content.match(envPattern)\n if (envMatch) {\n return envMatch[1] ?? null\n }\n\n const directPattern = new RegExp(\n `['\"]${key}['\"]\\\\s*=>\\\\s*['\"]([^'\"]+)['\"]`,\n )\n const directMatch = content.match(directPattern)\n if (directMatch) {\n return directMatch[1] ?? null\n }\n\n return null\n}\n\nfunction extractPhpArrayValue(content: string, key: string): string[] {\n const pattern = new RegExp(\n `['\"]${key}['\"]\\\\s*=>\\\\s*\\\\[([^\\\\]]*)]`,\n )\n const match = content.match(pattern)\n const arrayBody = match?.[1]\n if (arrayBody === undefined) return []\n\n const itemPattern = /['\"]([^'\"]+)['\"]/g\n const items: string[] = []\n let itemMatch: RegExpExecArray | null\n while ((itemMatch = itemPattern.exec(arrayBody)) !== null) {\n const item = itemMatch[1]\n if (item) items.push(item)\n }\n return items\n}\n\nfunction mergeLocaleCodes(filesystemCodes: string[], configCodes: string[]): string[] {\n const seen = new Set(filesystemCodes)\n const merged = [...filesystemCodes]\n for (const code of configCodes) {\n if (!seen.has(code)) {\n seen.add(code)\n merged.push(code)\n }\n }\n return merged.sort()\n}\n","import { readdir } from 'node:fs/promises'\nimport { join, resolve } from 'node:path'\nimport { existsSync } from 'node:fs'\nimport type { FrameworkAdapter, LocaleFileFormat } from '../types'\nimport type { I18nConfig, LocaleDefinition, LocaleDir } from '../../config/types'\nimport { loadProjectConfig } from '../../config/project-config'\nimport { log } from '../../utils/logger'\nimport { ConfigError } from '../../utils/errors'\n\nexport class GenericAdapter implements FrameworkAdapter {\n readonly name = 'generic'\n readonly label = 'Generic'\n readonly localeFileFormat: LocaleFileFormat = 'json'\n\n private cachedConfig: { projectDir: string; config: import('../../config/types').ProjectConfig | null } | null = null\n\n private async getProjectConfig(projectDir: string) {\n if (this.cachedConfig?.projectDir === projectDir) {\n return this.cachedConfig.config\n }\n const config = await loadProjectConfig(projectDir)\n this.cachedConfig = { projectDir, config }\n return config\n }\n\n async detect(projectDir: string): Promise<number> {\n const config = await this.getProjectConfig(projectDir)\n if (!config) return 0\n if (config.localeDirs && config.localeDirs.length > 0 && config.defaultLocale) {\n return 10\n }\n return 0\n }\n\n async resolve(projectDir: string): Promise<I18nConfig> {\n const projectConfig = await this.getProjectConfig(projectDir)\n if (!projectConfig?.localeDirs || projectConfig.localeDirs.length === 0 || !projectConfig.defaultLocale) {\n throw new ConfigError(\n 'GenericAdapter requires both \"localeDirs\" and \"defaultLocale\" in .i18n-mcp.json',\n )\n }\n\n const localeDirs: LocaleDir[] = projectConfig.localeDirs.map((entry) => {\n if (typeof entry === 'string') {\n return {\n path: resolve(projectDir, entry),\n layer: 'default',\n layerRootDir: projectDir,\n }\n }\n return {\n path: resolve(projectDir, entry.path),\n layer: entry.layer,\n layerRootDir: projectDir,\n }\n })\n\n for (const dir of localeDirs) {\n if (!existsSync(dir.path)) {\n throw new ConfigError(`Locale directory does not exist: ${dir.path}`)\n }\n }\n\n // localeDirs mirrors projectConfig.localeDirs, whose emptiness is guarded above\n const detectedFormat = await detectFileFormat(localeDirs[0]!.path)\n const discoveredLocales = projectConfig.locales ?? await discoverLocales(localeDirs, detectedFormat)\n\n if (discoveredLocales.length === 0) {\n throw new ConfigError(\n `No locale files found in ${localeDirs.map(d => d.path).join(', ')}`,\n )\n }\n\n const locales: LocaleDefinition[] = await Promise.all(discoveredLocales.map(async code => ({\n code,\n language: code,\n // `file` names the flat file to read and write. A namespaced PHP layout\n // (Laravel's lang/<locale>/<namespace>.php) has no single file, and\n // resolveLocaleEntries discovers those from disk instead.\n ...(await flatFileFor(localeDirs, code, detectedFormat)),\n })))\n\n log.info(\n `Generic adapter: ${locales.length} locale(s), format=${detectedFormat}, `\n + `dirs=${localeDirs.map(d => d.layer).join(', ')}`,\n )\n\n return {\n rootDir: projectDir,\n defaultLocale: projectConfig.defaultLocale,\n fallbackLocale: { default: [projectConfig.defaultLocale] },\n locales,\n localeDirs,\n layerRootDirs: [projectDir],\n projectConfig,\n localeFileFormat: detectedFormat,\n apps: [{ name: 'default', rootDir: projectDir, layers: localeDirs.map(d => d.layer) }],\n }\n }\n}\n\n/**\n * The flat file for a locale, when there is one. Absent for a namespaced\n * layout, where each namespace is its own file and no single name applies.\n */\nasync function flatFileFor(\n localeDirs: Array<{ path: string }>,\n code: string,\n format: LocaleFileFormat,\n): Promise<{ file?: string }> {\n const ext = format === 'php-array' ? '.php' : '.json'\n for (const dir of localeDirs) {\n if (existsSync(join(dir.path, `${code}${ext}`))) return { file: `${code}${ext}` }\n }\n return {}\n}\n\nasync function detectFileFormat(localeDir: string): Promise<LocaleFileFormat> {\n let entries: import('node:fs').Dirent[]\n try {\n entries = await readdir(localeDir, { withFileTypes: true })\n }\n catch {\n return 'json'\n }\n\n // Flat files: en.json, de.json — or en.php, de.php for a PHP project that\n // does not use Laravel's directory-per-locale layout.\n if (entries.some(e => e.isFile() && e.name.endsWith('.json'))) {\n return 'json'\n }\n if (entries.some(e => e.isFile() && e.name.endsWith('.php'))) {\n return 'php-array'\n }\n\n // Directory-per-locale: en/, de/ — check contents\n for (const entry of entries) {\n if (!entry.isDirectory()) continue\n const subFiles = await readdir(join(localeDir, entry.name)).catch(() => [] as string[])\n if (subFiles.some(f => f.endsWith('.php'))) return 'php-array'\n if (subFiles.some(f => f.endsWith('.json'))) return 'json'\n }\n\n return 'json'\n}\n\nconst NON_LOCALE_NAMES = new Set([\n 'index', 'readme', 'config', 'vendor', 'node_modules', '.git', '.DS_Store',\n])\n\nasync function discoverLocales(localeDirs: LocaleDir[], format: LocaleFileFormat): Promise<string[]> {\n const codes = new Set<string>()\n\n for (const dir of localeDirs) {\n let entries: import('node:fs').Dirent[]\n try {\n entries = await readdir(dir.path, { withFileTypes: true })\n }\n catch {\n continue\n }\n\n for (const entry of entries) {\n if (entry.name.startsWith('.')) continue\n\n const flatExt = format === 'php-array' ? '.php' : '.json'\n if (entry.isFile() && entry.name.endsWith(flatExt)) {\n const code = entry.name.slice(0, -flatExt.length)\n if (!NON_LOCALE_NAMES.has(code.toLowerCase())) {\n codes.add(code)\n }\n }\n else if (entry.isDirectory() && !NON_LOCALE_NAMES.has(entry.name.toLowerCase())) {\n const subFiles = await readdir(join(dir.path, entry.name)).catch(() => [] as string[])\n const ext = format === 'php-array' ? '.php' : '.json'\n if (subFiles.some(f => f.endsWith(ext))) {\n codes.add(entry.name)\n }\n }\n }\n }\n\n return [...codes].sort()\n}\n","/**\n * Executing config files that belong to *other* tools.\n *\n * Where a project already declares its locales authoritatively — next-intl's\n * `defineRouting`, next-translate's `i18n.js`, `next.config`'s `i18n` block —\n * reading it beats guessing from directory order. The only reason the adapters\n * ever guessed is that the CLI could not run TypeScript.\n *\n * The rule for everything in here: **never load-bearing**. A real\n * `next.config.js` is wrapped in `withNextIntl(...)` or `withSentryConfig(...)`\n * and throws without the right env or plugins installed. A file we cannot run\n * means falling back to the heuristics that were the only behaviour until now,\n * with a warning — never a failed command. This is the rule `config/artifact.ts`\n * already follows for the Nuxt artifact, for the same reason.\n */\nimport { existsSync } from 'node:fs'\nimport { resolve } from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport { toErrorMessage } from '../../utils/errors.js'\nimport { log } from '../../utils/logger.js'\n\n/**\n * Run one config file and hand back its exports, or null if anything at all\n * went wrong. Callers treat null as \"no information\", not as an error.\n */\nexport async function executeConfig(\n path: string,\n options: { alias?: Record<string, string> } = {},\n): Promise<Record<string, unknown> | null> {\n try {\n const { createJiti } = await import('jiti')\n const jiti = createJiti(import.meta.url, {\n interopDefault: false,\n ...(options.alias ? { alias: options.alias } : {}),\n })\n const module = await jiti.import(path)\n return module && typeof module === 'object' ? module as Record<string, unknown> : null\n }\n catch (error) {\n log.warn(\n `Could not read ${path}: ${toErrorMessage(error)}. `\n + 'Falling back to directory detection — declare the value in i18n-kit.config.ts to be sure of it.',\n )\n return null\n }\n}\n\n/**\n * The first of `candidates` that exists under `projectDir`, as an absolute\n * path.\n *\n * Absolute, not merely joined: `--projectDir playground/react` is a relative\n * path, and a relative path handed to jiti is read as a bare module specifier\n * and resolved against `node_modules`, where it is never found.\n */\nexport function firstExisting(projectDir: string, candidates: readonly string[]): string | null {\n for (const candidate of candidates) {\n const path = resolve(projectDir, candidate)\n if (existsSync(path)) return path\n }\n return null\n}\n\n/**\n * Locate one of the stub modules in `./stubs/`, as a path jiti can be aliased\n * to. Returns null when it cannot be found, which leaves normal resolution in\n * place.\n *\n * A stub has to survive the build as a *file* — it is referred to by path and\n * never imported, so it is declared as its own tsdown entry rather than\n * bundled. That gives it two possible homes, and this resolves against the\n * location of this module, which sits beside `stubs/` in the source tree and\n * at the root of `dist/` in the bundle.\n */\nexport function resolveStub(name: string): string | null {\n const candidates = [`./stubs/${name}.ts`, `./config/framework/stubs/${name}.js`]\n\n for (const candidate of candidates) {\n const path = fileURLToPath(new URL(candidate, import.meta.url))\n if (existsSync(path)) return path\n }\n return null\n}\n\n/** A module's default export, or the module itself for a CJS `module.exports`. */\nexport function defaultExport(module: Record<string, unknown>): unknown {\n return 'default' in module ? module.default : module\n}\n\n/** Narrow an unknown to a plain object, the shape every reader here digs into. */\nexport function asObject(value: unknown): Record<string, unknown> | null {\n return value !== null && typeof value === 'object' && !Array.isArray(value)\n ? value as Record<string, unknown>\n : null\n}\n\n/** A non-empty string, or undefined — config values are not to be trusted. */\nexport function asString(value: unknown): string | undefined {\n return typeof value === 'string' && value.length > 0 ? value : undefined\n}\n\n/** An array of non-empty strings, or undefined. */\nexport function asStringArray(value: unknown): string[] | undefined {\n if (!Array.isArray(value)) return undefined\n const strings = value.filter((v): v is string => typeof v === 'string' && v.length > 0)\n return strings.length > 0 ? strings : undefined\n}\n","/**\n * Reading where a Vue project keeps its locale files, from the place it\n * already says so: `@intlify/unplugin-vue-i18n`'s `include`.\n *\n * The adapter's alternative is six candidate directories and a regex over\n * `src/i18n/index.ts` looking for `messages:` — which finds nothing at all if\n * the project keeps its locales anywhere else.\n */\nimport { existsSync, statSync } from 'node:fs'\nimport { dirname, isAbsolute, resolve } from 'node:path'\nimport { asObject, executeConfig, firstExisting, resolveStub } from './execute.js'\nimport { log } from '../../utils/logger.js'\n\n/**\n * Where the stub leaves what it recorded. Spelled out rather than imported\n * from the stub: importing it would fold the stub into this bundle chunk, and\n * it has to survive the build as a standalone file to be aliased to by path.\n * A registered symbol is the same symbol in both, which is the point.\n */\nconst RECORDED = Symbol.for('the-i18n-kit:unplugin-vue-i18n-options')\n\nconst VITE_CONFIG_FILES = [\n 'vite.config.ts',\n 'vite.config.js',\n 'vite.config.mts',\n 'vite.config.mjs',\n]\n\n/** Every specifier the plugin is imported under, all pointing at the stub. */\nconst PLUGIN_SPECIFIERS = [\n '@intlify/unplugin-vue-i18n/vite',\n '@intlify/unplugin-vue-i18n',\n '@intlify/unplugin-vue-i18n/rollup',\n '@intlify/unplugin-vue-i18n/webpack',\n]\n\n/**\n * The directories `include` points at, or null when the config declares none,\n * cannot be read, or names paths that do not exist.\n *\n * Never throws: a `vite.config.ts` needs Vite and every plugin it imports to\n * be installed, and a project where that fails must keep working exactly as it\n * did before this could be read at all.\n */\nexport async function readVueI18nLocaleDirs(projectDir: string): Promise<string[] | null> {\n const path = firstExisting(projectDir, VITE_CONFIG_FILES)\n if (!path) return null\n\n // Answers are remembered per config file because the file itself can only be\n // executed once: Node caches an ES module by URL for the life of the\n // process, so a second read would import the cache, call no plugin, record\n // nothing, and quietly report that this project declares no locale\n // directories. Resolving the same project twice has to give the same answer.\n const remembered = answers.get(path)\n if (remembered !== undefined) return remembered\n\n const dirs = await readInclude(path)\n answers.set(path, dirs)\n return dirs\n}\n\nconst answers = new Map<string, string[] | null>()\n\nasync function readInclude(path: string): Promise<string[] | null> {\n const globalScope = globalThis as Record<symbol, unknown>\n delete globalScope[RECORDED]\n\n const alias = stubAlias()\n if (!alias) return null\n\n const module = await executeConfig(path, { alias })\n if (!module) return null\n\n // `defineConfig(({ mode }) => ({ ... }))` is as common as the object form,\n // and the plugin is only constructed once the function runs.\n const exported = 'default' in module ? module.default : module\n if (typeof exported === 'function') {\n try {\n await (exported as (env: unknown) => unknown)({ command: 'build', mode: 'production', isSsrBuild: false })\n }\n catch (error) {\n log.warn(`Could not evaluate the config function in ${path}: ${error instanceof Error ? error.message : String(error)}`)\n }\n }\n\n const options = asObject(globalScope[RECORDED])\n delete globalScope[RECORDED]\n if (!options) return null\n\n const dirs = includeToDirs(options.include, dirname(path))\n if (dirs.length === 0) return null\n\n log.info(`Read locale directories from ${path}: ${dirs.join(', ')}`)\n return dirs\n}\n\n/**\n * Turn `include` — one path or many, each usually a glob — into the existing\n * directories they live in. `resolve(__dirname, './src/locales/**')` is the\n * shape the plugin's own docs use.\n */\nfunction includeToDirs(include: unknown, configDir: string): string[] {\n const patterns = (Array.isArray(include) ? include : [include])\n .filter((v): v is string => typeof v === 'string' && v.length > 0)\n\n const dirs: string[] = []\n for (const pattern of patterns) {\n const dir = globRoot(pattern)\n if (!dir) continue\n\n const full = isAbsolute(dir) ? dir : resolve(configDir, dir)\n if (!dirs.includes(full) && isDirectory(full)) dirs.push(full)\n }\n return dirs\n}\n\n/** Everything before the first glob segment: `src/locales/**\\/*.json` → `src/locales`. */\nfunction globRoot(pattern: string): string | undefined {\n // `resolve()` in a vite.config produces backslashes on Windows, and a\n // backslash path split on '/' is one segment containing the wildcard —\n // which would discard the directory entirely.\n const segments = pattern.replace(/\\\\/g, '/').split('/')\n const wildcard = segments.findIndex(s => s.includes('*') || s.includes('?') || s.includes('['))\n const kept = wildcard === -1 ? segments.slice(0, -1) : segments.slice(0, wildcard)\n return kept.length > 0 ? kept.join('/') : undefined\n}\n\nfunction isDirectory(path: string): boolean {\n try {\n return existsSync(path) && statSync(path).isDirectory()\n }\n catch {\n return false\n }\n}\n\n/**\n * Map every specifier the plugin is imported under onto the stub that records\n * its options. Returns null if the stub cannot be located, which leaves the\n * real plugin in place — and the real plugin tells us nothing, so the caller\n * gives up rather than executing a config for no reason.\n */\nfunction stubAlias(): Record<string, string> | null {\n const path = resolveStub('unplugin-vue-i18n')\n if (!path) return null\n return Object.fromEntries(PLUGIN_SPECIFIERS.map(specifier => [specifier, path]))\n}\n","/**\n * Helpers shared by the single-locale-dir adapters (react, vue).\n */\n\nimport { existsSync } from 'node:fs'\nimport { readFile } from 'node:fs/promises'\nimport { join } from 'node:path'\nimport type { I18nConfig, LocaleDefinition, ProjectConfig } from '../config/types'\nimport { ConfigError } from '../utils/errors'\n\nconst NUXT_CONFIG_FILES = ['nuxt.config.ts', 'nuxt.config.js', 'nuxt.config.mjs']\n\nexport async function loadPackageJson(projectDir: string): Promise<Record<string, unknown> | null> {\n try {\n const raw = await readFile(join(projectDir, 'package.json'), 'utf-8')\n return JSON.parse(raw) as Record<string, unknown>\n }\n catch {\n return null\n }\n}\n\nexport function collectDependencies(pkg: Record<string, unknown>): Record<string, unknown> {\n return {\n ...(pkg.dependencies ?? {}) as Record<string, unknown>,\n ...(pkg.devDependencies ?? {}) as Record<string, unknown>,\n }\n}\n\nexport function hasNuxtConfig(dir: string): boolean {\n return NUXT_CONFIG_FILES.some(f => existsSync(join(dir, f)))\n}\n\nexport function noLocaleDirError(projectDir: string, searchedDirs: string[]): ConfigError {\n return new ConfigError(\n `No locale directory found in ${projectDir}. `\n + 'Looked in: ' + searchedDirs.join(', ') + '. '\n + 'Configure a .i18n-mcp.json with localeDirs for custom paths.',\n )\n}\n\n/** Build the I18nConfig for a project with a single locale directory ('root' layer). */\nexport function buildSingleDirConfig(opts: {\n projectDir: string\n localeDir: string\n defaultLocale: string\n locales: LocaleDefinition[]\n projectConfig: ProjectConfig | null\n}): I18nConfig {\n return {\n rootDir: opts.projectDir,\n defaultLocale: opts.defaultLocale,\n fallbackLocale: { default: [opts.defaultLocale] },\n locales: opts.locales,\n localeDirs: [{ path: opts.localeDir, layer: 'root', layerRootDir: opts.projectDir }],\n layerRootDirs: [opts.projectDir],\n projectConfig: opts.projectConfig ?? undefined,\n apps: [{ name: 'default', rootDir: opts.projectDir, layers: ['root'] }],\n }\n}\n","import { existsSync } from 'node:fs'\nimport { readdir, readFile } from 'node:fs/promises'\nimport { basename, dirname, join, resolve } from 'node:path'\nimport type { FrameworkAdapter, LocaleFileFormat } from '../types'\nimport type { I18nConfig, LocaleDefinition } from '../../config/types'\nimport { loadProjectConfig } from '../../config/project-config'\nimport { readVueI18nLocaleDirs } from '../../config/framework/vue'\nimport { applyLocaleOverride } from '../../config/locale-override'\nimport { ConfigError } from '../../utils/errors'\nimport { loadPackageJson, collectDependencies, hasNuxtConfig, noLocaleDirError, buildSingleDirConfig } from '../shared'\n\nconst COMMON_LOCALE_DIRS = [\n 'src/locales',\n 'locales',\n 'src/i18n/locales',\n 'i18n/locales',\n 'src/plugins/i18n/locales',\n 'src/i18n',\n]\n\nconst I18N_CONFIG_FILES = [\n 'src/i18n/index.ts',\n 'src/i18n/index.js',\n 'src/plugins/i18n.ts',\n 'src/plugins/i18n.js',\n 'src/i18n.ts',\n 'src/i18n.js',\n 'i18n.ts',\n 'i18n.js',\n]\n\nconst NUPT_INDICATORS = ['@nuxt/kit', 'nuxt']\n\nconst MESSAGES_REGEX = /messages\\s*:\\s*['\"]([^'\"]+)/\nconst LOCALE_DIR_REGEX = /localeDir\\s*:\\s*['\"]([^'\"]+)/\n\nexport class VueAdapter implements FrameworkAdapter {\n readonly name = 'vue'\n readonly label = 'Vue'\n readonly localeFileFormat: LocaleFileFormat = 'json'\n\n async detect(projectDir: string): Promise<number> {\n const pkg = await loadPackageJson(projectDir)\n if (!pkg) return 0\n\n const allDeps = collectDependencies(pkg)\n if (isNuxtProject(projectDir, allDeps)) return 0\n if (!('vue' in allDeps)) return 0\n\n return computeScore(projectDir, allDeps)\n }\n\n async resolve(projectDir: string): Promise<I18nConfig> {\n const projectConfig = await loadProjectConfig(projectDir)\n\n const dirs = await resolveLocaleDirs(projectDir)\n const discovered = await requireLocales(dirs)\n const locales = applyLocaleOverride(discovered, projectConfig?.locales)\n\n // Declared beats discovered. Falling through to the first locale is\n // alphabetical order dressed up as a decision (#296).\n const defaultLocale = projectConfig?.defaultLocale ?? locales[0]?.code ?? discovered[0].code\n\n // One directory keeps the shape it has always had, layer name and all.\n if (dirs.length === 1) {\n return buildSingleDirConfig({ projectDir, localeDir: dirs[0]!, defaultLocale, locales, projectConfig })\n }\n\n const localeDirs = dirs.map(path => ({ path, layer: layerNameFor(path, dirs), layerRootDir: projectDir }))\n\n return {\n rootDir: projectDir,\n defaultLocale,\n fallbackLocale: { default: [defaultLocale] },\n locales,\n localeDirs,\n layerRootDirs: [projectDir],\n ...(projectConfig ? { projectConfig } : {}),\n apps: [{ name: 'default', rootDir: projectDir, layers: localeDirs.map(d => d.layer) }],\n }\n }\n}\n\n// ─── Detection helpers ──────────────────────────────────────────\n\nfunction isNuxtProject(projectDir: string, deps: Record<string, unknown>): boolean {\n for (const indicator of NUPT_INDICATORS) {\n if (indicator in deps) return true\n }\n return hasNuxtConfig(projectDir)\n}\n\nasync function computeScore(projectDir: string, deps: Record<string, unknown>): Promise<number> {\n let score = 2 // 'vue' already confirmed present\n if ('vue-i18n' in deps) score += 3\n\n try {\n const localeDir = await findLocaleDir(projectDir)\n if (localeDir) {\n const files = await readdir(localeDir)\n if (files.some(f => f.endsWith('.json'))) score += 2\n }\n }\n catch {\n // Can't read dirs — skip bonus\n }\n\n return score\n}\n\n// ─── Resolution helpers ─────────────────────────────────────────\n\n/**\n * Where this project keeps its locale files: what the build already knows,\n * ahead of what the source looks like it means. `include` *is* where the\n * locale files are, by definition; the candidate list is a guess about where\n * people tend to put them.\n *\n * All of `include` is kept, not just the first entry — it is an array because\n * projects do split their messages across directories, and dropping the rest\n * would silently hide every key in them.\n */\nasync function resolveLocaleDirs(projectDir: string): Promise<string[]> {\n const fromPlugin = await readVueI18nLocaleDirs(projectDir)\n if (fromPlugin?.length) return fromPlugin\n\n const localeDir = await findLocaleDir(projectDir)\n if (!localeDir) throw noLocaleDirError(projectDir, COMMON_LOCALE_DIRS)\n return [localeDir]\n}\n\n/**\n * A layer name per directory: its own name, or enough of its path to tell it\n * apart from a sibling of the same name (`messages` and `admin/messages`).\n */\nfunction layerNameFor(path: string, all: string[]): string {\n const name = basename(path)\n const ambiguous = all.filter(other => basename(other) === name).length > 1\n return ambiguous ? `${basename(dirname(path))}/${name}` : name\n}\n\n/** The locales across every directory, or an explanation of why there are none. */\nasync function requireLocales(localeDirs: string[]): Promise<[LocaleDefinition, ...LocaleDefinition[]]> {\n const byCode = new Map<string, LocaleDefinition>()\n for (const dir of localeDirs) {\n for (const locale of await discoverLocales(dir)) {\n // First directory to define a locale owns its file name; the rest add\n // their keys to it, the way a layer does.\n if (!byCode.has(locale.code)) byCode.set(locale.code, locale)\n }\n }\n\n const locales = [...byCode.values()]\n const [first] = locales\n if (first === undefined) {\n throw new ConfigError(\n `No JSON locale files found in ${localeDirs.join(', ')}. `\n + 'Make sure your Vue i18n project has locale files like en.json, de.json etc.',\n )\n }\n return [first, ...locales.slice(1)]\n}\n\n/**\n * Detection-safe: pattern-matching only. `detect()` runs this for every\n * project the CLI is pointed at, including ones this adapter will lose, so it\n * must not execute anybody's config — that is `resolve()`'s business, once an\n * adapter has been chosen.\n */\nasync function findLocaleDir(projectDir: string): Promise<string | null> {\n const fromConfig = await tryExtractFromConfig(projectDir)\n if (fromConfig) return fromConfig\n\n return tryCommonPaths(projectDir)\n}\n\nfunction firstMatchGroup(content: string, regex: RegExp): string | undefined {\n return content.match(regex)?.[1]\n}\n\nasync function tryExtractFromConfig(projectDir: string): Promise<string | null> {\n for (const file of I18N_CONFIG_FILES) {\n const fullPath = join(projectDir, file)\n if (!existsSync(fullPath)) continue\n\n try {\n const content = await readFile(fullPath, 'utf-8')\n\n const dirMatch = firstMatchGroup(content, LOCALE_DIR_REGEX)\n if (dirMatch) {\n const extracted = resolve(projectDir, dirMatch)\n if (existsSync(extracted)) return extracted\n }\n\n const msgMatch = firstMatchGroup(content, MESSAGES_REGEX)\n if (msgMatch) {\n const configDir = fullPath.replace(/\\/[^/]+$/, '')\n const extracted = resolve(configDir, msgMatch)\n const parentDir = extracted.replace(/\\/[^/]+$/, '')\n if (existsSync(parentDir)) return parentDir\n }\n }\n catch {\n // Can't read — try next\n }\n }\n\n return null\n}\n\nfunction tryCommonPaths(projectDir: string): string | null {\n for (const dir of COMMON_LOCALE_DIRS) {\n const fullPath = join(projectDir, dir)\n if (existsSync(fullPath)) return fullPath\n }\n return null\n}\n\nasync function discoverLocales(localeDir: string): Promise<LocaleDefinition[]> {\n let files: string[]\n try {\n files = await readdir(localeDir)\n }\n catch {\n return []\n }\n\n return files\n .filter(f => f.endsWith('.json'))\n .sort()\n .map(f => ({\n code: f.replace(/\\.json$/, ''),\n language: f.replace(/\\.json$/, ''),\n file: f,\n }))\n}\n","/**\n * Reading what a Next.js project already says about its locales.\n *\n * Three setups declare it authoritatively, and the adapter used to read none\n * of them — it took the alphabetically first file in `messages/` and called\n * that the default locale, so `{de,en,fr}.json` made German the source of\n * truth for an English project (#296).\n */\nimport { createRequire } from 'node:module'\nimport { asObject, asString, asStringArray, defaultExport, executeConfig, firstExisting, resolveStub } from './execute.js'\nimport { toErrorMessage } from '../../utils/errors.js'\nimport { log } from '../../utils/logger.js'\n\n/** next-intl: `defineRouting({ locales, defaultLocale })`, usually re-exported as `routing`. */\nconst ROUTING_FILES = [\n 'src/i18n/routing.ts',\n 'src/i18n/routing.js',\n 'i18n/routing.ts',\n 'i18n/routing.js',\n]\n\n/** next-translate: a top-level `i18n.js` consumed by `nextTranslate(...)`. */\nconst NEXT_TRANSLATE_FILES = [\n 'i18n.js',\n 'i18n.ts',\n 'i18n.mjs',\n]\n\n/** Pages Router: `i18n: { locales, defaultLocale }` in the Next config itself. */\nconst NEXT_CONFIG_FILES = [\n 'next.config.ts',\n 'next.config.js',\n 'next.config.mjs',\n]\n\nexport interface NextI18n {\n defaultLocale?: string\n locales?: string[]\n /** Which file this came from, for the message that explains the choice. */\n source: string\n}\n\n/**\n * The first source that yields something, in order of how specifically it is\n * about i18n: a routing file exists only to declare locales, whereas\n * `next.config` is mostly about everything else and the likeliest to throw.\n *\n * Returns null when no source could be read — every failure is a warning and a\n * fallback to directory detection, never an error.\n */\nexport async function readNextI18n(projectDir: string): Promise<NextI18n | null> {\n const readers = [readRouting, readNextTranslate, readNextConfig]\n\n for (const reader of readers) {\n const found = await reader(projectDir)\n if (found?.defaultLocale || found?.locales) {\n log.info(`Read i18n from ${found.source}: defaultLocale=${found.defaultLocale ?? '(none)'}`)\n return found\n }\n }\n\n return null\n}\n\n/**\n * next-intl. `defineRouting` returns the object it was handed, so the exported\n * routing object carries `locales` and `defaultLocale` verbatim — which is why\n * substituting it (only when it is missing; see `routingAlias`) cannot change\n * the answer.\n */\nasync function readRouting(projectDir: string): Promise<NextI18n | null> {\n const path = firstExisting(projectDir, ROUTING_FILES)\n if (!path) return null\n\n const module = await executeConfig(path, { alias: routingAlias(path) })\n if (!module) return null\n\n // `export const routing = defineRouting(...)` is what the docs show, but a\n // default export is just as valid, and both may be present.\n for (const candidate of [module.routing, defaultExport(module)]) {\n const routing = asObject(candidate)\n if (!routing) continue\n\n const defaultLocale = asString(routing.defaultLocale)\n const locales = asStringArray(routing.locales)\n if (defaultLocale || locales) {\n return { ...(defaultLocale ? { defaultLocale } : {}), ...(locales ? { locales } : {}), source: path }\n }\n }\n\n return null\n}\n\n/**\n * Substitute `defineRouting` only when the real one cannot be found from the\n * routing file's own location.\n *\n * The real package always wins where it is installed — it is the authority on\n * its own API, and a substitution that silently shadowed it would be the kind\n * of quiet divergence this whole line of work is about. The stub is purely a\n * rescue for the case where the file exists and its dependencies do not, which\n * is a fresh clone, a CI job that skipped install, or a fixture. Reading a\n * declaration should not require a `node_modules` to be present.\n */\nfunction routingAlias(routingPath: string): Record<string, string> | undefined {\n if (resolvableFrom('next-intl/routing', routingPath)) return undefined\n\n const path = resolveStub('next-intl-routing')\n if (!path) return undefined\n\n log.debug(`next-intl is not installed for ${routingPath} — reading it with a stubbed defineRouting`)\n return { 'next-intl/routing': path }\n}\n\n/**\n * `next.config.js` may export `(phase, { defaultConfig }) => config`, sync or\n * async, which Next.js calls for you. An uncalled function is not a config, so\n * without this the `i18n` block of every project using that form is invisible.\n *\n * A function that throws yields nothing, like any other unreadable config.\n */\nasync function resolveConfigExport(exported: unknown, path: string): Promise<unknown> {\n if (typeof exported !== 'function') return exported\n\n try {\n return await (exported as (phase: string, context: unknown) => unknown)(\n 'phase-production-build',\n { defaultConfig: {} },\n )\n }\n catch (error) {\n log.warn(`Could not evaluate the config function in ${path}: ${toErrorMessage(error)}`)\n return undefined\n }\n}\n\nfunction resolvableFrom(specifier: string, fromFile: string): boolean {\n try {\n createRequire(fromFile).resolve(specifier)\n return true\n }\n catch {\n return false\n }\n}\n\n/** next-translate's `i18n.js`, a plain object of `{ locales, defaultLocale, pages }`. */\nasync function readNextTranslate(projectDir: string): Promise<NextI18n | null> {\n const path = firstExisting(projectDir, NEXT_TRANSLATE_FILES)\n if (!path) return null\n\n const module = await executeConfig(path)\n if (!module) return null\n\n const config = asObject(defaultExport(module))\n if (!config) return null\n\n const defaultLocale = asString(config.defaultLocale)\n const locales = asStringArray(config.locales)\n if (!defaultLocale && !locales) return null\n\n return { ...(defaultLocale ? { defaultLocale } : {}), ...(locales ? { locales } : {}), source: path }\n}\n\n/**\n * The Pages Router's `i18n` block. Read last because this is the file most\n * likely to throw: in a real project it is wrapped in `withNextIntl(...)`,\n * `withSentryConfig(...)` and whatever else, and needs their env to run.\n */\nasync function readNextConfig(projectDir: string): Promise<NextI18n | null> {\n const path = firstExisting(projectDir, NEXT_CONFIG_FILES)\n if (!path) return null\n\n const module = await executeConfig(path)\n if (!module) return null\n\n const config = asObject(await resolveConfigExport(defaultExport(module), path))\n const i18n = config && asObject(config.i18n)\n if (!i18n) return null\n\n const defaultLocale = asString(i18n.defaultLocale)\n const locales = asStringArray(i18n.locales)\n if (!defaultLocale && !locales) return null\n\n return { ...(defaultLocale ? { defaultLocale } : {}), ...(locales ? { locales } : {}), source: path }\n}\n","import { existsSync, statSync, readdirSync } from 'node:fs'\nimport { readdir, readFile } from 'node:fs/promises'\nimport { join } from 'node:path'\nimport type { FrameworkAdapter, LocaleFileFormat } from '../types'\nimport type { I18nConfig, LocaleDefinition } from '../../config/types'\nimport { loadProjectConfig } from '../../config/project-config'\nimport { readNextI18n } from '../../config/framework/next'\nimport { applyLocaleOverride } from '../../config/locale-override'\nimport { ConfigError } from '../../utils/errors'\nimport { loadPackageJson, collectDependencies, hasNuxtConfig, noLocaleDirError, buildSingleDirConfig } from '../shared'\n\nconst COMMON_LOCALE_DIRS = [\n 'messages',\n 'public/locales',\n 'locales',\n 'src/i18n',\n 'src/locales',\n 'i18n',\n]\n\nconst NEXT_CONFIG_FILES = ['next.config.ts', 'next.config.js', 'next.config.mjs']\n\nconst NUXT_INDICATORS = ['@nuxt/kit', 'nuxt']\nconst VUE_INDICATORS = ['vue']\n\nconst I18N_DEPS = ['next-intl', 'next-translate', 'next-i18next', 'react-i18next', 'react-intl']\n\nconst SKIP_DIRS = new Set(['node_modules', '.git', '.DS_Store'])\n\nexport class ReactAdapter implements FrameworkAdapter {\n readonly name = 'react'\n readonly label = 'React / Next.js'\n readonly localeFileFormat: LocaleFileFormat = 'json'\n\n async detect(projectDir: string): Promise<number> {\n const pkg = await loadPackageJson(projectDir)\n if (!pkg) return 0\n\n const allDeps = collectDependencies(pkg)\n if (isConflictingFramework(projectDir, allDeps)) return 0\n\n const isNext = 'next' in allDeps\n const isReact = 'react' in allDeps && 'react-dom' in allDeps\n\n if (!isNext && !isReact) return 0\n\n return computeScore(projectDir, allDeps, isNext)\n }\n\n async resolve(projectDir: string): Promise<I18nConfig> {\n const projectConfig = await loadProjectConfig(projectDir)\n\n const localeDir = await findLocaleDir(projectDir)\n if (!localeDir) {\n throw noLocaleDirError(projectDir, COMMON_LOCALE_DIRS)\n }\n\n const locales = await discoverLocales(localeDir)\n const [firstLocale] = locales\n if (firstLocale === undefined) {\n throw new ConfigError(\n `No locale files found in ${localeDir}. `\n + 'Make sure your React/Next.js project has locale directories like messages/en/ or locale JSON files.',\n )\n }\n\n // Declared first, then what the project already tells next-intl or Next\n // itself, and only then directory order — which is not a statement about\n // the default locale at all, merely the alphabet (#296).\n const framework = await readNextI18n(projectDir)\n const defaultLocale = projectConfig?.defaultLocale ?? framework?.defaultLocale ?? firstLocale.code\n\n return buildSingleDirConfig({\n projectDir,\n localeDir,\n defaultLocale,\n locales: applyLocaleOverride(locales, projectConfig?.locales),\n projectConfig,\n })\n }\n}\n\n// ─── Detection helpers ──────────────────────────────────────────\n\nfunction isConflictingFramework(projectDir: string, deps: Record<string, unknown>): boolean {\n for (const indicator of NUXT_INDICATORS) {\n if (indicator in deps) return true\n }\n for (const indicator of VUE_INDICATORS) {\n if (indicator in deps) return true\n }\n return hasNuxtConfig(projectDir)\n}\n\nfunction hasNextConfig(projectDir: string): boolean {\n return NEXT_CONFIG_FILES.some(f => existsSync(join(projectDir, f)))\n}\n\nasync function computeScore(\n projectDir: string,\n deps: Record<string, unknown>,\n isNext: boolean,\n): Promise<number> {\n let score = isNext ? 5 : 3\n\n if (I18N_DEPS.some(dep => dep in deps)) score += 2\n\n if (isNext && hasNextConfig(projectDir)) score += 1\n\n try {\n const localeDir = findLocaleDirSync(projectDir)\n if (localeDir) {\n const entries = readdirSync(localeDir)\n if (entries.some(e => isLocaleContent(join(localeDir, e)))) score += 2\n }\n }\n catch {\n // Can't read dirs — skip bonus\n }\n\n return score\n}\n\n// ─── Resolution helpers ─────────────────────────────────────────\n\nasync function findLocaleDir(projectDir: string): Promise<string | null> {\n const fromConfig = await tryNextConfig(projectDir)\n if (fromConfig) return fromConfig\n\n return findLocaleDirSync(projectDir)\n}\n\nfunction findLocaleDirSync(projectDir: string): string | null {\n for (const dir of COMMON_LOCALE_DIRS) {\n const fullPath = join(projectDir, dir)\n if (existsSync(fullPath)) return fullPath\n }\n return null\n}\n\nfunction isLocaleContent(path: string): boolean {\n if (path.endsWith('.json')) return true\n return isLocaleSubdir(path)\n}\n\nfunction isLocaleSubdir(path: string): boolean {\n if (!existsSync(path)) return false\n try {\n if (!statSync(path).isDirectory()) return false\n const files = readdirSync(path)\n return files.some(f => f.endsWith('.json'))\n }\n catch {\n return false\n }\n}\n\nasync function tryNextConfig(projectDir: string): Promise<string | null> {\n for (const file of NEXT_CONFIG_FILES) {\n const fullPath = join(projectDir, file)\n if (!existsSync(fullPath)) continue\n\n const result = await tryExtractFromNextConfig(projectDir, fullPath)\n if (result) return result\n }\n\n return null\n}\n\nasync function tryExtractFromNextConfig(\n projectDir: string,\n configPath: string,\n): Promise<string | null> {\n try {\n const content = await readFile(configPath, 'utf-8')\n\n if (/createNextIntlPlugin/.test(content) && existsSync(join(projectDir, 'messages'))) {\n return join(projectDir, 'messages')\n }\n\n if (/next-translate/.test(content) && existsSync(join(projectDir, 'locales'))) {\n return join(projectDir, 'locales')\n }\n\n return tryExtractPathFromConfig(projectDir, content)\n }\n catch {\n return null\n }\n}\n\nfunction tryExtractPathFromConfig(projectDir: string, content: string): string | null {\n const pathMatch = content.match(/(?:messages|localeDir|locales)\\s*:\\s*['\"]([^'\"]+)/)\n const rawPath = pathMatch?.[1]\n if (!rawPath) return null\n\n const path = rawPath.replace(/\\/\\*\\/\\*$/, '').replace(/\\/\\*$/, '')\n return existsSync(join(projectDir, path)) ? join(projectDir, path) : null\n}\n\nasync function discoverLocales(localeDir: string): Promise<LocaleDefinition[]> {\n let entries: string[]\n try {\n entries = await readdir(localeDir)\n }\n catch {\n return []\n }\n\n const localeCodes = new Set<string>()\n\n // Namespaced layout: messages/en/common.json\n for (const entry of entries) {\n if (SKIP_DIRS.has(entry) || entry.startsWith('.')) continue\n const fullPath = join(localeDir, entry)\n if (isLocaleSubdir(fullPath)) localeCodes.add(entry)\n }\n\n if (localeCodes.size > 0) {\n return [...localeCodes].sort().map(code => ({\n code,\n language: code,\n }))\n }\n\n // Flat JSON layout: locales/en.json — `file` is required here, otherwise\n // resolveLocaleEntries cannot resolve flat entries and every read/write\n // becomes a silent no-op (#194).\n return entries\n .filter(entry => entry.endsWith('.json'))\n .sort()\n .map(file => ({\n code: file.replace(/\\.json$/, ''),\n language: file.replace(/\\.json$/, ''),\n file,\n }))\n}\n","import type { I18nConfig } from './types'\nimport { registerAdapter, detectFramework } from '../adapters/registry'\nimport { NuxtAdapter } from '../adapters/nuxt/index'\nimport { LaravelAdapter } from '../adapters/laravel/index'\nimport { GenericAdapter } from '../adapters/generic/index'\nimport { VueAdapter } from '../adapters/vue/index'\nimport { ReactAdapter } from '../adapters/react/index'\nimport { loadProjectConfig } from './project-config'\nimport { log } from '../utils/logger'\nimport { canonicalPath } from './discovery'\n\nexport { discoverNuxtApps } from './discovery'\n\nregisterAdapter(new NuxtAdapter())\nregisterAdapter(new LaravelAdapter())\nregisterAdapter(new GenericAdapter())\nregisterAdapter(new VueAdapter())\nregisterAdapter(new ReactAdapter())\n\nconst configCache = new Map<string, I18nConfig>()\n\nlet lastConfig: I18nConfig | null = null\n\nexport async function detectI18nConfig(projectDir: string): Promise<I18nConfig> {\n const canonDir = canonicalPath(projectDir)\n const cached = configCache.get(canonDir)\n if (cached) {\n log.debug('Using cached i18n config')\n return cached\n }\n\n log.info(`Detecting i18n config from: ${projectDir}`)\n\n const projectConfig = await loadProjectConfig(projectDir)\n const hint = projectConfig?.framework\n\n const adapter = await detectFramework(projectDir, hint)\n log.info(`Detected framework: ${adapter.label}`)\n\n const config = await adapter.resolve(projectDir)\n config.framework = adapter.name\n configCache.set(canonDir, config)\n lastConfig = config\n\n log.info(`Detected ${config.locales.length} locales, ${config.localeDirs.length} locale directories`)\n return config\n}\n\nexport function clearConfigCache(): void {\n configCache.clear()\n lastConfig = null\n log.debug('Config cache cleared')\n}\n\nexport function getCachedConfig(): I18nConfig | null {\n return lastConfig\n}\n","import type { I18nConfig, LocaleDir } from './types'\n\n/**\n * Queryable view over the layer topology a resolved {@link I18nConfig}\n * already carries: which locale dirs are canonical (alias-free), which\n * layer owns an aliased dir, and which apps consume which layers.\n *\n * This is a pure derivation — no filesystem access, no config-shape\n * changes. Cross-layer tooling (duplicate detection, scope-aware orphan\n * scanning) builds on these queries instead of name-matching heuristics.\n *\n * ## Degenerate-case semantics\n *\n * These are load-bearing for consumers (scope-aware scanning must never\n * wrongly narrow its scan scope):\n *\n * - **No app info** (`config.apps` empty or absent, e.g. hand-built\n * configs): consumption edges are unknowable. `appsUsingLayer` and\n * `layersOfApp` return `[]`, and `sharedLayers` conservatively contains\n * *every* canonical layer — with no ownership information, every\n * layer's keys must be treated as globally visible.\n * - **Single-app config** (generic/Laravel/Vue/React adapters, or a Nuxt\n * project with one app): the strict definition applies, so\n * `sharedLayers` is empty (no layer is consumed by more than one app)\n * and `appsUsingLayer` returns that one app for the layers it consumes.\n * Per-layer scope then equals the whole project, which is correct.\n * - **Canonical layer consumed by no app** (in a multi-app config):\n * `appsUsingLayer` returns `[]` and the layer is not in `sharedLayers`.\n * Callers should treat such layers conservatively (global scope).\n */\nexport interface LayerGraph {\n /**\n * Alias-free locale dirs, in `config.localeDirs` order. Aliased entries\n * (e.g. `app-outlook` pointing at `app-shop`'s dir) are excluded.\n */\n canonicalLayers: LocaleDir[]\n /**\n * Resolve a layer name to the canonical layer that owns its locale dir.\n * Follows chained `aliasOf` links (an alias may point at a layer that\n * was itself demoted to an alias). Identity for canonical names and for\n * names unknown to `localeDirs` (e.g. layers without locale dirs).\n */\n ownerOf: (layer: string) => string\n /**\n * Names of apps whose consumed layers include the given layer. The\n * queried name and each app's layer list are alias-resolved via\n * {@link ownerOf} first, so querying an alias name yields the owner's\n * consumers. Returns `[]` when no app info exists.\n */\n appsUsingLayer: (layer: string) => string[]\n /**\n * Canonical layers consumed by more than one app — e.g. a shared root\n * layer in a multi-app monorepo, identified purely from consumption\n * edges (no name matching). When no app info exists, contains every\n * canonical layer (see degenerate-case semantics above).\n */\n sharedLayers: LocaleDir[]\n /**\n * Canonical layers the given app consumes (alias entries in the app's\n * layer list resolve to their owners; layers without locale dirs are\n * omitted). Returns `[]` for unknown apps or when no app info exists.\n */\n layersOfApp: (app: string) => LocaleDir[]\n}\n\n/**\n * Build the alias resolver: follows chained `aliasOf` links to the owning\n * canonical layer. Identity for canonical and unknown names; guarded\n * against (malformed) alias cycles.\n */\nfunction buildOwnerResolver(localeDirs: LocaleDir[]): (layer: string) => string {\n // alias name -> immediate target (owner or another alias)\n const aliasTargets = new Map<string, string>()\n for (const dir of localeDirs) {\n if (dir.aliasOf && dir.aliasOf !== dir.layer) {\n aliasTargets.set(dir.layer, dir.aliasOf)\n }\n }\n\n return (layer: string): string => {\n let current = layer\n const seen = new Set<string>()\n while (aliasTargets.has(current) && !seen.has(current)) {\n seen.add(current)\n current = aliasTargets.get(current)!\n }\n return current\n }\n}\n\ninterface ConsumptionEdges {\n /** canonical layer name -> names of apps consuming it */\n consumers: Map<string, Set<string>>\n /** app name -> canonical layer names it consumes (locale-dir-backed only) */\n consumed: Map<string, Set<string>>\n}\n\n/**\n * Build app → layer consumption edges, alias-resolving every layer name in\n * each app's layer list. `consumers` keeps edges for layers without locale\n * dirs too (so `appsUsingLayer` can answer for them); `consumed` is\n * restricted to canonical, locale-dir-backed layers.\n */\nfunction buildConsumptionEdges(\n apps: I18nConfig['apps'],\n ownerOf: (layer: string) => string,\n canonicalNames: Set<string>,\n): ConsumptionEdges {\n const consumers = new Map<string, Set<string>>()\n const consumed = new Map<string, Set<string>>()\n\n for (const app of apps) {\n const consumedByApp = consumed.get(app.name) ?? new Set<string>()\n consumed.set(app.name, consumedByApp)\n for (const layerName of app.layers) {\n const owner = ownerOf(layerName)\n const layerConsumers = consumers.get(owner) ?? new Set<string>()\n consumers.set(owner, layerConsumers)\n layerConsumers.add(app.name)\n if (canonicalNames.has(owner)) {\n consumedByApp.add(owner)\n }\n }\n }\n\n return { consumers, consumed }\n}\n\n/**\n * Build a {@link LayerGraph} from a resolved config's `localeDirs`\n * (with their `aliasOf` markers) and `apps` (app → consumed-layers edges).\n */\nexport function buildLayerGraph(config: I18nConfig): LayerGraph {\n const localeDirs = config.localeDirs ?? []\n const canonicalLayers = localeDirs.filter(d => !d.aliasOf)\n const canonicalNames = new Set(canonicalLayers.map(d => d.layer))\n const ownerOf = buildOwnerResolver(localeDirs)\n\n const apps = config.apps ?? []\n const { consumers, consumed } = buildConsumptionEdges(apps, ownerOf, canonicalNames)\n\n const sharedLayers = apps.length > 0\n ? canonicalLayers.filter(d => (consumers.get(d.layer)?.size ?? 0) > 1)\n : [...canonicalLayers]\n\n const appsUsingLayer = (layer: string): string[] =>\n [...(consumers.get(ownerOf(layer)) ?? [])]\n\n const layersOfApp = (app: string): LocaleDir[] => {\n const names = consumed.get(app)\n if (!names || names.size === 0) return []\n return canonicalLayers.filter(d => names.has(d.layer))\n }\n\n return { canonicalLayers, ownerOf, appsUsingLayer, sharedLayers, layersOfApp }\n}\n","import { readFile, stat } from 'node:fs/promises'\nimport { existsSync } from 'node:fs'\nimport { FileIOError } from '../utils/errors'\nimport { toErrorMessage } from '../utils/errors'\n\n/**\n * Creates an mtime-based file read cache.\n * Files are re-read only when their modification timestamp changes.\n * Cached data is cloned via structuredClone to prevent mutation.\n *\n * @param parser - Function that parses raw file content into the desired type\n * @param label - Human-readable label for error messages (e.g., 'JSON', 'PHP')\n * @param validate - Optional post-parse validation that throws FileIOError on failure\n */\nexport function createReadCache<T>(\n parser: (content: string) => T,\n label: string,\n validate?: (parsed: T, filePath: string) => void,\n) {\n const cache = new Map<string, { data: T; mtime: number }>()\n\n async function read(filePath: string): Promise<T> {\n if (!existsSync(filePath)) {\n throw new FileIOError(`File not found: ${filePath}`, filePath, 'FILE_NOT_FOUND')\n }\n\n try {\n const fileStat = await stat(filePath)\n const mtime = fileStat.mtimeMs\n\n const cached = cache.get(filePath)\n if (cached && cached.mtime === mtime) {\n return structuredClone(cached.data)\n }\n\n const content = await readFile(filePath, 'utf-8')\n const parsed = parser(content)\n\n validate?.(parsed, filePath)\n\n cache.set(filePath, { data: structuredClone(parsed), mtime })\n return parsed\n } catch (error) {\n if (error instanceof FileIOError) throw error\n throw new FileIOError(\n `Failed to read ${label} file: ${filePath}: ${toErrorMessage(error)}`,\n filePath,\n )\n }\n }\n\n function clear(): void {\n cache.clear()\n }\n\n function clearEntry(filePath: string): void {\n cache.delete(filePath)\n }\n\n return { read, clear, clearEntry }\n}\n","import { existsSync } from 'node:fs'\nimport { readFile } from 'node:fs/promises'\nimport { FileIOError } from '../utils/errors'\nimport { toErrorMessage } from '../utils/errors'\nimport { createReadCache } from './read-cache'\n\nconst { read: readLocaleFile, clear: clearFileCache, clearEntry: clearFileCacheEntry } = createReadCache<Record<string, unknown>>(\n (content) => {\n try {\n return JSON.parse(content) as Record<string, unknown>\n } catch {\n throw new SyntaxError('Invalid JSON')\n }\n },\n 'JSON',\n (parsed, filePath) => {\n if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {\n throw new FileIOError(`JSON file must contain an object: ${filePath}`, filePath, 'INVALID_JSON')\n }\n },\n)\n\nexport { readLocaleFile, clearFileCache, clearFileCacheEntry }\n\n/**\n * Detect the indentation style used in a JSON file.\n * Scans the first few indented lines to find the smallest indent unit.\n * Returns the indent string (e.g., '\\t', ' ', ' ').\n */\nexport function detectIndentation(content: string): string {\n const lines = content.split('\\n', 10)\n let minSpaces = Infinity\n let usesTabs = false\n\n for (const line of lines) {\n const indent = line.match(/^(\\s+)\\S/)?.[1]\n if (!indent) continue\n\n if (indent.includes('\\t')) {\n usesTabs = true\n break\n }\n\n if (indent.length < minSpaces) {\n minSpaces = indent.length\n }\n }\n\n if (usesTabs) return '\\t'\n if (minSpaces === Infinity) return '\\t' // no indentation found\n return ' '.repeat(minSpaces)\n}\n\n/**\n * Read a locale file and also return the raw content for format detection.\n * This function does NOT use the cache — it always reads fresh data from disk,\n * as it returns raw content and indent info needed for writes.\n */\nexport async function readLocaleFileWithMeta(filePath: string): Promise<{\n data: Record<string, unknown>\n rawContent: string\n indent: string\n trailingNewline: boolean\n}> {\n if (!existsSync(filePath)) {\n throw new FileIOError(`File not found: ${filePath}`, filePath, 'FILE_NOT_FOUND')\n }\n\n try {\n const rawContent = await readFile(filePath, 'utf-8')\n const data = JSON.parse(rawContent) as Record<string, unknown>\n const indent = detectIndentation(rawContent)\n const trailingNewline = rawContent.endsWith('\\n')\n\n return { data, rawContent, indent, trailingNewline }\n } catch (error) {\n if (error instanceof FileIOError) throw error\n if (error instanceof SyntaxError) {\n throw new FileIOError(`Invalid JSON in file: ${filePath}`, filePath, 'INVALID_JSON')\n }\n throw new FileIOError(\n `Failed to read file: ${filePath}: ${toErrorMessage(error)}`,\n filePath,\n )\n }\n}\n","import sortKeys from 'sort-keys'\n\n/**\n * Get a value from a nested object using a dot-separated path.\n */\nexport function getNestedValue(obj: Record<string, unknown>, path: string): unknown {\n const parts = path.split('.')\n let current: unknown = obj\n for (const part of parts) {\n if (current === null || current === undefined || typeof current !== 'object') {\n return undefined\n }\n current = (current as Record<string, unknown>)[part]\n }\n return current\n}\n\n/**\n * Set a value in a nested object using a dot-separated path.\n * Creates intermediate objects as needed.\n */\nexport function setNestedValue(obj: Record<string, unknown>, path: string, value: unknown): void {\n const parts = path.split('.')\n const lastKey = parts.pop()\n if (lastKey === undefined) return\n let current: Record<string, unknown> = obj\n for (const part of parts) {\n if (!(part in current) || typeof current[part] !== 'object' || current[part] === null) {\n current[part] = {}\n }\n current = current[part] as Record<string, unknown>\n }\n current[lastKey] = value\n}\n\n/**\n * Remove a value from a nested object using a dot-separated path.\n * Cleans up empty parent objects after removal.\n * Returns true if the key was found and removed.\n */\nexport function removeNestedValue(obj: Record<string, unknown>, path: string): boolean {\n const parts = path.split('.')\n const lastKey = parts.pop()\n if (lastKey === undefined) return false\n const parents: Array<{ obj: Record<string, unknown>; key: string }> = []\n let current: Record<string, unknown> = obj\n\n for (const part of parts) {\n if (!(part in current) || typeof current[part] !== 'object' || current[part] === null) {\n return false\n }\n parents.push({ obj: current, key: part })\n current = current[part] as Record<string, unknown>\n }\n\n if (!(lastKey in current)) {\n return false\n }\n\n delete current[lastKey]\n\n for (const { obj: parentObj, key } of [...parents].reverse()) {\n const child = parentObj[key] as Record<string, unknown>\n if (Object.keys(child).length === 0) {\n delete parentObj[key]\n } else {\n break\n }\n }\n\n return true\n}\n\n/**\n * Check if a key exists in a nested object.\n */\nexport function hasNestedKey(obj: Record<string, unknown>, path: string): boolean {\n return getNestedValue(obj, path) !== undefined\n}\n\n/**\n * Get all leaf keys as dot-separated paths.\n * A leaf key is one whose value is not a plain object (i.e., it's a string, number, array, etc.).\n */\nexport function getLeafKeys(obj: Record<string, unknown>, prefix = '', depth = 0): string[] {\n if (depth > 200) return [] // guard against deeply nested objects\n const keys: string[] = []\n for (const key of Object.keys(obj)) {\n const fullPath = prefix ? `${prefix}.${key}` : key\n const value = obj[key]\n if (value !== null && typeof value === 'object' && !Array.isArray(value)) {\n keys.push(...getLeafKeys(value as Record<string, unknown>, fullPath, depth + 1))\n } else {\n keys.push(fullPath)\n }\n }\n return keys\n}\n\n/**\n * Sort keys alphabetically at every nesting level (deep).\n * Returns a new object — does not mutate the input.\n */\nexport const sortKeysDeep = (obj: Record<string, unknown>): Record<string, unknown> =>\n sortKeys(obj, { deep: true }) as Record<string, unknown>\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\n/**\n * Reorder `data` (deeply) against a `reference` object that carries the\n * authoritative key order (typically the on-disk state before a mutation):\n *\n * - Keys that also exist in `reference` keep the reference's relative order.\n * - Keys new to `data` are inserted in sorted position among their siblings\n * (after the last existing sibling that sorts before them), so additions\n * land alphabetically without re-sorting existing keys.\n * - Without a reference, all keys are sorted (equivalent to sortKeysDeep).\n *\n * Returns a new object — does not mutate the input.\n */\nexport function orderKeysPreserving(\n data: Record<string, unknown>,\n reference?: Record<string, unknown>,\n): Record<string, unknown> {\n const orderedKeys = reference ? Object.keys(reference).filter(k => k in data) : []\n const existing = new Set(orderedKeys)\n const newKeys = Object.keys(data).filter(k => !existing.has(k)).sort()\n\n for (const key of newKeys) {\n let insertAt = 0\n for (let i = orderedKeys.length - 1; i >= 0; i--) {\n const existingKey = orderedKeys[i]\n if (existingKey !== undefined && existingKey <= key) {\n insertAt = i + 1\n break\n }\n }\n orderedKeys.splice(insertAt, 0, key)\n }\n\n const result: Record<string, unknown> = {}\n for (const key of orderedKeys) {\n result[key] = orderValuePreserving(data[key], reference?.[key])\n }\n return result\n}\n\nfunction orderValuePreserving(value: unknown, refValue: unknown): unknown {\n if (Array.isArray(value)) {\n const refArray = Array.isArray(refValue) ? refValue : undefined\n return value.map((item, i) => orderValuePreserving(item, refArray?.[i]))\n }\n if (isPlainObject(value)) {\n return orderKeysPreserving(value, isPlainObject(refValue) ? refValue : undefined)\n }\n return value\n}\n\n/**\n * Rename a key in a nested object. Preserves the value.\n * Returns true if the old key was found and renamed.\n */\nexport function renameNestedKey(\n obj: Record<string, unknown>,\n oldPath: string,\n newPath: string,\n): boolean {\n const value = getNestedValue(obj, oldPath)\n if (value === undefined) {\n return false\n }\n removeNestedValue(obj, oldPath)\n setNestedValue(obj, newPath, value)\n return true\n}\n\n/**\n * Validate a translation value. Returns a warning message if problematic, null if OK.\n * This is soft validation — callers should warn but not block.\n */\nexport function validateTranslationValue(value: unknown): string | null {\n if (typeof value !== 'string') {\n return `Value must be a string, got ${typeof value}`\n }\n\n const openBraces = (value.match(/\\{/g) || []).length\n const closeBraces = (value.match(/\\}/g) || []).length\n if (openBraces !== closeBraces) {\n return `Unbalanced placeholder braces: ${openBraces} opening vs ${closeBraces} closing in \"${value}\"`\n }\n\n if (value.includes('@:') && /@:\\s*$/.test(value)) {\n return `Malformed linked reference: \"@:\" at end of string with no target`\n }\n\n return null\n}\n","import { writeFile, rename, mkdir, unlink } from 'node:fs/promises'\nimport { dirname, join } from 'node:path'\nimport { randomUUID } from 'node:crypto'\n\n/**\n * Atomically write content to a file.\n * Writes to a temp file first, then renames to the target path.\n * Cleans up the temp file on failure.\n *\n * @param filePath - Target file path\n * @param content - String content to write\n * @param onSuccess - Optional callback after successful rename (e.g., to clear a cache entry)\n */\nexport async function atomicWrite(\n filePath: string,\n content: string,\n onSuccess?: () => void,\n): Promise<void> {\n await mkdir(dirname(filePath), { recursive: true })\n const tmpPath = join(dirname(filePath), `.${randomUUID()}.tmp`)\n\n try {\n await writeFile(tmpPath, content, 'utf-8')\n await rename(tmpPath, filePath)\n onSuccess?.()\n } catch (error) {\n try {\n await unlink(tmpPath)\n } catch {\n // Ignore cleanup errors\n }\n throw error\n }\n}\n","import { FileIOError } from '../utils/errors'\nimport { toErrorMessage } from '../utils/errors'\nimport { sortKeysDeep, orderKeysPreserving } from './key-operations'\nimport { clearFileCacheEntry, readLocaleFileWithMeta } from './json-reader'\nimport { atomicWrite } from './atomic-write'\n\nexport interface WriteOptions {\n indent?: string\n trailingNewline?: boolean\n sortKeys?: boolean\n}\n\nexport async function writeLocaleFile(\n filePath: string,\n data: Record<string, unknown>,\n options: WriteOptions = {},\n): Promise<void> {\n const {\n indent = '\\t',\n trailingNewline = true,\n sortKeys = true,\n } = options\n\n try {\n const outputData = sortKeys ? sortKeysDeep(data) : data\n let content = JSON.stringify(outputData, null, indent)\n if (trailingNewline) {\n content += '\\n'\n }\n\n await atomicWrite(filePath, content, () => clearFileCacheEntry(filePath))\n } catch (error) {\n if (error instanceof FileIOError) throw error\n throw new FileIOError(\n `Failed to write file: ${filePath}: ${toErrorMessage(error)}`,\n filePath,\n )\n }\n}\n\nexport async function writeReportFile(\n filePath: string,\n output: Record<string, unknown>,\n meta: { tool: string; args: Record<string, unknown> },\n): Promise<void> {\n const report = {\n generatedAt: new Date().toISOString(),\n tool: meta.tool,\n args: meta.args,\n ...output,\n }\n\n const content = JSON.stringify(report, null, 2) + '\\n'\n\n try {\n await atomicWrite(filePath, content)\n } catch (error) {\n if (error instanceof FileIOError) throw error\n throw new FileIOError(\n `Failed to write report file: ${filePath}: ${toErrorMessage(error)}`,\n filePath,\n )\n }\n}\n\n/**\n * Write a GitLab Code Quality report: a bare JSON array — the format is\n * externally specified, so no metadata wrapper (unlike writeReportFile).\n */\nexport async function writeCodequalityFile(\n filePath: string,\n issues: unknown[],\n): Promise<void> {\n const content = JSON.stringify(issues, null, 2) + '\\n'\n\n try {\n await atomicWrite(filePath, content)\n } catch (error) {\n if (error instanceof FileIOError) throw error\n throw new FileIOError(\n `Failed to write codequality file: ${filePath}: ${toErrorMessage(error)}`,\n filePath,\n )\n }\n}\n\n/**\n * Read, mutate, and write back a single JSON locale file while preserving its\n * existing formatting:\n *\n * - Indentation and trailing newline are detected from the file on disk\n * (readLocaleFileWithMeta bypasses the mtime read cache by design — it needs\n * the raw content; the subsequent write clears the cache entry, so cached\n * readers stay consistent).\n * - Existing keys keep their on-disk order; keys added by the mutation are\n * inserted in sorted position among their siblings.\n */\nexport async function mutateLocaleFile(\n filePath: string,\n mutate: (data: Record<string, unknown>) => void,\n): Promise<void> {\n const { data, indent, trailingNewline } = await readLocaleFileWithMeta(filePath)\n // Snapshot the on-disk key order before the mutation runs so existing keys\n // keep their positions even if the mutation rebuilds objects.\n const reference = structuredClone(data)\n mutate(data)\n const ordered = orderKeysPreserving(data, reference)\n await writeLocaleFile(filePath, ordered, { indent, trailingNewline, sortKeys: false })\n}\n","import { readFile } from 'node:fs/promises'\nimport { FileIOError } from '../utils/errors'\nimport { toErrorMessage } from '../utils/errors'\nimport { sortKeysDeep, orderKeysPreserving } from './key-operations'\nimport { clearPhpFileCacheEntry } from './php-reader'\nimport { atomicWrite } from './atomic-write'\n\nexport interface PhpWriteOptions {\n quoteStyle?: 'single' | 'double'\n indent?: string\n sortKeys?: boolean\n}\n\nexport async function writePhpLocaleFile(\n filePath: string,\n data: Record<string, unknown>,\n options: PhpWriteOptions = {},\n): Promise<void> {\n const {\n quoteStyle = 'double',\n indent = ' ',\n sortKeys = true,\n } = options\n\n try {\n const outputData = sortKeys ? sortKeysDeep(data) : data\n const content = serializePhpArray(outputData, quoteStyle, indent)\n\n await atomicWrite(filePath, content, () => clearPhpFileCacheEntry(filePath))\n }\n catch (error) {\n if (error instanceof FileIOError) throw error\n throw new FileIOError(\n `Failed to write PHP locale file: ${filePath}: ${toErrorMessage(error)}`,\n filePath,\n )\n }\n}\n\nfunction serializePhpArray(\n data: Record<string, unknown>,\n quoteStyle: 'single' | 'double',\n indent: string,\n): string {\n const q = quoteStyle === 'single' ? '\\'' : '\"'\n const lines: string[] = ['<?php', '', 'return [']\n\n renderEntries(data, lines, q, indent, 1)\n\n lines.push('];', '')\n return lines.join('\\n')\n}\n\nfunction renderEntries(\n obj: Record<string, unknown>,\n lines: string[],\n q: string,\n indent: string,\n depth: number,\n): void {\n const prefix = indent.repeat(depth)\n const entries = Object.entries(obj)\n\n for (const [key, value] of entries) {\n const escapedKey = escapePhpString(key, q)\n const keyPart = `${prefix}${q}${escapedKey}${q}`\n\n if (value === null) {\n lines.push(`${keyPart} => null,`)\n }\n else if (typeof value === 'boolean') {\n lines.push(`${keyPart} => ${value ? 'true' : 'false'},`)\n }\n else if (typeof value === 'number') {\n lines.push(`${keyPart} => ${value},`)\n }\n else if (Array.isArray(value)) {\n lines.push(`${keyPart} => [`)\n renderArray(value, lines, q, indent, depth + 1)\n lines.push(`${prefix}],`)\n }\n else if (typeof value === 'object') {\n lines.push(`${keyPart} => [`)\n renderEntries(value as Record<string, unknown>, lines, q, indent, depth + 1)\n lines.push(`${prefix}],`)\n }\n else {\n const escapedValue = escapePhpString(String(value), q)\n lines.push(`${keyPart} => ${q}${escapedValue}${q},`)\n }\n }\n}\n\nfunction escapePhpString(str: string, quote: string): string {\n if (quote === '\\'') {\n return str.replace(/\\\\/g, '\\\\\\\\').replace(/'/g, '\\\\\\'')\n }\n // Double-quoted PHP strings interpolate variables: an unescaped `$` (or\n // `{$…}`) in a value would become live PHP interpolation on read-back.\n return str.replace(/\\\\/g, '\\\\\\\\').replace(/\"/g, '\\\\\"').replace(/\\$/g, '\\\\$')\n}\n\nfunction renderArray(\n arr: unknown[],\n lines: string[],\n q: string,\n indent: string,\n depth: number,\n): void {\n const prefix = indent.repeat(depth)\n for (const value of arr) {\n if (value === null) {\n lines.push(`${prefix}null,`)\n }\n else if (typeof value === 'boolean') {\n lines.push(`${prefix}${value ? 'true' : 'false'},`)\n }\n else if (typeof value === 'number') {\n lines.push(`${prefix}${value},`)\n }\n else if (Array.isArray(value)) {\n lines.push(`${prefix}[`)\n renderArray(value, lines, q, indent, depth + 1)\n lines.push(`${prefix}],`)\n }\n else if (typeof value === 'object') {\n lines.push(`${prefix}[`)\n renderEntries(value as Record<string, unknown>, lines, q, indent, depth + 1)\n lines.push(`${prefix}],`)\n }\n else {\n const escaped = escapePhpString(String(value), q)\n lines.push(`${prefix}${q}${escaped}${q},`)\n }\n }\n}\n\nexport function detectPhpStyle(content: string): { quoteStyle: 'single' | 'double'; indent: string } {\n const keyPattern = /(['\"])([^'\"]+)\\1\\s*=>/g\n let singleCount = 0\n let doubleCount = 0\n let match: RegExpExecArray | null\n while ((match = keyPattern.exec(content)) !== null) {\n if (match[1] === '\\'') singleCount++\n else doubleCount++\n }\n const quoteStyle: 'single' | 'double' = singleCount > doubleCount ? 'single' : 'double'\n\n const indent = content.match(/^([ \\t]+)['\"]/m)?.[1] ?? ' '\n\n return { quoteStyle, indent }\n}\n\n/**\n * Read, mutate, and write back a single PHP locale file while preserving its\n * existing style (quote style + indentation, via detectPhpStyle) and key\n * order: existing keys keep their on-disk order; keys added by the mutation\n * are inserted in sorted position among their siblings.\n */\nexport async function mutatePhpLocaleFile(\n filePath: string,\n mutate: (data: Record<string, unknown>) => void,\n): Promise<void> {\n const { readPhpLocaleFile } = await import('./php-reader.js')\n const data = await readPhpLocaleFile(filePath)\n const rawContent = await readFile(filePath, 'utf-8')\n const { quoteStyle, indent } = detectPhpStyle(rawContent)\n // Snapshot the on-disk key order before the mutation runs.\n const reference = structuredClone(data)\n mutate(data)\n const ordered = orderKeysPreserving(data, reference)\n await writePhpLocaleFile(filePath, ordered, { quoteStyle, indent, sortKeys: false })\n}\n","import { extname } from 'node:path'\nimport { readLocaleFile } from './json-reader'\nimport { writeLocaleFile, mutateLocaleFile } from './json-writer'\nimport { readPhpLocaleFile } from './php-reader'\nimport { writePhpLocaleFile, mutatePhpLocaleFile } from './php-writer'\nimport { FileIOError } from '../utils/errors'\n\nexport async function readLocale(filePath: string): Promise<Record<string, unknown>> {\n const ext = extname(filePath).toLowerCase()\n switch (ext) {\n case '.json':\n return readLocaleFile(filePath)\n case '.php':\n return readPhpLocaleFile(filePath)\n default:\n throw new FileIOError(`Unsupported locale file format: ${ext}`, filePath)\n }\n}\n\nexport async function writeLocale(\n filePath: string,\n data: Record<string, unknown>,\n): Promise<void> {\n const ext = extname(filePath).toLowerCase()\n switch (ext) {\n case '.json':\n return writeLocaleFile(filePath, data)\n case '.php':\n return writePhpLocaleFile(filePath, data)\n default:\n throw new FileIOError(`Unsupported locale file format: ${ext}`, filePath)\n }\n}\n\nexport async function mutateLocale(\n filePath: string,\n mutate: (data: Record<string, unknown>) => void,\n): Promise<void> {\n const ext = extname(filePath).toLowerCase()\n switch (ext) {\n case '.json':\n return mutateLocaleFile(filePath, mutate)\n case '.php':\n return mutatePhpLocaleFile(filePath, mutate)\n default:\n throw new FileIOError(`Unsupported locale file format: ${ext}`, filePath)\n }\n}\n","import { join, extname } from 'node:path'\nimport { readdir, mkdir, unlink } from 'node:fs/promises'\nimport { existsSync } from 'node:fs'\nimport { readLocale, writeLocale, mutateLocale } from './locale-io'\nimport { clearFileCacheEntry } from './json-reader'\nimport { clearPhpFileCacheEntry } from './php-reader'\nimport type { I18nConfig, LocaleDefinition } from '../config/types'\nimport { log } from '../utils/logger'\nimport { FileIOError } from '../utils/errors'\n\n/**\n * A single locale file entry with its path and optional namespace.\n * - Nuxt: one entry with namespace = null (flat file)\n * - Laravel / Next.js / React: one entry per file, namespace = filename without extension\n */\nexport interface LocaleEntry {\n /** Absolute path to the locale file */\n path: string\n /** Namespace for this entry (e.g., 'auth', 'validation'). null for flat file formats. */\n namespace: string | null\n}\n\n/**\n * Resolve all locale file entries for a given locale in a layer.\n *\n * - Nuxt (json): Returns a single entry `[{path: \".../en-US.json\", namespace: null}]`\n * - Laravel (php-array): Scans `{langDir}/{localeCode}/*.php` and returns one entry per file\n * - Next.js / React (json): Scans `{localeDir}/{localeCode}/*.json` and returns one entry per file\n * e.g., `[{path: \".../en/common.json\", namespace: \"common\"}, ...]`\n *\n * Returns an empty array if the locale directory doesn't exist (e.g., new locale).\n */\nexport async function resolveLocaleEntries(\n config: I18nConfig,\n layer: string,\n locale: LocaleDefinition,\n): Promise<LocaleEntry[]> {\n const localeDir = resolveLayerDir(config, layer)\n if (!localeDir) return []\n\n if (config.localeFileFormat === 'php-array') {\n // Laravel: lang/<locale>/<namespace>.php\n const namespaced = await resolveNamespacedEntries(localeDir, locale.code, '.php')\n if (namespaced.length > 0) return namespaced\n\n // Flat: lang/<locale>.php. Not Laravel's layout, but a legitimate one that\n // the generic adapter meets in the wild (#308), and indistinguishable from\n // namespaced by format alone — only the directory says which it is.\n const flat = locale.file ?? `${locale.code}.php`\n return existsSync(join(localeDir, flat)) ? [{ path: join(localeDir, flat), namespace: null }] : []\n }\n\n // Namespaced JSON (Next.js/React: messages/en/common.json)\n const jsonEntries = await resolveNamespacedEntries(localeDir, locale.code, '.json')\n if (jsonEntries.length > 0) return jsonEntries\n\n // Flat JSON (Nuxt: i18n/en-US.json)\n if (!locale.file) {\n // A flat-layout locale without a `file` cannot be resolved at all — reads\n // would silently report {} and writes would vanish. If a matching flat\n // file exists on disk, this is a misconfiguration (e.g. an adapter that\n // forgot to set `file`): fail loudly instead of silently returning [].\n const flatCandidate = join(localeDir, `${locale.code}.json`)\n if (existsSync(flatCandidate)) {\n log.warn(\n `Locale '${locale.code}' (layer '${layer}') uses a flat JSON layout but has no 'file' set — `\n + `${flatCandidate} exists yet cannot be resolved. Reads will return no keys and writes will be no-ops. `\n + `Set 'file' on the locale definition (e.g. '${locale.code}.json').`,\n )\n }\n return []\n }\n return [{ path: join(localeDir, locale.file), namespace: null }]\n}\n\n/**\n * Read all locale data for a locale in a layer, merged into a single object.\n *\n * - Nuxt: Returns the JSON file contents as-is\n * - Laravel / Next.js / React: Reads each namespace file and mounts under its namespace key\n * e.g., `{ auth: { failed: \"...\" }, common: { welcome: \"Hello\" } }`\n *\n * Missing files are treated as empty objects (no error thrown).\n */\n/**\n * Like readLocaleData, but treats unreadable and empty layers as absent —\n * for scan loops that skip locales without data.\n */\nexport async function readLocaleDataIfPresent(\n config: I18nConfig,\n layer: string,\n locale: LocaleDefinition,\n): Promise<Record<string, unknown> | null> {\n let data: Record<string, unknown>\n try {\n data = await readLocaleData(config, layer, locale)\n }\n catch {\n return null\n }\n return Object.keys(data).length === 0 ? null : data\n}\n\nexport async function readLocaleData(\n config: I18nConfig,\n layer: string,\n locale: LocaleDefinition,\n): Promise<Record<string, unknown>> {\n const entries = await resolveLocaleEntries(config, layer, locale)\n if (entries.length === 0) return {}\n\n const merged: Record<string, unknown> = {}\n\n for (const entry of entries) {\n let data: Record<string, unknown>\n try {\n data = await readLocale(entry.path)\n }\n catch (err) {\n if (err instanceof FileIOError && err.code === 'FILE_NOT_FOUND') {\n data = {}\n }\n else {\n throw err\n }\n }\n\n if (entry.namespace === null) {\n Object.assign(merged, data)\n }\n else {\n merged[entry.namespace] = data\n }\n }\n\n return merged\n}\n\n/**\n * Read, mutate, and write back locale data for a locale in a layer.\n *\n * The mutation function receives the merged locale object (same shape as readLocaleData)\n * and may modify it in-place. After mutation:\n *\n * - Nuxt: Writes the entire object back to the single JSON file\n * - Laravel / Next.js / React: Splits by top-level namespace keys and writes each to its file.\n * New namespaces create new files. Empty namespaces delete the content (write empty object).\n *\n * Returns the set of file paths that were written.\n */\nexport async function mutateLocaleData(\n config: I18nConfig,\n layer: string,\n locale: LocaleDefinition,\n mutate: (data: Record<string, unknown>) => void,\n): Promise<Set<string>> {\n const data = await readLocaleData(config, layer, locale)\n\n const filesWritten = new Set<string>()\n\n const entries = await resolveLocaleEntries(config, layer, locale)\n // What is on disk decides, not the declared format: a PHP project may be\n // namespaced (Laravel) or flat (#308), and writing one as the other would\n // restructure someone's locale files rather than edit them. The format is\n // the fallback for a locale that has no files yet, where there is nothing\n // to observe — scaffolding a new Laravel locale, for instance.\n const isNamespaced = entries.length > 0\n ? entries.some(e => e.namespace !== null)\n : config.localeFileFormat === 'php-array' || await hasNamespacedJsonLayout(config, layer)\n\n if (isNamespaced) {\n // Per-namespace write (PHP arrays or namespaced JSON)\n const localeDir = resolveLayerDir(config, layer)\n if (!localeDir) return filesWritten\n\n const localePath = join(localeDir, locale.code)\n const firstEntry = entries[0]\n const fileExt = firstEntry\n ? extname(firstEntry.path) // '.php' or '.json'\n : config.localeFileFormat === 'php-array' ? '.php' : '.json'\n\n const preSnapshots = new Map<string, string>()\n for (const [ns, nsData] of Object.entries(data)) {\n preSnapshots.set(ns, JSON.stringify(nsData))\n }\n const mergedSnapshot = JSON.stringify(data)\n\n mutate(data)\n\n if (JSON.stringify(data) === mergedSnapshot) {\n return filesWritten\n }\n\n if (!existsSync(localePath)) {\n await mkdir(localePath, { recursive: true })\n }\n\n await writeChangedNamespaces(data, preSnapshots, localePath, fileExt, locale.code, filesWritten)\n await deleteRemovedNamespaceFiles(data, preSnapshots, localePath, fileExt, locale.code)\n }\n else {\n // Flat file write (Nuxt-style single JSON file)\n const snapshot = JSON.stringify(data)\n mutate(data)\n\n if (JSON.stringify(data) === snapshot) {\n return filesWritten\n }\n\n const firstEntry = entries[0]\n if (!firstEntry) return filesWritten\n const filePath = firstEntry.path\n await writeLocaleEntryFile(filePath, data)\n filesWritten.add(filePath)\n }\n\n return filesWritten\n}\n\n// ─── Internal helpers ───────────────────────────────────────────\n\n/** Write every namespace whose content changed relative to its pre-mutation snapshot. */\nasync function writeChangedNamespaces(\n data: Record<string, unknown>,\n preSnapshots: Map<string, string>,\n localePath: string,\n fileExt: string,\n localeCode: string,\n filesWritten: Set<string>,\n): Promise<void> {\n for (const [namespace, nsData] of Object.entries(data)) {\n if (typeof nsData !== 'object' || nsData === null) {\n log.warn(`Skipping non-object namespace '${namespace}' for locale '${localeCode}'`)\n continue\n }\n if (JSON.stringify(nsData) !== preSnapshots.get(namespace)) {\n const filePath = join(localePath, `${namespace}${fileExt}`)\n await writeLocaleEntryFile(filePath, nsData as Record<string, unknown>)\n filesWritten.add(filePath)\n }\n }\n}\n\n/**\n * Delete only the namespace files this mutation explicitly removed (top-level\n * keys present before the mutation and absent after) — never a\n * whole-directory reconciliation. Narrowing deletion to this mutation's own\n * removals also defuses the concurrency hazard: translateMissing runs one\n * mutation per locale in a Promise.all, and a directory-wide sweep could race\n * concurrent mutations on shared/aliased namespace dirs and delete files\n * another locale's mutation was about to write.\n */\nasync function deleteRemovedNamespaceFiles(\n data: Record<string, unknown>,\n preSnapshots: Map<string, string>,\n localePath: string,\n fileExt: string,\n localeCode: string,\n): Promise<void> {\n for (const namespace of preSnapshots.keys()) {\n if (namespace in data) continue\n const filePath = join(localePath, `${namespace}${fileExt}`)\n if (!existsSync(filePath)) continue\n log.warn(`Deleting namespace file '${filePath}': namespace '${namespace}' was removed from locale '${localeCode}'`)\n try {\n await unlink(filePath)\n }\n catch (err) {\n throw new FileIOError(\n `Failed to delete removed namespace file: ${filePath}: ${err instanceof Error ? err.message : String(err)}`,\n filePath,\n )\n }\n if (fileExt === '.php') clearPhpFileCacheEntry(filePath)\n else clearFileCacheEntry(filePath)\n }\n}\n\n/**\n * Write mutated locale data to a single file.\n *\n * Existing files go through the format-preserving mutate path\n * (mutateLocaleFile / mutatePhpLocaleFile): indentation, trailing newline and\n * PHP quote style are detected from the file on disk, existing keys keep\n * their on-disk order, and new keys are inserted in sorted position among\n * their siblings. The mutate path re-reads the file with metadata (bypassing\n * the mtime read cache by design) and the write clears the cache entry, so\n * cached readers stay consistent.\n *\n * New files are written with the writer defaults (sorted keys, standard\n * indentation).\n */\nasync function writeLocaleEntryFile(\n filePath: string,\n data: Record<string, unknown>,\n): Promise<void> {\n if (!existsSync(filePath)) {\n await writeLocale(filePath, data)\n return\n }\n\n await mutateLocale(filePath, (fileData) => {\n for (const key of Object.keys(fileData)) {\n delete fileData[key]\n }\n Object.assign(fileData, data)\n })\n}\n\nfunction resolveLayerDir(config: I18nConfig, layer: string): string | null {\n const dir = config.localeDirs.find(d => d.layer === layer)\n if (!dir) return null\n if (dir.aliasOf) {\n const aliasDir = config.localeDirs.find(d => d.layer === dir.aliasOf)\n if (aliasDir) return aliasDir.path\n }\n return dir.path\n}\n\nasync function hasNamespacedJsonLayout(config: I18nConfig, layer: string): Promise<boolean> {\n const localeDir = resolveLayerDir(config, layer)\n if (!localeDir) return false\n\n try {\n const entries = await readdir(localeDir)\n for (const entry of entries) {\n const subPath = join(localeDir, entry)\n try {\n if (existsSync(subPath)) {\n const subFiles = await readdir(subPath)\n if (subFiles.some(f => f.endsWith('.json'))) return true\n }\n }\n catch { /* skip unreadable dirs */ }\n }\n }\n catch { /* locale dir not readable */ }\n\n return false\n}\n\nasync function resolveNamespacedEntries(localeDir: string, localeCode: string, extension: '.json' | '.php'): Promise<LocaleEntry[]> {\n const localePath = join(localeDir, localeCode)\n\n if (!existsSync(localePath)) return []\n\n let files: string[]\n try {\n files = await readdir(localePath)\n }\n catch (err) {\n log.debug(`Failed to read locale directory ${localePath}: ${err instanceof Error ? err.message : String(err)}`)\n return []\n }\n\n return files\n .filter(f => f.endsWith(extension))\n .sort()\n .map(f => ({\n path: join(localePath, f),\n namespace: f.slice(0, -extension.length),\n }))\n}\n","/**\n * Translation operations: translate_missing and translate_key, plus the\n * prompt builders, placeholder validation, and fallback-context helpers\n * they share.\n */\n\nimport { detectI18nConfig } from '../config/detector.js'\nimport { buildLayerGraph } from '../config/layer-graph.js'\nimport type { I18nConfig, LocaleDefinition, ProjectConfig } from '../config/types.js'\nimport type { LocaleFileFormat } from '../adapters/types.js'\nimport { readLocaleData, mutateLocaleData } from '../io/locale-data.js'\nimport {\n getNestedValue,\n setNestedValue,\n getLeafKeys,\n} from '../io/key-operations.js'\nimport { log } from '../utils/logger.js'\nimport { ToolError, toErrorMessage } from '../utils/errors.js'\nimport { TranslateProviderError } from '../llm/providers.js'\n\nimport type {\n TranslateFn,\n ProgressFn,\n TranslateLayerTotals,\n TranslateMode,\n TranslateFailReason,\n TranslateSkipReason,\n TranslateKeyLocaleIssue,\n TranslateMissingLocaleResult,\n PlaceholderValidationResult,\n TranslateKeyResult,\n TranslateMissingResult,\n TranslateMissingOutcome,\n TranslateAllLayersResult,\n TranslateAllLayersSummary,\n} from './types.js'\nimport { findWritableLayerOrThrow, findReferenceLocaleOrThrow, findLocaleImpl, findLocaleOrThrow, localeRefInfo } from './shared.js'\n\n// ─── Shared helpers (exported for reuse) ────────────────────────\n\n/**\n * Fixed maxTokens budget for a translate request. Deliberately independent\n * of batch size — models simply stop when the JSON object is closed.\n */\nexport function computeMaxTokens(_batchKeyCount: number): number {\n return 16384\n}\n\n/**\n * Total progress steps for translate_missing: per locale with missing keys,\n * one step per batch plus a start and a complete step (the +2).\n */\nexport function computeProgressTotal(missingKeyCounts: number[], maxBatch: number): number {\n return missingKeyCounts.reduce((sum, count) => {\n if (count <= 0) return sum\n return sum + Math.ceil(count / maxBatch) + 2\n }, 0)\n}\n\n/**\n * Resolve the config's `protectedLocales` entries (any locale ref: code,\n * language tag, or file name) against the known locales. Entries that do not\n * match a known locale are ignored with a warning. Returns the resolved\n * definitions, deduplicated by canonical code.\n */\nexport function resolveProtectedLocales(config: I18nConfig): LocaleDefinition[] {\n const refs = config.projectConfig?.protectedLocales ?? []\n const resolved = new Map<string, LocaleDefinition>()\n for (const ref of refs) {\n const locale = findLocaleImpl(config, ref)\n if (!locale) {\n log.warn(\n `protectedLocales entry \"${ref}\" does not match any known locale — ignoring. `\n + `Available: ${config.locales.map(l => l.code).join(', ')}`,\n )\n continue\n }\n if (!resolved.has(locale.code)) {\n resolved.set(locale.code, locale)\n }\n }\n return [...resolved.values()]\n}\n\nfunction warnProtectedOverride(code: string): void {\n log.warn(`Locale \"${code}\" is protected (protectedLocales) — translating anyway because it was explicitly requested via targetLocales.`)\n}\n\n/**\n * Resolve translate_missing target locales. Protected locales are excluded\n * from the DEFAULT target set (returned separately so the caller can report\n * them as skipped); naming one explicitly in targetLocales overrides the\n * protection with a warning.\n */\nfunction resolveTranslateTargets(\n config: I18nConfig,\n refLocale: LocaleDefinition,\n requested: string[] | undefined,\n): { targets: LocaleDefinition[], protectedDefaults: LocaleDefinition[] } {\n const protectedCodes = new Set(resolveProtectedLocales(config).map(l => l.code))\n if (requested) {\n const targets = requested.map((code) => {\n const loc = findLocaleImpl(config, code)\n if (!loc) {\n throw new ToolError(`Target locale not found: \"${code}\". Available: ${config.locales.map(l => l.code).join(', ')}. Pass valid locale codes in targetLocales.`, 'LOCALE_NOT_FOUND')\n }\n if (protectedCodes.has(loc.code)) {\n warnProtectedOverride(loc.code)\n }\n return loc\n })\n return { targets, protectedDefaults: [] }\n }\n const defaults = config.locales.filter(l => l.code !== refLocale.code)\n return {\n targets: defaults.filter(l => !protectedCodes.has(l.code)),\n protectedDefaults: defaults.filter(l => protectedCodes.has(l.code)),\n }\n}\n\n/**\n * Build result entries for protected locales withheld from the default\n * target set of translate_missing — callers see what was NOT translated\n * and the totals stay meaningful.\n */\nasync function collectProtectedLocaleResults(\n config: I18nConfig,\n layer: string,\n mode: TranslateMode,\n protectedDefaults: LocaleDefinition[],\n missingKeysIn: (data: Record<string, unknown>) => string[],\n): Promise<Record<string, TranslateMissingLocaleResult>> {\n const results: Record<string, TranslateMissingLocaleResult> = {}\n for (const locale of protectedDefaults) {\n let data: Record<string, unknown> = {}\n try {\n data = await readLocaleData(config, layer, locale)\n } catch {}\n const missingKeys = missingKeysIn(data)\n if (missingKeys.length === 0) continue\n results[locale.code] = {\n mode,\n missing: missingKeys.length,\n translated: [],\n failed: [],\n skipped: missingKeys.map(key => ({ key, reason: 'protected-locale' as const })),\n }\n }\n return results\n}\n\n/**\n * Build the translate_key target list (deduplicated, source locale removed).\n * Protected locales are excluded from the default 'all' target set and\n * returned as skipped entries; naming one explicitly overrides the\n * protection with a warning.\n */\nfunction partitionTranslateKeyTargets(\n config: I18nConfig,\n resolved: LocaleDefinition[],\n sourceCode: string,\n usesDefaultTargets: boolean,\n): { targetLocales: LocaleDefinition[], protectedSkipped: Array<{ locale: string, reason: TranslateSkipReason }> } {\n const protectedCodes = new Set(resolveProtectedLocales(config).map(l => l.code))\n const byCode = new Map<string, LocaleDefinition>()\n const protectedSkipped: Array<{ locale: string, reason: TranslateSkipReason }> = []\n for (const locale of resolved) {\n if (locale.code === sourceCode || byCode.has(locale.code)) continue\n if (protectedCodes.has(locale.code)) {\n if (usesDefaultTargets) {\n protectedSkipped.push({ locale: locale.code, reason: 'protected-locale' })\n continue\n }\n warnProtectedOverride(locale.code)\n }\n byCode.set(locale.code, locale)\n }\n return { targetLocales: [...byCode.values()], protectedSkipped }\n}\n\n// ─── Translation prompt helpers ─────────────────────────────────\n\nfunction extractPlaceholders(value: string, format?: LocaleFileFormat): string[] {\n const placeholders = new Set<string>()\n\n if (format === 'php-array') {\n for (const match of value.matchAll(/:([A-Za-z_][A-Za-z0-9_]*)/g)) {\n placeholders.add(`:${match[1]}`)\n }\n } else {\n for (const match of value.matchAll(/\\{([A-Za-z_][A-Za-z0-9_]*)\\}/g)) {\n placeholders.add(`{${match[1]}}`)\n }\n for (const match of value.matchAll(/@:([A-Za-z0-9_.-]+)/g)) {\n placeholders.add(`@:${match[1]}`)\n }\n }\n\n return [...placeholders].sort()\n}\n\n/** vue-i18n plural variant separator: space-pipe-space. A bare `|` inside a\n * word (e.g. \"A|B\") is NOT a plural separator. */\nconst PLURAL_SEPARATOR = ' | '\n\n/** Split a vue-i18n message into plural variants. Only meaningful for the\n * json/vue format — PHP-style messages have no pipe plurals. */\nfunction splitPluralVariants(value: string): string[] {\n return value.split(PLURAL_SEPARATOR)\n}\n\nfunction diffPlaceholders(\n sourcePlaceholders: string[],\n targetValue: string,\n format?: LocaleFileFormat,\n): { missing: string[], extra: string[] } {\n const sourceSet = new Set(sourcePlaceholders)\n const targetSet = new Set(extractPlaceholders(targetValue, format))\n return {\n missing: sourcePlaceholders.filter(placeholder => !targetSet.has(placeholder)),\n extra: [...targetSet].filter(placeholder => !sourceSet.has(placeholder)).sort(),\n }\n}\n\nexport function validatePlaceholders(\n key: string,\n sourceValue: string,\n values: Array<{ locale: string, value: string }>,\n format?: LocaleFileFormat,\n): PlaceholderValidationResult {\n const sourcePlaceholders = extractPlaceholders(sourceValue, format)\n const errors: PlaceholderValidationResult['errors'] = []\n\n // Per-variant validation only applies to vue-i18n pipe plurals (json/vue\n // format). PHP arrays have no pipe plural convention.\n const sourceVariants = format === 'php-array' ? [sourceValue] : splitPluralVariants(sourceValue)\n const isPlural = sourceVariants.length > 1\n\n for (const { locale, value } of values) {\n if (isPlural) {\n const targetVariants = splitPluralVariants(value)\n if (targetVariants.length !== sourceVariants.length) {\n errors.push({\n locale,\n key,\n missing: [],\n extra: [],\n kind: 'plural-count',\n sourceVariants: sourceVariants.length,\n targetVariants: targetVariants.length,\n })\n continue\n }\n // Each variant's placeholder set must match its source counterpart —\n // a whole-value set comparison lets a variant drop {count} while\n // another keeps it.\n const missing = new Set<string>()\n const extra = new Set<string>()\n for (const [index, sourceVariant] of sourceVariants.entries()) {\n const targetVariant = targetVariants[index]\n if (targetVariant === undefined) continue\n const variantPlaceholders = extractPlaceholders(sourceVariant, format)\n const diff = diffPlaceholders(variantPlaceholders, targetVariant, format)\n for (const placeholder of diff.missing) missing.add(placeholder)\n for (const placeholder of diff.extra) extra.add(placeholder)\n }\n if (missing.size || extra.size) {\n errors.push({ locale, key, missing: [...missing].sort(), extra: [...extra].sort(), kind: 'placeholder' })\n }\n } else {\n const { missing, extra } = diffPlaceholders(sourcePlaceholders, value, format)\n if (missing.length || extra.length) {\n errors.push({ locale, key, missing, extra, kind: 'placeholder' })\n }\n }\n }\n\n return {\n ok: errors.length === 0,\n placeholders: sourcePlaceholders,\n errors,\n }\n}\n\n/** Map a validation issue to the translate fail reason it represents. */\nfunction failReasonForIssue(issue: PlaceholderValidationResult['errors'][number]): 'placeholder-mismatch' | 'plural-mismatch' {\n return issue.kind === 'plural-count' ? 'plural-mismatch' : 'placeholder-mismatch'\n}\n\nexport function mergePlaceholderValidation(\n validations: PlaceholderValidationResult[],\n): PlaceholderValidationResult | undefined {\n if (validations.length === 0) return undefined\n const placeholders = [...new Set(validations.flatMap(validation => validation.placeholders))].sort()\n const errors = validations.flatMap(validation => validation.errors)\n return { ok: errors.length === 0, placeholders, errors }\n}\n\nfunction placeholderInstruction(format?: LocaleFileFormat): string {\n if (format === 'php-array') {\n return 'Preserve all :placeholder parameters exactly as-is.'\n }\n return 'Preserve all {placeholder} parameters and @:linked.message references.'\n}\n\nexport function buildTranslationSystemPrompt(\n projectConfig: ProjectConfig | undefined,\n targetLocaleCode: string,\n localeFileFormat?: LocaleFileFormat,\n): string {\n const parts: string[] = [\n `You are a professional translator for software UI strings. ${placeholderInstruction(localeFileFormat)} Be concise — UI space is limited.`,\n ]\n\n if (projectConfig?.translationPrompt) {\n parts.push(projectConfig.translationPrompt)\n }\n\n if (projectConfig?.glossary && Object.keys(projectConfig.glossary).length > 0) {\n const glossaryLines = Object.entries(projectConfig.glossary)\n .map(([term, definition]) => `- ${term} → ${definition}`)\n .join('\\n')\n parts.push(`GLOSSARY — use these terms consistently:\\n${glossaryLines}`)\n }\n\n if (projectConfig?.localeNotes?.[targetLocaleCode]) {\n parts.push(`TARGET LOCALE NOTE (${targetLocaleCode}): ${projectConfig.localeNotes[targetLocaleCode]}`)\n }\n\n if (projectConfig?.examples && projectConfig.examples.length > 0) {\n const exampleLines = projectConfig.examples\n .map((ex) => {\n const pairs = Object.entries(ex)\n .filter(([k]) => k !== 'key' && k !== 'note')\n .map(([locale, val]) => `${locale}: \"${val}\"`)\n .join(', ')\n const note = ex.note ? ` (${ex.note})` : ''\n return `- ${ex.key}: ${pairs}${note}`\n })\n .join('\\n')\n parts.push(`STYLE EXAMPLES:\\n${exampleLines}`)\n }\n\n parts.push('Return ONLY a JSON object mapping keys to translated values. No markdown, no explanation, no code fences.')\n\n return parts.join('\\n\\n')\n}\n\nexport function buildTranslationUserMessage(\n referenceLocaleCode: string,\n targetLocaleCode: string,\n keysAndValues: Record<string, string>,\n localeFileFormat?: LocaleFileFormat,\n): string {\n return [\n `Translate the following i18n key-value pairs from ${referenceLocaleCode} to ${targetLocaleCode}.`,\n placeholderInstruction(localeFileFormat),\n '',\n JSON.stringify(keysAndValues),\n ].join('\\n')\n}\n\nexport function extractJsonFromResponse(responseText: string): Record<string, unknown> {\n const trimmed = responseText.trim()\n\n // Tier 1: direct parse\n try {\n return JSON.parse(trimmed) as Record<string, unknown>\n } catch {}\n\n // Tier 2: strip markdown code fences\n if (trimmed.startsWith('```')) {\n const stripped = trimmed.replace(/^```(?:json)?\\n?/, '').replace(/\\n?```$/, '')\n try {\n return JSON.parse(stripped) as Record<string, unknown>\n } catch {}\n }\n\n // Tier 3: balanced bracket extraction — find first complete {...}\n const start = trimmed.indexOf('{')\n if (start !== -1) {\n let depth = 0\n let inString = false\n let escape = false\n for (let i = start; i < trimmed.length; i++) {\n const ch = trimmed[i]\n if (escape) {\n escape = false\n continue\n }\n if (ch === '\\\\' && inString) {\n escape = true\n continue\n }\n if (ch === '\"') {\n inString = !inString\n continue\n }\n if (inString) continue\n if (ch === '{') depth++\n else if (ch === '}') {\n depth--\n if (depth === 0) {\n const candidate = trimmed.slice(start, i + 1)\n return JSON.parse(candidate) as Record<string, unknown>\n }\n }\n }\n }\n\n // Tier 4: the object never closed. Salvage the pairs that did arrive.\n //\n // Observed in the wild on a real Gemini response: the complete, correct\n // translation missing only its closing brace. Discarding the whole response\n // over two absent characters left keys untranslated across repeated runs,\n // and each rerun asked the model for them again.\n if (start !== -1) {\n const salvaged = salvageTruncatedObject(trimmed.slice(start))\n if (salvaged) {\n log.warn(\n `Translate response ended mid-object — recovered ${Object.keys(salvaged).length} complete pair(s). `\n + 'Remaining keys are reported as failed and can be retried.',\n )\n return salvaged\n }\n\n throw new Error(\n `Response ended mid-object before any pair completed. Preview: ${trimmed.substring(0, 200)}`,\n )\n }\n\n throw new Error(`No valid JSON object found in response. Preview: ${trimmed.substring(0, 200)}`)\n}\n\n/**\n * Close an object that was cut off, keeping the key/value pairs that arrived\n * whole. Tries the string as-is first — a response missing only its brace is\n * the common case — then falls back to the last pair that ended cleanly,\n * dropping whatever was half-written after it.\n */\nfunction salvageTruncatedObject(text: string): Record<string, unknown> | null {\n for (const candidate of [text, text.slice(0, lastCompletePairEnd(text))]) {\n if (!candidate) continue\n try {\n const parsed = JSON.parse(`${candidate}}`) as Record<string, unknown>\n if (parsed && typeof parsed === 'object' && Object.keys(parsed).length > 0) return parsed\n }\n catch {\n // Try the shorter cut.\n }\n }\n\n return null\n}\n\n/**\n * Index of the last top-level comma — the boundary after the last pair that\n * completed. Commas inside strings do not count, which is why this scans\n * rather than searching: a translated value may contain one.\n */\nfunction lastCompletePairEnd(text: string): number {\n let depth = 0\n let inString = false\n let escape = false\n let lastComma = 0\n\n for (let i = 0; i < text.length; i++) {\n const ch = text[i]\n if (escape) {\n escape = false\n continue\n }\n if (ch === '\\\\' && inString) {\n escape = true\n continue\n }\n if (ch === '\"') {\n inString = !inString\n continue\n }\n if (inString) continue\n if (ch === '{' || ch === '[') depth++\n else if (ch === '}' || ch === ']') depth--\n else if (ch === ',' && depth === 1) lastComma = i\n }\n\n return lastComma\n}\n\nfunction buildFallbackContext(\n projectConfig: ProjectConfig | undefined,\n referenceLocaleCode: string,\n targetLocaleCode: string,\n keysAndValues: Record<string, string>,\n): Record<string, unknown> {\n const context: Record<string, unknown> = {\n instruction: `Translate these keys from ${referenceLocaleCode} to ${targetLocaleCode}, then call write_translations (mode: 'upsert') to write them.`,\n referenceLocale: referenceLocaleCode,\n targetLocale: targetLocaleCode,\n keysToTranslate: keysAndValues,\n }\n\n if (projectConfig?.translationPrompt) {\n context.translationPrompt = projectConfig.translationPrompt\n }\n if (projectConfig?.glossary && Object.keys(projectConfig.glossary).length > 0) {\n context.glossary = projectConfig.glossary\n }\n if (projectConfig?.localeNotes?.[targetLocaleCode]) {\n context.localeNote = projectConfig.localeNotes[targetLocaleCode]\n }\n if (projectConfig?.examples && projectConfig.examples.length > 0) {\n context.examples = projectConfig.examples\n }\n\n return context\n}\n\n// ─── Operations ─────────────────────────────────────────────────\n\n/**\n * Find keys missing in target locales and translate them.\n *\n * When translateFn is provided, uses it to translate via LLM.\n * When translateFn is absent, returns fallback contexts for the agent.\n *\n * When `layer` is omitted, every canonical locale-backed layer is translated\n * in one run and the results are aggregated (see translateMissingAllLayers).\n */\nexport async function translateMissing(opts: {\n layer?: string\n referenceLocale?: string\n targetLocales?: string[]\n locales?: string[]\n keys?: string[]\n batchSize?: number\n dryRun?: boolean\n compact?: boolean\n projectDir?: string\n translateFn?: TranslateFn\n progressFn?: ProgressFn\n /** Called once after the pre-scan with the computed total number of progress steps. */\n onProgressTotal?: (total: number) => void\n}): Promise<TranslateMissingOutcome> {\n if (opts.layer === undefined) return translateMissingAllLayers(opts)\n const layer = opts.layer\n const dir = opts.projectDir ?? process.cwd()\n const config = await detectI18nConfig(dir)\n const isDryRun = opts.dryRun ?? false\n const maxBatch = opts.batchSize ?? 50\n if (!Number.isFinite(maxBatch) || maxBatch <= 0 || !Number.isInteger(maxBatch)) {\n throw new ToolError(`Invalid batchSize: ${opts.batchSize}. Must be a positive integer.`, 'INVALID_BATCH_SIZE')\n }\n\n findWritableLayerOrThrow(config, layer)\n\n const refCode = opts.referenceLocale ?? config.defaultLocale\n const refLocale = findReferenceLocaleOrThrow(config, opts.referenceLocale)\n\n const refData = await readLocaleData(config, layer, refLocale)\n if (Object.keys(refData).length === 0) {\n throw new ToolError(`No locale data found for reference locale \"${refCode}\" in layer \"${layer}\". Verify the layer exists and contains data for this locale using list_locale_dirs.`, 'NO_LOCALE_FILE')\n }\n const allRefKeys = getLeafKeys(refData).filter(k => {\n const v = getNestedValue(refData, k)\n return typeof v === 'string' ? v.length > 0 : v !== null && v !== undefined\n })\n\n const resolvedTargetLocales = opts.targetLocales ?? opts.locales\n const { targets, protectedDefaults } = resolveTranslateTargets(config, refLocale, resolvedTargetLocales)\n\n const mode: TranslateMode = isDryRun ? 'dry-run' : opts.translateFn ? 'provider' : 'agent'\n const reportProgress = opts.progressFn ?? (async () => {})\n\n function isKeyMissingIn(data: Record<string, unknown>, k: string): boolean {\n const v = getNestedValue(data, k)\n return v === undefined || v === '' || v === null\n }\n\n /** The keys missing in a target locale's data (scoped to opts.keys when given). */\n function missingKeysIn(data: Record<string, unknown>): string[] {\n const isMissing = (k: string) => isKeyMissingIn(data, k)\n return opts.keys\n ? opts.keys.filter(k => isMissing(k) && allRefKeys.includes(k))\n : allRefKeys.filter(k => isMissing(k))\n }\n\n // Pre-scan: count missing keys per target to compute progressTotal\n if (opts.onProgressTotal) {\n const preScanCounts: number[] = []\n for (const target of targets) {\n let scanData: Record<string, unknown> = {}\n try {\n scanData = await readLocaleData(config, layer, target)\n } catch {}\n preScanCounts.push(missingKeysIn(scanData).length)\n }\n // In dry-run or agent mode, only 2 steps per locale (start + complete),\n // no batch steps. Full batching only in provider mode.\n const willBatch = mode === 'provider'\n const total = willBatch\n ? computeProgressTotal(preScanCounts, maxBatch)\n : preScanCounts.filter(c => c > 0).length * 2\n opts.onProgressTotal(total)\n }\n\n const results: Record<string, TranslateMissingLocaleResult> = {}\n const fallbackContexts: Record<string, Record<string, unknown>> = {}\n\n // Pre-read all target locale data for missing key detection\n const targetDataCache = new Map<string, Record<string, unknown>>()\n for (const target of targets) {\n let targetData: Record<string, unknown> = {}\n try {\n targetData = await readLocaleData(config, layer, target)\n } catch {}\n targetDataCache.set(target.code, targetData)\n }\n\n // Set when any locale hits an auth error: the run is doomed, so sibling\n // locales must stop issuing provider requests and must not write output\n // after the abort. Locales that fully completed before the abort keep\n // their writes — translateMissing is idempotent (re-runs only fill the\n // still-missing keys), so completed work is never wasted or wrong.\n const runState = { aborted: false }\n\n async function translateOneLocale(\n target: LocaleDefinition,\n targetData: Record<string, unknown>,\n ): Promise<{ result: TranslateMissingLocaleResult, fallbackContext?: Record<string, unknown> }> {\n const missingKeys = missingKeysIn(targetData)\n\n if (missingKeys.length === 0) {\n return { result: { mode, missing: 0, translated: [], failed: [], skipped: [] } }\n }\n\n await reportProgress(`Starting ${target.code}: ${missingKeys.length} missing keys`)\n\n const keysAndValues: Record<string, string> = {}\n for (const key of missingKeys) {\n const value = getNestedValue(refData, key)\n if (typeof value === 'string') {\n keysAndValues[key] = value\n }\n }\n const missing = Object.keys(keysAndValues).length\n\n if (isDryRun) {\n await reportProgress(`Complete ${target.code} (dry run)`)\n return { result: { mode: 'dry-run', missing, translated: [], wouldTranslate: Object.keys(keysAndValues), failed: [], skipped: [] } }\n }\n\n if (opts.translateFn) {\n const translated: string[] = []\n const failed: Array<{ key: string, reason: TranslateFailReason }> = []\n const keyEntries = Object.entries(keysAndValues)\n const allTranslations: Record<string, string> = {}\n const totalBatches = Math.ceil(keyEntries.length / maxBatch)\n let model: string | undefined\n\n for (let i = 0; i < keyEntries.length; i += maxBatch) {\n if (runState.aborted) break\n const batchNum = Math.floor(i / maxBatch) + 1\n const batch = Object.fromEntries(keyEntries.slice(i, i + maxBatch))\n let batchTranslations: Record<string, string> | null = null\n let batchTruncated = false\n\n const systemPrompt = buildTranslationSystemPrompt(config.projectConfig, target.language || target.code, config.localeFileFormat)\n const userMessage = buildTranslationUserMessage(\n refLocale!.language || refLocale!.code,\n target.language || target.code,\n batch,\n config.localeFileFormat,\n )\n\n for (let attempt = 0; attempt < 2; attempt++) {\n if (attempt > 0) {\n const delayMs = 2000 * 2 ** attempt\n await new Promise(r => setTimeout(r, delayMs))\n }\n if (runState.aborted) break\n try {\n const response = await opts.translateFn({\n systemPrompt,\n userMessage,\n maxTokens: computeMaxTokens(Object.keys(batch).length),\n })\n\n model = response.model\n log.info(`Translation model: ${response.model}`)\n\n if (response.truncated) {\n // The batch response was cut off at the token limit — retrying\n // with the same budget would truncate again, so fail fast.\n batchTruncated = true\n log.warn(`Translate response truncated for batch ${batchNum} in ${target.code}: provider hit the token limit. Reduce batchSize.`)\n break\n }\n if (response.text.trim() === '') {\n throw new TranslateProviderError('Provider returned an empty response', 'provider')\n }\n\n const parsed = extractJsonFromResponse(response.text)\n const batchKeys = new Set(Object.keys(batch))\n batchTranslations = {} as Record<string, string>\n for (const [key, value] of Object.entries(parsed)) {\n if (batchKeys.has(key) && typeof value === 'string') {\n batchTranslations[key] = value\n }\n }\n break\n } catch (_error) {\n if (_error instanceof TranslateProviderError && _error.kind === 'auth') {\n // Auth failures affect every request — abort the whole run\n // instead of failing key by key through retries.\n runState.aborted = true\n throw new ToolError(\n `Provider authentication failed: ${_error.message}. Verify your API key (config apiKey or the provider's environment variable).`,\n 'PROVIDER_AUTH_ERROR',\n )\n }\n const errMsg = _error instanceof Error ? _error.message : String(_error)\n if (attempt === 0) {\n log.warn(`Translate request failed for batch ${batchNum} in ${target.code}: ${errMsg}. Retrying (attempt 2)`)\n } else {\n log.warn(`Translate retry failed for batch ${batchNum} in ${target.code}: ${errMsg}`)\n }\n }\n }\n\n // Account for every batch key: translated, omitted by the model,\n // or lost to a failed batch — totals must always reconcile.\n for (const key of Object.keys(batch)) {\n const value = batchTranslations?.[key]\n if (typeof value === 'string') {\n allTranslations[key] = value\n translated.push(key)\n } else {\n failed.push({\n key,\n reason: batchTruncated\n ? 'truncated'\n : batchTranslations === null ? 'provider-error' : 'omitted-by-model',\n })\n }\n }\n\n await reportProgress(`${target.code}: batch ${batchNum}/${totalBatches}`)\n }\n\n const placeholderValidation = mergePlaceholderValidation(Object.entries(allTranslations).map(([key, value]) => {\n return validatePlaceholders(key, keysAndValues[key] ?? '', [{ locale: target.code, value }], config.localeFileFormat)\n }))\n\n if (placeholderValidation && !placeholderValidation.ok) {\n const reasonByKey = new Map<string, TranslateFailReason>()\n for (const error of placeholderValidation.errors) {\n if (!reasonByKey.has(error.key)) {\n reasonByKey.set(error.key, failReasonForIssue(error))\n }\n }\n for (const [key, reason] of reasonByKey) {\n delete allTranslations[key]\n failed.push({ key, reason })\n }\n for (const key of [...translated]) {\n if (reasonByKey.has(key)) translated.splice(translated.indexOf(key), 1)\n }\n }\n\n if (runState.aborted) {\n // Run is doomed — don't write partial output for an in-flight locale.\n // The returned result is discarded by the rejecting Promise.all.\n return { result: { mode: 'provider', missing, translated: [], failed, skipped: [] } }\n }\n\n if (Object.keys(allTranslations).length > 0) {\n try {\n await mutateLocaleData(config, layer, target, (data) => {\n for (const [key, value] of Object.entries(allTranslations)) {\n setNestedValue(data, key, value)\n }\n })\n } catch (error) {\n log.warn(`Failed to write translations for ${target.code}: ${toErrorMessage(error)}`)\n // Only the keys that were about to be written failed on write;\n // earlier failures keep their own reasons.\n for (const key of translated) {\n failed.push({ key, reason: 'write-error' })\n }\n return { result: { mode: 'provider', missing, translated: [], failed, skipped: [], batches: totalBatches, model, writeError: toErrorMessage(error) } }\n }\n }\n\n await reportProgress(`Complete ${target.code}`)\n return { result: { mode: 'provider', missing, translated, failed, skipped: [], batches: totalBatches, model, ...(placeholderValidation ? { placeholderValidation } : {}) } }\n } else {\n // Agent mode: return context for the host agent to translate inline\n const fallbackContext = buildFallbackContext(\n config.projectConfig,\n refLocale!.language || refLocale!.code,\n target.language || target.code,\n keysAndValues,\n )\n await reportProgress(`Complete ${target.code}`)\n return {\n result: {\n mode: 'agent',\n missing,\n translated: [],\n failed: [],\n skipped: Object.keys(keysAndValues).map(key => ({ key, reason: 'no-provider' as const })),\n },\n fallbackContext,\n }\n }\n }\n\n const localeResults = await Promise.all(targets.map(async (target) => {\n const targetData = targetDataCache.get(target.code) ?? {}\n return translateOneLocale(target, targetData)\n }))\n\n for (const [i, { result, fallbackContext }] of localeResults.entries()) {\n const target = targets[i]\n if (target === undefined) continue\n const localeCode = target.code\n results[localeCode] = result\n if (fallbackContext) {\n fallbackContexts[localeCode] = fallbackContext\n }\n }\n\n Object.assign(results, await collectProtectedLocaleResults(config, layer, mode, protectedDefaults, missingKeysIn))\n\n const totalTranslated = Object.values(results).reduce((sum, r) => sum + r.translated.length, 0)\n const totalFailed = Object.values(results).reduce((sum, r) => sum + r.failed.length, 0)\n const totalSkipped = Object.values(results).reduce((sum, r) => sum + r.skipped.length, 0)\n const totalWouldTranslate = Object.values(results).reduce((sum, r) => sum + (r.wouldTranslate?.length ?? 0), 0)\n\n const summary: TranslateMissingResult['summary'] = {\n mode,\n totalTranslated,\n totalFailed,\n totalSkipped,\n ...(isDryRun ? { totalWouldTranslate } : {}),\n layer,\n referenceLocale: localeRefInfo(refLocale),\n targetLocales: targets.map(localeRefInfo),\n dryRun: isDryRun,\n }\n\n const hasFallbackContexts = Object.keys(fallbackContexts).length > 0\n\n // Compact is a projection of the full result: it may only drop per-key\n // detail, never the fallback contexts, reasons, or locale metadata that\n // change what the caller does next.\n if (opts.compact) {\n const byLocale = Object.entries(results).map(([code, r]) => {\n // Reduce per-key arrays to counts; drop per-key placeholder detail;\n // keep every other per-locale field (mode, missing, batches, model,\n // writeError, …).\n const { translated, failed, skipped, wouldTranslate, placeholderValidation: _placeholderValidation, ...rest } = r\n return {\n locale: code,\n translated: translated.length,\n failed: failed.length,\n skipped: skipped.length,\n ...(wouldTranslate ? { wouldTranslate: wouldTranslate.length } : {}),\n ...rest,\n }\n })\n return {\n summary: { ...summary, byLocale },\n ...(hasFallbackContexts ? { fallbackContexts } : {}),\n }\n }\n\n const output: TranslateMissingResult = { results, summary }\n if (hasFallbackContexts) {\n output.fallbackContexts = fallbackContexts\n }\n return output\n}\n\n/**\n * All-layers mode: run the single-layer translate pipeline once per canonical\n * locale-backed layer and aggregate into one result. The existing summary\n * fields (`totalTranslated`, `totalFailed`, `totalSkipped`,\n * `totalWouldTranslate`) become cross-layer totals — jq consumers of the\n * single-layer summary keep working unchanged. `summary.byLayer` and the\n * per-layer `layers` sections (each in the single-layer result shape) are\n * additive.\n */\nasync function translateMissingAllLayers(\n opts: Omit<Parameters<typeof translateMissing>[0], 'layer'>,\n): Promise<TranslateAllLayersResult> {\n const config = await detectI18nConfig(opts.projectDir ?? process.cwd())\n const layerNames = collectTranslatableLayers(config)\n if (layerNames.length === 0) {\n throw new ToolError('No locale-backed layers detected. Use list_locale_dirs to inspect the configuration.', 'LAYER_NOT_FOUND')\n }\n const { layers, byLayer } = await runLayerTranslations(layerNames, opts)\n return { layers, summary: buildAllLayersSummary(layers, byLayer, opts) }\n}\n\n/**\n * Layers eligible for an all-layers run: each physical locale dir exactly\n * once. Aliases are excluded via canonicalLayers, and same-path canonical\n * entries (possible in hand-written generic configs) collapse to the first —\n * an aliased app layer must never cause a second translate/write of its\n * owner's files.\n */\nfunction collectTranslatableLayers(config: I18nConfig): string[] {\n const seenPaths = new Set<string>()\n const layerNames: string[] = []\n for (const localeDir of buildLayerGraph(config).canonicalLayers) {\n if (seenPaths.has(localeDir.path)) continue\n seenPaths.add(localeDir.path)\n layerNames.push(localeDir.layer)\n }\n return layerNames\n}\n\nasync function runLayerTranslations(\n layerNames: string[],\n opts: Omit<Parameters<typeof translateMissing>[0], 'layer'>,\n): Promise<{ layers: Record<string, TranslateMissingResult>, byLayer: TranslateLayerTotals[] }> {\n const layers: Record<string, TranslateMissingResult> = {}\n const byLayer: TranslateLayerTotals[] = []\n for (const layerName of layerNames) {\n let layerResult: TranslateMissingResult\n try {\n // A named layer always takes the single-layer branch.\n layerResult = await translateMissing({ ...opts, layer: layerName }) as TranslateMissingResult\n } catch (error) {\n // A layer without reference-locale data has nothing to drive\n // translation — skip it so one sparse layer cannot fail the run for\n // its siblings. Explicit single-layer calls still throw.\n if (error instanceof ToolError && error.code === 'NO_LOCALE_FILE') {\n log.warn(`Skipping layer \"${layerName}\": ${error.message}`)\n continue\n }\n throw error\n }\n layers[layerName] = layerResult\n byLayer.push(layerTotals(layerName, layerResult))\n }\n return { layers, byLayer }\n}\n\nfunction layerTotals(layerName: string, layerResult: TranslateMissingResult): TranslateLayerTotals {\n const layerSummary = layerResult.summary as {\n totalTranslated: number\n totalFailed: number\n totalSkipped: number\n totalWouldTranslate?: number\n }\n return {\n layer: layerName,\n totalTranslated: layerSummary.totalTranslated,\n totalFailed: layerSummary.totalFailed,\n totalSkipped: layerSummary.totalSkipped,\n totalWouldTranslate: layerSummary.totalWouldTranslate ?? 0,\n }\n}\n\nfunction buildAllLayersSummary(\n layers: Record<string, TranslateMissingResult>,\n byLayer: TranslateLayerTotals[],\n opts: Omit<Parameters<typeof translateMissing>[0], 'layer'>,\n): TranslateAllLayersSummary {\n const isDryRun = opts.dryRun ?? false\n const total = (pick: (l: TranslateLayerTotals) => number): number =>\n byLayer.reduce((sum, l) => sum + pick(l), 0)\n // Locales are project-global, so mode, reference locale, and target set are\n // identical across layers — hoist them from the first translated layer.\n const first = Object.values(layers)[0]?.summary\n const summary: TranslateAllLayersSummary = {\n mode: isDryRun ? 'dry-run' : opts.translateFn ? 'provider' : 'agent',\n totalTranslated: total(l => l.totalTranslated),\n totalFailed: total(l => l.totalFailed),\n totalSkipped: total(l => l.totalSkipped),\n ...(isDryRun ? { totalWouldTranslate: total(l => l.totalWouldTranslate) } : {}),\n layers: byLayer.map(l => l.layer),\n byLayer,\n dryRun: isDryRun,\n }\n if (first?.referenceLocale !== undefined) summary.referenceLocale = first.referenceLocale\n if (first?.targetLocales !== undefined) summary.targetLocales = first.targetLocales\n return summary\n}\n\n/**\n * Translate one key from a source locale into target locales. Unlike\n * translate_missing, this can overwrite stale existing target values.\n */\nexport async function translateKey(opts: {\n layer: string\n key: string\n sourceLocale: string\n sourceValue?: string\n targetLocales?: string[] | 'all'\n overwrite?: boolean\n dryRun?: boolean\n includePreview?: boolean\n projectDir?: string\n translateFn?: TranslateFn\n}): Promise<TranslateKeyResult> {\n const dir = opts.projectDir ?? process.cwd()\n const config = await detectI18nConfig(dir)\n const isDryRun = opts.dryRun ?? false\n const overwrite = opts.overwrite ?? true\n const sourceLocale = findLocaleOrThrow(config, opts.sourceLocale)\n const usesDefaultTargets = opts.targetLocales === undefined || opts.targetLocales === 'all'\n const resolvedTargetLocales = opts.targetLocales === undefined || opts.targetLocales === 'all'\n ? config.locales\n : opts.targetLocales.map(localeRef => findLocaleOrThrow(config, localeRef))\n const { targetLocales, protectedSkipped } = partitionTranslateKeyTargets(\n config, resolvedTargetLocales, sourceLocale.code, usesDefaultTargets,\n )\n\n const sourceData = await readLocaleData(config, opts.layer, sourceLocale)\n const existingSourceValue = getNestedValue(sourceData, opts.key)\n const sourceValue = opts.sourceValue ?? (typeof existingSourceValue === 'string' ? existingSourceValue : undefined)\n\n if (sourceValue === undefined) {\n throw new ToolError(`Source value for key \"${opts.key}\" not found in locale \"${opts.sourceLocale}\". Provide sourceValue or add the key first.`, 'SOURCE_KEY_NOT_FOUND')\n }\n\n const preview: Record<string, string> = {}\n let filesWritten = 0\n let updatedSource = false\n\n if (opts.sourceValue !== undefined && existingSourceValue !== opts.sourceValue) {\n updatedSource = true\n if (opts.includePreview || isDryRun) preview[sourceLocale.code] = opts.sourceValue\n if (!isDryRun) {\n const written = await mutateLocaleData(config, opts.layer, sourceLocale, (data) => {\n setNestedValue(data, opts.key, opts.sourceValue!)\n })\n filesWritten += written.size\n }\n }\n\n const existingTargets: Array<{ locale: LocaleDefinition, existingValue: unknown }> = []\n const failed: TranslateKeyLocaleIssue[] = []\n for (const locale of targetLocales) {\n try {\n const data = await readLocaleData(config, opts.layer, locale)\n existingTargets.push({ locale, existingValue: getNestedValue(data, opts.key) })\n } catch (error) {\n failed.push({ locale: locale.code, reason: 'read-error', detail: toErrorMessage(error) })\n }\n }\n\n const targetsToTranslate = existingTargets.filter(({ existingValue }) => {\n return overwrite || existingValue === undefined || existingValue === '' || existingValue === null\n })\n const skipped: Array<{ locale: string, reason: TranslateSkipReason }> = [\n ...protectedSkipped,\n ...existingTargets\n .filter(({ existingValue }) => !overwrite && existingValue !== undefined && existingValue !== '' && existingValue !== null)\n .map(({ locale }) => ({ locale: locale.code, reason: 'already-translated' as const })),\n ]\n\n const basePlaceholderValidation = validatePlaceholders(opts.key, sourceValue, [{ locale: sourceLocale.code, value: sourceValue }], config.localeFileFormat)\n\n if (isDryRun) {\n return {\n key: opts.key,\n sourceLocale: localeRefInfo(sourceLocale),\n updatedSource,\n mode: 'dry-run',\n translated: [],\n wouldTranslate: targetsToTranslate.map(({ locale }) => locale.code),\n skipped,\n failed,\n filesWritten: 0,\n dryRun: true,\n placeholderValidation: basePlaceholderValidation,\n ...(opts.includePreview ? { preview } : {}),\n }\n }\n\n if (targetsToTranslate.length === 0) {\n return {\n key: opts.key,\n sourceLocale: localeRefInfo(sourceLocale),\n updatedSource,\n mode: opts.translateFn ? 'provider' : 'agent',\n translated: [],\n skipped,\n failed,\n filesWritten,\n dryRun: false,\n placeholderValidation: basePlaceholderValidation,\n ...(opts.includePreview ? { preview } : {}),\n }\n }\n\n if (!opts.translateFn) {\n // Agent mode: nothing was attempted — targets are skipped, not failed.\n return {\n key: opts.key,\n sourceLocale: localeRefInfo(sourceLocale),\n updatedSource,\n mode: 'agent',\n translated: [],\n skipped: [\n ...skipped,\n ...targetsToTranslate.map(({ locale }) => ({ locale: locale.code, reason: 'no-provider' as const })),\n ],\n failed,\n filesWritten,\n dryRun: false,\n placeholderValidation: basePlaceholderValidation,\n fallbackContext: buildFallbackContext(\n config.projectConfig,\n sourceLocale.language || sourceLocale.code,\n targetsToTranslate.map(({ locale }) => locale.language || locale.code).join(', '),\n { [opts.key]: sourceValue },\n ),\n ...(opts.includePreview ? { preview } : {}),\n }\n }\n\n const translated: string[] = []\n const placeholderValidations: PlaceholderValidationResult[] = [basePlaceholderValidation]\n let model: string | undefined\n\n for (const { locale } of targetsToTranslate) {\n let targetValue: string | undefined\n let requestFailed = false\n let responseTruncated = false\n const systemPrompt = buildTranslationSystemPrompt(config.projectConfig, locale.language || locale.code, config.localeFileFormat)\n const userMessage = buildTranslationUserMessage(\n sourceLocale.language || sourceLocale.code,\n locale.language || locale.code,\n { [opts.key]: sourceValue },\n config.localeFileFormat,\n )\n\n for (let attempt = 0; attempt < 2; attempt++) {\n if (attempt > 0) {\n const delayMs = 2000 * 2 ** attempt\n await new Promise(r => setTimeout(r, delayMs))\n }\n try {\n const response = await opts.translateFn({\n systemPrompt,\n userMessage,\n maxTokens: computeMaxTokens(1),\n })\n model = response.model\n if (response.truncated) {\n responseTruncated = true\n log.warn(`Translate response truncated for ${locale.code}: provider hit the token limit.`)\n } else if (response.text.trim() === '') {\n throw new TranslateProviderError('Provider returned an empty response', 'provider')\n } else {\n const parsed = extractJsonFromResponse(response.text)\n const parsedValue = parsed[opts.key]\n if (typeof parsedValue === 'string') {\n targetValue = parsedValue\n }\n }\n requestFailed = false\n break\n } catch (error) {\n if (error instanceof TranslateProviderError && error.kind === 'auth') {\n // Auth failures affect every request — abort the whole run.\n throw new ToolError(\n `Provider authentication failed: ${error.message}. Verify your API key (config apiKey or the provider's environment variable).`,\n 'PROVIDER_AUTH_ERROR',\n )\n }\n requestFailed = true\n if (attempt === 0) {\n log.warn(`Translate request failed for ${locale.code}: ${toErrorMessage(error)}. Retrying (attempt 2)`)\n } else {\n log.warn(`Translate retry failed for ${locale.code}: ${toErrorMessage(error)}`)\n }\n }\n }\n\n if (!targetValue) {\n failed.push({\n locale: locale.code,\n reason: responseTruncated ? 'truncated' : requestFailed ? 'provider-error' : 'omitted-by-model',\n })\n continue\n }\n\n const validation = validatePlaceholders(opts.key, sourceValue, [{ locale: locale.code, value: targetValue }], config.localeFileFormat)\n placeholderValidations.push(validation)\n const firstIssue = validation.errors[0]\n if (firstIssue !== undefined) {\n failed.push({ locale: locale.code, reason: failReasonForIssue(firstIssue) })\n continue\n }\n\n if (opts.includePreview) preview[locale.code] = targetValue\n try {\n const written = await mutateLocaleData(config, opts.layer, locale, (data) => {\n setNestedValue(data, opts.key, targetValue)\n })\n filesWritten += written.size\n translated.push(locale.code)\n } catch (error) {\n failed.push({ locale: locale.code, reason: 'write-error', detail: toErrorMessage(error) })\n }\n }\n\n return {\n key: opts.key,\n sourceLocale: localeRefInfo(sourceLocale),\n updatedSource,\n mode: 'provider',\n translated,\n skipped,\n failed,\n filesWritten,\n dryRun: false,\n model,\n placeholderValidation: mergePlaceholderValidation(placeholderValidations) ?? basePlaceholderValidation,\n ...(opts.includePreview ? { preview } : {}),\n }\n}\n","/**\n * Read-only operations: config detection, locale-dir listing, translation\n * lookup/search, missing/empty detection, and namespace browsing.\n */\n\nimport { readdir } from 'node:fs/promises'\n\nimport { detectI18nConfig, clearConfigCache } from '../config/detector.js'\nimport type { I18nConfig } from '../config/types.js'\nimport { writeReportFile } from '../io/json-writer.js'\nimport { readLocaleData, readLocaleDataIfPresent, resolveLocaleEntries } from '../io/locale-data.js'\nimport { getNestedValue, getLeafKeys } from '../io/key-operations.js'\nimport { ToolError } from '../utils/errors.js'\n\nimport type { LocaleDirInfo, SearchMatch, MissingTranslationsResult, EmptyTranslationsResult } from './types.js'\nimport { findLayerOrThrow, findReferenceLocaleOrThrow, findLocaleImpl, localeRefInfo, resolveLayersToScan } from './shared.js'\nimport { resolveOutputFile, resolveReportFilePath } from './report.js'\n\n/**\n * Detect the i18n configuration from the project, always bypassing the\n * config cache (clears it first).\n */\nexport async function detectConfig(projectDir?: string): Promise<I18nConfig> {\n const dir = projectDir ?? process.cwd()\n clearConfigCache()\n return detectI18nConfig(dir)\n}\n\n/**\n * List all i18n locale directories in the project, grouped by layer.\n */\nexport async function listLocaleDirs(projectDir?: string): Promise<LocaleDirInfo[]> {\n const dir = projectDir ?? process.cwd()\n const config = await detectI18nConfig(dir)\n\n const results: LocaleDirInfo[] = []\n\n for (const localeDir of config.localeDirs) {\n if (localeDir.aliasOf) {\n results.push({\n layer: localeDir.layer,\n path: localeDir.path,\n aliasOf: localeDir.aliasOf,\n fileCount: 0,\n topLevelKeys: [],\n })\n continue\n }\n\n if (config.localeFileFormat === 'php-array') {\n let subDirs: string[] = []\n try { subDirs = await readdir(localeDir.path) } catch {}\n\n const sampleLocale = config.locales[0]\n let namespaces: string[] = []\n if (sampleLocale) {\n try {\n const entries = await resolveLocaleEntries(config, localeDir.layer, sampleLocale)\n namespaces = entries.map(e => e.namespace).filter((n): n is string => n !== null)\n } catch {}\n }\n\n results.push({\n layer: localeDir.layer,\n path: localeDir.path,\n fileCount: subDirs.length,\n namespaces,\n })\n } else {\n const files = await readdir(localeDir.path)\n const jsonFiles = files.filter(f => f.endsWith('.json'))\n\n let topLevelKeys: string[] = []\n const sampleLocale = config.locales[0]\n if (sampleLocale !== undefined && jsonFiles.length > 0) {\n try {\n const data = await readLocaleData(config, localeDir.layer, sampleLocale)\n topLevelKeys = Object.keys(data)\n } catch {}\n }\n\n results.push({\n layer: localeDir.layer,\n path: localeDir.path,\n fileCount: jsonFiles.length,\n topLevelKeys,\n })\n }\n }\n\n return results\n}\n\n/**\n * Get translation values for given key paths from a specific locale and layer.\n */\nexport async function getTranslations(opts: {\n layer: string\n locale: string\n keys: string[]\n compact?: boolean\n projectDir?: string\n}): Promise<Record<string, Record<string, unknown>>> {\n const { layer, locale, keys } = opts\n const dir = opts.projectDir ?? process.cwd()\n const config = await detectI18nConfig(dir)\n\n findLayerOrThrow(config, layer)\n\n const localesToRead = locale === '*'\n ? config.locales\n : (() => {\n const found = findLocaleImpl(config, locale)\n if (!found) {\n throw new ToolError(`Locale not found: \"${locale}\". Available: ${config.locales.map(l => l.code).join(', ')}. Use one of the available locale codes or file names.`, 'LOCALE_NOT_FOUND')\n }\n return [found]\n })()\n\n const results: Record<string, Record<string, unknown>> = {}\n\n for (const loc of localesToRead) {\n const data = await readLocaleData(config, layer, loc)\n results[loc.code] = Object.fromEntries(\n keys.map(k => [k, getNestedValue(data, k) ?? null]),\n )\n }\n\n // Compact mode: summarize by key across all locales\n if (opts.compact && locale === '*' && localesToRead.length > 1) {\n const byKey: Record<string, { status: string; totalPresent: number; empty?: string[]; missing?: string[] }> = {}\n for (const key of keys) {\n let present = 0\n const empty: string[] = []\n const missing: string[] = []\n for (const loc of localesToRead) {\n const val = results[loc.code]?.[key]\n if (val === undefined || val === null) {\n missing.push(loc.code)\n } else if (val === '') {\n empty.push(loc.code)\n } else {\n present++\n }\n }\n byKey[key] = {\n status: present === localesToRead.length ? 'ok' : present > 0 ? 'partial' : 'missing',\n totalPresent: present,\n ...(empty.length > 0 && { empty }),\n ...(missing.length > 0 && { missing }),\n }\n }\n return { byKey } as unknown as Record<string, Record<string, unknown>>\n }\n\n return results\n}\n\n/**\n * Find translation keys that exist in the reference locale but are missing in other locales.\n */\nexport async function getMissingTranslations(opts: {\n layer?: string\n referenceLocale?: string\n targetLocales?: string[]\n locales?: string[]\n projectDir?: string\n outputFile?: string\n}): Promise<MissingTranslationsResult> {\n const { layer } = opts\n const dir = opts.projectDir ?? process.cwd()\n const config = await detectI18nConfig(dir)\n\n const refLocale = findReferenceLocaleOrThrow(config, opts.referenceLocale)\n\n const resolvedTargets = opts.targetLocales ?? opts.locales\n const targets = resolvedTargets\n ? resolvedTargets.map((code) => {\n const loc = findLocaleImpl(config, code)\n if (!loc) {\n throw new ToolError(`Target locale not found: \"${code}\". Available: ${config.locales.map(l => l.code).join(', ')}. Pass valid locale codes in targetLocales.`, 'LOCALE_NOT_FOUND')\n }\n return loc\n })\n : config.locales.filter(l => l.code !== refLocale.code)\n\n const layersToScan = resolveLayersToScan(config, layer)\n\n const result: Record<string, Record<string, string[]>> = {}\n let totalMissing = 0\n\n for (const localeDir of layersToScan) {\n const refData = await readLocaleDataIfPresent(config, localeDir.layer, refLocale)\n if (!refData) continue\n\n const refKeys = getLeafKeys(refData).filter(k => {\n const v = getNestedValue(refData, k)\n return typeof v === 'string' ? v.length > 0 : v !== null && v !== undefined\n })\n if (refKeys.length === 0) continue\n\n for (const target of targets) {\n let targetData: Record<string, unknown> = {}\n\n try {\n targetData = await readLocaleData(config, localeDir.layer, target)\n } catch {}\n\n const missing = refKeys.filter(k => {\n const v = getNestedValue(targetData, k)\n return v === undefined || v === '' || v === null\n })\n\n if (missing.length > 0) {\n (result[target.code] ??= {})[localeDir.layer] = missing\n totalMissing += missing.length\n }\n }\n }\n\n const output = {\n missing: result,\n summary: {\n referenceLocale: localeRefInfo(refLocale),\n targetLocales: targets.map(localeRefInfo),\n layersScanned: layersToScan.map(d => d.layer),\n totalMissingKeys: totalMissing,\n },\n }\n\n const reportPath = resolveOutputFile(dir, opts.outputFile) ?? resolveReportFilePath(config, dir, 'get_missing_translations')\n if (reportPath) {\n await writeReportFile(reportPath, output, {\n tool: 'get_missing_translations',\n args: { layer, referenceLocale: opts.referenceLocale, targetLocales: opts.targetLocales },\n })\n return { reportFile: reportPath, summary: output.summary }\n }\n\n return output\n}\n\n/**\n * Find translation keys that have empty string values in locale files.\n */\nexport async function findEmptyTranslations(opts: {\n layer?: string\n locale?: string\n projectDir?: string\n outputFile?: string\n}): Promise<EmptyTranslationsResult> {\n const { layer, locale } = opts\n const dir = opts.projectDir ?? process.cwd()\n const config = await detectI18nConfig(dir)\n\n const localesToCheck = locale\n ? (() => {\n const loc = findLocaleImpl(config, locale)\n if (!loc) {\n throw new ToolError(\n `Locale not found: \"${locale}\". Available: ${config.locales.map(l => l.code).join(', ')}`,\n 'LOCALE_NOT_FOUND',\n )\n }\n return [loc]\n })()\n : config.locales\n\n const layersToScan = layer\n ? config.localeDirs.filter(d => d.layer === layer)\n : config.localeDirs.filter(d => !d.aliasOf)\n\n if (layersToScan.length === 0) {\n if (layer) {\n findLayerOrThrow(config, layer)\n }\n throw new ToolError('No locale directories found.', 'LAYER_NOT_FOUND')\n }\n\n const emptyKeys: Record<string, Record<string, string[]>> = {}\n let totalEmpty = 0\n\n for (const localeDir of layersToScan) {\n for (const loc of localesToCheck) {\n const data = await readLocaleDataIfPresent(config, localeDir.layer, loc)\n if (!data) continue\n\n const leafKeys = getLeafKeys(data)\n const empty = leafKeys.filter(k => getNestedValue(data, k) === '')\n\n if (empty.length > 0) {\n (emptyKeys[loc.code] ??= {})[localeDir.layer] = empty\n totalEmpty += empty.length\n }\n }\n }\n\n const output = {\n emptyKeys,\n summary: {\n totalEmpty,\n localesChecked: localesToCheck.map(l => l.code),\n layersChecked: layersToScan.map(d => d.layer),\n },\n }\n\n const reportPath = resolveOutputFile(dir, opts.outputFile) ?? resolveReportFilePath(config, dir, 'find_empty_translations')\n if (reportPath) {\n await writeReportFile(reportPath, output, {\n tool: 'find_empty_translations',\n args: { layer, locale },\n })\n return { reportFile: reportPath, summary: output.summary }\n }\n\n return output\n}\n\n/**\n * Search translation files by key pattern or value substring.\n */\nexport async function searchTranslations(opts: {\n query: string\n searchIn?: 'keys' | 'values' | 'both'\n layer?: string\n locale?: string\n projectDir?: string\n outputFile?: string\n}): Promise<{ matches: SearchMatch[]; totalMatches: number } | { reportFile: string; summary: { totalMatches: number } }> {\n const { query, layer, locale, outputFile } = opts\n const dir = opts.projectDir ?? process.cwd()\n const config = await detectI18nConfig(dir)\n\n const mode = opts.searchIn ?? 'both'\n const queryLower = query.toLowerCase()\n\n const layersToSearch = (layer && layer !== '*')\n ? config.localeDirs.filter(d => d.layer === layer)\n : config.localeDirs.filter(d => !d.aliasOf)\n\n if (layersToSearch.length === 0) {\n if (layer && layer !== '*') {\n findLayerOrThrow(config, layer)\n }\n throw new ToolError('No locale directories found. Run detect_i18n_config to verify the project setup.', 'LAYER_NOT_FOUND')\n }\n\n const localesToSearch = locale\n ? (() => {\n const found = findLocaleImpl(config, locale)\n if (!found) {\n throw new ToolError(`Locale not found: \"${locale}\". Available: ${config.locales.map(l => l.code).join(', ')}. Use one of the available locale codes or file names.`, 'LOCALE_NOT_FOUND')\n }\n return [found]\n })()\n : config.locales\n\n const matches: SearchMatch[] = []\n\n for (const localeDir of layersToSearch) {\n for (const loc of localesToSearch) {\n const data = await readLocaleDataIfPresent(config, localeDir.layer, loc)\n if (!data) continue\n\n const leafKeys = getLeafKeys(data)\n\n for (const key of leafKeys) {\n const value = getNestedValue(data, key)\n const valueStr = typeof value === 'string' ? value : JSON.stringify(value)\n\n const keyMatch = mode === 'keys' || mode === 'both'\n ? key.toLowerCase().includes(queryLower)\n : false\n const valueMatch = mode === 'values' || mode === 'both'\n ? valueStr.toLowerCase().includes(queryLower)\n : false\n\n if (keyMatch || valueMatch) {\n matches.push({\n layer: localeDir.layer,\n locale: loc.code,\n key,\n value,\n })\n }\n }\n }\n }\n\n const output = { matches, totalMatches: matches.length }\n\n const reportPath = resolveOutputFile(dir, outputFile) ?? resolveReportFilePath(config, dir, 'search_translations')\n if (reportPath) {\n await writeReportFile(reportPath, output, {\n tool: 'search_translations',\n args: { query, searchIn: opts.searchIn, layer, locale },\n })\n return { reportFile: reportPath, summary: { totalMatches: matches.length } }\n }\n\n return output\n}\n\n// ─── list_namespaces ────────────────────────────────────────────\n\nexport interface NamespaceNode {\n keyCount: number\n children?: Record<string, NamespaceNode>\n}\n\nexport interface ListNamespacesResult {\n layers: Record<string, { namespaces: Record<string, NamespaceNode> }>\n}\n\n/**\n * Build a prefix tree of all translation keys grouped by layer and namespace.\n * Useful for agents to browse available keys without guesswork.\n */\nexport async function listNamespaces(opts: {\n layer?: string\n locale?: string\n projectDir?: string\n}): Promise<ListNamespacesResult> {\n const dir = opts.projectDir ?? process.cwd()\n const config = await detectI18nConfig(dir)\n\n if (opts.layer && opts.layer !== '*') {\n findLayerOrThrow(config, opts.layer)\n }\n const layersToScan = (opts.layer && opts.layer !== '*')\n ? config.localeDirs.filter(d => d.layer === opts.layer)\n : config.localeDirs.filter(d => !d.aliasOf)\n\n const localeToUse = opts.locale\n ? findLocaleImpl(config, opts.locale) ?? (() => {\n throw new ToolError(`Locale not found: \"${opts.locale}\". Available: ${config.locales.map(l => l.code).join(', ')}.`, 'LOCALE_NOT_FOUND')\n })()\n : findLocaleImpl(config, config.defaultLocale) ?? config.locales[0]\n\n if (!localeToUse) {\n throw new ToolError('No locales found in configuration.', 'LOCALE_NOT_FOUND')\n }\n\n const layers: Record<string, { namespaces: Record<string, NamespaceNode> }> = {}\n\n for (const ld of layersToScan) {\n let data: Record<string, unknown>\n try {\n data = await readLocaleData(config, ld.layer, localeToUse)\n }\n catch {\n continue\n }\n\n const keys = getLeafKeys(data)\n if (keys.length === 0) continue\n\n const root: NamespaceNode = { keyCount: 0, children: {} }\n\n for (const key of keys) {\n const segments = key.split('.')\n let node = root\n for (const seg of segments) {\n if (!node.children) node.children = {}\n if (!node.children[seg]) {\n node.children[seg] = { keyCount: 0 }\n }\n node = node.children[seg]\n }\n node.keyCount++ // leaf count at terminal node\n }\n\n propagateCounts(root)\n\n layers[ld.layer] = { namespaces: root.children ?? {} }\n }\n\n return { layers }\n}\n\nfunction propagateCounts(node: NamespaceNode): number {\n if (!node.children || Object.keys(node.children).length === 0) {\n return node.keyCount\n }\n\n let total = 0\n for (const child of Object.values(node.children)) {\n total += propagateCounts(child)\n }\n node.keyCount = total\n return total\n}\n","import type { LocaleFileFormat } from '../adapters/types.js'\n\n// ─── Types ──────────────────────────────────────────────────────\n\nexport interface ScanPatternSet {\n label: string\n filePatterns: string[]\n ignoreDirs: string[]\n /** Must capture: (1) callee, (2) quote char, (3) key */\n staticKeyPatterns: RegExp[]\n /** Must capture: (1) callee, (2) template content */\n dynamicKeyPatterns: RegExp[]\n /** Must capture: (1) callee, (2) quote char, (3) prefix */\n concatKeyPatterns: RegExp[]\n /**\n * Returns true if the callee should be skipped when the key has no dot.\n * Vue `t('word')` without a dot is likely not i18n — `emit`, `import`, etc.\n */\n requiresDotForCallee?: (callee: string) => boolean\n /**\n * When true, dynamic-pattern matches without interpolation are promoted to static keys.\n * Enables backtick literals like `t(\\`foo.bar\\`)` to be recognized as static.\n */\n promoteStaticDynamicMatches?: boolean\n /**\n * When true, `${identifier}` interpolations are resolved against same-file\n * `const`/`let` string-literal declarations before classification (#284).\n * JS/TS-only: PHP's `${var}` interpolation reads runtime variables, so\n * substituting declared constants there could wrongly narrow a pattern.\n */\n resolveLocalConsts?: boolean\n /**\n * Language family for the context-free bare-candidate collectors (#288).\n * 'js' (default) runs the template-literal and `+`-concat shapes; 'php'\n * runs the double-quoted `{$var}` interpolation shape instead. Ungated,\n * the PHP shape matches Vue template attributes (`v-if=\"$slots.header\"`),\n * producing `${_}.header`-class candidates that suppress every key ending\n * in those segments. Language-neutral shapes (dotted literals,\n * trailing-dot prefixes) always run.\n */\n bareShapes?: 'js' | 'php'\n}\n\n// ─── Vue / Nuxt Patterns ────────────────────────────────────────\n\n/**\n * Matches static i18n calls: $t('key'), t('key'), this.$t('key'), $te('key'), this.$te('key'), and double-quote variants.\n * Group 1: callee ($t | t | this.$t | $te | this.$te)\n * Group 2: quote character\n * Group 3: the key string\n */\nconst VUE_STATIC_KEY = /(?<!\\w)(this\\.\\$te?|\\$te?|\\bt)\\s*\\(\\s*(['\"])((?:(?!\\2).)*)\\2/g\n\n/**\n * Matches dynamic i18n calls with template literals: $t(`prefix.${var}`), t(`...`), this.$t(`...`), $te(`...`), this.$te(`...`)\n * Group 1: callee\n * Group 2: template literal content (without backticks)\n */\nconst VUE_DYNAMIC_KEY = /(?<!\\w)(this\\.\\$te?|\\$te?|\\bt)\\s*\\(\\s*`((?:[^`]|\\\\.)*)`/g\n\n/**\n * Matches concatenation-based dynamic keys: t('prefix.' + var), $t(\"key.\" + expr), $te('prefix.' + var), this.$te(\"key.\" + expr)\n * Group 1: callee\n * Group 2: quote character\n * Group 3: the static prefix string\n */\nconst VUE_CONCAT_KEY = /(?<!\\w)(this\\.\\$te?|\\$te?|\\bt)\\s*\\(\\s*(['\"])((?:(?!\\2).)*)\\2\\s*\\+/g\n\nexport const VUE_NUXT_PATTERNS: ScanPatternSet = {\n label: 'Vue / Nuxt',\n filePatterns: ['**/*.vue', '**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx', '**/*.mjs', '**/*.mts'],\n ignoreDirs: ['node_modules', '.nuxt', '.output', 'dist', '.git', 'coverage', '.tmp'],\n staticKeyPatterns: [VUE_STATIC_KEY],\n dynamicKeyPatterns: [VUE_DYNAMIC_KEY],\n concatKeyPatterns: [VUE_CONCAT_KEY],\n requiresDotForCallee: (callee: string) => callee === 't',\n promoteStaticDynamicMatches: true,\n resolveLocalConsts: true,\n bareShapes: 'js',\n}\n\n// ─── Laravel / PHP Patterns ─────────────────────────────────────\n\n/**\n * Matches Laravel static translation calls with single or double quotes:\n * __('key') → Group 1: __ Group 2: ' Group 3: key\n * trans('key') → Group 1: trans Group 2: ' Group 3: key\n * trans_choice('k',n) → Group 1: trans_choice Group 2: ' Group 3: k\n * Lang::get('key') → Group 1: Lang::get Group 2: ' Group 3: key\n * @lang('key') → Group 1: @lang Group 2: ' Group 3: key\n */\nconst LARAVEL_STATIC_KEY = /(?<!\\w)(__|\\btrans_choice|\\btrans|Lang::get|@lang)\\s*\\(\\s*(['\"])((?:(?!\\2).)*)\\2/g\n\n/**\n * Matches Laravel dynamic calls with PHP variable interpolation in double-quoted strings:\n * __(\"prefix.{$var}.suffix\") — PHP interpolation only works in double quotes\n * Group 1: callee\n * Group 2: the string content (may contain {$var} or $var)\n */\nconst LARAVEL_DYNAMIC_KEY = /(?<!\\w)(__|\\btrans_choice|\\btrans|Lang::get|@lang)\\s*\\(\\s*\"((?:[^\"\\\\]|\\\\.)*)\"\\s*[,)]/g\n\n/**\n * Matches Laravel concatenation-based dynamic keys:\n * __('prefix.' . $var) → PHP concat operator is `.`\n * trans('key.' . $expr)\n * Group 1: callee\n * Group 2: quote character\n * Group 3: the static prefix string\n */\nconst LARAVEL_CONCAT_KEY = /(?<!\\w)(__|\\btrans_choice|\\btrans|Lang::get|@lang)\\s*\\(\\s*(['\"])((?:(?!\\2).)*)\\2\\s*\\./g\n\nexport const LARAVEL_PATTERNS: ScanPatternSet = {\n label: 'Laravel',\n filePatterns: ['**/*.blade.php', '**/*.php'],\n ignoreDirs: ['vendor', 'storage', 'bootstrap/cache', 'node_modules', '.git', 'dist', 'coverage'],\n staticKeyPatterns: [LARAVEL_STATIC_KEY],\n dynamicKeyPatterns: [LARAVEL_DYNAMIC_KEY],\n concatKeyPatterns: [LARAVEL_CONCAT_KEY],\n bareShapes: 'php',\n}\n\n// ─── Resolution ─────────────────────────────────────────────────\n\n/**\n * Maps locale file format to the appropriate scan pattern set.\n * 'php-array' → Laravel (PHP translation helpers in Blade/PHP files).\n * 'json' / undefined → Vue/Nuxt ($t / t calls in Vue/TS/JS files).\n */\nexport function getPatternSet(format?: LocaleFileFormat): ScanPatternSet {\n switch (format) {\n case 'php-array':\n return LARAVEL_PATTERNS\n default:\n return VUE_NUXT_PATTERNS\n }\n}\n","import { readFile } from 'node:fs/promises'\nimport { isAbsolute, join, relative, sep } from 'node:path'\nimport { glob } from 'tinyglobby'\nimport { log } from '../utils/logger.js'\nimport type { ScanPatternSet } from './patterns.js'\nimport { VUE_NUXT_PATTERNS } from './patterns.js'\n\n// ─── Types ──────────────────────────────────────────────────────\n\nexport interface KeyUsage {\n key: string\n file: string\n line: number\n callee: string\n}\n\nexport interface DynamicKeyUsage {\n expression: string\n file: string\n line: number\n callee: string\n}\n\nexport interface ScanResult {\n usages: KeyUsage[]\n dynamicKeys: DynamicKeyUsage[]\n filesScanned: number\n uniqueKeys: Set<string>\n /**\n * All quoted strings containing at least one dot, extracted from source files.\n * These are NOT confirmed i18n keys — they must be intersected with actual\n * locale keys to identify bare key references (e.g., `{ name: 'common.actions.save', i18n: true }`).\n */\n bareStringCandidates: Set<string>\n /**\n * Template literal expressions containing at least one dot and `${...}` interpolation,\n * extracted from source files regardless of i18n call context.\n * Format: `` `prefix.${_}.suffix` `` — ready to feed into `buildDynamicKeyRegexes`.\n */\n bareDynamicCandidates: Set<string>\n}\n\n// ─── Const-table resolution (#284) ──────────────────────────────\n\n/**\n * Matches `const` declarations initialized to a key-shaped string literal\n * (≥1 dot). Scope is deliberately tight: identifier = literal only — no\n * object properties, no expressions, and no `let` (a reassigned binding\n * would substitute a stale literal and bypass the conservative widening).\n */\nconst CONST_KEY_DECL = /\\bconst\\s+([A-Za-z_$][\\w$]*)\\s*=\\s*(['\"])((?:[\\w-]+\\.)+[\\w-]+)\\2/g\n\n/**\n * Collects same-file `const NAME = 'dotted.path'` declarations so\n * `${NAME}` interpolations can be substituted with the literal value.\n * Same-file only — imported/cross-file constants are NOT resolved and fall\n * back to the conservative `${_}` widening in buildDynamicKeyRegexes.\n * A name bound to different values (shadowing across scopes) is ambiguous\n * and dropped: substituting one of several possible values could narrow a\n * pattern past a live key.\n */\nfunction collectConstKeyTable(content: string): Map<string, string> {\n const table = new Map<string, string>()\n const ambiguous = new Set<string>()\n CONST_KEY_DECL.lastIndex = 0\n for (const match of content.matchAll(CONST_KEY_DECL)) {\n const name = match[1]\n const value = match[3]\n if (!name || !value || ambiguous.has(name)) continue\n const existing = table.get(name)\n if (existing !== undefined && existing !== value) {\n table.delete(name)\n ambiguous.add(name)\n continue\n }\n table.set(name, value)\n }\n return table\n}\n\n/**\n * Substitutes `${NAME}` interpolations with the const table's literal value,\n * producing an exact or narrower pattern: `${i18nBase}.title` +\n * `const i18nBase = 'a.b.c'` → `a.b.c.title`. Only plain-identifier\n * interpolations qualify; member expressions and anything else stay dynamic.\n */\nfunction substituteConstIdentifiers(expr: string, table: Map<string, string>): string {\n if (table.size === 0 || !expr.includes('${')) return expr\n return expr.replace(/\\$\\{\\s*([A-Za-z_$][\\w$]*)\\s*\\}/g, (whole, name: string) => table.get(name) ?? whole)\n}\n\n// ─── Extraction ─────────────────────────────────────────────────\n\n/**\n * Extract all i18n key references from file content.\n * Returns static usages and dynamic (unresolvable) references.\n *\n * When `patterns` is omitted, defaults to Vue/Nuxt patterns.\n */\ninterface LineExtraction {\n pat: ScanPatternSet\n constTable: Map<string, string>\n filePath: string\n usages: KeyUsage[]\n dynamicKeys: DynamicKeyUsage[]\n /** Arguments to an ambiguous callee — see extractStaticMatches. */\n bareCandidates: Set<string>\n}\n\nfunction extractStaticMatches(line: string, lineNumber: number, ctx: LineExtraction): void {\n for (const regex of ctx.pat.staticKeyPatterns) {\n regex.lastIndex = 0\n for (const match of line.matchAll(regex)) {\n const callee = match[1] ?? ''\n const key = match[3]\n if (!key) continue\n if (key.includes('{$')) continue\n // A bare `t('word')` is ambiguous — `emit('save')` is not a translation —\n // so it is not evidence of usage. Dropping it entirely was worse: a flat\n // catalogue's keys then look unreferenced and remove-orphans offers a live\n // key for deletion (#298). Kept as a candidate instead, which protects a\n // key only when one of that exact name exists.\n if (ctx.pat.requiresDotForCallee?.(callee) && !key.includes('.')) {\n ctx.bareCandidates.add(key)\n continue\n }\n ctx.usages.push({ key, file: ctx.filePath, line: lineNumber, callee })\n }\n }\n}\n\n/**\n * Normalizes every interpolation syntax (JS `${expr}`, PHP `{$expr}` and\n * bare `$var->prop`) to `${_}` slots. Returns undefined when the expression\n * contains no interpolation at all.\n */\nfunction normalizeDynamicExpression(expression: string): string | undefined {\n const hasDollarBrace = expression.includes('${')\n const hasBraceDollar = expression.includes('{$')\n const hasBarePHP = !hasDollarBrace && !hasBraceDollar && /\\$[a-zA-Z_]/.test(expression)\n if (!hasDollarBrace && !hasBraceDollar && !hasBarePHP) return undefined\n return hasBraceDollar\n ? expression.replace(/\\{\\$[^}]+\\}/g, '${_}')\n : hasBarePHP\n ? expression.replace(/\\$[a-zA-Z_][a-zA-Z0-9_]*(?:->[a-zA-Z_][a-zA-Z0-9_]*)*/g, '${_}')\n : expression\n}\n\nfunction extractDynamicMatches(line: string, lineNumber: number, ctx: LineExtraction): void {\n for (const regex of ctx.pat.dynamicKeyPatterns) {\n regex.lastIndex = 0\n for (const match of line.matchAll(regex)) {\n const callee = match[1] ?? ''\n const raw = match[2]\n if (!raw) continue\n // Const-resolved expressions lose their interpolation and, with\n // promoteStaticDynamicMatches, become exact static usages below.\n const expression = substituteConstIdentifiers(raw, ctx.constTable)\n const normalized = normalizeDynamicExpression(expression)\n if (normalized === undefined) {\n if (!ctx.pat.promoteStaticDynamicMatches) continue\n if (!expression) continue\n if (ctx.pat.requiresDotForCallee?.(callee) && !expression.includes('.')) continue\n ctx.usages.push({ key: expression, file: ctx.filePath, line: lineNumber, callee })\n continue\n }\n ctx.dynamicKeys.push({ expression: `\\`${normalized}\\``, file: ctx.filePath, line: lineNumber, callee })\n }\n }\n}\n\nfunction extractConcatMatches(line: string, lineNumber: number, ctx: LineExtraction): void {\n for (const regex of ctx.pat.concatKeyPatterns) {\n regex.lastIndex = 0\n for (const match of line.matchAll(regex)) {\n const callee = match[1] ?? ''\n const prefix = match[3]\n if (!prefix) continue\n if (ctx.pat.requiresDotForCallee?.(callee) && !prefix.includes('.')) continue\n ctx.dynamicKeys.push({ expression: `\\`${prefix}\\${_}\\``, file: ctx.filePath, line: lineNumber, callee })\n }\n }\n}\n\nexport function extractKeys(content: string, filePath: string, patterns?: ScanPatternSet, constTable?: Map<string, string>): { usages: KeyUsage[]; dynamicKeys: DynamicKeyUsage[]; bareStringCandidates: Set<string> } {\n const pat = patterns ?? VUE_NUXT_PATTERNS\n const ctx: LineExtraction = {\n pat,\n constTable: constTable ?? (pat.resolveLocalConsts ? collectConstKeyTable(content) : new Map<string, string>()),\n filePath,\n usages: [],\n dynamicKeys: [],\n bareCandidates: new Set<string>(),\n }\n\n const lines = content.split('\\n')\n for (const [i, line] of lines.entries()) {\n const lineNumber = i + 1\n extractStaticMatches(line, lineNumber, ctx)\n extractDynamicMatches(line, lineNumber, ctx)\n extractConcatMatches(line, lineNumber, ctx)\n }\n\n return { usages: ctx.usages, dynamicKeys: ctx.dynamicKeys, bareStringCandidates: ctx.bareCandidates }\n}\n\n// ─── Dynamic key pattern matching ───────────────────────────────\n\n/**\n * Split a template literal expression on `${...}` interpolation boundaries,\n * returning only the static literal segments. Handles nested braces inside\n * interpolations (e.g. `${fn({a:1})}`) by tracking brace depth.\n */\nfunction splitInterpolations(expr: string): string[] {\n const parts: string[] = []\n let current = ''\n let i = 0\n\n while (i < expr.length) {\n if (expr[i] === '$' && expr[i + 1] === '{') {\n parts.push(current)\n current = ''\n i += 2\n let depth = 1\n while (i < expr.length && depth > 0) {\n if (expr[i] === '{') depth++\n else if (expr[i] === '}') depth--\n i++\n }\n } else {\n current += expr[i]\n i++\n }\n }\n\n parts.push(current)\n return parts\n}\n\n/**\n * Convert dynamic key expressions (template literals with interpolation) into\n * regex patterns that can match concrete translation keys.\n *\n * Example: `components.integrations.${type}.title` → /^components\\.integrations\\.[^.]+\\.title$/\n */\nexport function buildDynamicKeyRegexes(dynamicKeys: Pick<DynamicKeyUsage, 'expression'>[]): RegExp[] {\n const seen = new Set<string>()\n const regexes: RegExp[] = []\n\n for (const dk of dynamicKeys) {\n let expr = dk.expression\n if (expr.startsWith('`') && expr.endsWith('`')) {\n expr = expr.slice(1, -1)\n }\n\n if (!expr.includes('${')) continue\n\n const parts = splitInterpolations(expr)\n // #284: an interpolated variable can hold a dotted path (`${i18nBase}.title`\n // with `const i18nBase = 'a.b.c'`), so `${_}` compiles to `.+?` — any number\n // of segments — in leading, interior, and trailing position. This\n // over-suppresses: keys a single-segment variable could never reach are\n // counted as dynamic-matched. For a deletion tool that is the correct\n // trade-off — the safe-orphan list must stay safe. Guard: widening is\n // anchored to a literal key fragment — some literal part must contain a\n // word char adjacent to a dot (`.title`, `a.b.`). Without that anchor\n // (`${x}`, `${a}.${b}`, `v${major}.${minor}`) the widened regex would match\n // essentially every key (a real anny-ui scan drops to zero orphans), so\n // the bounded single-segment `[^.]+` stays for those.\n const wildcard = parts.some(part => /[\\w-]\\.|\\.[\\w-]/.test(part)) ? '.+?' : '[^.]+'\n const pattern = parts\n .map(part => part.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&'))\n .join(wildcard)\n\n if (seen.has(pattern)) continue\n seen.add(pattern)\n\n regexes.push(new RegExp(`^${pattern}$`))\n }\n\n return regexes\n}\n\nfunction suggestIgnorePattern(expression: string): string | undefined {\n let expr = expression\n if (expr.startsWith('`') && expr.endsWith('`')) expr = expr.slice(1, -1)\n const idx = expr.indexOf('${')\n if (idx <= 0) return undefined\n const prefix = expr.slice(0, idx).replace(/\\.$/, '')\n return `${prefix}.**`\n}\n\nfunction buildUnresolvedWarnings(dynamicKeys: DynamicKeyUsage[]): UnresolvedKeyWarning[] {\n // Glob order varies between runs; dedupe keeps the first entry per pattern,\n // so sort first to make the surviving representative the lexicographically\n // smallest location — report output must be byte-deterministic (CI diffing).\n const sorted = [...dynamicKeys].sort((a, b) =>\n a.file.localeCompare(b.file) || a.line - b.line || a.expression.localeCompare(b.expression))\n const seen = new Set<string>()\n const seenPatterns = new Set<string>()\n const warnings: UnresolvedKeyWarning[] = []\n for (const dk of sorted) {\n if (!dk.file || !dk.line) continue\n const pattern = suggestIgnorePattern(dk.expression)\n if (!pattern) continue\n if (seenPatterns.has(pattern)) continue\n seenPatterns.add(pattern)\n const dedup = `${dk.file}:${dk.line}:${dk.expression}`\n if (seen.has(dedup)) continue\n seen.add(dedup)\n warnings.push({\n expression: dk.expression,\n file: dk.file,\n line: dk.line,\n callee: dk.callee,\n suggestedIgnorePattern: pattern,\n })\n }\n return warnings\n}\n\n// ─── Bare candidate collection ──────────────────────────────────\n\nconst BARE_DOTTED_STRING = /(['\"])((?:[\\w-]+\\.)+[\\w-]+)\\1/g\n/**\n * Matches bare template literals with i18n-key shape, regardless of call\n * context: content outside `${...}` interpolations restricted to key-like\n * chars ([\\w.-], mirroring BARE_PHP_DYNAMIC), at least one interpolation\n * (single-level brace nesting), no newlines. A permissive backtick-to-next-\n * backtick match turns every stray backtick (comments, strings, markdown)\n * into a mega-expression swallowing whole code spans — bloating reports and\n * compiling to over-matching dynamic-key regexes when it starts with an\n * interpolation.\n */\nconst BARE_DYNAMIC_TEMPLATE = /`([\\w.-]*(?:\\$\\{(?:[^`{}\\n]|\\{[^`{}\\n]*\\})*\\}[\\w.-]*)+)`/g\n/** Longer candidates cannot plausibly be i18n keys — drop, don't truncate. */\nconst MAX_BARE_TEMPLATE_LENGTH = 120\n/**\n * Matches PHP double-quoted interpolated strings with i18n-key shape,\n * regardless of call context — `$transKey = \"api.x.{$key}\"` assigned first\n * and passed to Lang::get() later must still suppress api.x.* orphans.\n * Content is restricted to key-like chars plus {$expr} / $var->prop\n * interpolations: a permissive \"any double-quoted string containing $\"\n * match swallows the code BETWEEN quoted strings (PHP code is full of $),\n * shifting quote parity past the real candidates.\n */\nconst BARE_PHP_DYNAMIC = /\"((?:[\\w.-]|\\{\\$[^}]+\\}|\\$[a-zA-Z_][a-zA-Z0-9_]*(?:->[a-zA-Z_][a-zA-Z0-9_]*)*)+)\"/g\n/**\n * Matches prefix-shaped string literals (≥1 key-like segment, trailing dot,\n * closing quote right after the dot) regardless of call context: concat\n * prefixes ('menu.' + var, __('a.b.' . $x) — incl. multiline t() calls where\n * the prefix sits on its own line) and prefixes passed as plain arguments\n * (->translationPrefix('api.invoices.status.')) concatenated in a helper.\n * Group 2: the prefix including the trailing dot.\n */\nconst BARE_PREFIX_LITERAL = /(['\"])((?:[\\w-]+\\.)+)\\1/g\n/**\n * Matches suffix-only concat construction (#284): a string literal that is\n * entirely dot-leading key-shaped segments ('.labelPlural'), adjacent to\n * `+` on either side — `obj.translationPath + '.labelPlural'`. The variable\n * prefix is unresolvable, so the candidate keeps a bare `${_}` prefix; with\n * the `${_}` → `.+?` widening that suppresses every key ending in the\n * suffix. `+`-adjacency is required: dot-leading literals appear everywhere\n * (file extensions, decimals) and only concat context makes them key\n * evidence. A trailing `+` means the constructed key continues past the\n * literal, so the candidate gets a trailing `${_}` too. Template-literal\n * suffixes (`` `${x}.select` ``) already reach the same shape via\n * BARE_DYNAMIC_TEMPLATE. Group 1: `+` before; group 3: suffix; group 4: `+` after.\n */\nconst BARE_SUFFIX_CONCAT = /(\\+[ \\t]*)?(['\"])((?:\\.[\\w-]+)+)\\2(?:[ \\t]*(\\+))?/g\n\nfunction collectBareTemplateCandidates(content: string, constTable: Map<string, string>, bareStrings: Set<string>, bareDynamics: Set<string>): void {\n BARE_DYNAMIC_TEMPLATE.lastIndex = 0\n for (const match of content.matchAll(BARE_DYNAMIC_TEMPLATE)) {\n const raw = match[1]\n if (!raw || raw.length > MAX_BARE_TEMPLATE_LENGTH) continue\n const expr = substituteConstIdentifiers(raw, constTable)\n // Fully resolved by the const table → an exact candidate, not a pattern.\n if (!expr.includes('${')) {\n if (expr.includes('.')) bareStrings.add(expr)\n continue\n }\n const normalized = expr.replace(/\\$\\{(?:[^{}]|\\{[^}]*\\})*\\}/g, '${_}')\n // The dot must survive interpolation stripping (like BARE_PHP_DYNAMIC):\n // `${a.b}` alone has no literal key segment and would compile to a\n // match-any-single-segment regex.\n if (!normalized.replace(/\\$\\{_\\}/g, '').includes('.')) continue\n bareDynamics.add(`\\`${normalized}\\``)\n }\n}\n\nfunction collectBarePhpCandidates(content: string, bareDynamics: Set<string>): void {\n BARE_PHP_DYNAMIC.lastIndex = 0\n for (const match of content.matchAll(BARE_PHP_DYNAMIC)) {\n const expr = match[1]\n // The $-check keeps plain dotted strings out (BARE_DOTTED_STRING's job);\n // the dot must survive interpolation stripping so `{$a}$b` (no literal\n // key segment) does not become an everything-matches candidate.\n if (!expr?.includes('$')) continue\n const normalized = expr\n .replace(/\\{\\$[^}]+\\}/g, '${_}')\n .replace(/\\$[a-zA-Z_][a-zA-Z0-9_]*(?:->[a-zA-Z_][a-zA-Z0-9_]*)*/g, '${_}')\n if (!normalized.replace(/\\$\\{_\\}/g, '').includes('.')) continue\n bareDynamics.add(`\\`${normalized}\\``)\n }\n}\n\nfunction collectBarePrefixCandidates(content: string, bareDynamics: Set<string>): void {\n BARE_PREFIX_LITERAL.lastIndex = 0\n for (const match of content.matchAll(BARE_PREFIX_LITERAL)) {\n bareDynamics.add(`\\`${match[2]}\\${_}\\``)\n }\n}\n\nfunction collectBareSuffixConcatCandidates(content: string, bareDynamics: Set<string>): void {\n BARE_SUFFIX_CONCAT.lastIndex = 0\n for (const match of content.matchAll(BARE_SUFFIX_CONCAT)) {\n const suffix = match[3]\n if (!suffix || suffix.length > MAX_BARE_TEMPLATE_LENGTH) continue\n if (!match[1] && !match[4]) continue\n bareDynamics.add(`\\`\\${_}${suffix}${match[4] ? '${_}' : ''}\\``)\n }\n}\n\n/**\n * Collects context-free key evidence from one file's content: exact dotted\n * strings into `bareStrings`, interpolated/concatenated shapes into\n * `bareDynamics` (normalized to `${_}` slots for buildDynamicKeyRegexes).\n *\n * Interpolation shapes are gated by `bareShapes` (#288): the PHP shape on\n * Vue/JS files matches template attributes like `v-if=\"$slots.header\"`\n * (junk `${_}.header` families); conversely PHP key construction uses `.`\n * concat and `{$var}` interpolation only, so the backtick-template and\n * `+`-concat shapes have nothing legitimate to find there and can only\n * misfire (shell-exec backticks, `+`-adjacent decimals). Dotted literals\n * and trailing-dot prefixes are language-neutral and always run.\n */\nfunction collectBareCandidates(content: string, constTable: Map<string, string>, bareStrings: Set<string>, bareDynamics: Set<string>, bareShapes: 'js' | 'php' = 'js'): void {\n BARE_DOTTED_STRING.lastIndex = 0\n for (const match of content.matchAll(BARE_DOTTED_STRING)) {\n const candidate = match[2]\n if (candidate) bareStrings.add(candidate)\n }\n collectBarePrefixCandidates(content, bareDynamics)\n\n if (bareShapes === 'php') {\n collectBarePhpCandidates(content, bareDynamics)\n return\n }\n collectBareTemplateCandidates(content, constTable, bareStrings, bareDynamics)\n collectBareSuffixConcatCandidates(content, bareDynamics)\n}\n\n// ─── Scanning ───────────────────────────────────────────────────\n\n/**\n * Scan source files in a directory for i18n key usage.\n *\n * When `patterns` is omitted, defaults to Vue/Nuxt patterns.\n */\nexport async function scanSourceFiles(rootDir: string, excludeDirs?: string[], patterns?: ScanPatternSet): Promise<ScanResult> {\n const pat = patterns ?? VUE_NUXT_PATTERNS\n const ignore = [...pat.ignoreDirs, ...(excludeDirs ?? [])]\n\n let relativePaths: string[]\n try {\n // Sorted, because tinyglobby walks directories in parallel and promises no\n // stable order. Scan order becomes output order, so without this the same\n // binary disagrees with itself between runs on an unchanged tree — which\n // makes diffing before and after a change, the technique that catches what\n // unit tests miss, read as thousands of lines of noise (#327).\n relativePaths = (await glob(pat.filePatterns, { cwd: rootDir, ignore, dot: false, absolute: false })).sort()\n } catch {\n return { usages: [], dynamicKeys: [], filesScanned: 0, uniqueKeys: new Set(), bareStringCandidates: new Set(), bareDynamicCandidates: new Set() }\n }\n\n const allUsages: KeyUsage[] = []\n const allDynamicKeys: DynamicKeyUsage[] = []\n const bareStringCandidates = new Set<string>()\n const bareDynamicCandidates = new Set<string>()\n let filesScanned = 0\n\n for (const relPath of relativePaths) {\n const filePath = join(rootDir, relPath)\n let content: string\n try {\n content = await readFile(filePath, 'utf-8')\n } catch {\n log.warn(`Failed to read file: ${filePath}`)\n continue\n }\n\n const constTable = pat.resolveLocalConsts ? collectConstKeyTable(content) : new Map<string, string>()\n const { usages, dynamicKeys, bareStringCandidates: bareFromCalls } = extractKeys(content, filePath, pat, constTable)\n allUsages.push(...usages)\n allDynamicKeys.push(...dynamicKeys)\n for (const candidate of bareFromCalls) bareStringCandidates.add(candidate)\n\n collectBareCandidates(content, constTable, bareStringCandidates, bareDynamicCandidates, pat.bareShapes)\n\n filesScanned++\n }\n\n const uniqueKeys = new Set(allUsages.map(u => u.key))\n log.debug(`Scanned ${filesScanned} files, found ${uniqueKeys.size} unique keys, ${allDynamicKeys.length} dynamic references, ${bareStringCandidates.size} bare string candidates, ${bareDynamicCandidates.size} bare dynamic candidates`)\n\n return { usages: allUsages, dynamicKeys: allDynamicKeys, filesScanned, uniqueKeys, bareStringCandidates, bareDynamicCandidates }\n}\n\n// ─── Utilities ──────────────────────────────────────────────────\n\nexport function toRelativePath(filePath: string, rootDir: string): string {\n return relative(rootDir, filePath)\n}\n\n/**\n * Convert dot-path glob patterns (e.g., \"common.datetime.**\", \"pages.*.title\")\n * into RegExp objects for matching translation keys.\n *\n * - `**` matches any number of dot-separated segments (including zero)\n * - `*` matches exactly one segment (no dots)\n */\nexport function buildIgnorePatternRegexes(patterns: string[]): RegExp[] {\n return patterns.map((pattern) => {\n let regexStr = ''\n let i = 0\n while (i < pattern.length) {\n const ch = pattern.charAt(i)\n if (ch === '*' && pattern[i + 1] === '*') {\n regexStr += '.*'\n i += 2\n } else if (ch === '*') {\n regexStr += '[^.]*'\n i += 1\n } else if ('.+?^${}()|[]\\\\'.includes(ch)) {\n regexStr += '\\\\' + ch\n i += 1\n } else {\n regexStr += ch\n i += 1\n }\n }\n return new RegExp(`^${regexStr}$`)\n })\n}\n\n/** One scan root with a stable name (app or layer) for scope reporting. */\nexport interface ScanUnit {\n /** Unit name used in scope maps and misplaced-usage reports (app or layer name). */\n name: string\n /** Absolute directory scanned for this unit. */\n dir: string\n}\n\n/**\n * Scope-aware scan plan: which dirs to scan (each exactly once) and which\n * units vouch for each layer's keys being \"used\".\n */\nexport interface OrphanScanPlan {\n /**\n * Scan units, deduplicated by directory. When a unit's dir nests inside\n * another unit's dir, the nested subtree is excluded from the ancestor's\n * scan so every file is attributed to exactly one unit (same total file\n * work as one whole-project scan).\n */\n units: ScanUnit[]\n /**\n * Layer name → names of units whose usage evidence counts for that layer.\n * Layers missing from the map are checked against ALL units (conservative\n * global scope — never wrongly narrows).\n */\n scopeByLayer: Map<string, string[]>\n}\n\n/** A key referenced only from scan units outside its layer's consuming scope. */\nexport interface MisplacedUsage {\n key: string\n /** Layer the key is defined in. */\n layer: string\n /** Out-of-scope scan units (apps or layers) where the key was found. */\n usingApps: string[]\n}\n\ninterface OrphanScanBaseOptions {\n keysByLayer: Map<string, { keys: string[]; localeDir: { layer: string } }>\n excludeDirs?: string[]\n resolveIgnorePatterns: (layerName: string) => string[] | undefined\n patterns?: ScanPatternSet\n}\n\n/**\n * Exactly one scan source is required:\n * - `scanDirs` — explicit root directories, scanned recursively; manual\n * scope control. Every layer is checked against ONE combined usage set\n * (the pre-scope-aware global behavior) and no misplaced-usage\n * detection happens.\n * - `scanPlan` — scope-aware plan from `buildOrphanScanPlan`.\n */\nexport type OrphanScanOptions = OrphanScanBaseOptions & (\n | { scanDirs: string[]; scanPlan?: undefined }\n | { scanDirs?: undefined; scanPlan: OrphanScanPlan }\n)\n\nexport interface UnresolvedKeyWarning {\n /** The dynamic expression as detected (e.g., `` `notifications.subscriptions.${_}.message` ``) */\n expression: string\n /** Source file path */\n file: string\n /** Line number in source file */\n line: number\n /** The i18n function called (e.g., `__`, `$t`) */\n callee: string\n /** Suggested ignorePattern to suppress false-positive orphans from this expression */\n suggestedIgnorePattern: string\n}\n\nexport interface OrphanScanResult {\n orphansByLayer: Record<string, string[]>\n orphanCount: number\n uncertainByLayer: Record<string, string[]>\n uncertainCount: number\n totalFilesScanned: number\n /** Accumulated across layers — a key present in several layers counts once per layer. */\n dynamicMatchedCount: number\n /** Accumulated across layers, like dynamicMatchedCount. */\n ignoredCount: number\n allDynamicKeys: Array<{ expression: string; file: string; line: number; callee: string }>\n dirsScanned: string[]\n unresolvedKeyWarnings: UnresolvedKeyWarning[]\n /** Keys referenced only from units outside their layer's scope. Not counted as orphans. */\n misplacedUsages: MisplacedUsage[]\n /** Layer name → dirs whose scans vouched for that layer's keys being \"used\". */\n scanScopeByLayer: Record<string, string[]>\n}\n\n/** Per-unit scan evidence, with lazily built dynamic-key regexes. */\ninterface UnitEvidence {\n unit: ScanUnit\n result: ScanResult\n /** Dynamic key expressions incl. bare candidates, in legacy accumulation order. */\n dynamicRaw: DynamicKeyUsage[]\n dynRegexes?: RegExp[]\n}\n\n/**\n * Glob ignores for other units' dirs nested inside this unit's dir, so a\n * scope-aware scan visits every file exactly once and attributes it to the\n * innermost unit.\n */\nexport function nestedUnitIgnores(unit: ScanUnit, units: ScanUnit[]): string[] {\n const ignores: string[] = []\n for (const other of units) {\n if (other.dir === unit.dir) continue\n const rel = relative(unit.dir, other.dir)\n if (!rel || rel.startsWith('..') || isAbsolute(rel)) continue\n const posixRel = rel.split(sep).join('/')\n ignores.push(posixRel, `${posixRel}/**`)\n }\n return ignores\n}\n\nexport async function findOrphanKeysForConfig(options: OrphanScanOptions): Promise<OrphanScanResult> {\n const { keysByLayer, excludeDirs, resolveIgnorePatterns, patterns } = options\n\n // Explicit scanDirs = manual scope control: one combined usage set shared\n // by all layers, no misplaced-usage detection (pre-scope-aware behavior).\n const globalScope = options.scanDirs !== undefined\n const units: ScanUnit[] = options.scanDirs !== undefined\n ? options.scanDirs.map(d => ({ name: d, dir: d }))\n : options.scanPlan.units\n\n // Scan each unit dir exactly once. In plan mode, nested unit dirs are\n // excluded from ancestor scans; explicit scanDirs are scanned as given.\n const evidences: UnitEvidence[] = []\n const allDynamicKeysRaw: DynamicKeyUsage[] = []\n let totalFilesScanned = 0\n\n for (const unit of units) {\n const ignores = globalScope ? [] : nestedUnitIgnores(unit, units)\n const result = await scanSourceFiles(unit.dir, [...(excludeDirs ?? []), ...ignores], patterns)\n totalFilesScanned += result.filesScanned\n const dynamicRaw: DynamicKeyUsage[] = [\n ...result.dynamicKeys,\n ...[...result.bareDynamicCandidates].map(bd => ({ expression: bd, file: '', line: 0, callee: '' })),\n ]\n allDynamicKeysRaw.push(...dynamicRaw)\n evidences.push({ unit, result, dynamicRaw })\n }\n\n // Unresolved-key warnings and the derived \"uncertain\" classification stay\n // global (all units): a key overlapping ANY dynamic translation pattern is\n // withheld from removal regardless of where that pattern lives.\n const unresolvedWarnings = buildUnresolvedWarnings(allDynamicKeysRaw)\n const uncertainRegexes = buildIgnorePatternRegexes(unresolvedWarnings.map(w => w.suggestedIgnorePattern))\n\n const evidenceByName = new Map(evidences.map(e => [e.unit.name, e]))\n const allUnitNames = units.map(u => u.name)\n\n // Scope unions are memoized — sibling layers typically share a scope.\n const scopeCache = new Map<string, { unique: Set<string>; bare: Set<string>; dynRegexes: RegExp[] }>()\n const scopeEvidence = (names: string[]) => {\n const cacheKey = names.join('\\u0000')\n let cached = scopeCache.get(cacheKey)\n if (!cached) {\n const unique = new Set<string>()\n const bare = new Set<string>()\n const dynamicRaw: DynamicKeyUsage[] = []\n for (const name of names) {\n const evidence = evidenceByName.get(name)\n if (!evidence) continue\n for (const key of evidence.result.uniqueKeys) unique.add(key)\n for (const candidate of evidence.result.bareStringCandidates) bare.add(candidate)\n dynamicRaw.push(...evidence.dynamicRaw)\n }\n cached = { unique, bare, dynRegexes: buildDynamicKeyRegexes(dynamicRaw) }\n scopeCache.set(cacheKey, cached)\n }\n return cached\n }\n const unitDynRegexes = (evidence: UnitEvidence): RegExp[] =>\n evidence.dynRegexes ??= buildDynamicKeyRegexes(evidence.dynamicRaw)\n\n const orphansByLayer: Record<string, string[]> = {}\n let orphanCount = 0\n const uncertainByLayer: Record<string, string[]> = {}\n let uncertainCount = 0\n let dynamicMatchedCount = 0\n let ignoredCount = 0\n const misplacedUsages: MisplacedUsage[] = []\n const scanScopeByLayer: Record<string, string[]> = {}\n\n for (const [layerName, { keys }] of keysByLayer) {\n const scopeNames = (options.scanDirs !== undefined\n ? allUnitNames\n : options.scanPlan.scopeByLayer.get(layerName) ?? allUnitNames)\n .filter(name => evidenceByName.has(name))\n scanScopeByLayer[layerName] = scopeNames.map(name => evidenceByName.get(name)!.unit.dir)\n\n const scope = scopeEvidence(scopeNames)\n const scopeNameSet = new Set(scopeNames)\n const outOfScope = globalScope ? [] : evidences.filter(e => !scopeNameSet.has(e.unit.name))\n\n const ignorePatterns = resolveIgnorePatterns(layerName)\n const ignoreRegexes = ignorePatterns ? buildIgnorePatternRegexes(ignorePatterns) : []\n\n const orphans = keys.filter((k) => {\n if (scope.unique.has(k)) return false\n if (scope.bare.has(k)) return false\n if (scope.dynRegexes.some(re => re.test(k))) {\n dynamicMatchedCount++\n return false\n }\n if (ignoreRegexes.length > 0 && ignoreRegexes.some(re => re.test(k))) {\n ignoredCount++\n return false\n }\n return true\n }).sort()\n\n const certain: string[] = []\n const uncertain: string[] = []\n for (const k of orphans) {\n // Not vouched for in scope — but referenced from non-consuming units?\n // Then it's a misplaced usage, reported separately (not an orphan).\n const usingApps = outOfScope\n .filter(e =>\n e.result.uniqueKeys.has(k)\n || e.result.bareStringCandidates.has(k)\n || unitDynRegexes(e).some(re => re.test(k)))\n .map(e => e.unit.name)\n if (usingApps.length > 0) {\n misplacedUsages.push({ key: k, layer: layerName, usingApps })\n continue\n }\n if (uncertainRegexes.length > 0 && uncertainRegexes.some(re => re.test(k))) {\n uncertain.push(k)\n } else {\n certain.push(k)\n }\n }\n\n if (certain.length > 0) {\n orphansByLayer[layerName] = certain\n orphanCount += certain.length\n }\n if (uncertain.length > 0) {\n uncertainByLayer[layerName] = uncertain\n uncertainCount += uncertain.length\n }\n }\n\n misplacedUsages.sort((a, b) => a.layer.localeCompare(b.layer) || a.key.localeCompare(b.key))\n\n // File glob ordering varies between runs — sort the diagnostic arrays so\n // report output is byte-deterministic (CI artifact diffing relies on it).\n const byLocation = (a: { file: string; line: number; expression: string }, b: { file: string; line: number; expression: string }) =>\n a.file.localeCompare(b.file) || a.line - b.line || a.expression.localeCompare(b.expression)\n allDynamicKeysRaw.sort(byLocation)\n unresolvedWarnings.sort((a, b) => byLocation(a, b))\n\n return {\n orphansByLayer,\n orphanCount,\n uncertainByLayer,\n uncertainCount,\n totalFilesScanned,\n dynamicMatchedCount,\n ignoredCount,\n allDynamicKeys: allDynamicKeysRaw,\n dirsScanned: units.map(u => u.dir),\n unresolvedKeyWarnings: unresolvedWarnings,\n misplacedUsages,\n scanScopeByLayer,\n }\n}\n","import { existsSync } from 'node:fs'\nimport { basename, join } from 'node:path'\nimport type { I18nConfig, LocaleDefinition } from '../config/types'\nimport type { ScaffoldLocaleFileInfo } from '../core/types'\nimport { readLocaleData, resolveLocaleEntries } from '../io/locale-data'\nimport { readLocale, writeLocale } from '../io/locale-io'\nimport { getLeafKeys } from '../io/key-operations'\nimport { ToolError } from '../utils/errors'\n\nexport interface ScaffoldLocaleOptions {\n locales?: string[]\n layer?: string\n dryRun?: boolean\n}\n\nexport interface ScaffoldOutcome {\n created: ScaffoldLocaleFileInfo[]\n skipped: ScaffoldLocaleFileInfo[]\n}\n\nexport function buildEmptyStructure(data: Record<string, unknown>): Record<string, unknown> {\n const result: Record<string, unknown> = {}\n for (const [key, value] of Object.entries(data)) {\n if (typeof value === 'string') {\n result[key] = ''\n } else if (value !== null && typeof value === 'object' && !Array.isArray(value)) {\n result[key] = buildEmptyStructure(value as Record<string, unknown>)\n } else {\n result[key] = value\n }\n }\n return result\n}\n\nexport async function scaffoldLocale(\n config: I18nConfig,\n options: ScaffoldLocaleOptions = {},\n): Promise<ScaffoldOutcome> {\n const { locales: localeCodes, layer: layerFilter, dryRun } = options\n\n const layers = layerFilter\n ? config.localeDirs.filter(d => d.layer === layerFilter)\n : config.localeDirs.filter(d => !d.aliasOf)\n\n if (layerFilter && layers.length === 0) {\n throw new ToolError(\n `Layer not found: \"${layerFilter}\". Available: ${config.localeDirs.map(d => d.layer).join(', ')}`,\n 'LAYER_NOT_FOUND',\n )\n }\n\n if (layerFilter && layers[0]?.aliasOf) {\n throw new ToolError(\n `Layer \"${layerFilter}\" is an alias of \"${layers[0].aliasOf}\". Use the target layer instead.`,\n 'LAYER_IS_ALIAS',\n )\n }\n\n const refLocale = config.locales.find(l => l.code === config.defaultLocale)\n if (!refLocale) {\n throw new ToolError(\n `Default locale \"${config.defaultLocale}\" not found in config`,\n 'LOCALE_NOT_FOUND',\n )\n }\n\n const targetLocales = localeCodes\n ? localeCodes.map((code) => {\n const loc = config.locales.find(l => l.code === code)\n if (!loc) {\n throw new ToolError(\n `Locale \"${code}\" not found in config. Available: ${config.locales.map(l => l.code).join(', ')}`,\n 'LOCALE_NOT_FOUND',\n )\n }\n return loc\n })\n : findNewLocales(config, layers)\n\n const created: ScaffoldLocaleFileInfo[] = []\n const skipped: ScaffoldLocaleFileInfo[] = []\n\n for (const dir of layers) {\n if (config.localeFileFormat === 'php-array') {\n await scaffoldPhpLayer(config, dir, refLocale, targetLocales, dryRun, created, skipped)\n } else {\n await scaffoldJsonLayer(config, dir, refLocale, targetLocales, dryRun, created, skipped)\n }\n }\n\n return { created, skipped }\n}\n\nasync function scaffoldJsonLayer(\n config: I18nConfig,\n dir: I18nConfig['localeDirs'][0],\n refLocale: LocaleDefinition,\n targets: LocaleDefinition[],\n dryRun: boolean | undefined,\n created: ScaffoldLocaleFileInfo[],\n skipped: ScaffoldLocaleFileInfo[],\n): Promise<void> {\n const refData = await readLocaleData(config, dir.layer, refLocale)\n if (Object.keys(refData).length === 0) {\n throw new ToolError(\n `Reference locale \"${refLocale.code}\" has no data in layer \"${dir.layer}\". Cannot scaffold without reference keys.`,\n 'NO_REFERENCE_DATA',\n )\n }\n\n const emptyData = buildEmptyStructure(refData)\n const keyCount = getLeafKeys(refData).length\n\n for (const target of targets) {\n const entries = await resolveLocaleEntries(config, dir.layer, target)\n const targetPath = entries[0]?.path ?? join(dir.path, target.file ?? `${target.code}.json`)\n\n if (existsSync(targetPath)) {\n skipped.push({ locale: target.code, layer: dir.layer, file: targetPath, keys: keyCount })\n continue\n }\n\n if (!dryRun) {\n await writeLocale(targetPath, emptyData)\n }\n created.push({ locale: target.code, layer: dir.layer, file: targetPath, keys: keyCount })\n }\n}\n\nasync function scaffoldPhpLayer(\n config: I18nConfig,\n dir: I18nConfig['localeDirs'][0],\n refLocale: LocaleDefinition,\n targets: LocaleDefinition[],\n dryRun: boolean | undefined,\n created: ScaffoldLocaleFileInfo[],\n skipped: ScaffoldLocaleFileInfo[],\n): Promise<void> {\n const refEntries = await resolveLocaleEntries(config, dir.layer, refLocale)\n if (refEntries.length === 0) {\n throw new ToolError(\n `Reference locale \"${refLocale.code}\" has no PHP files in layer \"${dir.layer}\". Cannot scaffold without reference keys.`,\n 'NO_REFERENCE_DATA',\n )\n }\n\n for (const target of targets) {\n const targetDir = join(dir.path, target.code)\n const dirExists = existsSync(targetDir)\n\n for (const refEntry of refEntries) {\n const fileName = basename(refEntry.path)\n const namespace = fileName.replace(/\\.php$/, '')\n const targetPath = join(targetDir, fileName)\n\n if (dirExists && existsSync(targetPath)) {\n const refData = await readLocale(refEntry.path)\n const keyCount = getLeafKeys(refData).length\n skipped.push({ locale: target.code, layer: dir.layer, file: targetPath, keys: keyCount, namespace })\n continue\n }\n\n const refData = await readLocale(refEntry.path)\n const emptyData = buildEmptyStructure(refData)\n const keyCount = getLeafKeys(refData).length\n\n if (!dryRun) {\n await writeLocale(targetPath, emptyData)\n }\n created.push({ locale: target.code, layer: dir.layer, file: targetPath, keys: keyCount, namespace })\n }\n }\n}\n\nfunction findNewLocales(config: I18nConfig, layers: I18nConfig['localeDirs']): LocaleDefinition[] {\n if (config.localeFileFormat === 'php-array') {\n return config.locales.filter((locale) => {\n return layers.some((dir) => {\n return !existsSync(join(dir.path, locale.code))\n })\n })\n }\n\n return config.locales.filter((locale) => {\n return layers.some((dir) => {\n const filePath = join(dir.path, locale.file ?? `${locale.code}.json`)\n return !existsSync(filePath)\n })\n })\n}\n","/**\n * Mutating operations: write/add/update/remove/rename translation keys and\n * scaffold locale files.\n */\n\nimport { detectI18nConfig } from '../config/detector.js'\nimport type { I18nConfig, LocaleDefinition } from '../config/types.js'\nimport { readLocaleData, readLocaleDataIfPresent, mutateLocaleData } from '../io/locale-data.js'\nimport {\n getNestedValue,\n setNestedValue,\n hasNestedKey,\n removeNestedValue,\n renameNestedKey,\n validateTranslationValue,\n} from '../io/key-operations.js'\nimport { toRelativePath } from '../scanner/code-scanner.js'\nimport { log } from '../utils/logger.js'\nimport { ToolError } from '../utils/errors.js'\nimport { scaffoldLocale } from '../tools/scaffold-locale.js'\n\nimport type {\n MutationResult,\n AddTranslationsResult,\n WriteTranslationsResult,\n UpdateTranslationsResult,\n ScaffoldLocaleResult,\n ScaffoldLocaleFileInfo,\n PlaceholderValidationResult,\n UnresolvedLocaleRef,\n RemoveTranslationsResult,\n RenameTranslationKeyResult,\n} from './types.js'\nimport { findWritableLayerOrThrow, findLocaleImpl, findLocaleSuggestion, resolveLocaleRef } from './shared.js'\nimport type { LocaleRefAmbiguity } from './shared.js'\nimport { validatePlaceholders, mergePlaceholderValidation } from './ops-translate.js'\n\n/**\n * Shared logic for write_translations (supports add, update, and upsert modes).\n */\nasync function applyTranslations(\n config: I18nConfig,\n layer: string,\n translations: Record<string, Record<string, string>>,\n mode: 'add' | 'update' | 'upsert',\n findLocale: (config: I18nConfig, ref: string) => LocaleDefinition | undefined,\n dryRun = false,\n): Promise<MutationResult> {\n const applied: string[] = []\n const skipped: string[] = []\n const warnings: string[] = []\n const unresolved = new Map<string, UnresolvedLocaleRef>()\n const ambiguities = new Map<string, LocaleRefAmbiguity>()\n const filesWritten = new Set<string>()\n const preview: Array<{ locale: string; key: string; value: string }> = []\n\n const byLocale = new Map<LocaleDefinition, Array<{ key: string; value: string }>>()\n const placeholderValidations: PlaceholderValidationResult[] = []\n\n for (const [key, localeValues] of Object.entries(translations)) {\n const entries = Object.entries(localeValues)\n const sourceEntry = entries.find(([localeRef]) => {\n const locale = findLocale(config, localeRef)\n return locale?.code === config.defaultLocale\n }) ?? entries[0]\n if (sourceEntry) {\n placeholderValidations.push(validatePlaceholders(\n key,\n sourceEntry[1],\n entries.map(([localeRef, value]) => ({ locale: localeRef, value })),\n config.localeFileFormat,\n ))\n }\n\n for (const [localeRef, value] of Object.entries(localeValues)) {\n if (mode === 'add') {\n const warning = validateTranslationValue(value)\n if (warning) {\n warnings.push(`${key} (${localeRef}): ${warning}`)\n }\n }\n const { locale, ambiguity } = resolveLocaleRef(config, localeRef)\n if (ambiguity && !ambiguities.has(localeRef)) {\n ambiguities.set(localeRef, ambiguity)\n log.warn(\n `Locale ref \"${localeRef}\" matches ${ambiguity.candidates.length} locales by ${ambiguity.matchedBy} `\n + `(${ambiguity.candidates.join(', ')}) — using \"${ambiguity.resolvedTo}\". Use a locale code to be explicit.`,\n )\n }\n if (!locale) {\n // stderr alone is invisible to an MCP caller, and the key still lands\n // in `applied` via the other locales — so the result must carry this\n // or the write reads as a clean success (#301).\n const suggestion = findLocaleSuggestion(config, localeRef)\n log.warn(`Locale not found: ${localeRef}, skipping.${suggestion}`)\n const existing = unresolved.get(localeRef)\n if (existing) {\n existing.keys.push(key)\n } else {\n unresolved.set(localeRef, {\n ref: localeRef,\n keys: [key],\n ...(suggestion ? { suggestion: suggestion.trim() } : {}),\n })\n }\n continue\n }\n if (!byLocale.has(locale)) {\n byLocale.set(locale, [])\n }\n byLocale.get(locale)!.push({ key, value })\n }\n }\n\n for (const [locale, entries] of byLocale) {\n if (dryRun) {\n const data = await readLocaleData(config, layer, locale)\n for (const { key, value } of entries) {\n const exists = hasNestedKey(data, key)\n if (mode === 'add' && exists) {\n skipped.push(key)\n } else if (mode === 'update' && !exists) {\n skipped.push(key)\n } else {\n applied.push(key)\n preview.push({ locale: locale.code, key, value })\n }\n }\n } else {\n const written = await mutateLocaleData(config, layer, locale, (data) => {\n for (const { key, value } of entries) {\n const exists = hasNestedKey(data, key)\n if (mode === 'add' && exists) {\n skipped.push(key)\n } else if (mode === 'update' && !exists) {\n skipped.push(key)\n } else {\n setNestedValue(data, key, value)\n applied.push(key)\n }\n }\n })\n for (const f of written) filesWritten.add(f)\n }\n }\n\n const placeholderValidation = mergePlaceholderValidation(placeholderValidations)\n if (placeholderValidation && !placeholderValidation.ok) {\n warnings.push(...placeholderValidation.errors.map(error => error.kind === 'plural-count'\n ? `${error.key} (${error.locale}): plural variant count mismatch; expected ${error.sourceVariants}, got ${error.targetVariants}`\n : `${error.key} (${error.locale}): placeholder mismatch; missing: ${error.missing.join(', ') || '-'}; extra: ${error.extra.join(', ') || '-'}`))\n }\n\n // A dropped ref is a warning too, so callers that only read `warnings`\n // still see it — but it also gets its own field, because \"the write silently\n // did less than you asked\" is not the same class as a placeholder nit.\n for (const u of unresolved.values()) {\n warnings.push(\n `Locale \"${u.ref}\" matched no known locale — ${u.keys.length} key(s) not written for it.`\n + (u.suggestion ? ` ${u.suggestion}` : ''),\n )\n }\n\n const result: MutationResult = {\n applied: [...new Set(applied)],\n skipped: [...new Set(skipped)],\n warnings,\n filesWritten: filesWritten.size,\n }\n\n if (unresolved.size > 0) {\n result.unresolvedLocales = [...unresolved.values()]\n }\n\n if (ambiguities.size > 0) {\n result.ambiguousLocales = [...ambiguities.values()]\n }\n\n if (placeholderValidation) {\n result.placeholderValidation = placeholderValidation\n }\n\n if (dryRun) {\n result.preview = preview\n }\n\n return result\n}\n\n/** The optional diagnostics every mutation result carries. */\ninterface MutationDiagnostics {\n warnings?: string[]\n placeholderValidation?: PlaceholderValidationResult\n unresolvedLocales?: UnresolvedLocaleRef[]\n ambiguousLocales?: LocaleRefAmbiguity[]\n}\n\n/**\n * Copy the diagnostics off a MutationResult onto a public result, omitting the\n * empty ones so a clean run keeps its minimal shape. Centralised so the\n * write/add/update branches cannot drift apart on which of them they remember\n * to surface — they already had, before unresolvedLocales existed.\n */\nfunction attachDiagnostics<T extends MutationDiagnostics>(\n result: T,\n mutation: MutationResult,\n opts: { warnings?: boolean } = {},\n): T {\n if (opts.warnings !== false && mutation.warnings.length > 0) result.warnings = mutation.warnings\n if (mutation.placeholderValidation) result.placeholderValidation = mutation.placeholderValidation\n if (mutation.unresolvedLocales) result.unresolvedLocales = mutation.unresolvedLocales\n if (mutation.ambiguousLocales) result.ambiguousLocales = mutation.ambiguousLocales\n return result\n}\n\n/**\n * Write translation keys to the specified layer with mode control.\n *\n * Mode:\n * - 'upsert' (default): Adds new keys and updates existing ones. Never skips.\n * - 'add': Only creates new keys, skipping existing ones.\n * - 'update': Only modifies existing keys, skipping missing ones.\n */\nexport async function writeTranslations(opts: {\n layer: string\n translations: Record<string, Record<string, string>>\n mode?: 'add' | 'update' | 'upsert'\n dryRun?: boolean\n projectDir?: string\n}): Promise<WriteTranslationsResult> {\n const { layer, translations } = opts\n const dir = opts.projectDir ?? process.cwd()\n const config = await detectI18nConfig(dir)\n const mode = opts.mode ?? 'upsert'\n const isDryRun = opts.dryRun ?? false\n\n const mutation = await applyTranslations(config, layer, translations, mode, findLocaleImpl, isDryRun)\n const { applied, skipped, filesWritten, preview } = mutation\n\n if (isDryRun) {\n const result: WriteTranslationsResult = {\n dryRun: true,\n wouldWrite: preview,\n skipped,\n summary: {\n keysWritten: applied.length,\n keysSkipped: skipped.length,\n message: 'Call again with dryRun: false to apply these changes.',\n },\n }\n if (skipped.length > 0) { result.skippedKeys = skipped }\n return attachDiagnostics(result, mutation)\n }\n\n return attachDiagnostics({\n written: applied,\n skipped,\n filesWritten,\n } as WriteTranslationsResult, mutation)\n}\n\n/**\n * Add new translation keys to the specified layer.\n *\n * @deprecated Use writeTranslations with mode: 'add' instead.\n */\nexport async function addTranslations(opts: {\n layer: string\n translations: Record<string, Record<string, string>>\n dryRun?: boolean\n projectDir?: string\n}): Promise<AddTranslationsResult> {\n const { layer, translations } = opts\n const dir = opts.projectDir ?? process.cwd()\n const config = await detectI18nConfig(dir)\n const isDryRun = opts.dryRun ?? false\n\n const mutation = await applyTranslations(config, layer, translations, 'add', findLocaleImpl, isDryRun)\n const { applied, skipped, filesWritten, preview } = mutation\n\n if (isDryRun) {\n const result: AddTranslationsResult = {\n dryRun: true,\n wouldAdd: preview,\n skipped,\n summary: {\n keysToAdd: applied.length,\n keysSkipped: skipped.length,\n message: 'Call again with dryRun: false to apply these changes.',\n },\n }\n if (skipped.length > 0) {\n result.skippedKeys = skipped\n }\n return attachDiagnostics(result, mutation)\n }\n\n return attachDiagnostics({\n added: applied,\n skipped,\n filesWritten,\n } as AddTranslationsResult, mutation)\n}\n\n/**\n * Update existing translation keys in the specified layer.\n *\n * @deprecated Use writeTranslations with mode: 'update' instead.\n */\nexport async function updateTranslations(opts: {\n layer: string\n translations: Record<string, Record<string, string>>\n dryRun?: boolean\n projectDir?: string\n}): Promise<UpdateTranslationsResult> {\n const { layer, translations } = opts\n const dir = opts.projectDir ?? process.cwd()\n const config = await detectI18nConfig(dir)\n const isDryRun = opts.dryRun ?? false\n\n const mutation = await applyTranslations(config, layer, translations, 'update', findLocaleImpl, isDryRun)\n const { applied, skipped, filesWritten, preview } = mutation\n\n if (isDryRun) {\n const result: UpdateTranslationsResult = {\n dryRun: true,\n wouldUpdate: preview,\n skipped,\n summary: {\n keysToUpdate: applied.length,\n keysSkipped: skipped.length,\n message: 'Call again with dryRun: false to apply these changes.',\n },\n }\n if (skipped.length > 0) {\n result.skippedKeys = skipped\n }\n // warnings: false — updateTranslations has never surfaced them, and this\n // deprecated wrapper is not the place to change its contract.\n return attachDiagnostics(result, mutation, { warnings: false })\n }\n\n return attachDiagnostics({\n updated: applied,\n skipped,\n filesWritten,\n } as UpdateTranslationsResult, mutation, { warnings: false })\n}\n\n/**\n * Remove one or more translation keys from ALL locale files in the specified layer.\n */\nexport async function removeTranslations(opts: {\n layer: string\n keys: string[]\n dryRun?: boolean\n projectDir?: string\n}): Promise<RemoveTranslationsResult> {\n const { layer, keys } = opts\n const dir = opts.projectDir ?? process.cwd()\n const config = await detectI18nConfig(dir)\n const isDryRun = opts.dryRun ?? false\n\n findWritableLayerOrThrow(config, layer)\n\n const preview: Array<{ locale: string; key: string; oldValue: unknown }> = []\n const removed: string[] = []\n const notFound: string[] = []\n const filesWritten = new Set<string>()\n\n for (const locale of config.locales) {\n const data = await readLocaleDataIfPresent(config, layer, locale)\n if (!data) continue\n\n if (isDryRun) {\n for (const key of keys) {\n const value = getNestedValue(data, key)\n if (value !== undefined) {\n preview.push({ locale: locale.code, key, oldValue: value })\n }\n }\n } else {\n const written = await mutateLocaleData(config, layer, locale, (fileData) => {\n for (const key of keys) {\n if (removeNestedValue(fileData, key)) {\n removed.push(`${locale.code}:${key}`)\n } else {\n notFound.push(`${locale.code}:${key}`)\n }\n }\n })\n for (const f of written) filesWritten.add(f)\n }\n }\n\n if (isDryRun) {\n return {\n dryRun: true,\n wouldRemove: preview,\n summary: {\n keysFound: preview.length,\n message: 'Call again with dryRun: false to apply these changes.',\n },\n }\n }\n\n // Entries are \"locale:key\"; guard the split so a malformed entry cannot\n // put undefined into a string[] the type promises is dense.\n const uniqueRemoved = [...new Set(\n removed.map(r => r.split(':')[1]).filter((k): k is string => k !== undefined),\n )]\n return {\n removed: uniqueRemoved,\n removedPerLocale: removed,\n notFound: [...new Set(notFound)],\n filesWritten: filesWritten.size,\n }\n}\n\n/**\n * Rename/move a translation key across ALL locale files in a layer.\n */\nexport async function renameTranslationKey(opts: {\n layer: string\n oldKey: string\n newKey: string\n dryRun?: boolean\n projectDir?: string\n}): Promise<RenameTranslationKeyResult> {\n const { layer, oldKey, newKey } = opts\n const dir = opts.projectDir ?? process.cwd()\n const config = await detectI18nConfig(dir)\n const isDryRun = opts.dryRun ?? false\n\n if (oldKey === newKey) {\n throw new ToolError(`Old key and new key are the same: \"${oldKey}\". Provide a different newKey to rename to.`, 'SAME_KEY')\n }\n\n findWritableLayerOrThrow(config, layer)\n\n const preview: Array<{ locale: string; oldKey: string; newKey: string; value: unknown }> = []\n const renamed: string[] = []\n const notFoundArr: string[] = []\n const conflicts: string[] = []\n const filesWritten = new Set<string>()\n\n for (const locale of config.locales) {\n const data = await readLocaleDataIfPresent(config, layer, locale)\n if (!data) continue\n\n const oldValue = getNestedValue(data, oldKey)\n if (oldValue === undefined) {\n notFoundArr.push(locale.code)\n continue\n }\n\n if (hasNestedKey(data, newKey)) {\n conflicts.push(locale.code)\n continue\n }\n\n if (isDryRun) {\n preview.push({ locale: locale.code, oldKey, newKey, value: oldValue })\n } else {\n const written = await mutateLocaleData(config, layer, locale, (fileData) => {\n renameNestedKey(fileData, oldKey, newKey)\n })\n renamed.push(locale.code)\n for (const f of written) filesWritten.add(f)\n }\n }\n\n if (isDryRun) {\n const result: Record<string, unknown> = {\n dryRun: true,\n wouldRename: preview,\n summary: {\n localesAffected: preview.length,\n message: 'Call again with dryRun: false to apply these changes.',\n },\n }\n if (notFoundArr.length > 0) {\n result.notFoundInLocales = notFoundArr\n }\n if (conflicts.length > 0) {\n result.conflictsInLocales = conflicts\n result.summary = {\n ...(result.summary as Record<string, unknown>),\n warning: `New key \"${newKey}\" already exists in ${conflicts.length} locale(s). These will be skipped.`,\n }\n }\n return result\n }\n\n const summary: Record<string, unknown> = {\n renamed,\n filesWritten: filesWritten.size,\n oldKey,\n newKey,\n }\n if (notFoundArr.length > 0) {\n summary.notFoundInLocales = notFoundArr\n }\n if (conflicts.length > 0) {\n summary.skippedDueToConflict = conflicts\n }\n\n return summary\n}\n\n/**\n * Create empty locale files for new languages.\n */\nexport async function scaffoldLocaleFiles(opts: {\n locales?: string[]\n layer?: string\n dryRun?: boolean\n projectDir?: string\n}): Promise<ScaffoldLocaleResult> {\n const dir = opts.projectDir ?? process.cwd()\n const config = await detectI18nConfig(dir)\n\n const result = await scaffoldLocale(config, { locales: opts.locales, layer: opts.layer, dryRun: opts.dryRun })\n\n const toFileInfo = (f: ScaffoldLocaleFileInfo): ScaffoldLocaleFileInfo => ({\n locale: f.locale,\n layer: f.layer,\n file: toRelativePath(f.file, config.rootDir),\n keys: f.keys,\n ...(f.namespace ? { namespace: f.namespace } : {}),\n })\n\n return {\n created: result.created.map(toFileInfo),\n skipped: result.skipped.map(toFileInfo),\n dryRun: opts.dryRun ?? false,\n }\n}\n","/**\n * init: produce a schema-valid .i18n-mcp.json for a cold project.\n */\n\nimport { existsSync } from 'node:fs'\nimport { readdir, readFile } from 'node:fs/promises'\nimport { join, relative, resolve, sep } from 'node:path'\nimport { detectFrameworkMatch } from '../adapters/registry.js'\nimport { CONFIG_FILENAME, validateProjectConfig } from '../config/project-config.js'\nimport { writeLocaleFile } from '../io/json-writer.js'\nimport { ToolError } from '../utils/errors.js'\nimport { log } from '../utils/logger.js'\nimport type { FrameworkMatch } from '../adapters/registry.js'\nimport type { InitProjectConfigResult, GeneratedProjectConfig } from './types.js'\n\ntype Detected = InitProjectConfigResult['detected']\ntype CarriedLocaleConfig = Pick<GeneratedProjectConfig, 'localeDirs' | 'defaultLocale' | 'locales'>\n\n/** Where a project with no detected framework plausibly keeps its locales. */\nconst COMMON_LOCALE_DIRS = [\n 'locales',\n 'src/locales',\n 'i18n/locales',\n 'src/i18n/locales',\n 'i18n',\n 'src/i18n',\n 'lang',\n 'public/locales',\n 'messages',\n]\n\n/** The one adapter that cannot resolve without locale config in the file. */\nconst GENERIC_ADAPTER = 'generic'\n\nconst SCHEMA_URL = 'https://raw.githubusercontent.com/fabkho/the-i18n-kit/main/packages/mcp/schema.json'\n\n/**\n * Authoring fields no adapter can derive — the reason a config file exists at\n * all once the framework supplies the rest. Emitted as empty scaffolding so\n * the shape is discoverable without reading the schema.\n */\nfunction authoringScaffold(): GeneratedProjectConfig {\n return {\n $schema: SCHEMA_URL,\n context: '',\n glossary: {},\n translationPrompt: '',\n localeNotes: {},\n }\n}\n\n/**\n * Probe for a locale directory in a project with no framework. Returns paths\n * relative to the project dir — `.i18n-mcp.json` is committed, so absolute\n * paths would break for everyone but the author.\n */\nasync function probeLocaleDirs(projectDir: string): Promise<string[]> {\n const found: string[] = []\n for (const candidate of COMMON_LOCALE_DIRS) {\n const full = join(projectDir, candidate)\n if (!existsSync(full)) continue\n try {\n const entries = await readdir(full)\n if (entries.some(e => e.endsWith('.json') || e.endsWith('.php'))) found.push(candidate)\n }\n catch {\n // Unreadable — not a candidate.\n }\n }\n return found\n}\n\n/** Locale codes from the file names in a probed directory. */\nasync function probeLocaleCodes(projectDir: string, localeDir: string): Promise<string[]> {\n try {\n const entries = await readdir(join(projectDir, localeDir))\n return entries\n .filter(e => e.endsWith('.json'))\n .map(e => e.slice(0, -5))\n .sort()\n }\n catch {\n return []\n }\n}\n\n/** Leaf-key count of a locale file, or -1 when it cannot be read. */\nasync function countKeys(path: string): Promise<number> {\n try {\n const parsed: unknown = JSON.parse(await readFile(path, 'utf-8'))\n const walk = (v: unknown): number =>\n v !== null && typeof v === 'object'\n ? Object.values(v as Record<string, unknown>).reduce<number>((n, child) => n + walk(child), 0)\n : 1\n return walk(parsed)\n }\n catch {\n return -1\n }\n}\n\n/**\n * Guess the reference locale for a project with no framework config.\n *\n * The fullest file, not the alphabetically first: a source locale is the one\n * everything else is translated from, so it has the most keys. Picking\n * alphabetically is how a project ends up silently treating German as its\n * source because `de` sorts before `en` (cf. #296). Ties break alphabetically\n * so the result stays deterministic.\n */\nasync function guessDefaultLocale(\n projectDir: string,\n localeDir: string,\n codes: string[],\n): Promise<string | undefined> {\n let best: { code: string; keys: number } | undefined\n for (const code of codes) {\n const keys = await countKeys(join(projectDir, localeDir, `${code}.json`))\n if (!best || keys > best.keys) best = { code, keys }\n }\n return best?.code\n}\n\n/**\n * Config for a project whose framework was detected.\n *\n * Deliberately minimal: an adapter that derives locales, layers and the\n * default locale gets none of them written down. Generating a copy of what\n * `nuxt.config.ts` already states creates the second source of truth that\n * #305 exists to remove, and it goes stale silently. Only an adapter that\n * cannot resolve without them contributes any.\n */\nfunction detectedProject(\n match: FrameworkMatch,\n carried: CarriedLocaleConfig,\n): { config: GeneratedProjectConfig; detected: Detected } {\n return {\n config: { ...authoringScaffold(), ...carried },\n detected: {\n adapter: match.adapter.name,\n label: match.adapter.label,\n confidence: match.confidence,\n // A property of the adapter, not of this run: forcing over a config that\n // happens to carry localeDirs must not make Nuxt look like it needs them.\n derivesLocaleConfig: match.adapter.name !== GENERIC_ADAPTER,\n ...(match.runnersUp.length > 0 ? { runnersUp: match.runnersUp } : {}),\n },\n }\n}\n\n/**\n * Locale settings already present in the file being overwritten.\n *\n * `--force` regenerates the scaffolding, and for a project whose config is\n * load-bearing — anything relying on the generic adapter — dropping\n * `localeDirs` would leave it unresolvable. Preserving them is not\n * adapter-specific: no `--force` should ever silently delete locale settings\n * someone wrote by hand.\n */\nasync function carryLocaleConfig(configPath: string): Promise<CarriedLocaleConfig> {\n if (!existsSync(configPath)) return {}\n try {\n const existing = JSON.parse(await readFile(configPath, 'utf-8')) as Record<string, unknown>\n const carried: CarriedLocaleConfig = {}\n if (Array.isArray(existing.localeDirs)) carried.localeDirs = existing.localeDirs as string[]\n if (typeof existing.defaultLocale === 'string') carried.defaultLocale = existing.defaultLocale\n if (Array.isArray(existing.locales)) carried.locales = existing.locales as string[]\n return carried\n }\n catch {\n // Unreadable or malformed — nothing to preserve, and init is about to\n // replace it anyway.\n return {}\n }\n}\n\n/**\n * Config for a project no adapter claimed. The generic adapter only activates\n * with explicit localeDirs + defaultLocale, so these must be written or the\n * project stays unresolvable — the one case where init writes locale data it\n * inferred rather than read.\n */\nasync function undetectedProject(\n projectDir: string,\n carried: CarriedLocaleConfig,\n): Promise<{ config: GeneratedProjectConfig; detected: Detected }> {\n const localeDirs = await probeLocaleDirs(projectDir)\n const [firstDir] = localeDirs\n const locales = firstDir ? await probeLocaleCodes(projectDir, firstDir) : []\n const guessed = firstDir ? await guessDefaultLocale(projectDir, firstDir, locales) : undefined\n\n // A directory of flat .php files is matched as a locale dir but cannot be\n // resolved: the generic adapter infers the format from the directory and has\n // no flat-PHP branch (#308). Emitting locale codes for it would be worse than\n // emitting none — the adapter would then treat them as JSON and read every\n // file as empty. Say so instead of shipping a config that quietly does that.\n const phpOnly = firstDir !== undefined && locales.length === 0\n\n return {\n config: {\n ...authoringScaffold(),\n localeDirs: localeDirs.length > 0 ? localeDirs : ['locales'],\n defaultLocale: guessed ?? 'en',\n ...(locales.length > 0 ? { locales } : {}),\n ...carried,\n },\n detected: {\n adapter: GENERIC_ADAPTER,\n label: 'Generic',\n confidence: 0,\n derivesLocaleConfig: false,\n ...(localeDirs.length === 0\n ? { note: 'No framework and no locale directory found. Wrote a template — set localeDirs and defaultLocale before running other commands.' }\n : {}),\n ...(phpOnly\n ? { note: `Found ${firstDir} but no JSON locale files in it. Flat PHP locale files are not resolvable by the generic adapter — see the-i18n-kit#308.` }\n : {}),\n },\n }\n}\n\nexport async function initProjectConfig(opts: {\n projectDir?: string\n force?: boolean\n /** Resolve the config without touching disk. */\n dryRun?: boolean\n}): Promise<InitProjectConfigResult> {\n const dir = resolve(opts.projectDir ?? process.cwd())\n const configPath = join(dir, CONFIG_FILENAME)\n const exists = existsSync(configPath)\n\n if (exists && !opts.force) {\n throw new ToolError(\n `${CONFIG_FILENAME} already exists at ${configPath}. `\n + 'Pass --force to overwrite it, or --json to see what would be written.',\n 'CONFIG_EXISTS',\n )\n }\n\n const carried = await carryLocaleConfig(configPath)\n const match = await detectFrameworkMatch(dir)\n const { config, detected } = match\n ? detectedProject(match, carried)\n : await undetectedProject(dir, carried)\n\n // A generated config the tool would then reject is a bug in init, not\n // something to hand the user.\n const validation = validateProjectConfig(config)\n if (!validation.ok) {\n throw new ToolError(\n `init generated a config that fails its own schema: ${validation.error}`,\n 'INVALID_GENERATED_CONFIG',\n )\n }\n\n const result: InitProjectConfigResult = {\n config,\n detected,\n configPath: relative(dir, configPath).split(sep).join('/'),\n written: false,\n overwritten: false,\n }\n\n if (opts.dryRun) return result\n\n // sortKeys: false — a config reads best in authored order ($schema first,\n // scaffolding after), not alphabetised.\n await writeLocaleFile(configPath, config as unknown as Record<string, unknown>, {\n indent: ' ',\n sortKeys: false,\n })\n log.info(`Wrote ${CONFIG_FILENAME} (${detected.label}${detected.confidence > 0 ? `, confidence ${detected.confidence}` : ''})`)\n return { ...result, written: true, overwritten: exists }\n}\n","/**\n * status: translation coverage per locale and per layer, in one call.\n */\n\nimport { detectI18nConfig } from '../config/detector.js'\nimport { readLocaleData, readLocaleDataIfPresent } from '../io/locale-data.js'\nimport { getNestedValue, getLeafKeys } from '../io/key-operations.js'\nimport { writeReportFile } from '../io/json-writer.js'\nimport { findReferenceLocaleOrThrow, localeRefInfo, resolveLayersToScan } from './shared.js'\nimport { resolveProtectedLocales } from './ops-translate.js'\nimport { resolveOutputFile, resolveReportFilePath } from './report.js'\nimport type { LocaleDefinition, LocaleDir, I18nConfig } from '../config/types.js'\nimport type { TranslationStatusResult, LocaleStatus, LayerStatus } from './types.js'\n\n/**\n * Keys worth translating: present in the reference locale with a non-empty\n * value. An empty reference value is nothing to translate *from*, so counting\n * it would deflate every locale equally and hide real gaps.\n */\nfunction referenceKeys(refData: Record<string, unknown>): string[] {\n return getLeafKeys(refData).filter((k) => {\n const v = getNestedValue(refData, k)\n return typeof v === 'string' ? v.length > 0 : v !== null && v !== undefined\n })\n}\n\ntype KeyState = 'translated' | 'missing' | 'empty'\n\n/**\n * Classify one key in one locale. `missing` and `empty` are both untranslated\n * but distinct: a missing key was never written, an empty one was scaffolded\n * and never filled. Reporting them separately is what tells a scaffold-and-\n * forget locale apart from an untouched one — matching how\n * getMissingTranslations already treats empties as not-translated.\n */\nfunction classify(data: Record<string, unknown>, key: string): KeyState {\n const value = getNestedValue(data, key)\n if (value === undefined || value === null) return 'missing'\n if (typeof value === 'string' && value.length === 0) return 'empty'\n return 'translated'\n}\n\nfunction percent(translated: number, total: number): number {\n if (total === 0) return 100\n return Math.round((translated / total) * 1000) / 10\n}\n\ninterface Counts { total: number, translated: number, missing: number, empty: number }\n\nconst emptyCounts = (): Counts => ({ total: 0, translated: 0, missing: 0, empty: 0 })\n\n/** Counts for one locale against one layer's reference keys. */\nfunction countLocale(data: Record<string, unknown>, keys: string[]): Counts {\n const counts = emptyCounts()\n for (const key of keys) {\n counts.total += 1\n counts[classify(data, key)] += 1\n }\n return counts\n}\n\nfunction merge(into: Counts | undefined, from: Counts): void {\n if (!into) return\n into.total += from.total\n into.translated += from.translated\n into.missing += from.missing\n into.empty += from.empty\n}\n\n/**\n * Coverage for a project: per locale, per layer, and one overall figure.\n *\n * Protected locales are counted and reported but excluded from the overall\n * percentage — they are maintained by hand, so counting their gaps as project\n * debt makes a healthy project read as failing and moves a number nobody can\n * act on.\n */\nexport async function getTranslationStatus(opts: {\n layer?: string\n referenceLocale?: string\n projectDir?: string\n outputFile?: string\n}): Promise<TranslationStatusResult> {\n const dir = opts.projectDir ?? process.cwd()\n const config = await detectI18nConfig(dir)\n const refLocale = findReferenceLocaleOrThrow(config, opts.referenceLocale)\n\n const layersToScan = resolveLayersToScan(config, opts.layer)\n\n const protectedCodes = new Set(resolveProtectedLocales(config).map(l => l.code))\n const targets = config.locales.filter(l => l.code !== refLocale.code)\n\n const byLocale = new Map<string, Counts>(targets.map(l => [l.code, emptyCounts()]))\n const byLayer = new Map<string, Counts>(layersToScan.map(d => [d.layer, emptyCounts()]))\n\n await tally({ config, layersToScan, refLocale, targets, protectedCodes, byLocale, byLayer })\n\n const locales: LocaleStatus[] = targets.map((locale) => {\n const c = byLocale.get(locale.code) ?? emptyCounts()\n const isProtected = protectedCodes.has(locale.code)\n return {\n ...localeRefInfo(locale),\n ...c,\n completion: percent(c.translated, c.total),\n ...(isProtected ? { protected: true, excludedFromOverall: true } : {}),\n }\n })\n\n const layers: LayerStatus[] = [...byLayer.entries()].map(([layer, c]) => ({\n layer,\n ...c,\n completion: percent(c.translated, c.total),\n }))\n\n const counted = locales.filter(l => !l.protected)\n const overallTranslated = counted.reduce((n, l) => n + l.translated, 0)\n const overallTotal = counted.reduce((n, l) => n + l.total, 0)\n\n const output: TranslationStatusResult = {\n locales,\n layers,\n summary: {\n referenceLocale: localeRefInfo(refLocale),\n layersScanned: layersToScan.map(d => d.layer),\n localesChecked: counted.length,\n protectedLocales: locales.filter(l => l.protected).map(l => l.code),\n totalKeys: overallTotal,\n translatedKeys: overallTranslated,\n missingKeys: counted.reduce((n, l) => n + l.missing, 0),\n emptyKeys: counted.reduce((n, l) => n + l.empty, 0),\n // The gate in #248 reads this counter, so the name is load-bearing.\n completionPercent: percent(overallTranslated, overallTotal),\n },\n }\n\n const reportPath = resolveOutputFile(dir, opts.outputFile)\n ?? resolveReportFilePath(config, dir, 'get_translation_status')\n if (reportPath) {\n await writeReportFile(reportPath, output as unknown as Record<string, unknown>, {\n tool: 'get_translation_status',\n args: { layer: opts.layer, referenceLocale: opts.referenceLocale },\n })\n // Summary only: the per-locale and per-layer arrays grow with the project,\n // and a health check must never flood a caller's context.\n return { reportFile: reportPath, summary: output.summary }\n }\n\n return output\n}\n\nasync function readTargetData(\n config: I18nConfig,\n layer: string,\n target: LocaleDefinition,\n): Promise<Record<string, unknown>> {\n try {\n return await readLocaleData(config, layer, target)\n }\n catch {\n // A locale with no file in this layer is entirely missing, not an error.\n return {}\n }\n}\n\n/** Walk every layer once, accumulating both breakdowns in a single pass. */\nasync function tally(ctx: {\n config: I18nConfig\n layersToScan: LocaleDir[]\n refLocale: LocaleDefinition\n targets: LocaleDefinition[]\n protectedCodes: Set<string>\n byLocale: Map<string, Counts>\n byLayer: Map<string, Counts>\n}): Promise<void> {\n for (const localeDir of ctx.layersToScan) {\n const refData = await readLocaleDataIfPresent(ctx.config, localeDir.layer, ctx.refLocale)\n if (!refData) continue\n\n const keys = referenceKeys(refData)\n if (keys.length === 0) continue\n\n for (const target of ctx.targets) {\n const counts = countLocale(await readTargetData(ctx.config, localeDir.layer, target), keys)\n merge(ctx.byLocale.get(target.code), counts)\n // A protected locale's gaps are deliberate, so they must not drag the\n // layer figure down either — the layer is not what is incomplete.\n if (!ctx.protectedCodes.has(target.code)) merge(ctx.byLayer.get(localeDir.layer), counts)\n }\n }\n}\n","/**\n * GitLab Code Quality (CodeClimate) mapping — pure result-shape → issue-array\n * transforms, no I/O.\n *\n * Format contract (https://docs.gitlab.com/ci/testing/code_quality/):\n * every issue needs description, check_name, fingerprint, severity, and\n * location.path + location.lines.begin, where path is project-root-relative\n * (no leading `./`) and lines.begin is an integer ≥ 1.\n *\n * Fingerprints deliberately exclude line numbers so unrelated edits that\n * shift a usage do not churn findings as new/resolved in the MR widget.\n */\n\nimport { createHash } from 'node:crypto'\nimport { join, relative } from 'node:path'\n\nimport type { I18nConfig, LocaleDefinition } from '../config/types.js'\n\nimport type { UndefinedKeyFinding } from './ops-check.js'\n\nexport interface CodeQualityIssue {\n description: string\n check_name: string\n fingerprint: string\n severity: 'info' | 'minor' | 'major' | 'critical' | 'blocker'\n location: {\n path: string\n lines: { begin: number }\n }\n}\n\nconst UNDEFINED_KEY_CHECK = 'i18n.undefined-key'\nconst ORPHAN_KEY_CHECK = 'i18n.orphan-key'\n\n/** NUL-joined so no part can bleed into its neighbor (keys/paths never contain NUL). */\nfunction fingerprintOf(...parts: string[]): string {\n return createHash('sha256').update(parts.join('\\0')).digest('hex')\n}\n\n/** Normalize to the report's path shape: forward slashes, no leading `./`. */\nfunction toReportPath(path: string): string {\n const normalized = path.replaceAll('\\\\', '/')\n return normalized.startsWith('./') ? normalized.slice(2) : normalized\n}\n\n/** GitLab rejects non-positive or fractional line numbers. */\nfunction toLineBegin(line: number): number {\n return Math.max(1, Math.trunc(line))\n}\n\n/**\n * `check` findings → one issue per usage location. Uncertain findings are\n * not mapped: the widget has no \"maybe\" state, and a hard-looking finding\n * for an unverifiable key would train consumers to ignore the report.\n */\nexport function undefinedKeysToCodeQuality(undefinedKeys: UndefinedKeyFinding[]): CodeQualityIssue[] {\n const issues: CodeQualityIssue[] = []\n for (const finding of undefinedKeys) {\n for (const usage of finding.usages) {\n const path = toReportPath(usage.file)\n issues.push({\n description: `i18n key \"${finding.key}\" is referenced here but defined in no locale file of the consuming app's layers — it renders as the raw key at runtime.`,\n check_name: UNDEFINED_KEY_CHECK,\n fingerprint: fingerprintOf(UNDEFINED_KEY_CHECK, finding.key, path),\n severity: 'major',\n location: { path, lines: { begin: toLineBegin(usage.line) } },\n })\n }\n }\n return issues\n}\n\n/**\n * Orphan findings → one issue per key, anchored at the layer's\n * reference-locale file (line 1): orphans live in locale files, not code.\n * The fingerprint is anchor-independent (layer + key), so locale-file\n * renames or layout changes do not churn findings. Uncertain,\n * dynamic-matched, and misplaced keys are not part of `orphansByLayer`\n * and therefore never mapped.\n */\nexport function orphanKeysToCodeQuality(\n orphansByLayer: Record<string, string[]>,\n anchorFileByLayer: Record<string, string>,\n): CodeQualityIssue[] {\n const issues: CodeQualityIssue[] = []\n for (const [layer, keys] of Object.entries(orphansByLayer)) {\n // A missing anchor (layer absent from config) degrades to the layer name\n // as path — the fingerprint is path-independent, so findings stay stable.\n const path = toReportPath(anchorFileByLayer[layer] ?? layer)\n for (const key of keys) {\n issues.push({\n description: `Orphan i18n key \"${key}\" in layer \"${layer}\" has no source-code reference — it may be consumed dynamically; verify before deleting.`,\n check_name: ORPHAN_KEY_CHECK,\n fingerprint: fingerprintOf(ORPHAN_KEY_CHECK, layer, key),\n severity: 'minor',\n location: { path, lines: { begin: 1 } },\n })\n }\n }\n return issues\n}\n\n/**\n * Project-root-relative reference-locale file path per layer, the anchor for\n * orphan issues. Aliases resolve to their target layer's directory. Flat\n * layouts anchor at the locale file; namespaced layouts (per-locale\n * directories) have no single file and anchor at the locale's directory.\n */\nexport function referenceLocaleAnchorPaths(\n config: I18nConfig,\n layers: string[],\n locale: LocaleDefinition,\n projectDir: string,\n): Record<string, string> {\n const anchors: Record<string, string> = {}\n for (const layer of layers) {\n const localeDir = config.localeDirs.find(d => d.layer === layer)\n const resolved = localeDir?.aliasOf\n ? config.localeDirs.find(d => d.layer === localeDir.aliasOf) ?? localeDir\n : localeDir\n if (!resolved) continue\n const absPath = locale.file\n ? join(resolved.path, locale.file)\n : join(resolved.path, locale.code)\n anchors[layer] = toReportPath(relative(projectDir, absPath))\n }\n return anchors\n}\n","/**\n * Orphan-key operations: find/remove translation keys that are not\n * referenced in source code, plus code-usage scanning.\n */\n\nimport { isAbsolute, relative } from 'node:path'\n\nimport { detectI18nConfig } from '../config/detector.js'\nimport { buildLayerGraph } from '../config/layer-graph.js'\nimport type { I18nConfig, LocaleDefinition, LocaleDir } from '../config/types.js'\nimport { writeCodequalityFile, writeReportFile } from '../io/json-writer.js'\nimport { readLocaleData, mutateLocaleData } from '../io/locale-data.js'\nimport { getLeafKeys, removeNestedValue } from '../io/key-operations.js'\nimport { scanSourceFiles, toRelativePath, findOrphanKeysForConfig } from '../scanner/code-scanner.js'\nimport type { OrphanScanPlan, OrphanScanResult } from '../scanner/code-scanner.js'\nimport { getPatternSet } from '../scanner/patterns.js'\nimport type { FindOrphanKeysResult, RemoveOrphanKeysResult, CodeUsageResult } from './types.js'\nimport { ToolError } from '../utils/errors.js'\n\nimport { log } from '../utils/logger.js'\n\nimport { findLayerOrThrow, resolveReferenceLocale } from './shared.js'\nimport { validateReportPath, resolveOutputFile, resolveReportFilePath } from './report.js'\nimport { orphanKeysToCodeQuality, referenceLocaleAnchorPaths } from './codequality.js'\n\nconst MISPLACED_USAGE_NOTE\n = 'Keys referenced only from apps that do not consume their layer. '\n + 'Either the key belongs in a broader (shared) layer, or the usage is a bug. '\n + 'These keys are not counted as orphans and are never removed.'\n\n/** True when `child` equals `parent` or lies inside it. */\nfunction isWithin(child: string, parent: string): boolean {\n const rel = relative(parent, child)\n return rel === '' || (!rel.startsWith('..') && !isAbsolute(rel))\n}\n\n/**\n * Build the scope-aware scan plan for orphan detection from the layer graph.\n *\n * Units are the distinct app root dirs plus canonical layer root dirs (each\n * scanned exactly once — nested unit dirs are carved out of ancestor scans).\n * Layer L's scope is its own layer root dir plus the root dirs of all apps\n * in `appsUsingLayer(L)`.\n *\n * Degenerate cases (no app info, or a layer consumed by no app) fall back to\n * ALL units — the whole-project behavior of the pre-scope-aware scan. Code\n * outside every unit (e.g. scripts/ in a monorepo scanned from its root) is\n * claimed by a synthetic project-root unit that counts for every layer, so\n * scoping never produces orphans the old global scan would not have.\n */\nexport function buildOrphanScanPlan(config: I18nConfig, projectDir: string): OrphanScanPlan {\n const graph = buildLayerGraph(config)\n const apps = config.apps ?? []\n\n const nameByDir = new Map<string, string>()\n const usedNames = new Set<string>()\n const claim = (dir: string, name: string): void => {\n if (nameByDir.has(dir)) return\n let unique = name\n let n = 2\n while (usedNames.has(unique)) unique = `${name}#${n++}`\n nameByDir.set(dir, unique)\n usedNames.add(unique)\n }\n\n // Apps claim first so a dir shared by an app and its layer reports the app name.\n for (const app of apps) claim(app.rootDir, app.name)\n for (const layer of graph.canonicalLayers) claim(layer.layerRootDir, layer.layer)\n\n let projectUnitName: string | undefined\n if (![...nameByDir.keys()].some(unitDir => isWithin(projectDir, unitDir))) {\n claim(projectDir, 'project-root')\n projectUnitName = nameByDir.get(projectDir)\n }\n\n const units = [...nameByDir].map(([dir, name]) => ({ name, dir }))\n const allNames = units.map(u => u.name)\n\n const scopeByLayer = new Map<string, string[]>()\n for (const layer of graph.canonicalLayers) {\n const consumers = graph.appsUsingLayer(layer.layer)\n if (apps.length === 0 || consumers.length === 0) {\n scopeByLayer.set(layer.layer, allNames)\n continue\n }\n const scope = new Set<string>()\n if (projectUnitName) scope.add(projectUnitName)\n scope.add(nameByDir.get(layer.layerRootDir)!)\n for (const appName of consumers) {\n const app = apps.find(a => a.name === appName)\n if (app) scope.add(nameByDir.get(app.rootDir)!)\n }\n scopeByLayer.set(layer.layer, [...scope])\n }\n\n return { units, scopeByLayer }\n}\n\n/**\n * Report shape for a dynamic-key entry. Bare candidates are synthesized\n * without a source location (file: '', line: 0) — relativizing '' would\n * resolve against the process cwd and make reports cwd-dependent, so\n * file/line are omitted entirely for them.\n */\nfunction toDynamicKeyEntries(\n dynamicKeys: OrphanScanResult['allDynamicKeys'],\n projectDir: string,\n): Array<{ expression: string; file?: string; line?: number }> {\n return dynamicKeys.map(dk => dk.file\n ? { expression: dk.expression, file: toRelativePath(dk.file, projectDir), line: dk.line }\n : { expression: dk.expression })\n}\n\n/** Relativize each layer's scan-scope dirs against the project dir for reporting. */\nfunction relativeScanScope(result: OrphanScanResult, projectDir: string): Record<string, string[]> {\n const scanScope: Record<string, string[]> = {}\n for (const [layerName, dirs] of Object.entries(result.scanScopeByLayer)) {\n scanScope[layerName] = dirs.map(d => toRelativePath(d, projectDir) || '.')\n }\n return scanScope\n}\n\n/**\n * Shared scan entry for findOrphanKeys/removeOrphanKeys: explicit scanDirs\n * keep the global combined-scan behavior; otherwise a scope-aware plan is\n * built from the layer graph.\n */\nasync function runOrphanScan(\n config: I18nConfig,\n keysByLayer: Map<string, { keys: string[]; localeDir: LocaleDir }>,\n opts: { scanDirs?: string[]; excludeDirs?: string[]; dir: string },\n): Promise<OrphanScanResult> {\n return findOrphanKeysForConfig({\n keysByLayer,\n // an empty scanDirs array means \"not provided\" (matches excludeDirs)\n ...(opts.scanDirs?.length ? { scanDirs: opts.scanDirs } : { scanPlan: buildOrphanScanPlan(config, opts.dir) }),\n excludeDirs: opts.excludeDirs || undefined,\n resolveIgnorePatterns: layerName => resolveOrphanIgnorePatterns(config, layerName),\n patterns: getPatternSet(config.localeFileFormat),\n })\n}\n\n/**\n * `orphanScan` keys are matched against detected layer names — an unknown key\n * (e.g. keyed \"lang\" when the adapter names the layer \"root\") silently drops\n * its ignorePatterns, so warn on stderr listing the valid names (#265).\n */\nfunction warnUnknownOrphanScanLayers(config: I18nConfig): void {\n const orphanScan = config.projectConfig?.orphanScan\n if (!orphanScan) return\n const layerNames = config.localeDirs.map(d => d.layer)\n const known = new Set(layerNames)\n for (const key of Object.keys(orphanScan)) {\n if (known.has(key)) continue\n log.warn(`orphanScan config key \"${key}\" matches no detected layer — its ignorePatterns are not applied. Detected layers: ${layerNames.join(', ')}`)\n }\n}\n\nexport function resolveOrphanIgnorePatterns(\n config: I18nConfig,\n layer: string | undefined,\n): string[] | undefined {\n if (!layer || !config.projectConfig?.orphanScan) return undefined\n const layerConfig = config.projectConfig.orphanScan[layer]\n if (!layerConfig?.ignorePatterns?.length) return undefined\n return layerConfig.ignorePatterns\n}\n\n/**\n * Shared helper for findOrphanKeys and removeOrphanKeys.\n * Resolves the locale, filters layers, validates aliases, and builds the\n * keysByLayer Map. Returns the resolved context — or throws on invalid input.\n * The caller handles the empty-report case (totalKeys === 0).\n */\nasync function resolveOrphanScanContext(\n config: I18nConfig,\n opts: { layer?: string; locale?: string; dir: string; toolName: string },\n): Promise<{\n layersToCheck: LocaleDir[]\n keysByLayer: Map<string, { keys: string[]; localeDir: LocaleDir }>\n totalKeys: number\n localeCode: string\n localeDef: LocaleDefinition\n}> {\n const { localeCode, localeDef } = resolveReferenceLocale(config, opts.locale)\n\n const layersToCheck = opts.layer\n ? config.localeDirs.filter(d => d.layer === opts.layer)\n : config.localeDirs.filter(d => !d.aliasOf)\n\n if (layersToCheck.length === 0) {\n if (opts.layer) {\n findLayerOrThrow(config, opts.layer)\n }\n throw new ToolError('No locale directories found.', 'LAYER_NOT_FOUND')\n }\n\n if (opts.layer && layersToCheck[0]?.aliasOf) {\n throw new ToolError(\n `Layer \"${opts.layer}\" is an alias of \"${layersToCheck[0].aliasOf}\". Use the target layer instead.`,\n 'LAYER_IS_ALIAS',\n )\n }\n\n const keysByLayer = new Map<string, { keys: string[]; localeDir: LocaleDir }>()\n for (const ld of layersToCheck) {\n let data: Record<string, unknown>\n try {\n data = await readLocaleData(config, ld.layer, localeDef)\n } catch {\n continue\n }\n if (Object.keys(data).length === 0) continue\n keysByLayer.set(ld.layer, { keys: getLeafKeys(data), localeDir: ld })\n }\n\n const totalKeys = [...keysByLayer.values()].reduce((sum, v) => sum + v.keys.length, 0)\n\n return { layersToCheck, keysByLayer, totalKeys, localeCode, localeDef }\n}\n\n/**\n * Find translation keys that exist in locale files but are not referenced in source code.\n */\nexport async function findOrphanKeys(opts: {\n layer?: string\n locale?: string\n /**\n * Explicit scan roots — manual scope control. When set, all layers are\n * checked against one combined usage set from these dirs (no per-layer\n * scoping, no misplaced-usage detection). When absent, a scope-aware plan\n * from the layer graph is used: each layer is checked against the apps\n * that consume it.\n */\n scanDirs?: string[]\n excludeDirs?: string[]\n projectDir?: string\n outputFile?: string\n}): Promise<FindOrphanKeysResult> {\n const { layer, locale, scanDirs, excludeDirs } = opts\n const dir = opts.projectDir ?? process.cwd()\n const config = await detectI18nConfig(dir)\n warnUnknownOrphanScanLayers(config)\n\n const { layersToCheck, keysByLayer, totalKeys, localeCode } = await resolveOrphanScanContext(config, {\n layer,\n locale,\n dir,\n toolName: 'find_orphan_keys',\n })\n\n if (totalKeys === 0) {\n const emptyOutput = { orphanKeys: {} as Record<string, string[]>, summary: { totalKeys: 0, orphanCount: 0, filesScanned: 0, message: 'No translation keys found in locale files.' } }\n const reportPath = resolveOutputFile(dir, opts.outputFile) ?? resolveReportFilePath(config, dir, 'find_orphan_keys')\n if (reportPath) {\n await writeReportFile(reportPath, emptyOutput, {\n tool: 'find_orphan_keys',\n args: { layer, locale, scanDirs, excludeDirs },\n })\n return { reportFile: reportPath, summary: emptyOutput.summary }\n }\n return emptyOutput\n }\n\n const orphanResult = await runOrphanScan(config, keysByLayer, { scanDirs, excludeDirs, dir })\n\n const byLayer = orphanResult.orphansByLayer\n const allOrphanKeys: Array<{ key: string; layer: string }> = []\n for (const [layerName, keys] of Object.entries(byLayer)) {\n for (const key of keys) allOrphanKeys.push({ key, layer: layerName })\n }\n allOrphanKeys.sort((a, b) => a.layer.localeCompare(b.layer) || a.key.localeCompare(b.key))\n const sortedByLayer: Record<string, string[]> = {}\n for (const { key, layer: keyLayer } of allOrphanKeys) {\n if (!sortedByLayer[keyLayer]) sortedByLayer[keyLayer] = []\n sortedByLayer[keyLayer].push(key)\n }\n\n const misplacedCount = orphanResult.misplacedUsages.length\n const output: FindOrphanKeysResult = {\n orphanKeys: sortedByLayer,\n uncertainKeys: orphanResult.uncertainCount > 0 ? orphanResult.uncertainByLayer : undefined,\n misplacedUsages: misplacedCount > 0 ? orphanResult.misplacedUsages : undefined,\n misplacedUsageNote: misplacedCount > 0 ? MISPLACED_USAGE_NOTE : undefined,\n summary: {\n totalKeys,\n orphanCount: orphanResult.orphanCount,\n uncertainCount: orphanResult.uncertainCount,\n misplacedCount,\n dynamicMatchedCount: orphanResult.dynamicMatchedCount,\n ignoredCount: orphanResult.ignoredCount,\n usedCount: totalKeys - orphanResult.orphanCount - orphanResult.uncertainCount - misplacedCount,\n filesScanned: orphanResult.totalFilesScanned,\n layersChecked: layersToCheck.map(d => d.layer),\n dirsScanned: orphanResult.dirsScanned,\n scanScope: relativeScanScope(orphanResult, dir),\n locale: localeCode,\n },\n dynamicKeyWarning: orphanResult.allDynamicKeys.length > 0\n ? `${orphanResult.allDynamicKeys.length} dynamic key reference(s) found (template literals with interpolation). Some \"orphan\" keys may actually be used via dynamic keys. Review before removing. Note: string concatenation patterns (e.g. 'prefix.' + var) are not detected — use template literals for full coverage.`\n : undefined,\n dynamicKeys: orphanResult.allDynamicKeys.length > 0\n ? toDynamicKeyEntries(orphanResult.allDynamicKeys, dir)\n : undefined,\n unresolvedKeyWarnings: orphanResult.unresolvedKeyWarnings.length > 0\n ? orphanResult.unresolvedKeyWarnings.map(w => ({\n expression: w.expression,\n file: toRelativePath(w.file, dir),\n line: w.line,\n callee: w.callee,\n suggestedIgnorePattern: w.suggestedIgnorePattern,\n }))\n : undefined,\n }\n\n const reportPath = resolveOutputFile(dir, opts.outputFile) ?? resolveReportFilePath(config, dir, 'find_orphan_keys')\n if (reportPath) {\n await writeReportFile(reportPath, output as unknown as Record<string, unknown>, {\n tool: 'find_orphan_keys',\n args: { layer, locale, scanDirs, excludeDirs },\n })\n return { reportFile: reportPath, summary: output.summary }\n }\n\n return output\n}\n\n/**\n * Scan Vue/TS source files to find where translation keys are referenced.\n */\nexport async function scanCodeUsage(opts: {\n keys?: string[]\n scanDirs?: string[]\n excludeDirs?: string[]\n projectDir?: string\n outputFile?: string\n}): Promise<CodeUsageResult> {\n const { keys, scanDirs, excludeDirs } = opts\n const dir = opts.projectDir ?? process.cwd()\n const config = await detectI18nConfig(dir)\n\n const dirsToScan = scanDirs ?? config.layerRootDirs\n\n const allUsages: Array<{ key: string; file: string; line: number; callee: string }> = []\n const allDynamicKeys: Array<{ expression: string; file: string; line: number; callee: string }> = []\n let totalFilesScanned = 0\n\n for (const scanDir of dirsToScan) {\n const result = await scanSourceFiles(scanDir, excludeDirs, getPatternSet(config.localeFileFormat))\n totalFilesScanned += result.filesScanned\n allUsages.push(...result.usages)\n allDynamicKeys.push(...result.dynamicKeys)\n }\n\n const filteredUsages = keys\n ? allUsages.filter(u => keys.includes(u.key))\n : allUsages\n\n const byKey: Record<string, Array<{ file: string; line: number; callee: string }>> = {}\n for (const usage of filteredUsages) {\n (byKey[usage.key] ??= []).push({\n file: toRelativePath(usage.file, dir),\n line: usage.line,\n callee: usage.callee,\n })\n }\n\n const sortedByKey: Record<string, Array<{ file: string; line: number; callee: string }>> = {}\n for (const [key, locations] of Object.entries(byKey).sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))) {\n sortedByKey[key] = locations\n }\n\n const notFound = keys\n ? keys.filter(k => !byKey[k])\n : []\n\n const output: CodeUsageResult = {\n usages: sortedByKey,\n summary: {\n uniqueKeysFound: Object.keys(sortedByKey).length,\n totalReferences: filteredUsages.length,\n filesScanned: totalFilesScanned,\n dirsScanned: dirsToScan,\n },\n }\n\n if (notFound.length > 0) {\n output.notFoundInCode = notFound\n }\n\n if (allDynamicKeys.length > 0) {\n output.dynamicKeys = allDynamicKeys.map(dk => ({\n expression: dk.expression,\n file: toRelativePath(dk.file, dir),\n line: dk.line,\n }))\n }\n\n const reportPath = resolveOutputFile(dir, opts.outputFile) ?? resolveReportFilePath(config, dir, 'scan_code_usage')\n if (reportPath) {\n await writeReportFile(reportPath, output as unknown as Record<string, unknown>, {\n tool: 'scan_code_usage',\n args: { keys, scanDirs, excludeDirs },\n })\n return { reportFile: reportPath, summary: output.summary }\n }\n\n return output\n}\n\n/**\n * Find translation keys not referenced in source code and remove them.\n */\nexport async function removeOrphanKeys(opts: {\n layer?: string\n locale?: string\n /** Explicit scan roots — manual scope control, same semantics as {@link findOrphanKeys}. */\n scanDirs?: string[]\n excludeDirs?: string[]\n dryRun?: boolean\n projectDir?: string\n outputFile?: string\n /** Also write the orphan findings as a GitLab Code Quality JSON array to this path. */\n codequalityOutput?: string\n}): Promise<RemoveOrphanKeysResult> {\n const { layer, locale, scanDirs, excludeDirs } = opts\n const dir = opts.projectDir ?? process.cwd()\n const config = await detectI18nConfig(dir)\n warnUnknownOrphanScanLayers(config)\n const isDryRun = opts.dryRun ?? true\n\n const { keysByLayer, totalKeys, localeDef } = await resolveOrphanScanContext(config, {\n layer,\n locale,\n dir,\n toolName: 'remove_orphan_keys',\n })\n\n // Written in every branch, even without findings: an empty array on the\n // default branch is the baseline the MR widget diffs against.\n const writeCodequality = async (orphans: Record<string, string[]>): Promise<void> => {\n if (!opts.codequalityOutput) return\n validateReportPath(dir, opts.codequalityOutput)\n const anchors = referenceLocaleAnchorPaths(config, Object.keys(orphans), localeDef, dir)\n await writeCodequalityFile(opts.codequalityOutput, orphanKeysToCodeQuality(orphans, anchors))\n }\n\n if (totalKeys === 0) {\n await writeCodequality({})\n const emptyOutput = { orphanKeys: {}, removed: {}, summary: { totalKeys: 0, orphanCount: 0, message: 'No translation keys found.' } }\n const emptyReportPath = resolveOutputFile(dir, opts.outputFile) ?? resolveReportFilePath(config, dir, 'remove_orphan_keys')\n if (emptyReportPath) {\n await writeReportFile(emptyReportPath, emptyOutput as unknown as Record<string, unknown>, {\n tool: 'remove_orphan_keys',\n args: { layer, locale, scanDirs, excludeDirs, dryRun: opts.dryRun },\n })\n return { reportFile: emptyReportPath, summary: emptyOutput.summary }\n }\n return emptyOutput\n }\n\n const orphanResult = await runOrphanScan(config, keysByLayer, { scanDirs, excludeDirs, dir })\n await writeCodequality(orphanResult.orphansByLayer)\n const orphansByLayer = orphanResult.orphansByLayer\n const orphanCount = orphanResult.orphanCount\n const totalFilesScanned = orphanResult.totalFilesScanned\n const dynamicMatchedCount = orphanResult.dynamicMatchedCount\n const ignoredCount = orphanResult.ignoredCount\n const misplacedCount = orphanResult.misplacedUsages.length\n const misplacedUsages = misplacedCount > 0 ? orphanResult.misplacedUsages : undefined\n const misplacedUsageNote = misplacedCount > 0 ? MISPLACED_USAGE_NOTE : undefined\n const scanScope = relativeScanScope(orphanResult, dir)\n const allDynamicKeys = toDynamicKeyEntries(orphanResult.allDynamicKeys, dir)\n\n if (orphanCount === 0) {\n const messageParts: string[] = ['No orphan keys found.']\n if (dynamicMatchedCount > 0) messageParts.push(`${dynamicMatchedCount} key(s) were excluded by dynamic pattern matching.`)\n if (ignoredCount > 0) messageParts.push(`${ignoredCount} key(s) were excluded by ignore patterns.`)\n if (orphanResult.uncertainCount > 0) messageParts.push(`${orphanResult.uncertainCount} uncertain key(s) were excluded because they overlap with dynamic translation patterns.`)\n if (misplacedCount > 0) messageParts.push(`${misplacedCount} key(s) are referenced only outside their layer's scope (see misplacedUsages).`)\n if (dynamicMatchedCount === 0 && ignoredCount === 0 && orphanResult.uncertainCount === 0 && misplacedCount === 0) messageParts.push('All translation keys are referenced in code.')\n const zeroOutput: RemoveOrphanKeysResult = {\n orphanKeys: {},\n uncertainKeys: orphanResult.uncertainCount > 0 ? orphanResult.uncertainByLayer : undefined,\n misplacedUsages,\n misplacedUsageNote,\n summary: { totalKeys, orphanCount: 0, uncertainCount: orphanResult.uncertainCount, misplacedCount, dynamicMatchedCount, ignoredCount, filesScanned: totalFilesScanned, scanScope, message: messageParts.join(' ') },\n }\n const zeroReportPath = resolveOutputFile(dir, opts.outputFile) ?? resolveReportFilePath(config, dir, 'remove_orphan_keys')\n if (zeroReportPath) {\n await writeReportFile(zeroReportPath, zeroOutput as unknown as Record<string, unknown>, {\n tool: 'remove_orphan_keys',\n args: { layer, locale, scanDirs, excludeDirs, dryRun: opts.dryRun },\n })\n return { reportFile: zeroReportPath, summary: zeroOutput.summary }\n }\n return zeroOutput\n }\n\n // Dry run — just report\n if (isDryRun) {\n const output: RemoveOrphanKeysResult = {\n orphanKeys: orphansByLayer,\n uncertainKeys: orphanResult.uncertainCount > 0 ? orphanResult.uncertainByLayer : undefined,\n misplacedUsages,\n misplacedUsageNote,\n summary: {\n dryRun: true,\n totalKeys,\n orphanCount,\n uncertainCount: orphanResult.uncertainCount,\n misplacedCount,\n dynamicMatchedCount,\n ignoredCount,\n usedCount: totalKeys - orphanCount - orphanResult.uncertainCount - misplacedCount,\n filesScanned: totalFilesScanned,\n scanScope,\n message: `Found ${orphanCount} orphan key(s) safe to remove.${orphanResult.uncertainCount > 0 ? ` ${orphanResult.uncertainCount} uncertain key(s) excluded (overlap with dynamic translation patterns).` : ''}${misplacedCount > 0 ? ` ${misplacedCount} key(s) referenced only outside their layer's scope were excluded (see misplacedUsages).` : ''} ${dynamicMatchedCount > 0 ? `${dynamicMatchedCount} key(s) matched dynamic patterns and were excluded. ` : ''}${ignoredCount > 0 ? `${ignoredCount} key(s) matched ignore patterns and were excluded. ` : ''}Call again with dryRun: false to remove them.`,\n },\n }\n if (allDynamicKeys.length > 0) {\n output.dynamicKeyWarning = `${allDynamicKeys.length} dynamic key reference(s) found. Some \"orphan\" keys may be used via dynamic keys. Review before removing. Note: string concatenation patterns (e.g. 'prefix.' + var) are not detected — use template literals for full coverage.`\n output.dynamicKeys = allDynamicKeys\n }\n if (orphanResult.unresolvedKeyWarnings.length > 0) {\n output.unresolvedKeyWarnings = orphanResult.unresolvedKeyWarnings.map(w => ({\n expression: w.expression,\n file: toRelativePath(w.file, dir),\n line: w.line,\n callee: w.callee,\n suggestedIgnorePattern: w.suggestedIgnorePattern,\n }))\n }\n const dryRunReportPath = resolveOutputFile(dir, opts.outputFile) ?? resolveReportFilePath(config, dir, 'remove_orphan_keys')\n if (dryRunReportPath) {\n await writeReportFile(dryRunReportPath, output as unknown as Record<string, unknown>, {\n tool: 'remove_orphan_keys',\n args: { layer, locale, scanDirs, excludeDirs, dryRun: opts.dryRun },\n })\n return { reportFile: dryRunReportPath, summary: output.summary }\n }\n return output\n }\n\n // Actual removal\n const removedByLayer: Record<string, string[]> = {}\n let totalFilesWritten = 0\n\n for (const [layerName, orphans] of Object.entries(orphansByLayer)) {\n const ld = config.localeDirs.find(d => d.layer === layerName)!\n if (ld.aliasOf) continue\n\n for (const localeDef2 of config.locales) {\n try {\n const written = await mutateLocaleData(config, layerName, localeDef2, (fileData) => {\n for (const key of orphans) {\n removeNestedValue(fileData, key)\n }\n })\n totalFilesWritten += written.size\n } catch {\n continue\n }\n }\n\n removedByLayer[layerName] = orphans\n }\n\n const removalOutput: RemoveOrphanKeysResult = {\n removed: removedByLayer,\n uncertainKeys: orphanResult.uncertainCount > 0 ? orphanResult.uncertainByLayer : undefined,\n misplacedUsages,\n misplacedUsageNote,\n summary: {\n dryRun: false,\n totalKeys,\n removedCount: orphanCount,\n uncertainCount: orphanResult.uncertainCount,\n misplacedCount,\n dynamicMatchedCount,\n ignoredCount,\n remainingCount: totalKeys - orphanCount,\n filesWritten: totalFilesWritten,\n filesScanned: totalFilesScanned,\n scanScope,\n },\n }\n\n const removalReportPath = resolveOutputFile(dir, opts.outputFile) ?? resolveReportFilePath(config, dir, 'remove_orphan_keys')\n if (removalReportPath) {\n await writeReportFile(removalReportPath, removalOutput as unknown as Record<string, unknown>, {\n tool: 'remove_orphan_keys',\n args: { layer, locale, scanDirs, excludeDirs, dryRun: opts.dryRun },\n })\n return { reportFile: removalReportPath, summary: removalOutput.summary }\n }\n\n return removalOutput\n}\n","/**\n * Cross-layer duplicate-key detection: keys defined in both a shared layer\n * and a consuming child layer, compared in one reference locale.\n */\n\nimport { detectI18nConfig } from '../config/detector.js'\nimport { buildLayerGraph } from '../config/layer-graph.js'\nimport type { I18nConfig, LocaleDefinition, LocaleDir } from '../config/types.js'\nimport { writeReportFile } from '../io/json-writer.js'\nimport { readLocaleData } from '../io/locale-data.js'\nimport { getNestedValue, getLeafKeys } from '../io/key-operations.js'\nimport { ToolError } from '../utils/errors.js'\n\nimport { findLocaleImpl, findLocaleOrThrow } from './shared.js'\nimport { resolveOutputFile, resolveReportFilePath } from './report.js'\n\nexport interface DuplicateKeyCollision {\n key: string\n sharedLayer: string\n childLayer: string\n sharedValue: unknown\n childValue: unknown\n divergent: boolean\n}\n\nexport interface FindDuplicateKeysSummary {\n totalCollisions: number\n divergentCount: number\n pairsChecked: number\n locale: string\n message?: string\n}\n\nexport interface FindDuplicateKeysResult {\n collisions: DuplicateKeyCollision[]\n guidance: string\n summary: FindDuplicateKeysSummary\n}\n\nconst DUPLICATE_GUIDANCE\n = 'At runtime the child layer\\'s value shadows the shared layer\\'s value for the same key. '\n + 'Fix each collision by deleting one side — usually the shared copy when the child value is '\n + 'authoritative, or the child copy to fall through to the shared value. Never move the key: '\n + 'both layers already define it.'\n\n/** Leaf values may be arrays (getLeafKeys treats them as leaves) — compare\n * those structurally; identity covers primitives. */\nfunction valuesEqual(a: unknown, b: unknown): boolean {\n if (a === b) return true\n if (typeof a !== 'object' || typeof b !== 'object' || a === null || b === null) return false\n return JSON.stringify(a) === JSON.stringify(b)\n}\n\ninterface LayerPair {\n shared: LocaleDir\n child: LocaleDir\n}\n\n/**\n * Derive the (shared layer, child layer) pairs to check from each app's\n * configured layer order: earlier entries override later ones at runtime,\n * so for any two locale-backed layers an app consumes, the earlier one is\n * the child (its values shadow) and the later one is the shared base.\n * Pairs are deduped unordered; if two apps ever disagree on precedence\n * (pathological), the first-seen direction wins. With no app info there\n * are no pairs.\n */\n/** An app's locale-backed canonical layers in its configured precedence order. */\nfunction orderedCanonicalLayers(\n layerNames: string[],\n graph: ReturnType<typeof buildLayerGraph>,\n canonicalByName: Map<string, LocaleDir>,\n): LocaleDir[] {\n const ordered: LocaleDir[] = []\n const seen = new Set<string>()\n for (const name of layerNames) {\n const canonical = canonicalByName.get(graph.ownerOf(name))\n if (canonical && !seen.has(canonical.layer)) {\n seen.add(canonical.layer)\n ordered.push(canonical)\n }\n }\n return ordered\n}\n\nfunction deriveLayerPairs(config: I18nConfig, graph: ReturnType<typeof buildLayerGraph>): LayerPair[] {\n const canonicalByName = new Map(graph.canonicalLayers.map(d => [d.layer, d]))\n const pairs: LayerPair[] = []\n const seen = new Set<string>()\n\n for (const app of config.apps ?? []) {\n const ordered = orderedCanonicalLayers(app.layers, graph, canonicalByName)\n for (const [i, child] of ordered.entries()) {\n for (const shared of ordered.slice(i + 1)) {\n const id = [child.layer, shared.layer].sort().join('\\u0000')\n if (seen.has(id)) continue\n seen.add(id)\n pairs.push({ shared, child })\n }\n }\n }\n\n return pairs\n}\n\nfunction emptyPairsMessage(config: I18nConfig): string {\n if ((config.apps ?? []).length === 0) {\n return 'No app info in the config, so layer consumption edges are unknowable and no '\n + '(shared layer, child layer) pairs can be derived. Duplicate detection needs '\n + 'app-to-layer consumption info (e.g. a multi-app Nuxt monorepo config).'\n }\n return 'No (shared layer, child layer) pairs to check — no app consumes more than one locale-backed layer.'\n}\n\n/**\n * Find keys defined in both a shared layer and a consuming child layer,\n * comparing values in a single reference locale (default: the project\n * default locale). Pure locale-file I/O — no source scanning.\n */\nexport async function findDuplicateKeys(opts: {\n locale?: string\n projectDir?: string\n outputFile?: string\n} = {}): Promise<FindDuplicateKeysResult | { reportFile: string; summary: FindDuplicateKeysSummary }> {\n const dir = opts.projectDir ?? process.cwd()\n const config = await detectI18nConfig(dir)\n\n const locale: LocaleDefinition | undefined = opts.locale\n ? findLocaleOrThrow(config, opts.locale)\n : findLocaleImpl(config, config.defaultLocale) ?? config.locales[0]\n if (!locale) {\n throw new ToolError('No locales found in configuration.', 'LOCALE_NOT_FOUND')\n }\n\n const graph = buildLayerGraph(config)\n const pairs = deriveLayerPairs(config, graph)\n\n // Each layer's data is read once even when it appears in several pairs.\n const layerDataCache = new Map<string, Promise<Record<string, unknown>>>()\n const dataFor = (layer: string): Promise<Record<string, unknown>> => {\n let cached = layerDataCache.get(layer)\n if (!cached) {\n // readLocaleData already yields {} for missing files; real failures\n // (parse errors, permissions) must surface, not read as \"no keys\".\n cached = readLocaleData(config, layer, locale)\n layerDataCache.set(layer, cached)\n }\n return cached\n }\n\n const collisions: DuplicateKeyCollision[] = []\n\n for (const { shared, child } of pairs) {\n const sharedData = await dataFor(shared.layer)\n const childData = await dataFor(child.layer)\n\n const childKeys = new Set(getLeafKeys(childData))\n\n for (const key of getLeafKeys(sharedData)) {\n if (!childKeys.has(key)) continue\n const sharedValue = getNestedValue(sharedData, key)\n const childValue = getNestedValue(childData, key)\n collisions.push({\n key,\n sharedLayer: shared.layer,\n childLayer: child.layer,\n sharedValue,\n childValue,\n divergent: !valuesEqual(sharedValue, childValue),\n })\n }\n }\n\n const summary: FindDuplicateKeysSummary = {\n totalCollisions: collisions.length,\n divergentCount: collisions.filter(c => c.divergent).length,\n pairsChecked: pairs.length,\n locale: locale.code,\n ...(pairs.length === 0 ? { message: emptyPairsMessage(config) } : {}),\n }\n\n const output: FindDuplicateKeysResult = {\n collisions,\n guidance: DUPLICATE_GUIDANCE,\n summary,\n }\n\n const reportPath = resolveOutputFile(dir, opts.outputFile) ?? resolveReportFilePath(config, dir, 'find_duplicate_keys')\n if (reportPath) {\n await writeReportFile(reportPath, output as unknown as Record<string, unknown>, {\n tool: 'find_duplicate_keys',\n args: { locale: opts.locale },\n })\n return { reportFile: reportPath, summary }\n }\n\n return output\n}\n","/**\n * Used-but-undefined key detection — the inverse of orphan scanning.\n *\n * A key referenced in source code but defined in no locale file of the\n * using app's consumed layers renders as a raw key at runtime. Nothing\n * else catches this direction (orphan scanning only computes\n * locale − code; this computes code − locale, per scan unit).\n */\n\nimport { detectI18nConfig } from '../config/detector.js'\nimport type { I18nConfig, LocaleDefinition } from '../config/types.js'\nimport { writeCodequalityFile, writeReportFile } from '../io/json-writer.js'\nimport { readLocaleData } from '../io/locale-data.js'\nimport { getLeafKeys } from '../io/key-operations.js'\nimport {\n scanSourceFiles,\n toRelativePath,\n buildDynamicKeyRegexes,\n buildIgnorePatternRegexes,\n nestedUnitIgnores,\n} from '../scanner/code-scanner.js'\nimport type { DynamicKeyUsage, KeyUsage, ScanResult, ScanUnit } from '../scanner/code-scanner.js'\nimport { getPatternSet } from '../scanner/patterns.js'\n\nimport { resolveReferenceLocale } from './shared.js'\nimport { resolveOutputFile, resolveReportFilePath, validateReportPath } from './report.js'\nimport { buildOrphanScanPlan, resolveOrphanIgnorePatterns } from './ops-orphans.js'\nimport { undefinedKeysToCodeQuality } from './codequality.js'\n\nexport interface KeyUsageLocation {\n /** Source file path, relative to the project dir. */\n file: string\n line: number\n}\n\nexport interface UndefinedKeyFinding {\n key: string\n /** Scan unit the usage lives in (app name, layer name, or project-root). */\n app: string\n /** Layers whose keys this unit can resolve — all were searched. */\n searchedLayers: string[]\n usages: KeyUsageLocation[]\n}\n\nexport interface UncertainKeyFinding extends UndefinedKeyFinding {\n /** Why this is not a hard finding. */\n reason: string\n}\n\nexport interface CheckUndefinedKeysSummary {\n /** Distinct statically referenced keys across all scan units. */\n usedKeysChecked: number\n undefinedCount: number\n uncertainCount: number\n /** Unresolvable keys excluded by orphanScan ignorePatterns. */\n ignoredCount: number\n filesScanned: number\n locale: string\n /** Scan unit → layers searched for that unit's key usages. */\n searchedLayersByApp: Record<string, string[]>\n message: string\n}\n\nexport interface CheckUndefinedKeysResult {\n undefinedKeys: UndefinedKeyFinding[]\n uncertainKeys: UncertainKeyFinding[]\n limitation: string\n summary: CheckUndefinedKeysSummary\n}\n\nconst CHECK_LIMITATION\n = 'Static extraction is line-based: dynamically built keys (template literals, string '\n + 'concatenation) cannot be verified and appear under uncertainKeys, never as hard findings. '\n + 'Multiline translation calls are only caught heuristically.'\n\nconst UNCERTAIN_DYNAMIC_USAGE\n = 'dynamically built key — no defined key matches its pattern, but static analysis cannot verify which concrete keys it produces'\nconst UNCERTAIN_DYNAMIC_OVERLAP\n = 'matches a dynamic key pattern used in this app — may be a partial extraction of that expression'\nconst UNCERTAIN_EXISTENCE_CHECK\n = 'referenced only via existence checks ($te) — likely a deliberately optional key'\nconst UNCERTAIN_NAMESPACED_KEY\n = 'package-namespaced key (namespace::group.key) — vendor language files outside the project cannot be resolved'\nconst UNCERTAIN_STRING_KEY\n = 'string-key (no dot-separated key-path shape) — Laravel/JSON-style translations render these as-is when unresolved'\n\n/** Dot-separated identifier path; anything else is a JSON-style string-key. */\nconst KEY_PATH_SHAPE = /^[\\w-]+(?:\\.[\\w-]+)+$/\n/** Laravel package-namespace syntax: accounting::messages.invoice.total */\nconst NAMESPACED_KEY = /^[\\w-]+::/\n\n/** Existence-check callees probe whether a key is defined; a miss is handled by the caller. */\nfunction isExistenceCheckCallee(callee: string): boolean {\n return callee === '$te' || callee === 'this.$te'\n}\n\n/**\n * All resolvable dot-paths of one layer in the reference locale: leaf keys\n * plus every ancestor prefix, so parent-node references (`$tm('a.b')`,\n * scoped `useI18n` roots) resolve too.\n */\nasync function layerKeyPaths(\n config: I18nConfig,\n layer: string,\n locale: LocaleDefinition,\n): Promise<Set<string>> {\n // readLocaleData yields {} for missing files; real failures (parse errors,\n // permissions) must surface — reading them as \"no keys\" would flag every\n // key of that layer as undefined.\n const data = await readLocaleData(config, layer, locale)\n const paths = new Set<string>()\n for (const leaf of getLeafKeys(data)) {\n paths.add(leaf)\n let prefix = leaf\n for (let idx = prefix.lastIndexOf('.'); idx > 0; idx = prefix.lastIndexOf('.')) {\n prefix = prefix.slice(0, idx)\n if (paths.has(prefix)) break\n paths.add(prefix)\n }\n }\n return paths\n}\n\nfunction toLocations(usages: Array<{ file: string; line: number }>, projectDir: string): KeyUsageLocation[] {\n const seen = new Set<string>()\n const locations: KeyUsageLocation[] = []\n for (const u of usages) {\n const file = toRelativePath(u.file, projectDir)\n const id = `${file}:${u.line}`\n if (seen.has(id)) continue\n seen.add(id)\n locations.push({ file, line: u.line })\n }\n return locations.sort((a, b) => a.file.localeCompare(b.file) || a.line - b.line)\n}\n\nconst byAppThenKey = (a: { app: string; key: string }, b: { app: string; key: string }): number =>\n a.app.localeCompare(b.app) || a.key.localeCompare(b.key)\n\n// ─── Scan planning ──────────────────────────────────────────────\n\ninterface CheckScanPlan {\n units: ScanUnit[]\n /** True with explicit scanDirs: every layer resolvable from every unit. */\n globalScope: boolean\n /**\n * Layers a unit's code can resolve = layers whose orphan-scan scope\n * includes the unit (for app units this equals layersOfApp plus layers\n * with degenerate all-unit scope; project-root resolves everything).\n */\n layersForUnit: (unitName: string) => string[]\n}\n\nfunction buildCheckScanPlan(config: I18nConfig, projectDir: string, scanDirs: string[] | undefined): CheckScanPlan {\n const allLayerNames = config.localeDirs.filter(d => !d.aliasOf).map(d => d.layer)\n\n // An empty scanDirs array means \"not provided\" (matches the orphan ops).\n if (scanDirs?.length) {\n return {\n units: scanDirs.map(d => ({ name: toRelativePath(d, projectDir) || '.', dir: d })),\n globalScope: true,\n layersForUnit: () => allLayerNames,\n }\n }\n\n const plan = buildOrphanScanPlan(config, projectDir)\n return {\n units: plan.units,\n globalScope: false,\n layersForUnit: unitName => allLayerNames.filter((layer) => {\n const scope = plan.scopeByLayer.get(layer)\n return !scope || scope.includes(unitName)\n }),\n }\n}\n\n// ─── Per-unit classification ────────────────────────────────────\n\ninterface UnitCheckContext {\n unitName: string\n searchedLayers: string[]\n resolvable: Set<string>\n ignoreRegexes: RegExp[]\n projectDir: string\n}\n\ninterface UnitCheckOutcome {\n undefinedKeys: UndefinedKeyFinding[]\n uncertainKeys: UncertainKeyFinding[]\n ignoredCount: number\n checkedKeys: Set<string>\n}\n\nfunction groupBy<T>(items: T[], keyOf: (item: T) => string): Map<string, T[]> {\n const groups = new Map<string, T[]>()\n for (const item of items) {\n const key = keyOf(item)\n const list = groups.get(key) ?? []\n list.push(item)\n groups.set(key, list)\n }\n return groups\n}\n\n/**\n * Classify one unit's static keys: keys not resolvable in the searched\n * layers become undefined findings — unless an ignore pattern excludes\n * them, a dynamic pattern overlaps them (possible partial extraction of a\n * dynamic expression), or they are only probed via $te.\n */\nfunction classifyStaticKeys(\n usages: KeyUsage[],\n dynRegexes: RegExp[],\n ctx: UnitCheckContext,\n outcome: UnitCheckOutcome,\n): void {\n for (const [key, keyUsages] of groupBy(usages, u => u.key)) {\n // Concat-prefix artifact: the static pattern also extracts the quoted\n // prefix of `t('some.prefix.' + x)`. A trailing dot can never be a real\n // key, and the concat usage is already covered as a dynamic expression.\n if (key.endsWith('.')) continue\n if (ctx.resolvable.has(key)) continue\n if (ctx.ignoreRegexes.some(re => re.test(key))) {\n outcome.ignoredCount++\n continue\n }\n if (dynRegexes.some(re => re.test(key))) {\n pushUncertain(outcome, ctx, key, keyUsages, UNCERTAIN_DYNAMIC_OVERLAP)\n continue\n }\n if (keyUsages.every(u => isExistenceCheckCallee(u.callee))) {\n pushUncertain(outcome, ctx, key, keyUsages, UNCERTAIN_EXISTENCE_CHECK)\n continue\n }\n // Two Laravel idioms are never hard findings: namespaced keys resolve in\n // vendor lang dirs this scan cannot see, and string-keys (JSON-style\n // full-sentence translations) legitimately render as-is when undefined.\n if (NAMESPACED_KEY.test(key)) {\n pushUncertain(outcome, ctx, key, keyUsages, UNCERTAIN_NAMESPACED_KEY)\n continue\n }\n if (!KEY_PATH_SHAPE.test(key)) {\n pushUncertain(outcome, ctx, key, keyUsages, UNCERTAIN_STRING_KEY)\n continue\n }\n outcome.undefinedKeys.push({\n key,\n app: ctx.unitName,\n searchedLayers: ctx.searchedLayers,\n usages: toLocations(keyUsages, ctx.projectDir),\n })\n }\n}\n\n/**\n * Dynamic usages whose pattern matches NO resolvable key are likely to\n * produce raw keys too — but unverifiable, so always uncertain.\n */\nfunction classifyDynamicUsages(\n dynamicKeys: DynamicKeyUsage[],\n ctx: UnitCheckContext,\n outcome: UnitCheckOutcome,\n): void {\n for (const [expression, usages] of groupBy(dynamicKeys, dk => dk.expression)) {\n const [regex] = buildDynamicKeyRegexes([{ expression }])\n if (!regex) continue // no interpolation — already handled as static\n if ([...ctx.resolvable].some(key => regex.test(key))) continue\n pushUncertain(outcome, ctx, expression, usages, UNCERTAIN_DYNAMIC_USAGE)\n }\n}\n\nfunction pushUncertain(\n outcome: UnitCheckOutcome,\n ctx: UnitCheckContext,\n key: string,\n usages: Array<{ file: string; line: number }>,\n reason: string,\n): void {\n outcome.uncertainKeys.push({\n key,\n app: ctx.unitName,\n searchedLayers: ctx.searchedLayers,\n usages: toLocations(usages, ctx.projectDir),\n reason,\n })\n}\n\n/** Classify one unit's scan evidence (static keys, then dynamic usages). */\nfunction classifyUnitUsages(scan: ScanResult, ctx: UnitCheckContext): UnitCheckOutcome {\n const outcome: UnitCheckOutcome = {\n undefinedKeys: [],\n uncertainKeys: [],\n ignoredCount: 0,\n checkedKeys: new Set(scan.uniqueKeys),\n }\n\n // Dynamic evidence downgrades static findings to uncertain: bare\n // candidates are included here (conservative), but only located t()\n // dynamic calls are themselves reported as uncertain usages.\n const dynRegexes = buildDynamicKeyRegexes([\n ...scan.dynamicKeys,\n ...[...scan.bareDynamicCandidates].map(expression => ({ expression })),\n ])\n\n classifyStaticKeys(scan.usages, dynRegexes, ctx, outcome)\n classifyDynamicUsages(scan.dynamicKeys, ctx, outcome)\n return outcome\n}\n\nfunction buildCheckMessage(undefinedCount: number, uncertainCount: number): string {\n const uncertainNote = uncertainCount > 0\n ? ` ${uncertainCount} reference(s) are uncertain (see uncertainKeys).`\n : ''\n if (undefinedCount === 0) {\n return 'All statically referenced keys resolve to a definition in their app\\'s consumed layers.'\n + uncertainNote\n }\n return `${undefinedCount} key(s) are referenced in code but defined in no locale file of the `\n + 'using app\\'s consumed layers — they render as raw keys at runtime.'\n + uncertainNote\n}\n\n// ─── Operation ──────────────────────────────────────────────────\n\n/**\n * Find keys referenced in source code that resolve to no definition in the\n * using app's consumed layers (reference locale, default: project default).\n *\n * Mirrors the orphan scan's per-unit structure: with no explicit scanDirs,\n * the scope-aware plan from the layer graph decides which layers each scan\n * unit's code can resolve (the inversion of the orphan scan's\n * scopeByLayer — a unit resolves exactly the layers it vouches for). The\n * graph's degenerate semantics carry over: with no app info every layer is\n * resolvable everywhere, so only keys defined in NO layer are flagged.\n */\nexport async function checkUndefinedKeys(opts: {\n locale?: string\n /**\n * Explicit scan roots — manual scope control. Every layer is treated as\n * resolvable from every scanned dir (global behavior, no per-app scoping).\n */\n scanDirs?: string[]\n excludeDirs?: string[]\n projectDir?: string\n outputFile?: string\n /** Also write the findings as a GitLab Code Quality JSON array to this path. */\n codequalityOutput?: string\n} = {}): Promise<CheckUndefinedKeysResult | { reportFile: string; summary: CheckUndefinedKeysSummary }> {\n const dir = opts.projectDir ?? process.cwd()\n const config = await detectI18nConfig(dir)\n const { localeCode, localeDef } = resolveReferenceLocale(config, opts.locale)\n\n const pathsByLayer = new Map<string, Set<string>>()\n for (const localeDir of config.localeDirs.filter(d => !d.aliasOf)) {\n pathsByLayer.set(localeDir.layer, await layerKeyPaths(config, localeDir.layer, localeDef))\n }\n\n const { units, globalScope, layersForUnit } = buildCheckScanPlan(config, dir, opts.scanDirs)\n\n // Memoized per searched-layers signature — sibling units often share one.\n const resolvableCache = new Map<string, Set<string>>()\n const resolvablePaths = (layers: string[]): Set<string> => {\n const signature = layers.join(' ')\n let cached = resolvableCache.get(signature)\n if (!cached) {\n cached = new Set(layers.flatMap(layer => [...(pathsByLayer.get(layer) ?? [])]))\n resolvableCache.set(signature, cached)\n }\n return cached\n }\n\n const patterns = getPatternSet(config.localeFileFormat)\n const undefinedKeys: UndefinedKeyFinding[] = []\n const uncertainKeys: UncertainKeyFinding[] = []\n const searchedLayersByApp: Record<string, string[]> = {}\n const checkedKeys = new Set<string>()\n let filesScanned = 0\n let ignoredCount = 0\n\n for (const unit of units) {\n const ignores = globalScope ? [] : nestedUnitIgnores(unit, units)\n const scan = await scanSourceFiles(unit.dir, [...(opts.excludeDirs ?? []), ...ignores], patterns)\n filesScanned += scan.filesScanned\n\n const searchedLayers = layersForUnit(unit.name)\n searchedLayersByApp[unit.name] = searchedLayers\n\n const outcome = classifyUnitUsages(scan, {\n unitName: unit.name,\n searchedLayers,\n resolvable: resolvablePaths(searchedLayers),\n ignoreRegexes: buildIgnorePatternRegexes(\n searchedLayers.flatMap(layer => resolveOrphanIgnorePatterns(config, layer) ?? []),\n ),\n projectDir: dir,\n })\n undefinedKeys.push(...outcome.undefinedKeys)\n uncertainKeys.push(...outcome.uncertainKeys)\n ignoredCount += outcome.ignoredCount\n for (const key of outcome.checkedKeys) checkedKeys.add(key)\n }\n\n undefinedKeys.sort(byAppThenKey)\n uncertainKeys.sort(byAppThenKey)\n\n const summary: CheckUndefinedKeysSummary = {\n usedKeysChecked: checkedKeys.size,\n undefinedCount: undefinedKeys.length,\n uncertainCount: uncertainKeys.length,\n ignoredCount,\n filesScanned,\n locale: localeCode,\n searchedLayersByApp,\n message: buildCheckMessage(undefinedKeys.length, uncertainKeys.length),\n }\n\n const output: CheckUndefinedKeysResult = {\n undefinedKeys,\n uncertainKeys,\n limitation: CHECK_LIMITATION,\n summary,\n }\n\n // Written even when clean: an empty array on the default branch is the\n // baseline the MR widget diffs against.\n if (opts.codequalityOutput) {\n validateReportPath(dir, opts.codequalityOutput)\n await writeCodequalityFile(opts.codequalityOutput, undefinedKeysToCodeQuality(undefinedKeys))\n }\n\n const reportPath = resolveOutputFile(dir, opts.outputFile)\n ?? resolveReportFilePath(config, dir, 'find_undefined_keys')\n if (reportPath) {\n await writeReportFile(reportPath, output as unknown as Record<string, unknown>, {\n tool: 'find_undefined_keys',\n args: { locale: opts.locale, scanDirs: opts.scanDirs, excludeDirs: opts.excludeDirs },\n })\n return { reportFile: reportPath, summary }\n }\n\n return output\n}\n"],"mappings":"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"}