@the-i18n-kit/cli 7.0.0 → 8.0.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 (27) hide show
  1. package/dist/bin.js +1 -1
  2. package/dist/{cli-waEjbUM1.js → cli-BTMcWXGs.js} +3 -3
  3. package/dist/{cli-waEjbUM1.js.map → cli-BTMcWXGs.js.map} +1 -1
  4. package/dist/{define-config-e0B0VHq7.d.ts → define-config-BpdVaEVR.d.ts} +1 -1
  5. package/dist/{define-config-ChY3jk6K.d.ts → define-config-ZRZw5PWy.d.ts} +15 -3
  6. package/dist/define-config-ZRZw5PWy.d.ts.map +1 -0
  7. package/dist/define-config.d.ts +1 -1
  8. package/dist/{descriptors-10sgyqEs.js → descriptors-Bo5UM031.js} +23 -6
  9. package/dist/{descriptors-10sgyqEs.js.map → descriptors-Bo5UM031.js.map} +1 -1
  10. package/dist/{detector-DtFY4qm3.js → detector-B4z_RZde.js} +2 -2
  11. package/dist/{detector-DtFY4qm3.js.map → detector-B4z_RZde.js.map} +1 -1
  12. package/dist/{detector-BJTkyhQ0.js → detector-DTfFbwSU.js} +2 -2
  13. package/dist/{index-CdjJ71Ag.d.ts → index-CWFWYHf5.d.ts} +76 -50
  14. package/dist/index-CWFWYHf5.d.ts.map +1 -0
  15. package/dist/index.d.ts +1 -1
  16. package/dist/index.js +5 -5
  17. package/dist/operations-CKHEmLNJ.js +8 -0
  18. package/dist/{operations-CNqEBD__.js → operations-Daf2Ommm.js} +183 -46
  19. package/dist/operations-Daf2Ommm.js.map +1 -0
  20. package/dist/{project-config-C3ao4Uii.js → project-config-eLR0J377.js} +6 -2
  21. package/dist/project-config-eLR0J377.js.map +1 -0
  22. package/package.json +1 -1
  23. package/dist/define-config-ChY3jk6K.d.ts.map +0 -1
  24. package/dist/index-CdjJ71Ag.d.ts.map +0 -1
  25. package/dist/operations-CNqEBD__.js.map +0 -1
  26. package/dist/operations-D7IMu_xY.js +0 -8
  27. package/dist/project-config-C3ao4Uii.js.map +0 -1
@@ -1 +0,0 @@
1
- {"version":3,"file":"operations-CNqEBD__.js","names":["calleeName","readArgument","walk","walkChild"],"sources":["../src/io/locale-io.ts","../src/io/locale-data.ts","../src/core/translate/targets.ts","../src/core/translate/placeholders.ts","../src/core/translate/prompts.ts","../src/core/translate/json-salvage.ts","../src/config/layer-graph.ts","../src/core/translate/memory.ts","../src/core/translate/retry.ts","../src/core/translate/run.ts","../src/core/ops-read.ts","../src/scanner/frontends/oxc.ts","../src/scanner/frontends/php/index.ts","../src/scanner/frontends/php/patterns.ts","../src/scanner/frontends/php/blade.ts","../src/scanner/rules.ts","../src/scanner/frontends/patterns.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/ops-orphans.ts","../src/core/ops-duplicates.ts","../src/core/ops-check.ts"],"sourcesContent":["import { formatForFile } from './formats'\n\nexport async function readLocale(filePath: string): Promise<Record<string, unknown>> {\n return formatForFile(filePath).read(filePath)\n}\n\nexport async function writeLocale(\n filePath: string,\n data: Record<string, unknown>,\n): Promise<void> {\n return formatForFile(filePath).write(filePath, data)\n}\n\nexport async function mutateLocale(\n filePath: string,\n mutate: (data: Record<string, unknown>) => void,\n): Promise<void> {\n return formatForFile(filePath).mutate(filePath, mutate)\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 { formatForFile, getFormat } from './formats'\nimport type { LocaleFormat } from './formats'\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 const format = getFormat(config.localeFileFormat)\n\n // Namespaced: Laravel's lang/<locale>/<namespace>.php, Next.js/React's\n // messages/en/common.json.\n const namespaced = await resolveNamespacedEntries(localeDir, locale.code, format.extensions)\n if (namespaced.length > 0) return namespaced\n\n const defaultExt = format.extensions[0]!\n\n if (format.flatFileFromDisk) {\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}${defaultExt}`\n return existsSync(join(localeDir, flat)) ? [{ path: join(localeDir, flat), namespace: null }] : []\n }\n\n // Flat and declared (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}${defaultExt}`)\n if (existsSync(flatCandidate)) {\n log.warn(\n `Locale '${locale.code}' (layer '${layer}') uses a flat 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}${defaultExt}').`,\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 const format = getFormat(config.localeFileFormat)\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 : format.defaultLayout === 'namespaced' || await hasNamespacedLayout(config, layer, format)\n\n if (isNamespaced) {\n // Per-namespace write (PHP arrays, 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) // the extension the locale already uses\n : format.extensions[0]!\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 formatForFile(filePath).clearCacheEntry(filePath)\n }\n}\n\n/**\n * Write mutated locale data to a single file.\n *\n * Existing files go through their format's format-preserving mutate path:\n * indentation, trailing newline and PHP quote style are detected from the file\n * on disk, existing keys keep their on-disk order, and new keys are inserted\n * in sorted position among their siblings. The mutate path re-reads the file\n * 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 hasNamespacedLayout(config: I18nConfig, layer: string, format: LocaleFormat): 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 => matchExtension(f, format.extensions))) return true\n }\n }\n catch { /* skip unreadable dirs */ }\n }\n }\n catch { /* locale dir not readable */ }\n\n return false\n}\n\n/** The extension a file name ends with, out of the ones given. */\nfunction matchExtension(fileName: string, extensions: readonly string[]): string | undefined {\n return extensions.find(ext => fileName.toLowerCase().endsWith(ext))\n}\n\nasync function resolveNamespacedEntries(localeDir: string, localeCode: string, extensions: readonly string[]): 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 .flatMap((f) => {\n const extension = matchExtension(f, extensions)\n return extension ? [{ file: f, extension }] : []\n })\n .sort((a, b) => (a.file < b.file ? -1 : a.file > b.file ? 1 : 0))\n .map(({ file, extension }) => ({\n path: join(localePath, file),\n namespace: file.slice(0, -extension.length),\n }))\n}\n","/**\n * Target-locale resolution for the translate operations: which locales a run\n * writes to, and which are withheld because they are protected.\n */\n\nimport type { I18nConfig, LocaleDefinition } from '../../config/types.js'\nimport { readLocaleData } from '../../io/locale-data.js'\nimport { log } from '../../utils/logger.js'\nimport { ToolError } from '../../utils/errors.js'\n\nimport type {\n TranslateMode,\n TranslateSkipReason,\n TranslateMissingLocaleResult,\n} from '../types.js'\nimport { findLocaleImpl } from '../shared.js'\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 */\nexport function 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 */\nexport async 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 */\nexport function 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 * Placeholder and plural-variant parity between a source string and its\n * translations. Pure — no config loading, no IO, no logging — so it stays\n * cheap to call per key and easy to extend with further message-format rules.\n *\n * Two message shapes are covered. Plain interpolations (`{name}`, vue-i18n\n * `@:linked.refs` and pipe plurals, Laravel `:param`) are compared as sets per\n * plural variant. ICU MessageFormat sources (`{n, plural, …}`) are compared\n * structurally instead: their arms are language-dependent, so a set\n * comparison would report mismatches for perfectly good translations.\n */\n\nimport type { LocaleFileFormat } from '../../adapters/types.js'\n\nimport type { PlaceholderValidationIssue, PlaceholderValidationResult } from '../types.js'\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\n/* ── ICU MessageFormat ─────────────────────────────────────────────────── */\n\ntype IcuArgumentType = 'plural' | 'selectordinal' | 'select' | 'number' | 'date' | 'time' | 'none'\n\nconst ICU_ARGUMENT_TYPES: readonly string[] = ['plural', 'selectordinal', 'select', 'number', 'date', 'time']\n\n/** Cheap pre-check before parsing: only a *typed* argument makes a message\n * ICU. A plain `{name}` string keeps the set-comparison fast path, so results\n * for vue-i18n and Laravel projects are unchanged. */\nconst ICU_TYPED_ARGUMENT = /\\{\\s*[A-Za-z0-9_$]+\\s*,\\s*(?:plural|selectordinal|select|number|date|time)\\s*[,}]/\n\n/** Reported instead of a placeholder list when the candidate cannot be parsed\n * at all — the CLI renders `missing` into the warning line. */\nconst UNBALANCED_BRACES = 'unbalanced ICU braces'\n\ninterface IcuArgument {\n name: string\n type: IcuArgumentType\n /** Arm keys of a plural/selectordinal/select argument: `one`, `=0`, `male`. */\n arms: string[]\n /** Some arm interpolates the plural number, as `#` or as `{name}`. */\n usesNumber: boolean\n}\n\ninterface IcuMessage {\n /** Every argument of the message, at every nesting level, in source order. */\n args: IcuArgument[]\n /** False when a brace, an arm or an argument was left open. */\n balanced: boolean\n}\n\nfunction isChoiceType(type: IcuArgumentType): boolean {\n return type === 'plural' || type === 'selectordinal' || type === 'select'\n}\n\n/**\n * Structure-only scan of an ICU message: which arguments it declares, of which\n * type, and which arms a plural/select offers. Nothing is evaluated and no\n * message is rebuilt — this only has to be precise enough to compare two\n * messages, so an argument style it does not know is skipped, not interpreted.\n */\nfunction parseIcuMessage(input: string): IcuMessage {\n const args: IcuArgument[] = []\n let pos = 0\n let balanced = true\n\n function charAt(index: number): string | undefined {\n return input[index]\n }\n\n function skipSpace(): void {\n while (pos < input.length && /\\s/.test(input[pos] ?? '')) pos += 1\n }\n\n /** Consume an apostrophe run. `''` is a literal quote; `'` before `{`, `}`\n * or `#` opens a span ICU treats as plain text; every other apostrophe is\n * itself literal, which is why \"It's\" needs no escaping. */\n function skipApostrophe(): void {\n const next = charAt(pos + 1)\n if (next === '\\'') {\n pos += 2\n return\n }\n if (next !== '{' && next !== '}' && next !== '#') {\n pos += 1\n return\n }\n pos += 2\n while (pos < input.length) {\n if (charAt(pos) === '\\'') {\n // `''` inside a quoted span is an escaped quote, not the end of it.\n if (charAt(pos + 1) === '\\'') {\n pos += 2\n continue\n }\n pos += 1\n return\n }\n pos += 1\n }\n // An unterminated span runs to the end of the message — still text.\n }\n\n /** Read an argument name or type up to its delimiter. */\n function readToken(): string {\n const start = pos\n while (pos < input.length) {\n const char = charAt(pos)\n if (char === ',' || char === '}' || char === '{') break\n pos += 1\n }\n return input.slice(start, pos).trim()\n }\n\n /** Skip a style this parser does not interpret (`number`, `date`, custom)\n * up to the `}` that closes its argument. */\n function skipArgumentStyle(): void {\n let depth = 0\n while (pos < input.length) {\n const char = charAt(pos)\n if (char === '\\'') {\n skipApostrophe()\n continue\n }\n if (char === '{') depth += 1\n if (char === '}') {\n if (depth === 0) {\n pos += 1\n return\n }\n depth -= 1\n }\n pos += 1\n }\n balanced = false\n }\n\n function parseArms(argument: IcuArgument): void {\n skipSpace()\n while (pos < input.length && charAt(pos) !== '}') {\n const keyStart = pos\n while (pos < input.length && !/[\\s{}]/.test(input[pos] ?? '')) pos += 1\n const key = input.slice(keyStart, pos)\n skipSpace()\n if (charAt(pos) !== '{') {\n // `offset:1` is a plural parameter, not an arm — it has no body.\n if (key.startsWith('offset:')) continue\n balanced = false\n return\n }\n if (key === '') {\n balanced = false\n return\n }\n pos += 1\n const nestedFrom = args.length\n const sawHash = scanText(true)\n if (pos >= input.length) return // scanText already flagged the open arm\n pos += 1 // the arm's `}`\n argument.arms.push(key)\n // `#` and an explicit `{count}` are the same interpolation to a reader,\n // and a translation may legitimately swap one for the other.\n const referencesItself = args\n .slice(nestedFrom)\n .some(nested => nested.name === argument.name && nested.type === 'none')\n if (sawHash || referencesItself) argument.usesNumber = true\n skipSpace()\n }\n if (pos < input.length) pos += 1 // the argument's `}`\n else balanced = false\n }\n\n /** Parse one argument; `pos` sits just after its opening `{`. */\n function parseArgument(): void {\n skipSpace()\n const name = readToken()\n const afterName = charAt(pos)\n if (afterName === undefined || afterName === '{') {\n balanced = false\n return\n }\n if (afterName === '}') {\n pos += 1\n args.push({ name, type: 'none', arms: [], usesNumber: false })\n return\n }\n\n pos += 1 // the `,` after the name\n skipSpace()\n const rawType = readToken().toLowerCase()\n const type = ICU_ARGUMENT_TYPES.includes(rawType) ? rawType as IcuArgumentType : 'none'\n const argument: IcuArgument = { name, type, arms: [], usesNumber: false }\n args.push(argument)\n\n if (isChoiceType(type)) {\n if (charAt(pos) === ',') {\n pos += 1\n parseArms(argument)\n return\n }\n if (charAt(pos) === '}') {\n pos += 1 // `{n, plural}` — no arms at all, caught by the parity rules\n return\n }\n balanced = false\n return\n }\n skipArgumentStyle()\n }\n\n /** Scan message text to the end of the input or to an unconsumed `}`.\n * Returns whether a literal `#` appeared at this level. */\n function scanText(insideArm: boolean): boolean {\n let sawHash = false\n while (pos < input.length) {\n const char = charAt(pos)\n if (char === '\\'') {\n skipApostrophe()\n continue\n }\n if (char === '}') {\n if (insideArm) return sawHash\n balanced = false // a `}` with no argument open\n pos += 1\n continue\n }\n if (char === '{') {\n pos += 1\n parseArgument()\n continue\n }\n // `#` only interpolates inside a plural arm; anywhere else it is text.\n if (char === '#') sawHash = true\n pos += 1\n }\n if (insideArm) balanced = false // the arm was never closed\n return sawHash\n }\n\n scanText(false)\n return { args, balanced }\n}\n\ninterface IcuArgumentSummary {\n name: string\n types: Set<IcuArgumentType>\n arms: Set<string>\n usesNumber: boolean\n}\n\n/**\n * Merge the repeated mentions of one argument name. A `{gender, select, …}`\n * nested in a plural appears once per arm, and a bare `{count}` inside a\n * plural arm is the same argument as the plural that encloses it.\n */\nfunction summariseIcuArguments(message: IcuMessage): Map<string, IcuArgumentSummary> {\n const byName = new Map<string, IcuArgumentSummary>()\n for (const argument of message.args) {\n let summary = byName.get(argument.name)\n if (!summary) {\n summary = { name: argument.name, types: new Set(), arms: new Set(), usesNumber: false }\n byName.set(argument.name, summary)\n }\n summary.types.add(argument.type)\n for (const arm of argument.arms) summary.arms.add(arm)\n if (argument.usesNumber) summary.usesNumber = true\n }\n // A typed mention wins over a bare reference to the same argument.\n for (const summary of byName.values()) {\n if (summary.types.size > 1) summary.types.delete('none')\n }\n return byName\n}\n\n/** The ICU structure of a source value, or undefined when the source is not\n * ICU (or is too broken to compare against) and the fast path applies. */\nfunction icuSourceStructure(value: string, format?: LocaleFileFormat): Map<string, IcuArgumentSummary> | undefined {\n // Laravel messages use `:param` and have no ICU convention.\n if (format === 'php-array') return undefined\n if (!ICU_TYPED_ARGUMENT.test(value)) return undefined\n const message = parseIcuMessage(value)\n // A source we cannot parse would turn every translation into a failure.\n if (!message.balanced) return undefined\n if (!message.args.some(argument => argument.type !== 'none')) return undefined\n return summariseIcuArguments(message)\n}\n\nfunction describeArgument(name: string, type?: IcuArgumentType): string {\n return type === undefined || type === 'none' ? `{${name}}` : `{${name}, ${type}}`\n}\n\nfunction describeArm(name: string, type: IcuArgumentType, arm: string): string {\n return `{${name}, ${type}, ${arm}}`\n}\n\nfunction pluralTypeOf(summary: IcuArgumentSummary): IcuArgumentType {\n return summary.types.has('selectordinal') ? 'selectordinal' : 'plural'\n}\n\nfunction validateIcuPlural(\n key: string,\n locale: string,\n source: IcuArgumentSummary,\n candidate: IcuArgumentSummary,\n): PlaceholderValidationIssue | undefined {\n const type = pluralTypeOf(source)\n const missing: string[] = []\n\n // `other` is the only arm every language must have; the CLDR categories\n // (zero one two few many) are a property of the target language, so English\n // `one|other` becoming Polish `one|few|many|other` is correct, not a loss.\n if (!candidate.arms.has('other')) missing.push(describeArm(source.name, type, 'other'))\n // `=N` arms are exact-value matches, not categories — dropping one changes\n // the message for that value in every language.\n for (const arm of [...source.arms].sort()) {\n if (arm.startsWith('=') && !candidate.arms.has(arm)) missing.push(describeArm(source.name, type, arm))\n }\n\n if (missing.length > 0) {\n return {\n locale,\n key,\n missing,\n extra: [],\n kind: 'plural-count',\n sourceVariants: source.arms.size,\n targetVariants: candidate.arms.size,\n }\n }\n\n // Losing the number itself leaves \"items\" where \"3 items\" was meant.\n if (source.usesNumber && !candidate.usesNumber) {\n return {\n locale,\n key,\n missing: [describeArm(source.name, type, '#')],\n extra: [],\n kind: 'placeholder',\n }\n }\n\n return undefined\n}\n\nfunction validateIcuSelect(\n key: string,\n locale: string,\n source: IcuArgumentSummary,\n candidate: IcuArgumentSummary,\n): PlaceholderValidationIssue | undefined {\n // Select keys are values the application passes in, not language\n // categories — they must survive translation exactly.\n const missing = [...source.arms].filter(arm => !candidate.arms.has(arm)).sort()\n const extra = [...candidate.arms].filter(arm => !source.arms.has(arm)).sort()\n if (missing.length === 0 && extra.length === 0) return undefined\n return {\n locale,\n key,\n missing: missing.map(arm => describeArm(source.name, 'select', arm)),\n extra: extra.map(arm => describeArm(source.name, 'select', arm)),\n kind: 'plural-count',\n sourceVariants: source.arms.size,\n targetVariants: candidate.arms.size,\n }\n}\n\n/** Compare one candidate translation with the ICU structure of its source.\n * Returns the first issue found — the reason codes are the same closed set\n * the plain path uses, so the detail travels in `missing`/`extra`. */\nfunction validateIcuValue(\n key: string,\n locale: string,\n source: Map<string, IcuArgumentSummary>,\n value: string,\n): PlaceholderValidationIssue | undefined {\n const parsed = parseIcuMessage(value)\n if (!parsed.balanced) {\n return { locale, key, missing: [UNBALANCED_BRACES], extra: [], kind: 'placeholder' }\n }\n const candidate = summariseIcuArguments(parsed)\n\n const missingNames = [...source.keys()].filter(name => !candidate.has(name)).sort()\n const extraNames = [...candidate.keys()].filter(name => !source.has(name)).sort()\n if (missingNames.length > 0 || extraNames.length > 0) {\n return {\n locale,\n key,\n missing: missingNames.map(name => describeArgument(name)),\n extra: extraNames.map(name => describeArgument(name)),\n kind: 'placeholder',\n }\n }\n\n for (const sourceArgument of source.values()) {\n const candidateArgument = candidate.get(sourceArgument.name)\n if (!candidateArgument) continue\n\n if (sourceArgument.types.has('plural') || sourceArgument.types.has('selectordinal')) {\n const issue = validateIcuPlural(key, locale, sourceArgument, candidateArgument)\n if (issue) return issue\n continue\n }\n if (sourceArgument.types.has('select')) {\n const issue = validateIcuSelect(key, locale, sourceArgument, candidateArgument)\n if (issue) return issue\n continue\n }\n\n // `number`, `date`, `time`: the format has to survive, its style may not —\n // a target locale can prefer another date style.\n const lostTypes = [...sourceArgument.types].filter(type => type !== 'none' && !candidateArgument.types.has(type))\n if (lostTypes.length > 0) {\n return {\n locale,\n key,\n missing: lostTypes.map(type => describeArgument(sourceArgument.name, type)).sort(),\n extra: [...candidateArgument.types].map(type => describeArgument(sourceArgument.name, type)).sort(),\n kind: 'placeholder',\n }\n }\n }\n\n return undefined\n}\n\n/** vue-i18n linked messages. ICU knows nothing about them, but a project can\n * mix both, so they stay checked on the ICU path too. */\nfunction extractLinkedRefs(value: string): string[] {\n const refs = new Set<string>()\n for (const match of value.matchAll(/@:([A-Za-z0-9_.-]+)/g)) refs.add(`@:${match[1]}`)\n return [...refs].sort()\n}\n\nfunction validateIcuLinkedRefs(\n key: string,\n locale: string,\n sourceRefs: string[],\n value: string,\n): PlaceholderValidationIssue | undefined {\n const targetRefs = new Set(extractLinkedRefs(value))\n const missing = sourceRefs.filter(ref => !targetRefs.has(ref))\n const extra = [...targetRefs].filter(ref => !sourceRefs.includes(ref)).sort()\n if (missing.length === 0 && extra.length === 0) return undefined\n return { locale, key, missing, extra, kind: 'placeholder' }\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 // An ICU source is compared structurally instead: its `|` is literal text,\n // and its plural arms follow the target language, so neither the pipe split\n // nor the whole-value set comparison below would hold for it.\n const icuSource = icuSourceStructure(sourceValue, format)\n if (icuSource) {\n const sourceRefs = extractLinkedRefs(sourceValue)\n for (const { locale, value } of values) {\n const issue = validateIcuValue(key, locale, icuSource, value)\n ?? validateIcuLinkedRefs(key, locale, sourceRefs, value)\n if (issue) errors.push(issue)\n }\n const placeholders = [...new Set([\n ...sourcePlaceholders,\n ...[...icuSource.keys()].map(name => `{${name}}`),\n ])].sort()\n return { ok: errors.length === 0, placeholders, errors }\n }\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. */\nexport function 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","/**\n * Prompt construction for the translate operations: the provider-mode system\n * and user messages, plus the agent-mode fallback context that carries the\n * same project instructions to a host agent.\n */\n\nimport type { ProjectConfig } from '../../config/types.js'\nimport type { LocaleFileFormat } from '../../adapters/types.js'\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\n/**\n * The agent-mode counterpart of the prompt builders: everything a host agent\n * needs to translate the batch itself and write it back.\n */\nexport function 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 * Parsing of a translate response into a key → value object, including the\n * salvage path for responses the provider cut off mid-object.\n */\n\nimport { log } from '../../utils/logger.js'\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","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/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\n/**\n * The graph as plain data, for surfaces that can only carry JSON.\n *\n * {@link LayerGraph} is function-valued, so it cannot be serialised directly.\n * This answers the question an agent actually has — *which layer does this key\n * belong in* — which the flat layer list `discover` returned could not: a key\n * used by more than one app belongs in a layer those apps share, and `shared`\n * names those layers outright (#342).\n *\n * The degenerate cases documented on {@link LayerGraph} survive the flattening,\n * because they are what keeps a consumer from wrongly narrowing scope:\n * a config with no app info reports *every* canonical layer as shared, and a\n * layer no app consumes appears in `consumers` with an empty array rather than\n * being left out. Absent and \"none\" must not look alike here.\n */\nexport interface SerializedLayerGraph {\n /** Alias-free layer names, in `config.localeDirs` order. */\n canonical: string[]\n /** Canonical layers consumed by more than one app — where shared keys belong. */\n shared: string[]\n /** Alias layer name → the canonical layer whose locale dir it points at. */\n aliases: Record<string, string>\n /** Canonical layer name → the apps consuming it. Every canonical layer is a key. */\n consumers: Record<string, string[]>\n}\n\n/** Flatten {@link buildLayerGraph}'s view of `config` into plain JSON. */\nexport function serializeLayerGraph(config: I18nConfig): SerializedLayerGraph {\n const graph = buildLayerGraph(config)\n const canonical = graph.canonicalLayers.map(dir => dir.layer)\n\n return {\n canonical,\n shared: graph.sharedLayers.map(dir => dir.layer),\n aliases: Object.fromEntries(\n (config.localeDirs ?? [])\n .filter(dir => dir.aliasOf)\n // ownerOf, not aliasOf: an alias may point at a layer that was itself\n // demoted to one, and a reader wants the dir that actually holds the keys.\n .map(dir => [dir.layer, graph.ownerOf(dir.layer)]),\n ),\n consumers: Object.fromEntries(\n canonical.map(layer => [layer, graph.appsUsingLayer(layer)]),\n ),\n }\n}\n","/**\n * Translation memory: what each target locale was translated *from*.\n *\n * State alone cannot tell a current translation from an outdated one — a target\n * value exists either way. So every write records a hash of the source text it\n * was produced from, in `.i18n-kit.lock.json` at the project root (next to\n * `.i18n-mcp.json`). A later run compares that hash against the source text on\n * disk and knows whether the target still matches the sentence it translated.\n *\n * File shape:\n *\n * ```json\n * {\n * \"version\": 1,\n * \"sourceLocale\": \"en\",\n * \"entries\": {\n * \"<layer>\": { \"<key>\": { \"de\": \"<hash>\", \"fr\": \"<hash>\" } }\n * }\n * }\n * ```\n *\n * The hash under a target locale is of the *source* value at the moment that\n * locale was written, not of the translation. That is the whole question this\n * file answers: for (layer, key, targetLocale), was the target written against\n * the current source? Nothing else is stored — in particular no copy of the\n * current source hash, which would be a second source of truth that silently\n * disagrees with the locale files as soon as someone edits one by hand. The\n * live source value is always read from disk instead.\n *\n * Two consequences worth knowing:\n *\n * - A key with no record is *not* stale. First runs, hand-written translations\n * and files that predate the lockfile would otherwise all report as outdated,\n * which would re-spend tokens on translations that are perfectly good.\n * - Editing the source value marks every target for that key stale for free:\n * the recorded hashes no longer match, so no bookkeeping is needed on a\n * source-locale write.\n *\n * Hashes are of one locale's text, so the memory only means anything relative\n * to a single source locale. It is recorded in the file, entries are dropped if\n * the project's default locale changes, and operations that translate from some\n * other reference locale bypass the memory entirely.\n *\n * The file is written sorted and atomically, so its git diff shows exactly the\n * keys a run touched. It is opt-in: nothing is read or written unless\n * `translationMemory` is enabled in the project config.\n */\n\nimport { createHash } from 'node:crypto'\nimport { readFile } from 'node:fs/promises'\nimport { join } from 'node:path'\n\nimport type { I18nConfig } from '../../config/types.js'\nimport { atomicWrite } from '../../io/atomic-write.js'\nimport { getNestedValue } from '../../io/key-operations.js'\nimport { readLocaleData } from '../../io/locale-data.js'\nimport { toErrorMessage } from '../../utils/errors.js'\nimport { log } from '../../utils/logger.js'\nimport { findLocaleImpl } from '../shared.js'\n\n/** Lockfile name, at the project root. */\nexport const MEMORY_FILE = '.i18n-kit.lock.json'\n\n/** Bumped only when the shape changes; older/newer files are ignored. */\nexport const MEMORY_VERSION = 1\n\n/** layer → key → target locale code → hash of the source value it was written from. */\nexport type MemoryEntries = Record<string, Record<string, Record<string, string>>>\n\nexport interface TranslationMemory {\n version: number\n /** Locale whose values the hashes are of. Empty for a memory that has none yet. */\n sourceLocale: string\n entries: MemoryEntries\n}\n\nexport function memoryFilePath(dir: string): string {\n return join(dir, MEMORY_FILE)\n}\n\n/**\n * The stored fingerprint of a source value. Truncated to 16 hex chars: the\n * lockfile is read by humans in diffs, and 64 bits is far past the point where\n * two edited sentences in one project collide.\n */\nexport function sourceHash(value: string): string {\n return createHash('sha256').update(value, 'utf8').digest('hex').slice(0, 16)\n}\n\nexport function emptyMemory(sourceLocale = ''): TranslationMemory {\n return { version: MEMORY_VERSION, sourceLocale, entries: {} }\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\n/** Accept only what this version wrote; anything else is treated as no memory. */\nfunction parseMemory(raw: string): TranslationMemory | null {\n let parsed: unknown\n try {\n parsed = JSON.parse(raw)\n } catch {\n return null\n }\n if (!isRecord(parsed) || parsed.version !== MEMORY_VERSION) return null\n if (typeof parsed.sourceLocale !== 'string' || !isRecord(parsed.entries)) return null\n\n const entries: MemoryEntries = {}\n for (const [layer, keys] of Object.entries(parsed.entries)) {\n if (!isRecord(keys)) continue\n const layerEntries: Record<string, Record<string, string>> = {}\n for (const [key, locales] of Object.entries(keys)) {\n if (!isRecord(locales)) continue\n const localeHashes: Record<string, string> = {}\n for (const [locale, hash] of Object.entries(locales)) {\n if (typeof hash === 'string') localeHashes[locale] = hash\n }\n layerEntries[key] = localeHashes\n }\n entries[layer] = layerEntries\n }\n return { version: MEMORY_VERSION, sourceLocale: parsed.sourceLocale, entries }\n}\n\n/**\n * Read the lockfile, plus whether the file on disk has to be replaced: an\n * unreadable one is reported as empty so the run continues, and rewritten at\n * the end rather than left to fail every future run the same way.\n */\nasync function loadMemory(dir: string): Promise<{ memory: TranslationMemory, unreadable: boolean }> {\n let raw: string\n try {\n raw = await readFile(memoryFilePath(dir), 'utf-8')\n } catch (error) {\n // No file is the ordinary first run, not a problem to report.\n const missing = (error as NodeJS.ErrnoException).code === 'ENOENT'\n if (!missing) {\n log.warn(`Could not read ${MEMORY_FILE}: ${toErrorMessage(error)} — continuing with an empty translation memory.`)\n }\n return { memory: emptyMemory(), unreadable: !missing }\n }\n\n const parsed = parseMemory(raw)\n if (!parsed) {\n log.warn(`${MEMORY_FILE} is not a version ${MEMORY_VERSION} translation memory — continuing as if empty and rewriting it.`)\n return { memory: emptyMemory(), unreadable: true }\n }\n return { memory: parsed, unreadable: false }\n}\n\n/**\n * Read the lockfile. Never throws: an unreadable or foreign file yields an\n * empty memory (and a warning), because a translate run must not fail over its\n * own bookkeeping.\n */\nexport async function readMemory(dir: string): Promise<TranslationMemory> {\n return (await loadMemory(dir)).memory\n}\n\n/** Sort every level so the file's diff shows only the keys a run touched. */\nfunction sortMemory(memory: TranslationMemory): TranslationMemory {\n const entries: MemoryEntries = {}\n for (const layer of Object.keys(memory.entries).sort()) {\n const keys = memory.entries[layer] ?? {}\n const layerEntries: Record<string, Record<string, string>> = {}\n for (const key of Object.keys(keys).sort()) {\n const locales = keys[key] ?? {}\n const localeHashes: Record<string, string> = {}\n for (const locale of Object.keys(locales).sort()) {\n localeHashes[locale] = locales[locale]!\n }\n layerEntries[key] = localeHashes\n }\n entries[layer] = layerEntries\n }\n return { version: memory.version, sourceLocale: memory.sourceLocale, entries }\n}\n\nexport async function writeMemory(dir: string, memory: TranslationMemory): Promise<void> {\n await atomicWrite(memoryFilePath(dir), JSON.stringify(sortMemory(memory), null, 2) + '\\n')\n}\n\n/**\n * Whether `locale`'s value for this key was written from different source text\n * than the one on disk now. Unknown keys are not stale — see the module header.\n */\nexport function isStale(\n memory: TranslationMemory,\n layer: string,\n key: string,\n locale: string,\n currentSourceValue: string,\n): boolean {\n const recorded = memory.entries[layer]?.[key]?.[locale]\n if (recorded === undefined) return false\n return recorded !== sourceHash(currentSourceValue)\n}\n\n/**\n * Remember that `locale`'s value for this key was written from `sourceValue`.\n * Returns whether anything changed, so a run that re-records what the file\n * already says does not rewrite it.\n */\nexport function recordTranslation(\n memory: TranslationMemory,\n layer: string,\n key: string,\n locale: string,\n sourceValue: string,\n): boolean {\n const hash = sourceHash(sourceValue)\n const layerEntries = (memory.entries[layer] ??= {})\n const localeHashes = (layerEntries[key] ??= {})\n if (localeHashes[locale] === hash) return false\n localeHashes[locale] = hash\n return true\n}\n\n/** True when the project asked for a lockfile. */\nexport function isTranslationMemoryEnabled(config: I18nConfig): boolean {\n return config.projectConfig?.translationMemory === true\n}\n\n/** The project's default locale, as a canonical code. */\nfunction defaultLocaleCode(config: I18nConfig): string {\n return findLocaleImpl(config, config.defaultLocale)?.code ?? config.defaultLocale\n}\n\n/**\n * One operation's view of the memory: the loaded file, the questions the\n * translate operations ask of it, and a single write at the end.\n */\nexport interface TranslationMemorySession {\n readonly memory: TranslationMemory\n isStale(layer: string, key: string, locale: string, sourceValue: string): boolean\n record(layer: string, key: string, locale: string, sourceValue: string): void\n /** Overwrite the lockfile if this session changed it. Never throws. */\n flush(): Promise<void>\n}\n\n/**\n * Open the memory for one operation, or return null when it does not apply:\n * the feature is off, or the operation translates from something other than\n * the project's default locale — hashes of another locale's text cannot answer\n * whether a target matches the source.\n */\nexport async function openTranslationMemory(opts: {\n config: I18nConfig\n projectDir: string\n /** Canonical code of the locale this operation translates from. */\n sourceLocale: string\n dryRun?: boolean\n}): Promise<TranslationMemorySession | null> {\n const { config, projectDir, sourceLocale } = opts\n if (!isTranslationMemoryEnabled(config)) return null\n\n const defaultCode = defaultLocaleCode(config)\n if (sourceLocale !== defaultCode) {\n log.debug(`Translation memory skipped: translating from \"${sourceLocale}\" rather than the project source locale \"${defaultCode}\".`)\n return null\n }\n\n const loaded = await loadMemory(projectDir)\n // Hashes recorded against another source locale are of text this run cannot\n // compare against, so they are dropped rather than trusted.\n const localeChanged = loaded.memory.sourceLocale !== '' && loaded.memory.sourceLocale !== defaultCode\n if (localeChanged) {\n log.warn(`${MEMORY_FILE} was recorded against source locale \"${loaded.memory.sourceLocale}\" but the project's is \"${defaultCode}\" — starting a fresh translation memory.`)\n }\n const memory: TranslationMemory = localeChanged\n ? emptyMemory(defaultCode)\n : { ...loaded.memory, sourceLocale: defaultCode }\n // Both cases leave the file on disk wrong, so it is rewritten at the end of\n // the run even if nothing new is recorded.\n let dirty = localeChanged || loaded.unreadable\n\n return {\n memory,\n isStale: (layer, key, locale, sourceValue) => isStale(memory, layer, key, locale, sourceValue),\n record: (layer, key, locale, sourceValue) => {\n if (recordTranslation(memory, layer, key, locale, sourceValue)) dirty = true\n },\n flush: async () => {\n // A dry run answers questions, it does not leave anything behind.\n if (opts.dryRun || !dirty) return\n try {\n await writeMemory(projectDir, memory)\n dirty = false\n } catch (error) {\n log.warn(`Could not write ${MEMORY_FILE}: ${toErrorMessage(error)} — translations were written, the translation memory was not updated.`)\n }\n },\n }\n}\n\n/**\n * Post-write hook for the plain write operations: record the target values a\n * write just put on disk as written against the current source text.\n *\n * Writes to the source locale itself need no entry — they change the text the\n * recorded hashes are compared against, which is exactly what marks that key's\n * targets stale.\n */\nexport async function recordWrittenTranslations(opts: {\n config: I18nConfig\n projectDir: string\n layer: string\n /** What the write actually applied, as canonical locale code and key. */\n writes: Array<{ locale: string, key: string }>\n}): Promise<void> {\n const { config, projectDir, layer } = opts\n if (!isTranslationMemoryEnabled(config)) return\n\n const sourceCode = defaultLocaleCode(config)\n const targetWrites = opts.writes.filter(w => w.locale !== sourceCode)\n if (targetWrites.length === 0) return\n\n const session = await openTranslationMemory({ config, projectDir, sourceLocale: sourceCode })\n if (!session) return\n\n const sourceLocale = findLocaleImpl(config, sourceCode)\n if (!sourceLocale) return\n\n let sourceData: Record<string, unknown>\n try {\n // Read after the write, so a call that set the source value in the same\n // request records its targets against the new text rather than the old.\n sourceData = await readLocaleData(config, layer, sourceLocale)\n } catch (error) {\n log.warn(`Could not read source locale \"${sourceCode}\" to update ${MEMORY_FILE}: ${toErrorMessage(error)}`)\n return\n }\n\n for (const { locale, key } of targetWrites) {\n const value = getNestedValue(sourceData, key)\n if (typeof value === 'string') session.record(layer, key, locale, value)\n }\n await session.flush()\n}\n","/**\n * The provider request loop shared by translate_missing and translate_key:\n * one attempt plus one retry, with the two outcomes that must never be\n * retried — an auth failure (fatal for the whole run) and a truncated\n * response (the same token budget would truncate again).\n */\n\nimport { log } from '../../utils/logger.js'\nimport { ToolError, toErrorMessage } from '../../utils/errors.js'\nimport { TranslateProviderError } from '../../llm/providers.js'\n\nimport type { TranslateFn, TranslateRequest } from '../types.js'\n\n/** One initial attempt plus one retry. */\nconst MAX_ATTEMPTS = 2\n\n/** Backoff before attempt n: 4s before the single retry. */\nfunction backoffMs(attempt: number): number {\n return 2000 * 2 ** attempt\n}\n\n/**\n * Cooperative abort flag shared by the locales of one run. Set when a request\n * fails authentication: every sibling request would fail the same way, so they\n * stop issuing requests and their results are discarded.\n */\nexport interface TranslateRunState {\n aborted: boolean\n}\n\nexport interface RequestContext<T> {\n /**\n * Turn a complete response body into the caller's shape. Throwing counts as\n * a failed attempt and is retried, so parse errors and transport errors\n * share one retry budget.\n */\n parse: (responseText: string) => T\n /** Identifies the request in warnings, e.g. `batch 2 in en` or `en`. */\n label: string\n /** Actionable advice appended to the truncation warning. */\n truncationHint?: string\n /** Report the responding model — batched runs log it per response. */\n logModel?: boolean\n runState?: TranslateRunState\n}\n\n/**\n * `failed` covers every attempt throwing as well as an abort observed before\n * a request went out; the caller maps it to its own fail reason.\n */\nexport type RequestOutcome<T>\n = | { status: 'ok', value: T, model?: string }\n | { status: 'truncated', model?: string }\n | { status: 'failed', model?: string }\n\n/**\n * Issue one translate request, retrying once on failure. `model` reports the\n * last responding model even for a truncated or unparsable response, so\n * callers can attribute a partially failed run.\n */\nexport async function requestWithRetry<T>(\n translateFn: TranslateFn,\n req: TranslateRequest,\n ctx: RequestContext<T>,\n): Promise<RequestOutcome<T>> {\n let model: string | undefined\n\n for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {\n if (attempt > 0) {\n await new Promise(r => setTimeout(r, backoffMs(attempt)))\n }\n // A sibling locale may have aborted the run while this attempt waited.\n if (ctx.runState?.aborted) break\n\n try {\n const response = await translateFn(req)\n\n model = response.model\n if (ctx.logModel) log.info(`Translation model: ${response.model}`)\n\n if (response.truncated) {\n // The response was cut off at the token limit — retrying with the\n // same budget would truncate again, so fail fast.\n const hint = ctx.truncationHint ? ` ${ctx.truncationHint}` : ''\n log.warn(`Translate response truncated for ${ctx.label}: provider hit the token limit.${hint}`)\n return { status: 'truncated', model }\n }\n if (response.text.trim() === '') {\n throw new TranslateProviderError('Provider returned an empty response', 'provider')\n }\n\n return { status: 'ok', value: ctx.parse(response.text), model }\n } catch (error) {\n if (error instanceof TranslateProviderError && error.kind === 'auth') {\n // Auth failures affect every request — abort the whole run instead of\n // failing key by key through retries.\n if (ctx.runState) ctx.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 = toErrorMessage(error)\n if (attempt === 0) {\n log.warn(`Translate request failed for ${ctx.label}: ${errMsg}. Retrying (attempt 2)`)\n } else {\n log.warn(`Translate retry failed for ${ctx.label}: ${errMsg}`)\n }\n }\n }\n\n return { status: 'failed', model }\n}\n","/**\n * The translate operations themselves: translate_missing (single layer and\n * all layers) and translate_key. Target resolution, prompts, response parsing\n * and the request loop live in the sibling modules.\n */\n\nimport { detectI18nConfig } from '../../config/detector.js'\nimport { buildLayerGraph } from '../../config/layer-graph.js'\nimport type { I18nConfig, LocaleDefinition } from '../../config/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'\n\nimport type {\n TranslateFn,\n TranslateLayerTotals,\n TranslateMode,\n TranslateFailReason,\n TranslateKeyLocaleIssue,\n TranslateKeySkip,\n TranslateMissingLocaleResult,\n TranslateMissingOptions,\n PlaceholderValidationResult,\n TranslateKeyResult,\n TranslateMissingResult,\n TranslateMissingOutcome,\n TranslateAllLayersResult,\n TranslateAllLayersSummary,\n} from '../types.js'\nimport { findWritableLayerOrThrow, findReferenceLocaleOrThrow, findLocaleOrThrow, localeRefInfo } from '../shared.js'\nimport { resolveTranslateTargets, collectProtectedLocaleResults, partitionTranslateKeyTargets } from './targets.js'\nimport { validatePlaceholders, mergePlaceholderValidation, failReasonForIssue } from './placeholders.js'\nimport { buildTranslationSystemPrompt, buildTranslationUserMessage, buildFallbackContext } from './prompts.js'\nimport { extractJsonFromResponse } from './json-salvage.js'\nimport { openTranslationMemory } from './memory.js'\nimport { requestWithRetry } from './retry.js'\nimport type { TranslateRunState } from './retry.js'\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 */\nconst TRANSLATE_MAX_TOKENS = 16384\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 * 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: TranslateMissingOptions): 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 // Null unless the project opted into the lockfile; every use below is\n // guarded, so the operation behaves exactly as before when it is off.\n const memory = await openTranslationMemory({\n config,\n projectDir: dir,\n sourceLocale: refLocale.code,\n dryRun: isDryRun,\n })\n const overwriteStale = opts.overwriteStale ?? false\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 in scope for this run: every reference key, or the requested subset. */\n function scopedRefKeys(): string[] {\n return opts.keys ? opts.keys.filter(k => allRefKeys.includes(k)) : allRefKeys\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 return scopedRefKeys().filter(k => isKeyMissingIn(data, k))\n }\n\n /**\n * Keys this locale already has a value for, written from source text that has\n * changed since. Empty without a translation memory: state alone cannot tell\n * an outdated translation from a current one.\n */\n function staleKeysIn(data: Record<string, unknown>, localeCode: string): string[] {\n if (!memory) return []\n return scopedRefKeys().filter((k) => {\n if (isKeyMissingIn(data, k)) return false\n const source = getNestedValue(refData, k)\n return typeof source === 'string' && memory.isStale(layer, k, localeCode, source)\n })\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 + (overwriteStale ? staleKeysIn(scanData, target.code).length : 0))\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: TranslateRunState = { 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 const staleKeys = staleKeysIn(targetData, target.code)\n // Stale keys are candidates only when asked for: the documented contract is\n // that this operation fills gaps and never overwrites an existing value.\n // Untouched, they are reported instead of translated.\n const candidateKeys = overwriteStale ? [...missingKeys, ...staleKeys] : missingKeys\n const untouchedStale = overwriteStale ? [] : staleKeys\n const withStale = (result: TranslateMissingLocaleResult): TranslateMissingLocaleResult =>\n (untouchedStale.length > 0 ? { ...result, stale: untouchedStale } : result)\n\n if (candidateKeys.length === 0) {\n return { result: withStale({ mode, missing: 0, translated: [], failed: [], skipped: [] }) }\n }\n\n await reportProgress(`Starting ${target.code}: ${candidateKeys.length} missing keys`)\n\n const keysAndValues: Record<string, string> = {}\n for (const key of candidateKeys) {\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: withStale({ 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\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 const outcome = await requestWithRetry(\n opts.translateFn,\n { systemPrompt, userMessage, maxTokens: TRANSLATE_MAX_TOKENS },\n {\n label: `batch ${batchNum} in ${target.code}`,\n truncationHint: 'Reduce batchSize.',\n logModel: true,\n runState,\n parse: (text) => {\n // Keys the model invented are dropped here; keys it omitted are\n // accounted for below.\n const parsed = extractJsonFromResponse(text)\n const batchKeys = new Set(Object.keys(batch))\n const batchTranslations: 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 return batchTranslations\n },\n },\n )\n model = outcome.model ?? model\n const batchTranslations = outcome.status === 'ok' ? outcome.value : null\n const batchTruncated = outcome.status === 'truncated'\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: withStale({ mode: 'provider', missing, translated: [], failed, skipped: [], batches: totalBatches, model, writeError: toErrorMessage(error) }) }\n }\n // Written, so remember what each value was translated from — whether or\n // not this run was allowed to overwrite stale ones.\n for (const key of Object.keys(allTranslations)) {\n const source = keysAndValues[key]\n if (source !== undefined) memory?.record(layer, key, target.code, source)\n }\n }\n\n await reportProgress(`Complete ${target.code}`)\n return { result: withStale({ 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: withStale({\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 await memory?.flush()\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 const staleCount = Object.values(results).reduce((sum, r) => sum + (r.stale?.length ?? 0), 0)\n\n const summary: TranslateMissingResult['summary'] = {\n mode,\n totalTranslated,\n totalFailed,\n totalSkipped,\n ...(isDryRun ? { totalWouldTranslate } : {}),\n // Omitted rather than zero, so a project without a translation memory sees\n // the result it has always seen.\n ...(staleCount > 0 ? { staleCount } : {}),\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, stale, 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 ...(stale ? { stale: stale.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<TranslateMissingOptions, '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<TranslateMissingOptions, '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<TranslateMissingOptions, '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 // Read off the layer summaries rather than byLayer: stale keys are not one of\n // the reconciling totals, so they are not part of the per-layer breakdown.\n const staleCount = Object.values(layers).reduce((sum, l) => sum + (l.summary.staleCount ?? 0), 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 ...(staleCount > 0 ? { staleCount } : {}),\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 // Null unless the project opted in; a changed source value needs no entry of\n // its own, since it is what the recorded target hashes are compared against.\n const memory = await openTranslationMemory({\n config,\n projectDir: dir,\n sourceLocale: sourceLocale.code,\n dryRun: isDryRun,\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: TranslateKeySkip[] = [\n ...protectedSkipped,\n ...existingTargets\n .filter(({ existingValue }) => !overwrite && existingValue !== undefined && existingValue !== '' && existingValue !== null)\n // 'already-translated' says a value is there; `stale` says whether it\n // still answers the source text, which is a different question and so a\n // separate field rather than a fourth skip reason.\n .map(({ locale }) => ({\n locale: locale.code,\n reason: 'already-translated' as const,\n ...(memory ? { stale: memory.isStale(opts.layer, opts.key, locale.code, sourceValue) } : {}),\n })),\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 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 const outcome = await requestWithRetry(\n opts.translateFn,\n { systemPrompt, userMessage, maxTokens: TRANSLATE_MAX_TOKENS },\n {\n label: locale.code,\n parse: (text) => {\n const parsedValue = extractJsonFromResponse(text)[opts.key]\n return typeof parsedValue === 'string' ? parsedValue : undefined\n },\n },\n )\n model = outcome.model ?? model\n const targetValue = outcome.status === 'ok' ? outcome.value : undefined\n\n if (!targetValue) {\n failed.push({\n locale: locale.code,\n reason: outcome.status === 'truncated'\n ? 'truncated'\n : outcome.status === 'failed' ? '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 memory?.record(opts.layer, opts.key, locale.code, sourceValue)\n } catch (error) {\n failed.push({ locale: locale.code, reason: 'write-error', detail: toErrorMessage(error) })\n }\n }\n\n await memory?.flush()\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: project discovery, config detection, locale-dir\n * listing, translation lookup/search, missing/empty detection, and namespace\n * browsing.\n */\n\nimport { readdir } from 'node:fs/promises'\n\nimport { detectI18nConfig, clearConfigCache } from '../config/detector.js'\nimport { serializeLayerGraph } from '../config/layer-graph.js'\nimport type { I18nConfig } from '../config/types.js'\nimport { readLocaleData, readLocaleDataIfPresent, resolveLocaleEntries } from '../io/locale-data.js'\nimport { getFormat } from '../io/formats.js'\nimport { getNestedValue, getLeafKeys } from '../io/key-operations.js'\nimport { ToolError } from '../utils/errors.js'\n\nimport type {\n DescribeProjectResult,\n LocaleDirInfo,\n MissingTranslationsResult,\n EmptyTranslationsResult,\n SearchMatch,\n SearchTranslationsResult,\n} from './types.js'\nimport { findLayerOrThrow, findReferenceLocaleOrThrow, findLocaleImpl, localeRefInfo, resolveLayersToScan } from './shared.js'\nimport { resolveProtectedLocales } from './ops-translate.js'\n\n/**\n * Everything a caller needs to know about a project before touching it:\n * resolved config, locale directories, the layer topology, and which locales\n * are hand-maintained.\n *\n * This composition used to live in the MCP `discover` handler, so the terminal\n * had no way to ask the question its own docs told people to ask — and the two\n * surfaces would have had to be kept in step by hand once one of them grew a\n * field. Callers add whatever is theirs alone (the MCP server adds the\n * translation backend it resolved at startup); the project half is here.\n */\nexport async function describeProject(opts: {\n projectDir?: string\n} = {}): Promise<DescribeProjectResult> {\n // detectConfig first: it warms the config cache listLocaleDirs reuses.\n const config = await detectConfig(opts.projectDir)\n const layers = await listLocaleDirs(opts.projectDir)\n\n return {\n ...config,\n protectedLocales: resolveProtectedLocales(config).map(l => l.code),\n layers,\n layerGraph: serializeLayerGraph(config),\n }\n}\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 const format = getFormat(config.localeFileFormat)\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 // A namespaced layout counts directories and reports the namespaces in\n // one; a flat one counts locale files and reports the keys in one.\n if (format.defaultLayout === 'namespaced') {\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 localeFiles = files.filter(f => format.extensions.some(ext => f.toLowerCase().endsWith(ext)))\n\n let topLevelKeys: string[] = []\n const sampleLocale = config.locales[0]\n if (sampleLocale !== undefined && localeFiles.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: localeFiles.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}): 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 return {\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\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}): Promise<EmptyTranslationsResult> {\n const { layer, locale } = opts\n const dir = opts.projectDir ?? process.cwd()\n const config = await detectI18nConfig(dir)\n\n return collectEmptyTranslations(config, { layer, locale })\n}\n\n/**\n * The scan behind {@link findEmptyTranslations}, against a config the caller\n * already has.\n *\n * Separate so `getTranslationStatus` can embed the listing under its own\n * `--list-empty` flag without detecting the project a second time.\n */\nexport async function collectEmptyTranslations(\n config: I18nConfig,\n opts: { layer?: string, locale?: string },\n): Promise<{\n emptyKeys: Record<string, Record<string, string[]>>\n summary: { totalEmpty: number, localesChecked: string[], layersChecked: string[] }\n}> {\n const { layer, locale } = opts\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 return {\n emptyKeys,\n summary: {\n totalEmpty,\n localesChecked: localesToCheck.map(l => l.code),\n layersChecked: layersToScan.map(d => d.layer),\n },\n }\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}): Promise<SearchTranslationsResult> {\n const { query, layer, locale } = 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 discover 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 return { matches, totalMatches: matches.length }\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 { extname } from 'node:path'\nimport type { CallArgument, CallSite, LanguageFrontend } from './types.js'\nimport { log } from '../../utils/logger.js'\n\n/**\n * JavaScript, TypeScript and Vue SFCs, read as syntax rather than matched as\n * text (#332).\n *\n * What this buys is one thing: it can follow `t` back to where it came from.\n * A regex sees the name and has to guess from the argument's shape whether the\n * call is a translation — the guess that offered a live key for deletion in\n * #298. Here, a `t` destructured from `useI18n()` is known to be i18n, and a\n * `t` that is anything else is known not to be.\n */\n\nconst JS_EXTENSIONS = new Set(['.js', '.jsx', '.mjs', '.cjs', '.mts', '.cts', '.ts', '.tsx'])\n\n/** Packages whose exports are translation functions. */\nconst I18N_MODULES = new Set(['vue-i18n', '@nuxtjs/i18n', 'next-intl', 'react-i18next', 'i18next', 'petite-vue-i18n'])\n\n/** Composables whose destructured `t` is a translation function. */\nconst I18N_FACTORIES = new Set(['useI18n', 'useTranslation', 'useTranslations', 'getTranslations'])\n\n/**\n * Callees that are unambiguous wherever they appear: a Vue template's `$t`, or\n * `this.$t` in an options-API component. Nothing else is named that.\n */\nconst ALWAYS_I18N = new Set(['$t', '$te', '$tc'])\n\n/**\n * Names that might be a translation function without proving it — the same set\n * the patterns match. Without this the frontend reports every call in the file,\n * and `axios.get('/api/v1')` or `require('node:fs')` become translation keys\n * because their argument happens to contain a dot.\n *\n * A call whose callee resolves to an i18n import is reported whatever it is\n * named, which is the point: the list bounds guesswork, not knowledge.\n */\nconst MAYBE_I18N = new Set(['t', 'te', 'tc', '$t', '$te', '$tc'])\n\ntype Node = Record<string, any> // eslint-disable-line @typescript-eslint/no-explicit-any -- untyped AST from the parser\n\nexport function createOxcFrontend(): LanguageFrontend {\n return {\n name: 'oxc',\n\n handles(filePath: string): boolean {\n return JS_EXTENSIONS.has(extname(filePath)) || filePath.endsWith('.vue')\n },\n\n async read(content: string, filePath: string): Promise<CallSite[] | null> {\n const parse = await loadParser()\n if (!parse) return null\n\n const blocks = readableBlocks(content, filePath)\n if (!blocks) return null\n\n const parsed: ParsedBlockPair[] = []\n for (const block of blocks) {\n const ast = parseBlock(parse, block.source, filePath)\n if (!ast) return null\n parsed.push({ block, ast })\n }\n\n return collectAcrossBlocks(parsed)\n },\n }\n}\n\ninterface ParsedBlockPair { block: VueBlock, ast: ParsedBlock }\n\n/**\n * The parseable blocks of a file, or null to decline it. A .vue file with\n * neither a template nor a script tag is not an SFC: the block splitter would\n * read only the fragments it recognises and silently drop everything between\n * them — declining hands the whole file to the fallback instead. The same\n * goes for an SFC yielding no blocks at all.\n */\nfunction readableBlocks(content: string, filePath: string): VueBlock[] | null {\n if (!filePath.endsWith('.vue')) return [{ source: content, lineOffset: 0 }]\n if (!/<template[\\s>]|<script[\\s>]/.test(content)) return null\n const blocks = vueBlocks(content)\n return blocks.length === 0 ? null : blocks\n}\n\n/**\n * An SFC is one scope split across blocks: a template uses what the script\n * declared. Collecting per block would leave `t(`${base}.title`)` in the\n * template unresolvable, and it is the same file.\n */\nfunction collectAcrossBlocks(parsed: ParsedBlockPair[]): CallSite[] {\n const i18nNames = new Set(parsed.flatMap(p => [...p.ast.i18nNames]))\n const constants: ConstantTable = new Map()\n for (const { ast } of parsed) {\n for (const [name, value] of ast.constants) addConstant(constants, name, value)\n }\n\n const sites: CallSite[] = []\n for (const { block, ast } of parsed) {\n collect({ ...ast, i18nNames, constants }, sites, lineResolver(block.source, block.lineOffset))\n }\n return sites\n}\n\ntype ParseSync = typeof import('oxc-parser').parseSync\n\n/**\n * Loaded on first use rather than at import, so a command that never scans does\n * not pay for a native binary it will not touch. Cached including the failure,\n * so a broken install is reported once and then falls back quietly.\n *\n * Not registered with the config cache: this is a module load, not anything\n * read out of the project, so clearing it could only reload the same binary.\n */\nlet parserPromise: Promise<ParseSync | null> | undefined\n\nfunction loadParser(): Promise<ParseSync | null> {\n parserPromise ??= import('oxc-parser')\n .then(m => m.parseSync)\n .catch((error: unknown) => {\n log.warn(\n `oxc-parser could not be loaded (${error instanceof Error ? error.message : String(error)}) — `\n + 'falling back to pattern matching for JavaScript and TypeScript.',\n )\n return null\n })\n return parserPromise\n}\n\n/**\n * Parse one block. Returns null when the parser reports errors, which sends the\n * caller to the fallback rather than letting one unusual file break a scan.\n */\ninterface ParsedBlock {\n program: Node\n source: string\n i18nNames: Set<string>\n constants: ConstantTable\n}\n\nfunction parseBlock(parseSync: ParseSync, source: string, filePath: string): ParsedBlock | null {\n // .ts so TypeScript syntax parses; a plain .js file is a subset of it.\n const result = parseSync(filePath.endsWith('.vue') ? 'block.ts' : filePath, source)\n if (result.errors.length > 0) {\n log.debug(`oxc declined ${filePath}: ${result.errors[0]?.message ?? 'parse error'}`)\n return null\n }\n\n return {\n program: result.program as Node,\n source,\n i18nNames: collectI18nNames(result.program as Node),\n constants: collectStringConstants(result.program as Node),\n }\n}\n\n/**\n * The names bound to a translation function in this file — imports from an\n * i18n package, and destructures of the composables those packages expose.\n */\nfunction collectI18nNames(program: Node): Set<string> {\n const names = new Set<string>()\n const factories = new Set<string>()\n\n walk(program, (node) => {\n if (node.type !== 'ImportDeclaration') return\n if (typeof node.source?.value !== 'string' || !I18N_MODULES.has(node.source.value)) return\n readImportSpecifiers(node, names, factories)\n })\n\n // `const { t } = useI18n()` — including a factory imported under another name.\n walk(program, (node) => {\n if (node.type !== 'VariableDeclarator') return\n if (!isI18nFactoryCall(node.init, factories)) return\n readBoundNames(node.id, names)\n })\n\n return names\n}\n\nfunction isI18nFactoryCall(init: Node | undefined, factories: Set<string>): boolean {\n if (init?.type !== 'CallExpression') return false\n const callee = calleeName(init.callee)\n return callee !== undefined && (factories.has(callee) || I18N_FACTORIES.has(callee))\n}\n\n/** The names a destructure or a plain assignment binds. */\nfunction readBoundNames(id: Node | undefined, names: Set<string>): void {\n if (!id) return\n\n if (id.type === 'Identifier') {\n names.add(id.name)\n return\n }\n if (id.type !== 'ObjectPattern') return\n\n for (const prop of id.properties ?? []) {\n const local = propertyName(prop)\n if (local) names.add(local)\n }\n}\n\n/** `{ t }` binds `t`; `{ t: translate }` binds `translate`. */\nfunction propertyName(prop: Node): string | undefined {\n return prop.value?.name ?? prop.key?.name\n}\n\n/**\n * String constants, so a key assembled from one resolves to the key it names.\n *\n * const base = 'pages.settings'\n * t(`${base}.title`) → pages.settings.title\n *\n * The regex path approximates this with a table of textual substitutions. Here\n * it is the binding itself, which is the difference between resolving a name\n * and hoping no other name looks like it.\n */\nfunction collectStringConstants(program: Node): ConstantTable {\n const constants: ConstantTable = new Map()\n\n walk(program, (node) => {\n if (node.type !== 'VariableDeclarator') return\n if (node.id?.type !== 'Identifier') return\n if (node.init?.type !== 'Literal' || typeof node.init.value !== 'string') return\n addConstant(constants, node.id.name, node.init.value)\n })\n\n return constants\n}\n\n/**\n * `null` marks a name bound to more than one value. Collection is name-keyed\n * with no scope tracking, so two `const base = …` in different functions would\n * otherwise resolve last-write-wins — and a template resolved through the\n * wrong one reports a static key the code never produces. A conflicted name\n * resolves nothing, which leaves the template a dynamic key.\n */\ntype ConstantTable = Map<string, string | null>\n\nfunction addConstant(table: ConstantTable, name: string, value: string | null): void {\n const existing = table.get(name)\n if (existing === undefined) table.set(name, value)\n else if (existing !== value) table.set(name, null)\n}\n\nfunction readImportSpecifiers(node: Node, names: Set<string>, factories: Set<string>): void {\n for (const spec of node.specifiers ?? []) {\n const local = spec.local?.name\n if (!local) continue\n const imported = spec.imported?.name ?? local\n if (I18N_FACTORIES.has(imported)) factories.add(local)\n else names.add(local)\n }\n}\n\n/**\n * Offsets to line numbers. The parser reports positions as byte offsets, so\n * the line index is built once per block rather than counted per call site.\n */\nfunction lineResolver(source: string, lineOffset: number): (offset: number) => number {\n const starts: number[] = [0]\n for (let i = 0; i < source.length; i++) {\n if (source[i] === '\\n') starts.push(i + 1)\n }\n\n return (offset: number) => {\n let low = 0\n let high = starts.length - 1\n while (low < high) {\n const mid = Math.ceil((low + high) / 2)\n if ((starts[mid] ?? 0) <= offset) low = mid\n else high = mid - 1\n }\n return low + 1 + lineOffset\n }\n}\n\nfunction collect(parsed: ParsedBlock, sites: CallSite[], lineAt: (offset: number) => number): void {\n walk(parsed.program, (node) => {\n if (node.type !== 'CallExpression') return\n const site = toCallSite(node, parsed, lineAt)\n if (site) sites.push(site)\n })\n}\n\nfunction toCallSite(node: Node, parsed: ParsedBlock, lineAt: (offset: number) => number): CallSite | undefined {\n const callee = resolveCallee(node.callee, parsed.i18nNames)\n if (!callee) return undefined\n if (!callee.resolved && !MAYBE_I18N.has(callee.name)) return undefined\n\n const [first] = node.arguments ?? []\n if (!first) return undefined\n\n return {\n callee: callee.name,\n binding: callee.resolved ? 'resolved' : 'ambiguous',\n argument: readArgument(first, parsed),\n line: lineAt(node.start ?? 0),\n }\n}\n\n/**\n * Name a callee and decide whether its binding proves it is i18n.\n *\n * An identifier resolves through what this file bound — an i18n import or a\n * destructure of `useI18n()`. A member call resolves only through its own\n * shape: `$t` is unambiguous on any receiver, and `t` proves i18n only when\n * the receiver itself is an i18n binding (`const i18n = useI18n(); i18n.t(…)`).\n * A local `t` from `useI18n()` says nothing about `client.t(…)` — matching a\n * member by its property name against local bindings would resolve exactly the\n * calls this frontend exists to tell apart.\n */\nfunction resolveCallee(node: Node | undefined, i18nNames: Set<string>): ResolvedCallee | undefined {\n if (!node) return undefined\n if (node.type === 'Identifier') {\n return { name: node.name, resolved: i18nNames.has(node.name) || ALWAYS_I18N.has(node.name) }\n }\n if (node.type === 'MemberExpression') return resolveMemberCallee(node, i18nNames)\n return undefined\n}\n\ninterface ResolvedCallee { name: string, resolved: boolean }\n\nfunction resolveMemberCallee(node: Node, i18nNames: Set<string>): ResolvedCallee | undefined {\n if (node.property?.type !== 'Identifier') return undefined\n const name = node.property.name\n const receiverIsI18n = node.object?.type === 'Identifier' && i18nNames.has(node.object.name)\n return { name, resolved: ALWAYS_I18N.has(name) || (receiverIsI18n && MAYBE_I18N.has(name)) }\n}\n\nfunction readArgument(node: Node, parsed: ParsedBlock): CallArgument {\n if (node.type === 'Literal' && typeof node.value === 'string') {\n return { kind: 'static', value: node.value }\n }\n\n if (node.type === 'TemplateLiteral') {\n return readTemplateArgument(node, parsed)\n }\n\n // `'common.' + name` — the literal side bounds what the call can produce.\n if (node.type === 'BinaryExpression' && node.operator === '+' && typeof node.left?.value === 'string') {\n return { kind: 'concat', prefix: node.left.value }\n }\n\n return { kind: 'unknown' }\n}\n\nfunction readTemplateArgument(node: Node, parsed: ParsedBlock): CallArgument {\n // No expressions is a plain string written with backticks.\n if ((node.expressions ?? []).length === 0) {\n const only = node.quasis?.[0]?.value?.cooked\n return typeof only === 'string' ? { kind: 'static', value: only } : { kind: 'unknown' }\n }\n\n // Every slot filled by a known constant makes the whole thing a literal.\n const resolved = resolveTemplate(node, parsed.constants)\n if (resolved !== undefined) return { kind: 'static', value: resolved }\n\n // As written in the file, backticks excluded — reports stay byte-identical\n // with the pattern scanner's for unchanged code.\n const expression = typeof node.start === 'number' && typeof node.end === 'number'\n ? parsed.source.slice(node.start + 1, node.end - 1)\n : (node.quasis ?? []).map((q: Node) => q.value?.cooked ?? '').join('${_}')\n return { kind: 'template', expression }\n}\n\n/**\n * The literal a template resolves to, or undefined when any slot is something\n * other than a constant this file declares.\n */\nfunction resolveTemplate(node: Node, constants: ConstantTable): string | undefined {\n const parts: string[] = []\n const quasis = node.quasis ?? []\n const expressions = node.expressions ?? []\n\n for (const [i, quasi] of quasis.entries()) {\n parts.push(quasi.value?.cooked ?? '')\n const expression = expressions[i]\n if (!expression) continue\n\n if (expression.type !== 'Identifier') return undefined\n const value = constants.get(expression.name)\n if (typeof value !== 'string') return undefined\n parts.push(value)\n }\n\n return parts.join('')\n}\n\nfunction calleeName(node: Node | undefined): string | undefined {\n if (!node) return undefined\n if (node.type === 'Identifier') return node.name\n // this.$t / i18n.t / vm.$t — the property is what names the function.\n if (node.type === 'MemberExpression' && node.property?.type === 'Identifier') return node.property.name\n return undefined\n}\n\nfunction walk(node: Node | undefined, visit: (node: Node) => void): void {\n if (!node || typeof node !== 'object') return\n if (typeof node.type === 'string') visit(node)\n\n for (const [key, child] of Object.entries(node)) {\n if (SKIP_KEYS.has(key)) continue\n walkChild(child, visit)\n }\n}\n\n/** Positional metadata, not syntax — walking it wastes time and finds nothing. */\nconst SKIP_KEYS = new Set(['loc', 'range', 'parent'])\n\nfunction walkChild(child: unknown, visit: (node: Node) => void): void {\n if (Array.isArray(child)) {\n for (const item of child) walkChild(item, visit)\n return\n }\n if (child && typeof child === 'object') walk(child as Node, visit)\n}\n\n\n/**\n * Split an SFC into parseable blocks. A template block's expressions are not\n * JavaScript, so its interpolations are lifted out and parsed as expressions —\n * the same trick the PHP frontend uses for Blade directives.\n */\nfunction vueBlocks(content: string): VueBlock[] {\n return [...scriptBlocks(content), ...templateExpressionBlocks(content)]\n}\n\ninterface VueBlock { source: string, lineOffset: number }\n\nconst lineOffsetAt = (content: string, offset: number): number =>\n content.slice(0, offset).split('\\n').length - 1\n\nfunction scriptBlocks(content: string): VueBlock[] {\n const blocks: VueBlock[] = []\n for (const match of content.matchAll(/<script[^>]*>([\\s\\S]*?)<\\/script>/g)) {\n // Offset to the block body, not the tag: an opening tag written across\n // several lines (`<script\\n setup\\n lang=\"ts\">`) otherwise shifts every\n // line the block reports.\n const openTagLength = match[0].length - (match[1]?.length ?? 0) - '</script>'.length\n blocks.push({ source: match[1] ?? '', lineOffset: lineOffsetAt(content, (match.index ?? 0) + openTagLength) })\n }\n return blocks\n}\n\nfunction templateExpressionBlocks(content: string): VueBlock[] {\n const blocks: VueBlock[] = []\n for (const match of content.matchAll(/\\{\\{([\\s\\S]*?)\\}\\}|(?:v-[a-z-]+|:[\\w-]+|@[\\w-]+)=(?:\"([^\"]*)\"|'([^']*)')/g)) {\n const expression = match[1] ?? match[2] ?? match[3]\n if (!expression?.trim()) continue\n // Wrapped so a bare expression parses as a statement.\n blocks.push({ source: `(${expression})`, lineOffset: lineOffsetAt(content, match.index ?? 0) })\n }\n return blocks\n}\n","import { createRequire } from 'node:module'\nimport { dirname, isAbsolute, join } from 'node:path'\nimport type { CallArgument, CallSite, LanguageFrontend } from '../types.js'\nimport { log } from '../../../utils/logger.js'\n\n/**\n * Laravel PHP, read as syntax (#403, #332).\n *\n * The helpers are global functions, so recognition is by name rather than by\n * following an import — but the *arguments* are read from a real parse:\n * `\"statuses.{$status}.label\"` is an interpolated string with known parts, not\n * a regex's guess about where the quotes end. Heredocs, escapes and nested\n * quotes come for free.\n *\n * Blade is not read here: templates are lifted to PHP expressions by their own\n * frontend. This one declines `.blade.php`.\n */\n\n/** The Laravel translation helpers. Global names; nothing else is called that. */\nconst PHP_I18N_CALLEES = new Set(['__', 'trans', 'trans_choice'])\n\nexport function createPhpFrontend(): LanguageFrontend {\n return {\n name: 'php',\n\n handles(filePath: string): boolean {\n return filePath.endsWith('.php') && !filePath.endsWith('.blade.php')\n },\n\n async read(content: string, filePath: string): Promise<CallSite[] | null> {\n const parser = await loadPhpParser(filePath)\n if (!parser) return null\n\n let program: PhpNode\n try {\n program = parser.parseCode(content, filePath) as unknown as PhpNode\n } catch (error) {\n log.debug(`php frontend declined ${filePath}: ${error instanceof Error ? error.message : String(error)}`)\n return null\n }\n\n return collectPhpSites(program)\n },\n }\n}\n\n/**\n * Call sites in a parsed program. Shared with the Blade frontend, which parses\n * lifted expressions through the same engine and interprets them identically —\n * a key's fate must not depend on which file type referenced it (#332).\n */\nexport function collectPhpSites(program: PhpNode, lineOffset = 0, calleeOverride?: string): CallSite[] {\n const sites: CallSite[] = []\n walk(program, (node) => {\n if (node.kind !== 'call') return\n const callee = calleeName(node.what as PhpNode | undefined)\n if (!callee) return\n\n const [first] = (node.arguments as PhpNode[] | undefined) ?? []\n if (!first) return\n\n sites.push({\n callee: calleeOverride ?? callee,\n // A global helper name is not shadowable in idiomatic Laravel; the\n // name is the binding.\n binding: 'resolved',\n argument: readArgument(first),\n line: ((node.loc as { start?: { line?: number } } | undefined)?.start?.line ?? 1) + lineOffset,\n })\n })\n return sites\n}\n\nfunction calleeName(what: PhpNode | undefined): string | undefined {\n if (!what) return undefined\n if (what.kind === 'name' && typeof what.name === 'string' && PHP_I18N_CALLEES.has(what.name)) {\n return what.name\n }\n // Lang::get('key') — the facade spelling of the same helper.\n return isLangGet(what) ? 'Lang::get' : undefined\n}\n\nfunction isLangGet(what: PhpNode): boolean {\n if (what.kind !== 'staticlookup') return false\n return (what.what as PhpNode | undefined)?.name === 'Lang'\n && (what.offset as PhpNode | undefined)?.name === 'get'\n}\n\nfunction readArgument(node: PhpNode): CallArgument {\n if ((node.kind === 'string' || node.kind === 'nowdoc') && typeof node.value === 'string') {\n return { kind: 'static', value: node.value }\n }\n if (node.kind === 'encapsed' && Array.isArray(node.value)) {\n return readEncapsed(node.value as PhpNode[])\n }\n if (node.kind === 'bin' && node.type === '.') {\n return readConcat(node)\n }\n return { kind: 'unknown' }\n}\n\n/** `'orders.status.' . $status` — the literal side bounds what the call can produce. */\nfunction readConcat(node: PhpNode): CallArgument {\n const left = node.left as PhpNode | undefined\n if (left?.kind === 'string' && typeof left.value === 'string') {\n return { kind: 'concat', prefix: left.value }\n }\n return { kind: 'unknown' }\n}\n\n/**\n * Double-quoted or heredoc string with interpolation: known literal parts\n * around `${_}` slots — or a plain string after all, when nothing\n * interpolates.\n */\nfunction readEncapsed(parts: PhpNode[]): CallArgument {\n const rendered = parts.map((part) => {\n const expression = part.expression as PhpNode | undefined\n return expression?.kind === 'string' && typeof expression.value === 'string' ? expression.value : '${_}'\n }).join('')\n return rendered.includes('${_}')\n ? { kind: 'template', expression: rendered }\n : { kind: 'static', value: rendered }\n}\n\n// ─── Parser loading ─────────────────────────────────────────────\n\n/**\n * `php-parser` is an optional peer: only Laravel projects install it. It is\n * resolved from the scanned file outward, so a CLI running from the npx cache\n * still finds the parser installed in the user's project — then from this\n * package's own tree, which covers the workspace and test setup. Cached\n * including the failure, so a missing install is reported once.\n */\nexport interface PhpParserEngine { parseCode(code: string, filename: string): unknown }\nexport type PhpNode = Record<string, any> // eslint-disable-line @typescript-eslint/no-explicit-any -- untyped AST from the parser\n\n// Keyed by the scanned file's directory: a long-lived server scanning several\n// projects must not pin every scan — or a cached failure — to whichever\n// project came first.\nconst parserPromises = new Map<string, Promise<PhpParserEngine | null>>()\n\nexport function loadPhpParser(fromFile: string): Promise<PhpParserEngine | null> {\n const key = dirname(fromFile)\n let promise = parserPromises.get(key)\n if (!promise) {\n promise = resolveParser(fromFile)\n parserPromises.set(key, promise)\n }\n return promise\n}\n\n/** Tests exercising resolution reset the cache between scenarios. */\nexport function resetPhpParserCacheForTests(): void {\n parserPromises.clear()\n}\n\nlet warnedMissingParser = false\n\nasync function resolveParser(fromFile: string): Promise<PhpParserEngine | null> {\n const Engine = requireFromProject(fromFile) ?? await importFromOwnTree()\n if (!Engine) {\n if (warnedMissingParser) return null\n warnedMissingParser = true\n log.warn(\n 'PHP files found, but php-parser is not installed — falling back to pattern matching. '\n + 'Laravel projects need the PHP packages installed: npm i -D php-parser php-array-reader',\n )\n return null\n }\n return new Engine({ parser: { php7: true, suppressErrors: false }, ast: { withPositions: true } })\n}\n\ntype EngineConstructor = new (options: object) => PhpParserEngine\n\nfunction requireFromProject(fromFile: string): EngineConstructor | undefined {\n try {\n const from = isAbsolute(fromFile) ? fromFile : join(process.cwd(), fromFile)\n const require = createRequire(from)\n return require('php-parser') as EngineConstructor\n } catch {\n return undefined\n }\n}\n\nasync function importFromOwnTree(): Promise<EngineConstructor | undefined> {\n try {\n const mod = await import('php-parser') as unknown as { default?: EngineConstructor }\n return mod.default ?? (mod as unknown as EngineConstructor)\n } catch {\n return undefined\n }\n}\n\nfunction walk(node: PhpNode | undefined, visit: (node: PhpNode) => void): void {\n if (!node || typeof node !== 'object') return\n if (typeof node.kind === 'string') visit(node)\n\n for (const [key, child] of Object.entries(node)) {\n if (key === 'loc' || key === 'parent') continue\n walkChild(child, visit)\n }\n}\n\nfunction walkChild(child: unknown, visit: (node: PhpNode) => void): void {\n if (Array.isArray(child)) {\n for (const item of child) walkChild(item, visit)\n return\n }\n if (child && typeof child === 'object') walk(child as PhpNode, visit)\n}\n","import type { ScanPatternSet } from '../../patterns.js'\n\n/**\n * Everything PHP the scanner knows, in the PHP frontend's home (#406): the\n * Laravel pattern set the fallback reads, and the PHP shape of the\n * bare-candidate net. The core imports these; it defines nothing PHP itself.\n */\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\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\nexport function 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","import type { CallSite, LanguageFrontend } from '../types.js'\nimport { collectPhpSites, loadPhpParser } from './index.js'\nimport type { PhpNode, PhpParserEngine } from './index.js'\nimport { log } from '../../../utils/logger.js'\n\n/**\n * Blade, by lifting (#404, #332).\n *\n * No maintained Blade AST parser exists, and none is needed: every construct\n * that can carry a translation key wraps a PHP expression. The lexical pass\n * here finds those wrappers — echoes, `@lang`/`@choice`, `@php` blocks, raw\n * PHP tags — and hands the expression inside to the same parser and the same\n * site collection plain PHP uses. The regex frames text; it never decides\n * what a key is.\n *\n * A lifted chunk the parser cannot read declines the whole file to the\n * pattern fallback: partially-read templates would silently drop keys.\n */\nexport function createBladeFrontend(): LanguageFrontend {\n return {\n name: 'blade',\n\n handles(filePath: string): boolean {\n return filePath.endsWith('.blade.php')\n },\n\n async read(content: string, filePath: string): Promise<CallSite[] | null> {\n const parser = await loadPhpParser(filePath)\n if (!parser) return null\n\n const sites: CallSite[] = []\n for (const chunk of liftChunks(content)) {\n const parsed = parseChunk(parser, chunk, filePath)\n if (!parsed) {\n // A control-flow directive's argument (`@foreach($items as $item)`)\n // is Blade grammar, not a PHP expression — skipping it loses nothing\n // a call site could carry. The constructs that do carry keys must\n // parse, or the whole file declines: partially-read templates would\n // silently drop keys.\n if (chunk.optional) continue\n return null\n }\n sites.push(...collectPhpSites(parsed, chunk.lineOffset, chunk.callee))\n }\n sites.sort((a, b) => a.line - b.line)\n return sites\n },\n }\n}\n\ninterface Chunk {\n /** A statement the PHP parser can read, `<?php` prefix excluded. */\n source: string\n /** Lines before the chunk in the template — added to every reported line. */\n lineOffset: number\n /** Report sites under the directive's own name (`@lang`), as written. */\n callee?: string\n /** A chunk that may fail to parse without declining the file. */\n optional?: boolean\n}\n\nfunction parseChunk(parser: PhpParserEngine, chunk: Chunk, filePath: string): PhpNode | null {\n try {\n return parser.parseCode(`<?php ${chunk.source}`, filePath) as unknown as PhpNode\n } catch (error) {\n log.debug(`blade frontend declined ${filePath}: ${error instanceof Error ? error.message : String(error)}`)\n return null\n }\n}\n\nconst BLADE_COMMENT = /\\{\\{--[\\s\\S]*?--\\}\\}/g\n\nfunction liftChunks(content: string): Chunk[] {\n // Comments may contain anything, including things shaped like echoes.\n const source = content.replace(BLADE_COMMENT, m => m.replace(/[^\\n]/g, ' '))\n const lineAt = (offset: number) => source.slice(0, offset).split('\\n').length - 1\n\n return [\n ...echoChunks(source, lineAt),\n ...phpBlockChunks(source, lineAt),\n ...boundAttributeChunks(source, lineAt),\n ...directiveChunks(source, lineAt),\n ]\n}\n\ntype LineAt = (offset: number) => number\n\n/** {{ expr }} and {!! expr !!} — echoes of a PHP expression. */\nfunction echoChunks(source: string, lineAt: LineAt): Chunk[] {\n return expressionChunks(source, /\\{\\{([\\s\\S]*?)\\}\\}|\\{!!([\\s\\S]*?)!!\\}/g, lineAt,\n expression => ({ source: `${expression};` }))\n}\n\nfunction expressionChunks(source: string, pattern: RegExp, lineAt: LineAt, shape: (expression: string) => Pick<Chunk, 'source' | 'optional'>): Chunk[] {\n const chunks: Chunk[] = []\n for (const match of source.matchAll(pattern)) {\n const expression = match[1] ?? match[2]\n if (!expression?.trim()) continue\n chunks.push({ ...shape(expression), lineOffset: lineAt(match.index ?? 0) })\n }\n return chunks\n}\n\n/** @php ... @endphp and raw <?php ... ?> — statements as written. */\nfunction phpBlockChunks(source: string, lineAt: LineAt): Chunk[] {\n return [\n ...bodyChunks(source, /@php\\b(?!\\s*\\()([\\s\\S]*?)@endphp/g, lineAt),\n ...bodyChunks(source, /<\\?php\\b([\\s\\S]*?)(?:\\?>|$)/g, lineAt),\n ]\n}\n\nfunction bodyChunks(source: string, pattern: RegExp, lineAt: LineAt): Chunk[] {\n const chunks: Chunk[] = []\n for (const match of source.matchAll(pattern)) {\n const body = match[1]\n if (body?.trim()) chunks.push({ source: body, lineOffset: lineAt(match.index ?? 0) })\n }\n return chunks\n}\n\n/**\n * Bound component attributes — :message=\"__('alerts.saved')\" compiles to a\n * PHP expression. `::` escapes to a literal colon and carries none.\n */\nfunction boundAttributeChunks(source: string, lineAt: LineAt): Chunk[] {\n return expressionChunks(source, /(?<![:\\w]):[\\w-]+=(?:\"([^\"]*)\"|'([^']*)')/g, lineAt,\n expression => ({ source: `__args__(${expression});`, optional: true }))\n}\n\n/**\n * Directive arguments. @lang and @choice are thin wrappers over __ and\n * trans_choice — the argument list is the translation call, reported under\n * the directive's own name. Every other directive gets its arguments read\n * as an expression list (`@section('title', __('Forbidden'))` carries a\n * real call), best-effort: what is not an expression is Blade grammar.\n * The inline @php($x = ...) form is an expression list like any other.\n */\nfunction directiveChunks(source: string, lineAt: LineAt): Chunk[] {\n const chunks: Chunk[] = []\n // Block @php ... @endphp is handled elsewhere; the inline form\n // @php($x = ...) is an expression list like any other directive argument.\n for (const match of source.matchAll(/@(\\w+)\\s*\\(/g)) {\n const open = (match.index ?? 0) + match[0].length - 1\n const args = balancedParens(source, open)\n if (args === undefined) continue\n const chunk = directiveChunk(match[1] ?? '', args, lineAt(match.index ?? 0))\n if (chunk) chunks.push(chunk)\n }\n return chunks\n}\n\nfunction directiveChunk(directive: string, args: string, lineOffset: number): Chunk | undefined {\n if (directive === 'lang' || directive === 'choice') {\n const helper = directive === 'lang' ? '__' : 'trans_choice'\n return { source: `${helper}(${args});`, lineOffset, callee: `@${directive}` }\n }\n return args.trim() ? { source: `__args__(${args});`, lineOffset, optional: true } : undefined\n}\n\n/**\n * The argument text between a directive's parentheses, quote-aware — a `)`\n * inside a string does not close the call. Returns undefined when the call\n * never closes, which declines the construct rather than guessing.\n */\nfunction balancedParens(source: string, openIndex: number): string | undefined {\n let depth = 0\n for (let i = openIndex; i < source.length; i++) {\n const ch = source[i]\n if (ch === '\\'' || ch === '\"') {\n i = skipString(source, i)\n } else if (ch === '(') {\n depth++\n } else if (ch === ')' && --depth === 0) {\n return source.slice(openIndex + 1, i)\n }\n }\n return undefined\n}\n\n/** The index of a string literal's closing quote, escapes respected. */\nfunction skipString(source: string, start: number): number {\n const quote = source[start]\n for (let i = start + 1; i < source.length; i++) {\n if (source[i] === '\\\\') i++\n else if (source[i] === quote) return i\n }\n return source.length\n}\n","import type { CallSite, FileEvidence } from './frontends/types.js'\n\n/**\n * What counts as a translation.\n *\n * One definition for every language. Frontends report what they saw in their\n * own terms; this decides what it means, so adding a parser never means\n * restating the semantics (#332).\n */\n\nexport interface RuleContext {\n filePath: string\n /**\n * Whether a dotless key from an *ambiguous* callee should be treated as\n * evidence of use.\n *\n * A bare `t('word')` is ambiguous — `emit('save')` is not a translation — so\n * it is not counted as a 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). It becomes a candidate, which protects a key only\n * when one of that exact name exists.\n *\n * A frontend that *resolved* the binding is exempt: it knows the call is\n * i18n, so the dot tells it nothing it does not already know. That exemption\n * is the whole reason for parsing rather than matching.\n */\n ambiguousCalleeNeedsDot: (callee: string) => boolean\n}\n\n/**\n * The one callee whose dotless arguments are not evidence: a bare `t` is what\n * `emit`, test helpers and local functions are also called. Every other name\n * the pattern sets match (`$t`, `__`, `trans`, ...) is distinctive enough that\n * its argument counts, dot or no dot. Formerly per-pattern-set configuration;\n * it is a rule about meaning, so it lives with the rules.\n */\nexport const ambiguousCalleeNeedsDot = (callee: string): boolean => callee === 't'\n\nexport function interpret(sites: CallSite[], ctx: RuleContext): FileEvidence {\n const usages: FileEvidence['usages'] = []\n const dynamicKeys: FileEvidence['dynamicKeys'] = []\n const bareStringCandidates = new Set<string>()\n\n for (const site of sites) {\n const { callee, line, argument } = site\n const guarded = site.binding === 'ambiguous' && ctx.ambiguousCalleeNeedsDot(callee)\n\n switch (argument.kind) {\n case 'static': {\n if (guarded && !argument.value.includes('.')) {\n bareStringCandidates.add(argument.value)\n break\n }\n usages.push({ key: argument.value, file: ctx.filePath, line, callee })\n break\n }\n\n case 'template': {\n dynamicKeys.push({ expression: `\\`${argument.expression}\\``, file: ctx.filePath, line, callee })\n break\n }\n\n case 'concat': {\n // The prefix bounds what the call can produce; everything after it is\n // unknown, so it becomes a slot.\n if (guarded && !argument.prefix.includes('.')) break\n dynamicKeys.push({ expression: `\\`${argument.prefix}\\${_}\\``, file: ctx.filePath, line, callee })\n break\n }\n\n case 'unknown':\n // A key the frontend could not read is not evidence either way. The\n // bare-candidate net, which reads strings from anywhere in the file,\n // is what protects keys reached this way.\n break\n }\n }\n\n return { usages, dynamicKeys, bareStringCandidates }\n}\n","import type { CallSite, LanguageFrontend } from './types.js'\nimport type { ScanPatternSet } from '../patterns.js'\n\n/**\n * The regex path as a language frontend (#332).\n *\n * Regexes frame text and report call sites; what a site means is decided once,\n * in the rules, the same as for every other frontend. Binding is always\n * `ambiguous`, because a regex can never prove what a name is bound to — which\n * is the entire reason the syntax frontends exist.\n *\n * This frontend never declines: it is the floor every scan can fall back to.\n */\nexport function createPatternsFrontend(pat: ScanPatternSet): LanguageFrontend {\n return {\n name: 'patterns',\n handles: () => true,\n read: (content, filePath) => Promise.resolve(readPatternSites(content, filePath, pat)),\n }\n}\n\n/**\n * Synchronous core, so the sync `extractKeys` contract the scanner suites are\n * written against keeps working unchanged.\n */\nexport function readPatternSites(content: string, _filePath: string, pat: ScanPatternSet): CallSite[] {\n const sites: CallSite[] = []\n // A pattern set whose static and dynamic regexes cover the same quote style\n // (Laravel: both read double quotes) would report one call twice; the\n // frontend reports each site once.\n const seen = new Set<string>()\n\n const lines = content.split('\\n')\n for (const [i, line] of lines.entries()) {\n const lineNumber = i + 1\n staticSites(line, lineNumber, pat, sites, seen)\n dynamicSites(line, lineNumber, pat, sites, seen)\n concatSites(line, lineNumber, pat, sites)\n }\n\n return sites\n}\n\nfunction pushStatic(sites: CallSite[], seen: Set<string>, callee: string, value: string, line: number): void {\n const id = `${line}:${callee}:${value}`\n if (seen.has(id)) return\n seen.add(id)\n sites.push({ callee, binding: 'ambiguous', argument: { kind: 'static', value }, line })\n}\n\nfunction staticSites(line: string, lineNumber: number, pat: ScanPatternSet, sites: CallSite[], seen: Set<string>): void {\n for (const regex of 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 // A quoted string carrying PHP interpolation is not a static key; the\n // dynamic pattern reads it.\n if (key.includes('{$')) continue\n pushStatic(sites, seen, callee, key, lineNumber)\n }\n }\n}\n\nfunction dynamicSites(line: string, lineNumber: number, pat: ScanPatternSet, sites: CallSite[], seen: Set<string>): void {\n for (const regex of 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 normalized = normalizeDynamicExpression(raw)\n // No interpolation is a plain string written in template syntax.\n if (normalized === undefined) {\n pushStatic(sites, seen, callee, raw, lineNumber)\n continue\n }\n sites.push({ callee, binding: 'ambiguous', argument: { kind: 'template', expression: normalized }, line: lineNumber })\n }\n }\n}\n\nfunction concatSites(line: string, lineNumber: number, pat: ScanPatternSet, sites: CallSite[]): void {\n for (const regex of 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 sites.push({ callee, binding: 'ambiguous', argument: { kind: 'concat', prefix }, line: lineNumber })\n }\n }\n}\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 for the\n * bare-candidate net, which stays deliberately non-syntactic (#332): a\n * substituted candidate is an exact protector where an unsubstituted one\n * would be a wildcard. Usage extraction no longer consults this — the syntax\n * frontends resolve real bindings instead.\n */\nexport function 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 */\nexport function 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/**\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","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 * 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 * Two-valued only because the two supported languages need two shape sets.\n * A third frontend makes this a language identifier rather than a family\n * flag — widen it then, and key it on the language the scanner is reading.\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 bareShapes: 'js',\n}\n\n// ─── Laravel / PHP Patterns ─────────────────────────────────────\n\n// Defined with the PHP frontend (#406); re-exported here so the public\n// surface and getPatternSet stay where they always were.\nexport { LARAVEL_PATTERNS } from './frontends/php/patterns.js'\nimport { LARAVEL_PATTERNS } from './frontends/php/patterns.js'\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' / 'yaml' / undefined → Vue/Nuxt ($t / t calls in Vue/TS/JS files).\n *\n * The locale-file format works as the key only while each format implies one\n * source language — 'json' and 'yaml' mean JS/TS/Vue here purely by\n * coincidence (a Rails project writes YAML and is not scanned by these\n * patterns at all), and a third language frontend writing JSON or YAML breaks\n * the mapping. Adding one means keying pattern sets on the language being\n * scanned and having callers pass that; the format would then select the IO\n * layer only.\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 { createOxcFrontend } from './frontends/oxc.js'\nimport { createPhpFrontend } from './frontends/php/index.js'\nimport { collectBarePhpCandidates } from './frontends/php/patterns.js'\nimport { createBladeFrontend } from './frontends/php/blade.js'\nimport type { LanguageFrontend } from './frontends/types.js'\nimport { interpret } from './rules.js'\nimport type { RuleContext } from './rules.js'\nimport { ambiguousCalleeNeedsDot } from './rules.js'\nimport { createPatternsFrontend, readPatternSites, collectConstKeyTable, substituteConstIdentifiers } from './frontends/patterns.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 /**\n * Files an active syntax frontend handled but declined — unparseable, or a\n * parser that would not load — so pattern matching read them instead. A\n * broken parser install shows up here rather than as silently weaker scans.\n */\n declinedFiles: string[]\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// ─── Extraction ─────────────────────────────────────────────────\n\n/**\n * One file's evidence through the pattern frontend — the sync contract the\n * scanner suites are written against. Same pipeline as every scan: the\n * frontend reports call sites, the rules decide what they mean.\n */\nexport function extractKeys(content: string, filePath: string, patterns?: ScanPatternSet): { usages: KeyUsage[]; dynamicKeys: DynamicKeyUsage[]; bareStringCandidates: Set<string> } {\n const pat = patterns ?? VUE_NUXT_PATTERNS\n return interpret(readPatternSites(content, filePath, pat), ruleContext(filePath))\n}\n\nfunction ruleContext(filePath: string): RuleContext {\n return { filePath, ambiguousCalleeNeedsDot }\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 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\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 */\n/**\n * One file's evidence, preferring a frontend that reads the language as syntax\n * over one that matches it as text (#332).\n *\n * A frontend that declines — an unparseable file, a parser that would not load\n * — falls through to pattern matching rather than failing. Better evidence when\n * it is available, never a scan that stops because it was not.\n */\nasync function extractFileEvidence(\n content: string,\n filePath: string,\n frontends: LanguageFrontend[],\n patterns?: ScanPatternSet,\n): Promise<{ usages: KeyUsage[], dynamicKeys: DynamicKeyUsage[], bareStringCandidates: Set<string>, declined: boolean }> {\n const pat = patterns ?? VUE_NUXT_PATTERNS\n let declined = false\n\n for (const frontend of frontends) {\n if (!frontend.handles(filePath)) continue\n\n const sites = await frontend.read(content, filePath)\n if (!sites) {\n // Declining is not an error: the next frontend — ultimately the pattern\n // one, which never declines — reads the file instead.\n declined = true\n continue\n }\n\n return { ...interpret(sites, ruleContext(filePath)), declined }\n }\n\n return { ...extractKeys(content, filePath, pat), declined }\n}\n\n/**\n * Opt-in, via `I18N_SCANNER=ast`.\n *\n * The architecture is settled; the migration is not. #332 gates each frontend\n * on a differential run showing it is at least as conservative as what it\n * replaces, and on anny-ui the AST frontend still misses 13 keys the patterns\n * find — most of them regex artifacts, a handful genuine. A key the outgoing\n * frontend saw and the incoming one does not becomes an orphan, and orphans\n * get deleted, so the default stays where the evidence is.\n *\n * Flip it once `packages/cli/scripts/scanner-diff.mjs` reports nothing in that direction.\n */\nlet warnedRegexHatch = false\n\n/**\n * The syntax frontends are the default (#402 for JS/TS/Vue, #405 for\n * PHP/Blade); patterns read only what they decline. `I18N_SCANNER=regex`\n * restores the old scanner for exactly one release — an escape hatch for\n * reporting a regression, not a mode.\n */\nfunction defaultFrontends(pat: ScanPatternSet): LanguageFrontend[] {\n if (process.env.I18N_SCANNER === 'regex') {\n if (!warnedRegexHatch) {\n warnedRegexHatch = true\n log.warn('I18N_SCANNER=regex is deprecated and will be removed in the next major. If the default scanner misses something the regex found, please file it: https://github.com/fabkho/the-i18n-kit/issues')\n }\n return [createPatternsFrontend(pat)]\n }\n const syntax = pat.bareShapes === 'php' ? [phpFrontend, bladeFrontend] : [oxcFrontend]\n return [...syntax, createPatternsFrontend(pat)]\n}\n\nconst oxcFrontend = createOxcFrontend()\nconst phpFrontend = createPhpFrontend()\nconst bladeFrontend = createBladeFrontend()\n\n/**\n * Files in flight while scanning. Reading and parsing a file is mostly waiting\n * on the filesystem, so a handful of files in flight hides that latency; far\n * more only multiplies open descriptors, and the parsing itself is single-\n * threaded anyway.\n */\nconst FILE_SCAN_CONCURRENCY = 12\n\n/**\n * Run `worker` over `items` with at most `limit` in flight, returning results\n * in INPUT order — never completion order. Scan order is output order, and\n * report bytes must not depend on which file finished first.\n */\nasync function mapWithConcurrency<T, R>(\n items: T[],\n limit: number,\n worker: (item: T, index: number) => Promise<R>,\n): Promise<R[]> {\n const results = new Array<R>(items.length)\n let next = 0\n\n const runner = async (): Promise<void> => {\n for (let index = next++; index < items.length; index = next++) {\n results[index] = await worker(items[index]!, index)\n }\n }\n\n await Promise.all(Array.from({ length: Math.min(limit, items.length) }, runner))\n return results\n}\n\n/** One file's evidence, held until the ordered merge pass consumes it. */\ninterface ScannedFile {\n relPath: string\n usages: KeyUsage[]\n dynamicKeys: DynamicKeyUsage[]\n /** Call-site candidates first, then the context-free ones — the merge order the shared set sees. */\n bareStrings: Set<string>\n bareDynamics: Set<string>\n declined: boolean\n}\n\n/**\n * The files a scan of `rootDir` would read, in scan order.\n *\n * Split out of `scanSourceFiles` because a caller reporting progress has to\n * know how much work there is before the first file is read.\n */\nexport async function globSourceFiles(rootDir: string, excludeDirs?: string[], patterns?: ScanPatternSet): Promise<string[]> {\n const pat = patterns ?? VUE_NUXT_PATTERNS\n const ignore = [...pat.ignoreDirs, ...(excludeDirs ?? [])]\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 return (await glob(pat.filePatterns, { cwd: rootDir, ignore, dot: false, absolute: false })).sort()\n } catch {\n // An unwalkable root is an empty scan, not a failed one.\n return []\n }\n}\n\n/** What a caller can hand a scan beyond its inputs. */\nexport interface ScanSourceFilesHooks {\n /** Files to read, relative to `rootDir` — a {@link globSourceFiles} result, reused instead of walking the tree twice. */\n files?: string[]\n /**\n * Called once per file, right after it was read. `done` counts completions,\n * so it fires in completion order while the scan result itself stays in\n * input order — a counter is all that depends on the timing.\n */\n onFile?: (done: number, total: number, file: string) => void\n}\n\nexport async function scanSourceFiles(rootDir: string, excludeDirs?: string[], patterns?: ScanPatternSet, frontends?: LanguageFrontend[], hooks?: ScanSourceFilesHooks): Promise<ScanResult> {\n const pat = patterns ?? VUE_NUXT_PATTERNS\n const active = frontends ?? defaultFrontends(pat)\n const relativePaths = hooks?.files ?? await globSourceFiles(rootDir, excludeDirs, pat)\n const fileTotal = relativePaths.length\n let filesDone = 0\n\n // Each file yields its own evidence, so reads and parses can overlap; the\n // accumulators below are filled in a second, strictly ordered pass.\n const scanned = await mapWithConcurrency(relativePaths, FILE_SCAN_CONCURRENCY, async (relPath): Promise<ScannedFile | null> => {\n const filePath = join(rootDir, relPath)\n let content: string\n try {\n content = await readFile(filePath, 'utf-8')\n } catch {\n // An unreadable file still consumed one of the counted files, so it\n // reports too — otherwise `done` never reaches the announced total.\n hooks?.onFile?.(++filesDone, fileTotal, relPath)\n return null\n }\n\n const constTable = (pat.bareShapes ?? 'js') === 'js' ? collectConstKeyTable(content) : new Map<string, string>()\n const { usages, dynamicKeys, bareStringCandidates: bareFromCalls, declined } = await extractFileEvidence(content, filePath, active, pat)\n\n const bareStrings = new Set(bareFromCalls)\n const bareDynamics = new Set<string>()\n collectBareCandidates(content, constTable, bareStrings, bareDynamics, pat.bareShapes)\n\n hooks?.onFile?.(++filesDone, fileTotal, relPath)\n return { relPath, usages, dynamicKeys, bareStrings, bareDynamics, declined }\n })\n\n const allUsages: KeyUsage[] = []\n const allDynamicKeys: DynamicKeyUsage[] = []\n const bareStringCandidates = new Set<string>()\n const bareDynamicCandidates = new Set<string>()\n const declinedFiles: string[] = []\n let filesScanned = 0\n\n for (const [index, file] of scanned.entries()) {\n if (!file) {\n // Warned here rather than in the worker, so the log reads in scan order too.\n log.warn(`Failed to read file: ${join(rootDir, relativePaths[index]!)}`)\n continue\n }\n if (file.declined) declinedFiles.push(file.relPath)\n allUsages.push(...file.usages)\n allDynamicKeys.push(...file.dynamicKeys)\n for (const candidate of file.bareStrings) bareStringCandidates.add(candidate)\n for (const candidate of file.bareDynamics) bareDynamicCandidates.add(candidate)\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, declinedFiles, 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\n/**\n * Progress hooks for a scan that spans several units.\n *\n * `onTotal` fires once, after every unit has been globbed and before the first\n * file is read: a caller that reports progress has to know the size of the\n * work before it reports any of it.\n */\nexport interface OrphanScanProgress {\n /** Total number of source files this scan will read, across all units. */\n onTotal: (total: number) => void\n /** `done`/`total` count files across all units; `unit` and `file` say where the scan currently is. */\n onFile: (done: number, total: number, unit: string, file: string) => void\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 /** Set by a caller that wants to watch a scan that takes seconds. */\n progress?: OrphanScanProgress\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 /**\n * Keys alive solely through the bare-candidate net — a dotted string\n * somewhere shares their name (a comment, a data structure, a coincidence),\n * or an ambiguous bare call the rules would not commit to (#298). Visible\n * rather than silently kept: dead references hide here (#402).\n */\n candidateOnlyByLayer: Record<string, string[]>\n candidateOnlyCount: number\n totalFilesScanned: number\n /** Files a syntax frontend declined; pattern matching read them instead. */\n totalFilesDeclined: 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 let totalFilesDeclined = 0\n\n // Every unit is globbed before any file is read. The file lists are what the\n // scan needs anyway; taking them first is what makes the total knowable\n // before the first file is reported.\n const unitExcludes = units.map(unit =>\n [...(excludeDirs ?? []), ...(globalScope ? [] : nestedUnitIgnores(unit, units))])\n const unitFiles = await Promise.all(units.map((unit, index) =>\n globSourceFiles(unit.dir, unitExcludes[index], patterns)))\n const fileTotal = unitFiles.reduce((sum, files) => sum + files.length, 0)\n options.progress?.onTotal(fileTotal)\n\n const progress = options.progress\n let filesDone = 0\n\n for (const [index, unit] of units.entries()) {\n const result = await scanSourceFiles(unit.dir, unitExcludes[index], patterns, undefined, {\n files: unitFiles[index],\n // The per-unit counts the scanner reports are folded into one running\n // count, so a caller sees one scan rather than one per app.\n onFile: progress === undefined\n ? undefined\n : (_done, _total, file) => progress.onFile(++filesDone, fileTotal, unit.name, file),\n })\n totalFilesScanned += result.filesScanned\n totalFilesDeclined += result.declinedFiles.length\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 candidateOnlyByLayer: Record<string, string[]> = {}\n let candidateOnlyCount = 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 candidateOnly: string[] = []\n const orphans = keys.filter((k) => {\n if (scope.unique.has(k)) return false\n if (scope.bare.has(k)) {\n // Protected, but by nothing a frontend could call a usage.\n if (!scope.dynRegexes.some(re => re.test(k))) candidateOnly.push(k)\n return false\n }\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 if (candidateOnly.length > 0) {\n candidateOnlyByLayer[layerName] = candidateOnly.sort()\n candidateOnlyCount += candidateOnly.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 candidateOnlyByLayer,\n candidateOnlyCount,\n totalFilesScanned,\n totalFilesDeclined,\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, extname, 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 { getFormat } from '../io/formats'\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 const format = getFormat(config.localeFileFormat)\n\n for (const dir of layers) {\n if (format.defaultLayout === 'namespaced') {\n await scaffoldNamespacedLayer(config, dir, refLocale, targetLocales, dryRun, created, skipped)\n } else {\n await scaffoldFlatLayer(config, dir, refLocale, targetLocales, dryRun, created, skipped)\n }\n }\n\n return { created, skipped }\n}\n\n/** One file per locale, named by the locale definition. */\nasync function scaffoldFlatLayer(\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 const defaultExt = getFormat(config.localeFileFormat).extensions[0]!\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}${defaultExt}`)\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\n/** One directory per locale, one file per namespace, copied from the reference. */\nasync function scaffoldNamespacedLayer(\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 = basename(fileName, extname(fileName))\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 const format = getFormat(config.localeFileFormat)\n\n if (format.defaultLayout === 'namespaced') {\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}${format.extensions[0]!}`)\n return !existsSync(filePath)\n })\n })\n}\n","/**\n * Mutating operations: write/remove/rename/move translation keys and scaffold\n * 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 WriteTranslationsResult,\n ScaffoldLocaleResult,\n ScaffoldLocaleFileInfo,\n PlaceholderValidationResult,\n UnresolvedLocaleRef,\n RemoveTranslationsResult,\n RenameTranslationKeyResult,\n MoveTranslationKeyOutcome,\n MoveTranslationKeyPlanEntry,\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'\nimport { recordWrittenTranslations } from './translate/memory.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 & { writes: AppliedWrite[] }> {\n const applied: string[] = []\n const writes: AppliedWrite[] = []\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 writes.push({ locale: locale.code, 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 & { writes: AppliedWrite[] } = {\n applied: [...new Set(applied)],\n skipped: [...new Set(skipped)],\n warnings,\n filesWritten: filesWritten.size,\n writes,\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/** One locale/key pair a write actually put on disk. */\ninterface AppliedWrite {\n locale: string\n key: string\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 * dry-run and write 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 // Post-write hook: a hand-written target value counts as translated from the\n // source text now on disk, so the translation memory records it as such and\n // does not report the key as outdated afterwards. No-op unless the project\n // enabled the memory.\n await recordWrittenTranslations({ config, projectDir: dir, layer, writes: mutation.writes })\n\n return attachDiagnostics({\n written: applied,\n skipped,\n filesWritten,\n } as WriteTranslationsResult, mutation)\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 a translation key across ALL locale files in one layer.\n *\n * Reachable on both surfaces through {@link moveTranslationKey}, which routes a\n * same-layer request here. Kept exported because renaming within a layer is a\n * complete operation on its own, and a programmatic caller that means exactly\n * that should not have to express it as a move to nowhere.\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: RenameTranslationKeyResult = {\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!,\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 result: RenameTranslationKeyResult = {\n renamed,\n filesWritten: filesWritten.size,\n oldKey,\n newKey,\n summary: {\n localesAffected: renamed.length,\n message: `Renamed \"${oldKey}\" to \"${newKey}\" in ${renamed.length} locale(s).`,\n },\n }\n if (notFoundArr.length > 0) {\n result.notFoundInLocales = notFoundArr\n }\n if (conflicts.length > 0) {\n result.skippedDueToConflict = conflicts\n result.summary = {\n ...result.summary!,\n warning: `New key \"${newKey}\" already existed in ${conflicts.length} locale(s), which were left untouched.`,\n }\n }\n\n return result\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/**\n * Move a key: to another layer, to another key path, or both.\n *\n * One entry point rather than two, because the caller's intent is \"this key\n * belongs somewhere else\" and whether that somewhere else is a different layer\n * is a detail of the project's shape, not a different operation. Omitting\n * `toLayer` (or naming the layer the key already lives in) is a rename within\n * the layer and routes to {@link renameTranslationKey}.\n *\n * Promoting an app-layer key to the shared layer once a second app needs it is\n * a first-class operation in a layered monorepo, and composing it out of\n * get/write/remove is three calls across up to thirty locales with no way to\n * fail cleanly: a truncation between the write and the remove leaves the key in\n * both layers, which is the state `find_duplicate_keys` exists to flag (#341).\n *\n * So the whole move is planned before anything is written. A target that\n * already holds a *different* value is a conflict, and one conflict in one\n * locale writes nothing at all — a half-moved key across thirty files is worse\n * than a refusal. A target already holding the *same* value is not a conflict\n * but a duplicate the move resolves: the source copy is dropped and the locale\n * is reported as deduplicated.\n *\n * Locales come from the resolved config rather than from caller-supplied refs,\n * so there is no ref to leave unresolved (#301) — a locale the source layer\n * does not define is reported in `notFoundInLocales` rather than skipped\n * silently.\n */\nexport async function moveTranslationKey(opts: {\n /** Layer the key lives in today. */\n layer: string\n key: string\n /** Layer to move it to. Omitted, or equal to `layer`, means a rename in place. */\n toLayer?: string\n newKey?: string\n dryRun?: boolean\n projectDir?: string\n}): Promise<MoveTranslationKeyOutcome> {\n const { layer: fromLayer, key } = opts\n const targetKey = opts.newKey ?? key\n\n if (opts.toLayer === undefined || opts.toLayer === fromLayer) {\n if (opts.newKey === undefined) {\n throw new ToolError(\n `Nothing to move: \"${key}\" would stay in \"${fromLayer}\" under the same name. `\n + 'Pass toLayer to move it to another layer, newKey to rename it in place, or both.',\n 'NO_DESTINATION',\n )\n }\n return renameTranslationKey({\n layer: fromLayer,\n oldKey: key,\n newKey: opts.newKey,\n dryRun: opts.dryRun,\n projectDir: opts.projectDir,\n })\n }\n\n const toLayer = opts.toLayer\n const config = await detectI18nConfig(opts.projectDir ?? process.cwd())\n\n // Both ends must be real, writable layers before anything is read: an alias\n // target would write into the layer it points at, silently landing the key\n // somewhere the caller did not name.\n findWritableLayerOrThrow(config, fromLayer)\n findWritableLayerOrThrow(config, toLayer)\n\n const { plan, notFound, conflicts } = await planMove(config, { fromLayer, toLayer, key, targetKey })\n\n const identity = {\n fromLayer,\n toLayer,\n key,\n ...(opts.newKey ? { newKey: opts.newKey } : {}),\n ...(notFound.length > 0 ? { notFoundInLocales: notFound } : {}),\n }\n\n // Refuse before writing, not part-way through.\n if (conflicts.length > 0) {\n return {\n ...identity,\n conflictsInLocales: conflicts,\n summary: {\n localesAffected: 0,\n message: 'Nothing was written.',\n warning: `\"${targetKey}\" already exists in \"${toLayer}\" with a different value in ${conflicts.length} locale(s). `\n + 'Resolve those locales first — reconcile the values, or move to a key that does not collide.',\n },\n }\n }\n\n if (opts.dryRun ?? false) {\n return {\n dryRun: true,\n wouldMove: plan,\n ...identity,\n summary: {\n localesAffected: plan.length,\n message: 'Call again with dryRun: false to apply these changes.',\n },\n }\n }\n\n const applied = await applyMove(config, { fromLayer, toLayer, key, targetKey }, plan)\n\n return {\n movedLocales: applied.moved,\n ...(applied.deduplicated.length > 0 ? { deduplicatedLocales: applied.deduplicated } : {}),\n filesWritten: applied.filesWritten,\n ...identity,\n }\n}\n\n/** Where a move reads from and writes to, with the key on each end. */\ninterface MoveTarget {\n fromLayer: string\n toLayer: string\n key: string\n targetKey: string\n}\n\n/**\n * Decide every locale's outcome before any of them is written, so that one\n * conflicting locale can stop the whole move rather than half of it.\n */\nasync function planMove(\n config: I18nConfig,\n { fromLayer, toLayer, key, targetKey }: MoveTarget,\n): Promise<{ plan: MoveTranslationKeyPlanEntry[], notFound: string[], conflicts: string[] }> {\n const plan: MoveTranslationKeyPlanEntry[] = []\n const notFound: string[] = []\n const conflicts: string[] = []\n\n for (const locale of config.locales) {\n const source = await readLocaleDataIfPresent(config, fromLayer, locale)\n const value = source ? getNestedValue(source, key) : undefined\n if (value === undefined) {\n notFound.push(locale.code)\n continue\n }\n\n const target = await readLocaleDataIfPresent(config, toLayer, locale)\n const existing = target ? getNestedValue(target, targetKey) : undefined\n\n if (existing === undefined) plan.push({ locale: locale.code, value, action: 'move' })\n else if (sameTranslation(existing, value)) plan.push({ locale: locale.code, value, action: 'deduplicate' })\n else conflicts.push(locale.code)\n }\n\n return { plan, notFound, conflicts }\n}\n\n/** Execute an already-validated plan. */\nasync function applyMove(\n config: I18nConfig,\n { fromLayer, toLayer, key, targetKey }: MoveTarget,\n plan: MoveTranslationKeyPlanEntry[],\n): Promise<{ moved: string[], deduplicated: string[], filesWritten: number }> {\n const moved: string[] = []\n const deduplicated: string[] = []\n const filesWritten = new Set<string>()\n\n for (const entry of plan) {\n const locale = findLocaleImpl(config, entry.locale)\n if (!locale) continue\n\n // Target first: if the run dies between the two, the key exists in both\n // layers — recoverable, and visible to find_duplicate_keys. The other order\n // loses the translation outright.\n if (entry.action === 'move') {\n for (const file of await mutateLocaleData(config, toLayer, locale, (data) => {\n setNestedValue(data, targetKey, entry.value)\n })) filesWritten.add(file)\n moved.push(entry.locale)\n } else {\n deduplicated.push(entry.locale)\n }\n\n for (const file of await mutateLocaleData(config, fromLayer, locale, (data) => {\n removeNestedValue(data, key)\n })) filesWritten.add(file)\n }\n\n return { moved, deduplicated, filesWritten: filesWritten.size }\n}\n\n/**\n * Whether the target already holds what the move would write. Values are\n * usually strings, but a key can name a whole namespace object, so this\n * compares structurally rather than by identity.\n */\nfunction sameTranslation(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","/**\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 by an adapter that derives\n * its own locale settings.\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.\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: true,\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 the generic adapter resolves: one no adapter claimed,\n * or one it claimed by probing a conventional directory rather than by reading\n * a declaration. Its locale settings are written down — the one case where\n * init writes locale data it inferred rather than read — because a committed\n * path is the only thing that survives moving the files somewhere less\n * conventional.\n *\n * A file that already declares both keys is left to say what it says: probing\n * over it would add a locale list nobody asked for.\n */\nasync function genericProject(\n projectDir: string,\n carried: CarriedLocaleConfig,\n match: FrameworkMatch | undefined,\n): Promise<{ config: GeneratedProjectConfig; detected: Detected }> {\n const declared = carried.localeDirs !== undefined && carried.defaultLocale !== undefined\n const probe = declared ? null : await probeLocaleConfig(projectDir)\n\n return {\n config: {\n ...authoringScaffold(),\n ...probe?.config,\n ...carried,\n },\n detected: {\n adapter: GENERIC_ADAPTER,\n label: 'Generic',\n confidence: match?.confidence ?? 0,\n derivesLocaleConfig: false,\n ...(match && match.runnersUp.length > 0 ? { runnersUp: match.runnersUp } : {}),\n ...(probe?.note ? { note: probe.note } : {}),\n },\n }\n}\n\n/** The locale settings a probe of the conventional directories yields. */\nasync function probeLocaleConfig(\n projectDir: string,\n): Promise<{ config: CarriedLocaleConfig; note?: string }> {\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 localeDirs: localeDirs.length > 0 ? localeDirs : ['locales'],\n defaultLocale: guessed ?? 'en',\n ...(locales.length > 0 ? { locales } : {}),\n },\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\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 && match.adapter.name !== GENERIC_ADAPTER\n ? detectedProject(match, carried)\n : await genericProject(dir, carried, match)\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 { buildLayerGraph } from '../config/layer-graph.js'\nimport { readLocaleData, readLocaleDataIfPresent } from '../io/locale-data.js'\nimport { getNestedValue, getLeafKeys } from '../io/key-operations.js'\nimport { findReferenceLocaleOrThrow, localeRefInfo, resolveLayersToScan } from './shared.js'\nimport { resolveProtectedLocales } from './ops-translate.js'\nimport { collectEmptyTranslations } from './ops-read.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 /**\n * Also list the keys behind `summary.emptyKeys`, under `empty`. Off by\n * default: the count is what a health check reads, and the list grows with\n * the project.\n */\n listEmpty?: boolean\n projectDir?: 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 // The layer graph already knows which apps declare which layers; a layer no\n // app consumes holds keys nothing can render, which no other tool reports.\n const graph = buildLayerGraph(config)\n\n const layers: LayerStatus[] = [...byLayer.entries()].map(([layer, c]) => ({\n layer,\n ...c,\n completion: percent(c.translated, c.total),\n consumedBy: graph.appsUsingLayer(layer),\n }))\n\n // With one app (what every single-locale-dir adapter builds) every layer is\n // either that app's or nobody's, and \"nobody's\" is then an artefact of the\n // config rather than a monorepo smell. Only flag it where apps compete.\n const unconsumedLayers = (config.apps ?? []).length > 1\n ? layers.filter(l => l.consumedBy.length === 0).map(l => l.layer)\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 return {\n locales,\n layers,\n ...(opts.listEmpty ? await listEmptyKeys(config, layersToScan, refLocale, opts.layer) : {}),\n summary: {\n referenceLocale: localeRefInfo(refLocale),\n layersScanned: layersToScan.map(d => d.layer),\n unconsumedLayers,\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\n/**\n * The two listings behind `--listEmpty`. `empty` holds exactly the keys\n * `summary.emptyKeys` counts: empty in a target locale while the reference has\n * a value. A key that is empty in the reference locale is nothing to translate\n * from, so it is excluded from every count and listed separately — it is\n * usually intentional, and it should not read as translation debt.\n */\nasync function listEmptyKeys(\n config: I18nConfig,\n layersToScan: LocaleDir[],\n refLocale: LocaleDefinition,\n layer: string | undefined,\n): Promise<Pick<TranslationStatusResult, 'empty' | 'emptyInReference'>> {\n const emptyInRefByLayer = new Map<string, Set<string>>()\n for (const localeDir of layersToScan) {\n const refData = await readLocaleDataIfPresent(config, localeDir.layer, refLocale)\n if (!refData) continue\n emptyInRefByLayer.set(localeDir.layer, new Set(\n getLeafKeys(refData).filter(k => getNestedValue(refData, k) === ''),\n ))\n }\n\n const all = (await collectEmptyTranslations(config, { layer })).emptyKeys\n const empty: Record<string, Record<string, string[]>> = {}\n const emptyInReference: Record<string, string[]> = {}\n for (const [locale, byLayer] of Object.entries(all)) {\n for (const [layerName, keys] of Object.entries(byLayer)) {\n const refEmpty = emptyInRefByLayer.get(layerName) ?? new Set<string>()\n const counted = keys.filter(k => !refEmpty.has(k))\n if (locale === refLocale.code) {\n if (keys.length > 0) emptyInReference[layerName] = keys\n continue\n }\n if (counted.length > 0) (empty[locale] ??= {})[layerName] = counted\n }\n }\n return {\n empty,\n ...(Object.keys(emptyInReference).length > 0 ? { emptyInReference } : {}),\n }\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 * 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 { 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, OrphanScanProgress, OrphanScanResult } from '../scanner/code-scanner.js'\nimport { getPatternSet } from '../scanner/patterns.js'\nimport type { FindOrphanKeysResult, RemoveOrphanKeysResult, CodeUsageResult, ProgressFn } from './types.js'\nimport { ToolError } from '../utils/errors.js'\n\nimport { log } from '../utils/logger.js'\n\nimport { findLayerOrThrow, resolveReferenceLocale } from './shared.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\nconst CANDIDATE_ONLY_NOTE = 'These keys are protected only by the bare-candidate net: either a dotted string somewhere merely shares their name (often a comment or a data structure), or a call too ambiguous to commit to references them (a bare t(...) that could be anything). They are not offered for removal, but dead references hide here - verify before pruning.'\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/** What a surface passes in to watch a scan. Only MCP does; the CLI reports nothing. */\ninterface ScanProgressOptions {\n progressFn?: ProgressFn\n /** Called once with the number of progress steps, before the first `progressFn` call. */\n onProgressTotal?: (total: number) => void\n}\n\n/**\n * Most progress notifications one scan sends, whatever the project size.\n * A monorepo with 12k source files reporting each one would put more traffic\n * on the wire than the scan it describes, so files are reported in strides of\n * `ceil(total / MAX_PROGRESS_STEPS)`.\n */\nconst MAX_PROGRESS_STEPS = 100\n\n/**\n * Adapt the scanner's per-file hooks to a surface's progress callbacks.\n *\n * The reporter on the other end counts calls rather than files, so the total\n * is announced in that same unit — one step per stride — and the last file\n * always reports, which is what makes the final step equal the total.\n *\n * `ProgressFn` is async while the scanner's hooks are not, so calls are\n * chained onto one promise: notifications keep the order the files completed\n * in, a failing notification cannot fail a scan, and `drain` waits for the\n * queue so no notification arrives after the result it describes.\n */\nfunction scanProgress(opts: ScanProgressOptions): { progress?: OrphanScanProgress; drain: () => Promise<void> } {\n const { progressFn, onProgressTotal } = opts\n if (!progressFn && !onProgressTotal) return { drain: async () => {} }\n\n let queue: Promise<void> = Promise.resolve()\n let stride = 1\n\n return {\n progress: {\n onTotal: (total) => {\n stride = Math.max(1, Math.ceil(total / MAX_PROGRESS_STEPS))\n onProgressTotal?.(Math.ceil(total / stride))\n },\n onFile: (done, total, unit, file) => {\n if (!progressFn) return\n if (done % stride !== 0 && done !== total) return\n const message = `Scanning ${unit}: ${done}/${total} files (${file})`\n queue = queue.then(() => progressFn(message)).catch(() => {})\n },\n },\n drain: async () => { await queue },\n }\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 } & ScanProgressOptions,\n): Promise<OrphanScanResult> {\n const { progress, drain } = scanProgress(opts)\n const result = await 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 progress,\n })\n await drain()\n return result\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 },\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 /** Scanning every source file of every app takes seconds — a caller that asked for progress hears about each stride of files. */\n progressFn?: ProgressFn\n /** Called once with the number of progress steps, before the first `progressFn` call. */\n onProgressTotal?: (total: number) => void\n}): Promise<FindOrphanKeysResult> {\n const { layer, locale, scanDirs, excludeDirs, progressFn, onProgressTotal } = 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 })\n\n if (totalKeys === 0) {\n return { orphanKeys: {}, summary: { totalKeys: 0, orphanCount: 0, filesScanned: 0, message: 'No translation keys found in locale files.' } }\n }\n\n const orphanResult = await runOrphanScan(config, keysByLayer, { scanDirs, excludeDirs, dir, progressFn, onProgressTotal })\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 // Code-point order, not localeCompare: the report is diffed in CI, and ICU\n // collation differs between machines and treats \".\" as ignorable.\n allOrphanKeys.sort((a, b) => byCodePoint(a.layer, b.layer) || byCodePoint(a.key, 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 candidateOnlyKeys: orphanResult.candidateOnlyCount > 0 ? orphanResult.candidateOnlyByLayer : undefined,\n candidateOnlyNote: orphanResult.candidateOnlyCount > 0 ? CANDIDATE_ONLY_NOTE : 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 candidateOnlyCount: orphanResult.candidateOnlyCount,\n misplacedCount,\n dynamicMatchedCount: orphanResult.dynamicMatchedCount,\n ignoredCount: orphanResult.ignoredCount,\n usedCount: totalKeys - orphanResult.orphanCount - orphanResult.uncertainCount - misplacedCount,\n filesScanned: orphanResult.totalFilesScanned,\n filesDeclined: orphanResult.totalFilesDeclined,\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 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}): 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 let totalFilesDeclined = 0\n\n for (const scanDir of dirsToScan) {\n const result = await scanSourceFiles(scanDir, excludeDirs, getPatternSet(config.localeFileFormat))\n totalFilesScanned += result.filesScanned\n totalFilesDeclined += result.declinedFiles.length\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]) => byCodePoint(a, b))) {\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 filesDeclined: totalFilesDeclined,\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 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 /** Same scan as {@link findOrphanKeys}, same reporting. */\n progressFn?: ProgressFn\n /** Called once with the number of progress steps, before the first `progressFn` call. */\n onProgressTotal?: (total: number) => void\n}): Promise<RemoveOrphanKeysResult> {\n const { layer, locale, scanDirs, excludeDirs, progressFn, onProgressTotal } = 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 } = await resolveOrphanScanContext(config, {\n layer,\n locale,\n dir,\n })\n\n if (totalKeys === 0) {\n return { orphanKeys: {}, removed: {}, summary: { totalKeys: 0, orphanCount: 0, message: 'No translation keys found.' } }\n }\n\n const orphanResult = await runOrphanScan(config, keysByLayer, { scanDirs, excludeDirs, dir, progressFn, onProgressTotal })\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 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 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 return removalOutput\n}\n\nfunction byCodePoint(a: string, b: string): number {\n return a < b ? -1 : a > b ? 1 : 0\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 { readLocaleData } from '../io/locale-data.js'\nimport { getNestedValue, getLeafKeys } from '../io/key-operations.js'\nimport { buildIgnorePatternRegexes } from '../scanner/code-scanner.js'\nimport { ToolError } from '../utils/errors.js'\n\nimport { findLocaleImpl, findLocaleOrThrow } from './shared.js'\nimport { resolveOrphanIgnorePatterns } from './ops-orphans.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\n/** One key carrying a duplicated value, with the layer it lives in. */\nexport interface ValueDuplicateMember {\n key: string\n layer: string\n /**\n * True when this layer is one another layer falls through to at runtime, so\n * a key here is already reachable from the layers above it.\n */\n shared: boolean\n}\n\n/**\n * What to do about a group, and therefore how it sorts. `reuse` first: the\n * shared key already exists, so the fix costs nothing but deletions.\n */\nexport type ValueDuplicateAction = 'reuse' | 'promote' | 'consolidate'\n\nexport interface ValueDuplicateGroup {\n /** The value as written, from the first member. */\n value: string\n /** What the members were grouped by — trimmed, case-folded, punctuation-stripped. */\n normalized: string\n action: ValueDuplicateAction\n members: ValueDuplicateMember[]\n}\n\nexport interface FindDuplicateKeysSummary {\n totalCollisions: number\n divergentCount: number\n pairsChecked: number\n locale: string\n /** Present when value duplicates were requested. */\n valueGroups?: number\n reusableGroups?: number\n message?: string\n}\n\nexport interface FindDuplicateKeysResult {\n collisions: DuplicateKeyCollision[]\n /** Present when value duplicates were requested. */\n valueDuplicates?: ValueDuplicateGroup[]\n guidance: string\n summary: FindDuplicateKeysSummary\n}\n\n/**\n * Values shorter than this are excluded from value grouping. \"Ja\", \"OK\" and\n * \"Nein\" repeat across unrelated namespaces legitimately, and reporting them\n * buries the findings worth acting on. A length floor is a blunt rule, which\n * is the point — anything cleverer would be guessing at intent.\n */\nconst DEFAULT_MIN_VALUE_LENGTH = 4\n\n/**\n * A floor arrives as a CLI string or an MCP number, so it can be NaN or\n * negative by the time it lands here. Comparing a length against NaN is always\n * false, which silently removes the floor and buries the report in \"OK\" — the\n * opposite of what asking for a floor means. Saying so beats defaulting: the\n * caller asked for a specific threshold and would not learn it was ignored.\n */\nfunction resolveMinValueLength(requested: number | undefined): number {\n if (requested === undefined) return DEFAULT_MIN_VALUE_LENGTH\n if (!Number.isFinite(requested) || requested < 0) {\n throw new ToolError(\n // String(), not JSON.stringify(): the latter renders NaN as \"null\", which\n // is the one value this message most needs to name.\n `minValueLength must be a non-negative number, got ${String(requested)}.`,\n 'INVALID_MIN_VALUE_LENGTH',\n )\n }\n return Math.floor(requested)\n}\n\nconst VALUE_DUPLICATE_GUIDANCE\n = 'Different keys carrying the same value. \"reuse\" means a shared layer already defines this '\n + 'value: delete the app-layer keys and repoint their call sites at the shared key. \"promote\" '\n + 'means two or more app layers define it and no shared layer does: move one to a shared layer '\n + '(move_translation_key) and repoint the rest. \"consolidate\" is duplication inside one layer. '\n + 'Each duplicate is translated into every locale separately, so removing one saves provider '\n + 'spend on every future translate run, not just tidiness. Short generic labels (\"Name\", '\n + '\"Status\") dominate the list by group size and are the least worth acting on individually; '\n + 'raise minValueLength to see the longer copy, where duplication is rarely deliberate.'\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 * Group keys by the value they carry, so that two keys spelling the same\n * string differently are visible as the duplication they are.\n *\n * Key-path collision detection cannot see this: `common.actions.save` and\n * `calendar.views.save` collide on nothing while both holding \"Speichern\"\n * (#343). Each duplicate is then translated into every locale independently,\n * so the duplication costs provider spend on every run, not just tidiness.\n */\nfunction groupByValue(\n entries: Array<{ key: string, layer: string, value: unknown }>,\n sharedLayers: Set<string>,\n minValueLength: number,\n): ValueDuplicateGroup[] {\n const byNormalized = new Map<string, ValueDuplicateGroup>()\n\n for (const entry of entries) {\n if (typeof entry.value !== 'string') continue\n const normalized = normalizeValue(entry.value)\n if (normalized.length < minValueLength) continue\n\n const group = byNormalized.get(normalized) ?? {\n value: entry.value,\n normalized,\n action: 'consolidate' as ValueDuplicateAction,\n members: [],\n }\n group.members.push({ key: entry.key, layer: entry.layer, shared: sharedLayers.has(entry.layer) })\n byNormalized.set(normalized, group)\n }\n\n const groups = [...byNormalized.values()].filter(group =>\n group.members.length > 1\n // One key path defined in several layers is a collision, which the\n // pair-wise check above already reports with both values. Repeating it\n // here as a value duplicate says nothing new.\n && new Set(group.members.map(m => m.key)).size > 1,\n )\n for (const group of groups) group.action = classifyGroup(group.members)\n\n // Actionability first, then size: the biggest reuse opportunity is the one\n // worth reading, and a long list of same-layer consolidations is not.\n const rank: Record<ValueDuplicateAction, number> = { reuse: 0, promote: 1, consolidate: 2 }\n return groups.sort((a, b) =>\n rank[a.action] - rank[b.action]\n || b.members.length - a.members.length\n || a.normalized.localeCompare(b.normalized),\n )\n}\n\nfunction classifyGroup(members: ValueDuplicateMember[]): ValueDuplicateAction {\n const inShared = members.filter(m => m.shared)\n // A shared layer already carries the value, so nothing needs moving.\n if (inShared.length > 0 && inShared.length < members.length) return 'reuse'\n if (new Set(members.map(m => m.layer)).size > 1) return 'promote'\n return 'consolidate'\n}\n\n/**\n * Fold away the differences that do not change what a translator would write:\n * surrounding space, capitalisation, internal whitespace runs and trailing\n * punctuation. \"Speichern\", \"speichern \" and \"Speichern.\" group together.\n */\nfunction normalizeValue(value: string): string {\n return value\n .trim()\n .replace(/\\s+/g, ' ')\n .replace(/[.!?:;,\\u2026]+$/u, '')\n // toLowerCase, not toLocaleLowerCase: the latter folds by the host's\n // locale, so the same repository would group differently on a Turkish\n // machine. A grouping key has to be a property of the data.\n .toLowerCase()\n}\n\n/** Every leaf key of every canonical layer, minus the ones config says to ignore. */\nasync function collectLayerEntries(\n config: I18nConfig,\n layers: LocaleDir[],\n dataFor: (layer: string) => Promise<Record<string, unknown>>,\n): Promise<Array<{ key: string, layer: string, value: unknown }>> {\n const entries: Array<{ key: string, layer: string, value: unknown }> = []\n\n for (const layer of layers) {\n const data = await dataFor(layer.layer)\n // The same patterns the orphan scan honours: a key deliberately excluded\n // there is not one a duplicate report should raise either.\n const ignore = buildIgnorePatternRegexes(resolveOrphanIgnorePatterns(config, layer.layer) ?? [])\n for (const key of getLeafKeys(data)) {\n if (ignore.some(re => re.test(key))) continue\n entries.push({ key, layer: layer.layer, value: getNestedValue(data, key) })\n }\n }\n\n return entries\n}\n\n/** Keys defined on both sides of each (shared, child) pair, in one locale. */\nasync function findCollisions(\n pairs: LayerPair[],\n dataFor: (layer: string) => Promise<Record<string, unknown>>,\n): Promise<DuplicateKeyCollision[]> {\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 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 return collisions\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 /**\n * Also group keys by the value they carry. Off by default: it reads every\n * canonical layer rather than only the paired ones, and the existing result\n * shape stays exactly as it was for callers that do not ask.\n */\n byValue?: boolean\n /** Shortest value worth grouping. Below it, repetition is usually legitimate. */\n minValueLength?: number\n} = {}): Promise<FindDuplicateKeysResult> {\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 = await findCollisions(pairs, dataFor)\n\n const valueDuplicates = opts.byValue\n ? groupByValue(\n await collectLayerEntries(config, graph.canonicalLayers, dataFor),\n // The layers something falls through to, from the same pairs the\n // collision check uses — not graph.sharedLayers, which means \"consumed\n // by more than one app\". A single-app project has no layer shared in\n // that sense, yet its root layer is still the one whose keys the app\n // can reuse, which is the question this report answers.\n new Set(pairs.map(pair => pair.shared.layer)),\n resolveMinValueLength(opts.minValueLength),\n )\n : undefined\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 ...(valueDuplicates\n ? {\n valueGroups: valueDuplicates.length,\n reusableGroups: valueDuplicates.filter(g => g.action === 'reuse').length,\n }\n : {}),\n ...(pairs.length === 0 ? { message: emptyPairsMessage(config) } : {}),\n }\n\n return {\n collisions,\n ...(valueDuplicates ? { valueDuplicates } : {}),\n guidance: valueDuplicates ? `${DUPLICATE_GUIDANCE}\\n\\n${VALUE_DUPLICATE_GUIDANCE}` : DUPLICATE_GUIDANCE,\n summary,\n }\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 { 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'\nimport { ToolError } from '../utils/errors.js'\n\nimport { findWritableLayerOrThrow, resolveReferenceLocale } from './shared.js'\nimport { buildOrphanScanPlan, resolveOrphanIgnorePatterns } from './ops-orphans.js'\nimport { writeTranslations } from './ops-write.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\n/** The locale file `write` extracted the undefined keys into. */\nexport interface ExtractedUndefinedKeys {\n layer: string\n /** The project's default locale — the source every other locale is filled from. */\n locale: string\n /** The keys that reached the file, in alphabetical order. */\n keys: string[]\n}\n\nexport interface CheckUndefinedKeysSummary {\n /** Distinct statically referenced keys across all scan units. */\n usedKeysChecked: number\n /**\n * Keys that render raw at runtime, which is what the gate reads. After a\n * `write` run this counts the ones still undefined: an extracted key now has\n * a definition — an empty one — so it resolves, and `status` reports it as an\n * empty translation instead.\n */\n undefinedCount: number\n /** Keys extracted into a locale file. Present only alongside `written`. */\n writtenCount?: number\n uncertainCount: number\n /** Unresolvable keys excluded by orphanScan ignorePatterns. */\n ignoredCount: number\n filesScanned: number\n /** Files a syntax frontend declined; pattern matching read them instead. */\n filesDeclined: 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 /**\n * The findings as the scan made them. A `write` run leaves them in place —\n * what was extracted is named in `written`, and the summary counts what is\n * left — because the call sites are what a reader has to visit either way.\n */\n undefinedKeys: UndefinedKeyFinding[]\n uncertainKeys: UncertainKeyFinding[]\n limitation: string\n /**\n * Present only when `write` was asked for and the scan found something to\n * write. A clean scan reports nothing here, having written nothing.\n */\n written?: ExtractedUndefinedKeys\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\n// ─── Extraction ─────────────────────────────────────────────────\n\n/**\n * The layer the undefined keys are extracted into.\n *\n * The findings already carry the layers the using code resolves against, so\n * that is what decides: a project — or an app — with one such layer has exactly\n * one answer and needs no flag. Where the code resolves several, nothing in the\n * evidence says which of them a key belongs in; that is a judgement about the\n * project's topology, so the run refuses rather than writing keys into a layer\n * someone then has to move them out of.\n */\nfunction resolveExtractionLayer(\n config: I18nConfig,\n findings: UndefinedKeyFinding[],\n requested: string | undefined,\n): string {\n if (requested !== undefined) {\n findWritableLayerOrThrow(config, requested)\n return requested\n }\n\n const candidates = new Set(findings.flatMap(finding => finding.searchedLayers))\n // A scan unit that resolves no layer at all leaves nothing to read here; the\n // project's own layers are the candidates then, which is still one answer for\n // a single-layer project.\n if (candidates.size === 0) {\n for (const localeDir of config.localeDirs.filter(d => !d.aliasOf)) candidates.add(localeDir.layer)\n }\n\n const [only] = candidates\n if (candidates.size === 1 && only !== undefined) {\n findWritableLayerOrThrow(config, only)\n return only\n }\n\n throw new ToolError(\n `The undefined keys resolve against ${candidates.size} layers (${[...candidates].join(', ')}), so `\n + 'there is no single layer to write them into. Name the one they belong in: '\n + '--layer <name> at a terminal, layer over MCP.',\n 'AMBIGUOUS_LAYER',\n )\n}\n\nfunction buildExtractionNote(written: ExtractedUndefinedKeys, skipped: number): string {\n const note = ` ${written.keys.length} key(s) written to layer \"${written.layer}\" as empty `\n + `${written.locale} translations — fill them in (status --listEmpty lists them).`\n return skipped === 0\n ? note\n : `${note} ${skipped} key(s) already had a value in that layer and were left untouched.`\n}\n\n/**\n * Write the undefined keys into one layer's default-locale file and account for\n * it in the result.\n *\n * The value is the empty string rather than the key: an empty value is the kit's\n * own \"scaffolded, not filled in\" state, so `status --listEmpty` lists exactly\n * these keys and a translate run fills the other locales once the source text is\n * written. Mode `add` is what keeps the run non-destructive — a key that already\n * has a value in that layer is skipped and stays a finding.\n */\nasync function extractUndefinedKeys(\n result: CheckUndefinedKeysResult,\n ctx: { config: I18nConfig; projectDir: string; layer?: string },\n): Promise<CheckUndefinedKeysResult> {\n const layer = resolveExtractionLayer(ctx.config, result.undefinedKeys, ctx.layer)\n // Resolved rather than read off the config, so a default locale nothing\n // matches says so instead of writing every key into no file at all.\n const { localeCode } = resolveReferenceLocale(ctx.config)\n const keys = [...new Set(result.undefinedKeys.map(finding => finding.key))].sort()\n\n const write = await writeTranslations({\n layer,\n translations: Object.fromEntries(keys.map(key => [key, { [localeCode]: '' }])),\n mode: 'add',\n projectDir: ctx.projectDir,\n })\n\n const written: ExtractedUndefinedKeys = {\n layer,\n locale: localeCode,\n keys: [...new Set(write.written ?? [])].sort(),\n }\n const extracted = new Set(written.keys)\n const remaining = result.undefinedKeys.filter(finding => !extracted.has(finding.key)).length\n\n return {\n ...result,\n written,\n summary: {\n ...result.summary,\n undefinedCount: remaining,\n writtenCount: written.keys.length,\n message: buildCheckMessage(remaining, result.summary.uncertainCount)\n + buildExtractionNote(written, keys.length - written.keys.length),\n },\n }\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 *\n * With `write`, the findings are also extracted into a locale file as empty\n * translations, which is what turns a report into the first half of the fix.\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 /** Extract the undefined keys into a locale file. See extractUndefinedKeys. */\n write?: boolean\n /** The layer to extract into, where the findings alone do not decide. */\n layer?: string\n projectDir?: string\n} = {}): Promise<CheckUndefinedKeysResult> {\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 filesDeclined = 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 filesDeclined += scan.declinedFiles.length\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 filesDeclined,\n locale: localeCode,\n searchedLayersByApp,\n message: buildCheckMessage(undefinedKeys.length, uncertainKeys.length),\n }\n\n const result: CheckUndefinedKeysResult = {\n undefinedKeys,\n uncertainKeys,\n limitation: CHECK_LIMITATION,\n summary,\n }\n\n // Nothing found is nothing to write: a clean scan must not fail on an\n // ambiguous layer it never had to pick, which is what a pipeline running\n // `check --write` on every merge request would hit on its good days.\n if (opts.write !== true || undefinedKeys.length === 0) return result\n return await extractUndefinedKeys(result, { config, projectDir: dir, layer: opts.layer })\n}\n"],"mappings":"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"}