@scoutello/i18n-magic 0.59.1 → 0.61.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.
- package/README.md +20 -14
- package/dist/cli.js +16 -4
- package/dist/cli.js.map +1 -1
- package/dist/commands/remove-key.d.ts +3 -0
- package/dist/commands/remove-key.d.ts.map +1 -0
- package/dist/commands/remove-key.js +93 -0
- package/dist/commands/remove-key.js.map +1 -0
- package/dist/lib/utils.d.ts.map +1 -1
- package/dist/lib/utils.js +94 -65
- package/dist/lib/utils.js.map +1 -1
- package/dist/mcp-server.js +50 -103
- package/dist/mcp-server.js.map +1 -1
- package/package.json +1 -1
- package/src/cli.ts +18 -5
- package/src/commands/remove-key.ts +123 -0
- package/src/lib/utils.ts +102 -74
- package/src/mcp-server.ts +61 -131
package/README.md
CHANGED
|
@@ -30,11 +30,12 @@ Stop context switching. Let AI handle your translations while you stay in your c
|
|
|
30
30
|
## CLI Commands
|
|
31
31
|
|
|
32
32
|
```bash
|
|
33
|
-
npx @scoutello/i18n-magic scan
|
|
34
|
-
npx @scoutello/i18n-magic sync
|
|
35
|
-
npx @scoutello/i18n-magic replace [key]
|
|
36
|
-
npx @scoutello/i18n-magic
|
|
37
|
-
npx @scoutello/i18n-magic
|
|
33
|
+
npx @scoutello/i18n-magic scan # Find & add missing translations
|
|
34
|
+
npx @scoutello/i18n-magic sync # Translate to all languages
|
|
35
|
+
npx @scoutello/i18n-magic replace [key] # Update existing translation
|
|
36
|
+
npx @scoutello/i18n-magic remove-key [key] # Remove a specific key
|
|
37
|
+
npx @scoutello/i18n-magic clean # Remove unused keys
|
|
38
|
+
npx @scoutello/i18n-magic check-missing # CI/CD validation
|
|
38
39
|
```
|
|
39
40
|
|
|
40
41
|
**`scan`** - Scans your codebase for missing keys, prompts you for values, auto-translates to all locales.
|
|
@@ -43,6 +44,8 @@ npx @scoutello/i18n-magic check-missing # CI/CD validation
|
|
|
43
44
|
|
|
44
45
|
**`replace`** - Update an existing translation key across all locales. Detects which namespaces use the key automatically.
|
|
45
46
|
|
|
47
|
+
**`remove-key`** - Remove a specific translation key from all namespaces and locales. Useful when deprecating keys.
|
|
48
|
+
|
|
46
49
|
**`clean`** - Removes unused translation keys from all locales. Great for keeping files lean.
|
|
47
50
|
|
|
48
51
|
**`check-missing`** - Dry-run check. Exits with error code if translations are missing. Perfect for CI pipelines.
|
|
@@ -66,21 +69,24 @@ Result: Separate files per feature (`common.json`, `dashboard.json`, `mobile.jso
|
|
|
66
69
|
|
|
67
70
|
**MCP (Model Context Protocol)** = API for AI agents.
|
|
68
71
|
|
|
69
|
-
Install the i18n-magic MCP server in Cursor, and your AI gets
|
|
72
|
+
Install the i18n-magic MCP server in Cursor, and your AI gets 6 tools:
|
|
70
73
|
|
|
71
74
|
### 1. `search_translations` - Prevent Duplicates
|
|
72
75
|
Fuzzy search across all translations. AI searches before adding anything.
|
|
73
76
|
|
|
74
77
|
### 2. `add_translation_key` - Add New Keys
|
|
75
|
-
Adds
|
|
78
|
+
Adds a single key. If API translation is configured, it auto-translates to other locales; otherwise run `sync`.
|
|
79
|
+
|
|
80
|
+
### 3. `add_translation_keys` - Add Multiple Keys Fast
|
|
81
|
+
Batch add 2+ keys in one call with better performance than multiple single-key calls.
|
|
76
82
|
|
|
77
|
-
###
|
|
83
|
+
### 4. `get_translation_key` - Check What Exists
|
|
78
84
|
Retrieve current value for any key.
|
|
79
85
|
|
|
80
|
-
###
|
|
86
|
+
### 5. `update_translation_key` - Fix & Auto-Translate
|
|
81
87
|
Update a key and **instantly translate to all languages**. No sync needed!
|
|
82
88
|
|
|
83
|
-
###
|
|
89
|
+
### 6. `list_untranslated_keys` - Batch Check
|
|
84
90
|
Show all missing keys across your codebase.
|
|
85
91
|
|
|
86
92
|
---
|
|
@@ -160,9 +166,9 @@ Done! Test by asking: *"Search for translations with 'password'"*
|
|
|
160
166
|
**Result**: No duplicate keys, instant code.
|
|
161
167
|
|
|
162
168
|
If nothing exists:
|
|
163
|
-
- AI adds key
|
|
164
|
-
-
|
|
165
|
-
-
|
|
169
|
+
- AI adds key(s): `add_translation_key` or `add_translation_keys`
|
|
170
|
+
- If API translation is configured, other locales are updated automatically
|
|
171
|
+
- If not configured, run: `npx @scoutello/i18n-magic sync`
|
|
166
172
|
|
|
167
173
|
### Pattern 2: Updating Translations
|
|
168
174
|
|
|
@@ -329,7 +335,7 @@ ls -la i18n-magic.js
|
|
|
329
335
|
npx @scoutello/i18n-magic sync
|
|
330
336
|
```
|
|
331
337
|
|
|
332
|
-
`add_translation_key`
|
|
338
|
+
`add_translation_key`/`add_translation_keys` translate automatically when API translation is configured; otherwise run `sync`.
|
|
333
339
|
|
|
334
340
|
### MCP Tools Not Appearing
|
|
335
341
|
|
package/dist/cli.js
CHANGED
|
@@ -4,6 +4,7 @@ import dotenv from "dotenv";
|
|
|
4
4
|
import OpenAI from "openai";
|
|
5
5
|
import { checkMissing } from "./commands/check-missing.js";
|
|
6
6
|
import { removeUnusedKeys } from "./commands/clean.js";
|
|
7
|
+
import { removeKey } from "./commands/remove-key.js";
|
|
7
8
|
import { replaceTranslation } from "./commands/replace.js";
|
|
8
9
|
import { restoreFromNamespaces } from "./commands/restore-from-namespaces.js";
|
|
9
10
|
import { translateMissing } from "./commands/scan.js";
|
|
@@ -47,18 +48,29 @@ const commands = [
|
|
|
47
48
|
description: "Restore missing keys by searching for them in other namespace files across all locales.",
|
|
48
49
|
action: restoreFromNamespaces,
|
|
49
50
|
},
|
|
51
|
+
{
|
|
52
|
+
name: "remove-key",
|
|
53
|
+
description: "Remove a specific translation key from all namespaces and locales.",
|
|
54
|
+
action: removeKey,
|
|
55
|
+
},
|
|
50
56
|
];
|
|
51
57
|
for (const command of commands) {
|
|
52
58
|
const cmd = program.command(command.name).description(command.description);
|
|
53
|
-
// Add key option to replace
|
|
59
|
+
// Add key option to replace and remove-key commands
|
|
54
60
|
if (command.name === "replace") {
|
|
55
61
|
cmd
|
|
56
62
|
.option("-k, --key <key>", "translation key to replace")
|
|
57
63
|
.allowExcessArguments(true)
|
|
58
64
|
.argument("[key]", "translation key to replace");
|
|
59
65
|
}
|
|
66
|
+
if (command.name === "remove-key") {
|
|
67
|
+
cmd
|
|
68
|
+
.option("-k, --key <key>", "translation key to remove")
|
|
69
|
+
.allowExcessArguments(true)
|
|
70
|
+
.argument("[key]", "translation key to remove");
|
|
71
|
+
}
|
|
60
72
|
cmd.action(async (arg, options) => {
|
|
61
|
-
|
|
73
|
+
dotenv.config({
|
|
62
74
|
path: program.opts().env || ".env",
|
|
63
75
|
});
|
|
64
76
|
const config = await loadConfig({
|
|
@@ -81,8 +93,8 @@ for (const command of commands) {
|
|
|
81
93
|
baseURL: "https://generativelanguage.googleapis.com/v1beta/openai/",
|
|
82
94
|
}),
|
|
83
95
|
});
|
|
84
|
-
// For replace
|
|
85
|
-
if (command.name === "replace") {
|
|
96
|
+
// For replace and remove-key commands, check for key in argument or option
|
|
97
|
+
if (command.name === "replace" || command.name === "remove-key") {
|
|
86
98
|
// If key is provided as positional argument, use that first
|
|
87
99
|
const keyToUse = typeof arg === "string" ? arg : options.key;
|
|
88
100
|
command.action({ ...config, openai }, keyToUse);
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACnC,OAAO,MAAM,MAAM,QAAQ,CAAA;AAC3B,OAAO,MAAM,MAAM,QAAQ,CAAA;AAC3B,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAA;AAC1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAA;
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"remove-key.d.ts","sourceRoot":"","sources":["../../src/commands/remove-key.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAGpD,eAAO,MAAM,SAAS,GAAU,QAAQ,aAAa,EAAE,MAAM,MAAM,kBAsHlE,CAAA"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import prompts from "prompts";
|
|
2
|
+
import { loadLocalesFile, writeLocalesFile } from "../lib/utils.js";
|
|
3
|
+
export const removeKey = async (config, key) => {
|
|
4
|
+
const { namespaces, locales, loadPath, savePath } = config;
|
|
5
|
+
if (!key) {
|
|
6
|
+
const response = await prompts({
|
|
7
|
+
type: "text",
|
|
8
|
+
name: "key",
|
|
9
|
+
message: "Enter the translation key to remove:",
|
|
10
|
+
validate: (value) => (value ? true : "Key cannot be empty"),
|
|
11
|
+
onState: (state) => {
|
|
12
|
+
if (state.aborted) {
|
|
13
|
+
process.nextTick(() => {
|
|
14
|
+
process.exit(0);
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
});
|
|
19
|
+
key = response.key;
|
|
20
|
+
}
|
|
21
|
+
if (!key) {
|
|
22
|
+
console.log("\n❌ Operation cancelled. No key was provided.");
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
console.log(`\n🔍 Searching for key "${key}" across all namespaces and locales...`);
|
|
26
|
+
const foundIn = [];
|
|
27
|
+
for (const namespace of namespaces) {
|
|
28
|
+
for (const locale of locales) {
|
|
29
|
+
try {
|
|
30
|
+
const existingKeys = await loadLocalesFile(loadPath, locale, namespace, {
|
|
31
|
+
silent: true,
|
|
32
|
+
});
|
|
33
|
+
if (Object.hasOwn(existingKeys, key)) {
|
|
34
|
+
foundIn.push({ namespace, locale });
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
// Skip if file doesn't exist or can't be read
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
if (foundIn.length === 0) {
|
|
43
|
+
console.log(`\n❌ Key "${key}" not found in any namespace or locale.`);
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
console.log(`\n⚠️ Key "${key}" found in ${foundIn.length} location(s):\n`);
|
|
47
|
+
const maxToShow = 20;
|
|
48
|
+
const itemsToShow = foundIn.slice(0, maxToShow);
|
|
49
|
+
for (const { namespace, locale } of itemsToShow) {
|
|
50
|
+
console.log(` • ${locale}:${namespace}`);
|
|
51
|
+
}
|
|
52
|
+
if (foundIn.length > maxToShow) {
|
|
53
|
+
console.log(` ... and ${foundIn.length - maxToShow} more`);
|
|
54
|
+
}
|
|
55
|
+
console.log("");
|
|
56
|
+
const { confirmed } = await prompts({
|
|
57
|
+
type: "confirm",
|
|
58
|
+
name: "confirmed",
|
|
59
|
+
message: `Do you want to remove "${key}" from ${foundIn.length} location(s)?`,
|
|
60
|
+
initial: false,
|
|
61
|
+
onState: (state) => {
|
|
62
|
+
if (state.aborted) {
|
|
63
|
+
process.nextTick(() => {
|
|
64
|
+
process.exit(0);
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
if (!confirmed) {
|
|
70
|
+
console.log("\n❌ Operation cancelled. No keys were removed.");
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
console.log(`\n🗑️ Removing key "${key}"...`);
|
|
74
|
+
let removedCount = 0;
|
|
75
|
+
for (const { namespace, locale } of foundIn) {
|
|
76
|
+
try {
|
|
77
|
+
const existingKeys = await loadLocalesFile(loadPath, locale, namespace, {
|
|
78
|
+
silent: true,
|
|
79
|
+
});
|
|
80
|
+
if (Object.hasOwn(existingKeys, key)) {
|
|
81
|
+
delete existingKeys[key];
|
|
82
|
+
await writeLocalesFile(savePath, locale, namespace, existingKeys);
|
|
83
|
+
removedCount++;
|
|
84
|
+
console.log(` ✓ Removed from ${locale}:${namespace}`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
catch (error) {
|
|
88
|
+
console.error(` ✗ Failed to remove from ${locale}:${namespace}: ${error instanceof Error ? error.message : String(error)}`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
console.log(`\n✅ Successfully removed key "${key}" from ${removedCount} location(s)`);
|
|
92
|
+
};
|
|
93
|
+
//# sourceMappingURL=remove-key.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"remove-key.js","sourceRoot":"","sources":["../../src/commands/remove-key.ts"],"names":[],"mappings":"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"}
|
package/dist/lib/utils.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/lib/utils.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAA;AAGhC,OAAO,KAAK,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAElE,eAAO,MAAM,UAAU,GAAU,kBAE9B;IACD,UAAU,CAAC,EAAE,MAAM,CAAA;CACf,iBA+CL,CAAA;AAED,wBAAgB,yBAAyB,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,CAAC,EAAE,CAE1D;AA0BD,eAAO,MAAM,YAAY,GAAU,gFAQhC;IACD,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC9B,OAAO,EAAE,MAAM,CAAA;IACf,aAAa,EAAE,MAAM,CAAA;IACrB,cAAc,EAAE,MAAM,CAAA;IACtB,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,UAAU,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;CACxD,oCAkEA,CAAA;AAED,eAAO,MAAM,eAAe,GAC1B,UACI,MAAM,GACN,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,EAC5E,QAAQ,MAAM,EACd,WAAW,MAAM,EACjB,UAAU;IAAE,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,oCAgC/B,CAAA;AAED,eAAO,MAAM,gBAAgB,GAC3B,UACI,MAAM,GACN,CAAC,CACC,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KACzB,OAAO,CAAC,IAAI,CAAC,CAAC,EACvB,QAAQ,MAAM,EACd,WAAW,MAAM,EACjB,MAAM,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,kBAmB7B,CAAA;AAED,eAAO,MAAM,UAAU,GACrB,KAAK,MAAM,EACX,YAAY,MAAM,EAClB,YAAY,OAAO,WAiBpB,CAAA;AAED;;GAEG;AACH,eAAO,MAAM,mBAAmB,GAC9B,cAAc,CAAC,MAAM,GAAG,iBAAiB,CAAC,EAAE,KAC3C,MAAM,EAIR,CAAA;AAED;;GAEG;AACH,eAAO,MAAM,oBAAoB,GAC/B,UAAU,MAAM,EAChB,cAAc,CAAC,MAAM,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,EAAE,EACpE,kBAAkB,MAAM,KACvB,MAAM,EAyCR,CAAA;AAED;;GAEG;AACH,eAAO,MAAM,2BAA2B,GACtC,WAAW,MAAM,EACjB,cAAc,CAAC,MAAM,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,EAAE,KACnE,MAAM,EAcR,CAAA;AAED;;GAEG;AACH,eAAO,MAAM,qBAAqB,GAAU,qCAGzC,IAAI,CAAC,aAAa,EAAE,cAAc,GAAG,kBAAkB,CAAC;SAoBlD,MAAM;gBACC,MAAM,EAAE;UACd,MAAM;IAyDf,CAAA;AAED,eAAO,MAAM,cAAc,GAAU,0EAMlC,aAAa,mBAwIf,CAAA;AAED;;GAEG;AACH,eAAO,MAAM,uBAAuB,GAClC,KAAK,MAAM,EACX,YAAY,MAAM,EAAE,EACpB,QAAQ,MAAM,EACd,UACI,MAAM,GACN,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,KAC3E,OAAO,CAAC,MAAM,GAAG,IAAI,CAcvB,CAAA;AAED;;GAEG;AACH,eAAO,MAAM,wBAAwB,GACnC,MAAM,MAAM,EAAE,EACd,YAAY,MAAM,EAAE,EACpB,QAAQ,MAAM,EACd,UACI,MAAM,GACN,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,EAC5E,UAAU;IAAE,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,KAC7B,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,CAiFvC,CAAA;AAED,eAAO,MAAM,YAAY,GAAU,KAAK,MAAM,EAAE,aAAa,MAAM,EAAE,oBAoBpE,CAAA;AAED,eAAO,MAAM,iBAAiB,GAAU,mHAUrC,aAAa,kBA4Df,CAAA;AAED,qBAAa,gBAAiB,SAAQ,KAAK;IAGhC,MAAM,CAAC,EAAE,MAAM;IACf,SAAS,CAAC,EAAE,MAAM;IAClB,KAAK,CAAC,EAAE,KAAK;gBAHpB,OAAO,EAAE,MAAM,EACR,MAAM,CAAC,EAAE,MAAM,EACf,SAAS,CAAC,EAAE,MAAM,EAClB,KAAK,CAAC,EAAE,KAAK;CAKvB;AAED;;;;GAIG;AACH;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,GAAU,mBAGtC;IACD,IAAI,EAAE,KAAK,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IAC9D,MAAM,EAAE,aAAa,CAAA;CACtB;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/lib/utils.ts"],"names":[],"mappings":"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"}
|
package/dist/lib/utils.js
CHANGED
|
@@ -590,43 +590,86 @@ export const addTranslationKeys = async ({ keys, config, }) => {
|
|
|
590
590
|
const scanTime = performance.now() - scanStartTime;
|
|
591
591
|
log(`⏱️ Codebase scan completed in ${scanTime.toFixed(2)}ms`);
|
|
592
592
|
// Step 2: Determine namespaces for each key
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
593
|
+
// Resolution order:
|
|
594
|
+
// 1) Explicit namespace prefix (e.g. "dashboard:welcome")
|
|
595
|
+
// 2) Code usage scan matches
|
|
596
|
+
// 3) Existing key in default locale namespace files
|
|
597
|
+
// 4) Key prefix matching a namespace (e.g. "dashboard.title")
|
|
598
|
+
// 5) Default namespace fallback
|
|
599
|
+
const defaultLocaleKeysByNamespace = new Map();
|
|
600
|
+
await Promise.all(namespaces.map(async (namespace) => {
|
|
601
|
+
try {
|
|
602
|
+
const nsKeys = await loadLocalesFile(loadPath, defaultLocale, namespace, {
|
|
603
|
+
silent: true,
|
|
604
|
+
});
|
|
605
|
+
defaultLocaleKeysByNamespace.set(namespace, nsKeys);
|
|
606
|
+
}
|
|
607
|
+
catch {
|
|
608
|
+
defaultLocaleKeysByNamespace.set(namespace, {});
|
|
604
609
|
}
|
|
605
|
-
|
|
606
|
-
|
|
610
|
+
}));
|
|
611
|
+
const preparedKeys = keys.map(({ key, value, language = "en" }) => {
|
|
612
|
+
const splitKey = key.split(":");
|
|
613
|
+
const hasExplicitNamespace = splitKey.length > 1 && namespaces.includes(splitKey[0]);
|
|
614
|
+
const normalizedKey = hasExplicitNamespace ? splitKey.slice(1).join(":") : key;
|
|
615
|
+
const explicitNamespace = hasExplicitNamespace ? splitKey[0] : null;
|
|
616
|
+
const foundNamespaces = new Set();
|
|
617
|
+
if (explicitNamespace) {
|
|
618
|
+
foundNamespaces.add(explicitNamespace);
|
|
607
619
|
}
|
|
608
620
|
else {
|
|
609
|
-
|
|
610
|
-
|
|
621
|
+
for (const entry of keysWithNamespaces) {
|
|
622
|
+
for (const namespace of entry.namespaces) {
|
|
623
|
+
const pureKey = getPureKey(entry.key, namespace, namespace === defaultNamespace);
|
|
624
|
+
if (entry.key === normalizedKey || pureKey === normalizedKey) {
|
|
625
|
+
foundNamespaces.add(namespace);
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
if (foundNamespaces.size === 0) {
|
|
630
|
+
for (const namespace of namespaces) {
|
|
631
|
+
const namespaceKeys = defaultLocaleKeysByNamespace.get(namespace) || {};
|
|
632
|
+
if (Object.hasOwn(namespaceKeys, normalizedKey)) {
|
|
633
|
+
foundNamespaces.add(namespace);
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
if (foundNamespaces.size === 0) {
|
|
638
|
+
const keyPrefix = normalizedKey.split(".")[0];
|
|
639
|
+
if (namespaces.includes(keyPrefix)) {
|
|
640
|
+
foundNamespaces.add(keyPrefix);
|
|
641
|
+
}
|
|
642
|
+
}
|
|
611
643
|
}
|
|
612
|
-
|
|
613
|
-
|
|
644
|
+
if (foundNamespaces.size === 0) {
|
|
645
|
+
foundNamespaces.add(defaultNamespace);
|
|
646
|
+
}
|
|
647
|
+
return {
|
|
648
|
+
key: normalizedKey,
|
|
649
|
+
value,
|
|
650
|
+
language,
|
|
651
|
+
namespaces: foundNamespaces,
|
|
652
|
+
};
|
|
653
|
+
});
|
|
614
654
|
// Step 3: Group keys by namespace and locale
|
|
615
655
|
const namespaceLocaleToKeys = new Map();
|
|
616
|
-
for (const
|
|
617
|
-
const
|
|
618
|
-
|
|
619
|
-
const mapKey = `${namespace}:${language}`;
|
|
656
|
+
for (const keyEntry of preparedKeys) {
|
|
657
|
+
for (const namespace of keyEntry.namespaces) {
|
|
658
|
+
const mapKey = `${namespace}:${keyEntry.language}`;
|
|
620
659
|
if (!namespaceLocaleToKeys.has(mapKey)) {
|
|
621
660
|
namespaceLocaleToKeys.set(mapKey, []);
|
|
622
661
|
}
|
|
623
|
-
namespaceLocaleToKeys.get(mapKey).push({
|
|
662
|
+
namespaceLocaleToKeys.get(mapKey).push({
|
|
663
|
+
key: keyEntry.key,
|
|
664
|
+
value: keyEntry.value,
|
|
665
|
+
language: keyEntry.language,
|
|
666
|
+
});
|
|
624
667
|
}
|
|
625
668
|
}
|
|
626
669
|
// Step 4: Collect all unique namespaces that will be affected
|
|
627
670
|
const affectedNamespaces = new Set();
|
|
628
|
-
for (const
|
|
629
|
-
for (const ns of namespaces) {
|
|
671
|
+
for (const keyEntry of preparedKeys) {
|
|
672
|
+
for (const ns of keyEntry.namespaces) {
|
|
630
673
|
affectedNamespaces.add(ns);
|
|
631
674
|
}
|
|
632
675
|
}
|
|
@@ -634,25 +677,27 @@ export const addTranslationKeys = async ({ keys, config, }) => {
|
|
|
634
677
|
// This ensures we preserve existing keys in all locales
|
|
635
678
|
const fileIOStartTime = performance.now();
|
|
636
679
|
const localeFiles = new Map();
|
|
680
|
+
const originalKeyCounts = new Map();
|
|
637
681
|
const loadErrors = [];
|
|
638
|
-
// Load files for all locales × all affected namespaces
|
|
682
|
+
// Load files for all locales × all affected namespaces in parallel (read-only)
|
|
683
|
+
const localeLoadPromises = [];
|
|
639
684
|
for (const namespace of affectedNamespaces) {
|
|
640
685
|
for (const locale of config.locales) {
|
|
641
686
|
const fileKey = `${locale}:${namespace}`;
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
catch
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
687
|
+
localeLoadPromises.push(loadLocalesFile(loadPath, locale, namespace, { silent: true })
|
|
688
|
+
.then((existingKeys) => {
|
|
689
|
+
localeFiles.set(fileKey, existingKeys);
|
|
690
|
+
originalKeyCounts.set(fileKey, Object.keys(existingKeys).length);
|
|
691
|
+
})
|
|
692
|
+
.catch((error) => {
|
|
693
|
+
// Don't silently ignore - track the error and DO NOT set empty object
|
|
694
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
695
|
+
loadErrors.push({ fileKey, error: errorMsg });
|
|
696
|
+
log(`⚠️ Failed to load ${fileKey}: ${errorMsg}`);
|
|
697
|
+
}));
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
await Promise.all(localeLoadPromises);
|
|
656
701
|
// If any files failed to load, abort to prevent data loss
|
|
657
702
|
if (loadErrors.length > 0) {
|
|
658
703
|
throw new Error(`Failed to load ${loadErrors.length} locale file(s). Aborting to prevent data loss.\n` +
|
|
@@ -677,14 +722,14 @@ export const addTranslationKeys = async ({ keys, config, }) => {
|
|
|
677
722
|
if (openai) {
|
|
678
723
|
// Group keys by input language
|
|
679
724
|
const keysByLanguage = new Map();
|
|
680
|
-
for (const
|
|
681
|
-
if (!keysByLanguage.has(language)) {
|
|
682
|
-
keysByLanguage.set(language, []);
|
|
725
|
+
for (const keyEntry of preparedKeys) {
|
|
726
|
+
if (!keysByLanguage.has(keyEntry.language)) {
|
|
727
|
+
keysByLanguage.set(keyEntry.language, []);
|
|
683
728
|
}
|
|
684
|
-
keysByLanguage.get(language).push({
|
|
685
|
-
key,
|
|
686
|
-
value,
|
|
687
|
-
namespaces:
|
|
729
|
+
keysByLanguage.get(keyEntry.language).push({
|
|
730
|
+
key: keyEntry.key,
|
|
731
|
+
value: keyEntry.value,
|
|
732
|
+
namespaces: keyEntry.namespaces,
|
|
688
733
|
});
|
|
689
734
|
}
|
|
690
735
|
// Translate each language group to all other locales
|
|
@@ -741,22 +786,6 @@ export const addTranslationKeys = async ({ keys, config, }) => {
|
|
|
741
786
|
// Step 8: Validate and write all files
|
|
742
787
|
// Safety check: ensure we're not accidentally removing keys (only adding)
|
|
743
788
|
const writeStartTime = performance.now();
|
|
744
|
-
const originalKeyCounts = new Map();
|
|
745
|
-
// Store original key counts for validation
|
|
746
|
-
for (const namespace of affectedNamespaces) {
|
|
747
|
-
for (const locale of config.locales) {
|
|
748
|
-
const fileKey = `${locale}:${namespace}`;
|
|
749
|
-
try {
|
|
750
|
-
const original = await loadLocalesFile(loadPath, locale, namespace, {
|
|
751
|
-
silent: true,
|
|
752
|
-
});
|
|
753
|
-
originalKeyCounts.set(fileKey, Object.keys(original).length);
|
|
754
|
-
}
|
|
755
|
-
catch {
|
|
756
|
-
originalKeyCounts.set(fileKey, 0);
|
|
757
|
-
}
|
|
758
|
-
}
|
|
759
|
-
}
|
|
760
789
|
// Validate: new file should have at least as many keys as original
|
|
761
790
|
for (const [fileKey, newKeys] of localeFiles) {
|
|
762
791
|
const originalCount = originalKeyCounts.get(fileKey) || 0;
|
|
@@ -776,8 +805,8 @@ export const addTranslationKeys = async ({ keys, config, }) => {
|
|
|
776
805
|
const totalTime = performance.now() - startTime;
|
|
777
806
|
log(`✅ Batch operation completed in ${totalTime.toFixed(2)}ms (${(totalTime / keys.length).toFixed(2)}ms per key)`);
|
|
778
807
|
// Build results
|
|
779
|
-
const results =
|
|
780
|
-
const
|
|
808
|
+
const results = preparedKeys.map(({ key, value, language, namespaces }) => {
|
|
809
|
+
const resolvedNamespaces = Array.from(namespaces);
|
|
781
810
|
const savedLocales = new Set([language]);
|
|
782
811
|
if (openai) {
|
|
783
812
|
config.locales.forEach((locale) => {
|
|
@@ -792,7 +821,7 @@ export const addTranslationKeys = async ({ keys, config, }) => {
|
|
|
792
821
|
return {
|
|
793
822
|
key,
|
|
794
823
|
value,
|
|
795
|
-
namespace:
|
|
824
|
+
namespace: resolvedNamespaces.join(", "),
|
|
796
825
|
locale: Array.from(savedLocales).sort().join(", "),
|
|
797
826
|
};
|
|
798
827
|
});
|