@scoutello/i18n-magic 0.19.0 → 0.21.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 +15 -31
- package/dist/cli.js +0 -6
- package/dist/cli.js.map +1 -1
- package/dist/commands/clean.d.ts.map +1 -1
- package/dist/commands/clean.js +16 -29
- package/dist/commands/clean.js.map +1 -1
- package/dist/commands/replace.d.ts.map +1 -1
- package/dist/commands/replace.js +79 -32
- package/dist/commands/replace.js.map +1 -1
- package/dist/commands/scan.d.ts.map +1 -1
- package/dist/commands/scan.js +5 -9
- package/dist/commands/scan.js.map +1 -1
- package/dist/index.d.ts +0 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -5
- package/dist/index.js.map +1 -1
- package/dist/lib/utils.d.ts +10 -2
- package/dist/lib/utils.d.ts.map +1 -1
- package/dist/lib/utils.js +53 -19
- package/dist/lib/utils.js.map +1 -1
- package/package.json +2 -1
- package/src/cli.ts +1 -6
- package/src/commands/clean.ts +19 -32
- package/src/commands/replace.ts +97 -46
- package/src/commands/scan.ts +0 -1
- package/src/index.ts +2 -7
- package/src/lib/utils.ts +73 -23
- package/src/commands/create-pruned-namespace-automated.ts +0 -165
- package/src/commands/create-pruned-namespace.ts +0 -168
package/dist/lib/utils.js
CHANGED
|
@@ -3,10 +3,11 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.TranslationError = exports.checkAllKeysExist = exports.getTextInput = exports.getMissingKeys = exports.getGlobPatternsForNamespace = exports.getNamespacesForFile = exports.extractGlobPatterns = exports.getPureKey = exports.writeLocalesFile = exports.loadLocalesFile = exports.translateKey = exports.loadConfig = void 0;
|
|
6
|
+
exports.TranslationError = exports.checkAllKeysExist = exports.getTextInput = exports.getMissingKeys = exports.getKeysWithNamespaces = exports.getGlobPatternsForNamespace = exports.getNamespacesForFile = exports.extractGlobPatterns = exports.getPureKey = exports.writeLocalesFile = exports.loadLocalesFile = exports.translateKey = exports.loadConfig = void 0;
|
|
7
7
|
exports.removeDuplicatesFromArray = removeDuplicatesFromArray;
|
|
8
8
|
const fast_glob_1 = __importDefault(require("fast-glob"));
|
|
9
9
|
const i18next_scanner_1 = require("i18next-scanner");
|
|
10
|
+
const minimatch_1 = require("minimatch");
|
|
10
11
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
11
12
|
const node_path_1 = __importDefault(require("node:path"));
|
|
12
13
|
const prompts_1 = __importDefault(require("prompts"));
|
|
@@ -129,19 +130,20 @@ exports.extractGlobPatterns = extractGlobPatterns;
|
|
|
129
130
|
/**
|
|
130
131
|
* Gets the namespaces associated with a specific file path based on glob pattern configuration
|
|
131
132
|
*/
|
|
132
|
-
const getNamespacesForFile = (filePath, globPatterns) => {
|
|
133
|
+
const getNamespacesForFile = (filePath, globPatterns, defaultNamespace) => {
|
|
133
134
|
const matchingNamespaces = [];
|
|
134
135
|
for (const pattern of globPatterns) {
|
|
135
136
|
if (typeof pattern === "object") {
|
|
136
|
-
// Use minimatch
|
|
137
|
-
|
|
138
|
-
// For now, using a simple includes check - in production you'd want proper glob matching
|
|
139
|
-
if (filePath.includes(globPattern.replace("**/*", "").replace("*", ""))) {
|
|
137
|
+
// Use minimatch for proper glob pattern matching
|
|
138
|
+
if ((0, minimatch_1.minimatch)(filePath, pattern.pattern)) {
|
|
140
139
|
matchingNamespaces.push(...pattern.namespaces);
|
|
141
140
|
}
|
|
142
141
|
}
|
|
143
142
|
}
|
|
144
|
-
|
|
143
|
+
// If no specific namespaces found, use default namespace
|
|
144
|
+
return matchingNamespaces.length > 0
|
|
145
|
+
? [...new Set(matchingNamespaces)]
|
|
146
|
+
: [defaultNamespace];
|
|
145
147
|
};
|
|
146
148
|
exports.getNamespacesForFile = getNamespacesForFile;
|
|
147
149
|
/**
|
|
@@ -162,32 +164,64 @@ const getGlobPatternsForNamespace = (namespace, globPatterns) => {
|
|
|
162
164
|
return patterns;
|
|
163
165
|
};
|
|
164
166
|
exports.getGlobPatternsForNamespace = getGlobPatternsForNamespace;
|
|
165
|
-
|
|
167
|
+
/**
|
|
168
|
+
* Extracts keys with their associated namespaces based on the files they're found in
|
|
169
|
+
*/
|
|
170
|
+
const getKeysWithNamespaces = async ({ globPatterns, defaultNamespace, }) => {
|
|
166
171
|
const parser = new i18next_scanner_1.Parser({
|
|
167
172
|
nsSeparator: false,
|
|
168
173
|
keySeparator: false,
|
|
169
174
|
});
|
|
170
175
|
const allPatterns = (0, exports.extractGlobPatterns)(globPatterns);
|
|
171
176
|
const files = await (0, fast_glob_1.default)([...allPatterns, "!**/node_modules/**"]);
|
|
172
|
-
const
|
|
177
|
+
const keysWithNamespaces = [];
|
|
173
178
|
for (const file of files) {
|
|
174
179
|
const content = node_fs_1.default.readFileSync(file, "utf-8");
|
|
180
|
+
const fileKeys = [];
|
|
175
181
|
parser.parseFuncFromString(content, { list: ["t"] }, (key) => {
|
|
176
|
-
|
|
182
|
+
fileKeys.push(key);
|
|
177
183
|
});
|
|
184
|
+
// Get namespaces for this file
|
|
185
|
+
const fileNamespaces = (0, exports.getNamespacesForFile)(file, globPatterns, defaultNamespace);
|
|
186
|
+
// Add each key with its associated namespaces
|
|
187
|
+
for (const key of fileKeys) {
|
|
188
|
+
keysWithNamespaces.push({
|
|
189
|
+
key,
|
|
190
|
+
namespaces: fileNamespaces,
|
|
191
|
+
file,
|
|
192
|
+
});
|
|
193
|
+
}
|
|
178
194
|
}
|
|
179
|
-
|
|
195
|
+
return keysWithNamespaces;
|
|
196
|
+
};
|
|
197
|
+
exports.getKeysWithNamespaces = getKeysWithNamespaces;
|
|
198
|
+
const getMissingKeys = async ({ globPatterns, namespaces, defaultNamespace, defaultLocale, loadPath, }) => {
|
|
199
|
+
const keysWithNamespaces = await (0, exports.getKeysWithNamespaces)({
|
|
200
|
+
globPatterns,
|
|
201
|
+
defaultNamespace,
|
|
202
|
+
});
|
|
180
203
|
const newKeys = [];
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
for (const
|
|
204
|
+
// Group keys by namespace
|
|
205
|
+
const keysByNamespace = {};
|
|
206
|
+
for (const { key, namespaces: keyNamespaces } of keysWithNamespaces) {
|
|
207
|
+
for (const namespace of keyNamespaces) {
|
|
208
|
+
if (!keysByNamespace[namespace]) {
|
|
209
|
+
keysByNamespace[namespace] = new Set();
|
|
210
|
+
}
|
|
185
211
|
const pureKey = (0, exports.getPureKey)(key, namespace, namespace === defaultNamespace);
|
|
186
|
-
if (
|
|
187
|
-
|
|
212
|
+
if (pureKey) {
|
|
213
|
+
keysByNamespace[namespace].add(pureKey);
|
|
188
214
|
}
|
|
189
|
-
|
|
190
|
-
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
// Check for missing keys in each namespace
|
|
218
|
+
for (const namespace of namespaces) {
|
|
219
|
+
const existingKeys = await (0, exports.loadLocalesFile)(loadPath, defaultLocale, namespace);
|
|
220
|
+
console.log(Object.keys(existingKeys).length, "existing keys in", namespace);
|
|
221
|
+
const keysForNamespace = keysByNamespace[namespace] || new Set();
|
|
222
|
+
for (const key of keysForNamespace) {
|
|
223
|
+
if (!existingKeys[key]) {
|
|
224
|
+
newKeys.push({ key, namespace });
|
|
191
225
|
}
|
|
192
226
|
}
|
|
193
227
|
}
|
package/dist/lib/utils.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/lib/utils.ts"],"names":[],"mappings":";;;;;;
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/lib/utils.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@scoutello/i18n-magic",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.21.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"description": "Intelligent CLI toolkit that automates internationalization workflows with AI-powered translations for JavaScript/TypeScript projects",
|
|
@@ -68,6 +68,7 @@
|
|
|
68
68
|
"dotenv": "^16.5.0",
|
|
69
69
|
"fast-glob": "^3.3.3",
|
|
70
70
|
"i18next-scanner": "^4.6.0",
|
|
71
|
+
"minimatch": "^10.0.1",
|
|
71
72
|
"prompts": "^2.4.2",
|
|
72
73
|
"zod": "^3.24.2"
|
|
73
74
|
},
|
package/src/cli.ts
CHANGED
|
@@ -3,7 +3,7 @@ import dotenv from "dotenv"
|
|
|
3
3
|
import OpenAI from "openai"
|
|
4
4
|
import { checkMissing } from "./commands/check-missing"
|
|
5
5
|
import { removeUnusedKeys } from "./commands/clean"
|
|
6
|
-
|
|
6
|
+
|
|
7
7
|
import { replaceTranslation } from "./commands/replace"
|
|
8
8
|
import { translateMissing } from "./commands/scan"
|
|
9
9
|
import { syncLocales } from "./commands/sync-locales"
|
|
@@ -52,11 +52,6 @@ const commands: CommandType[] = [
|
|
|
52
52
|
"Remove unused translations from all locales. Useful for a CI/CD pipeline or husky hook.",
|
|
53
53
|
action: removeUnusedKeys,
|
|
54
54
|
},
|
|
55
|
-
{
|
|
56
|
-
name: "prune",
|
|
57
|
-
description: "Create a pruned namespace from the other namespaces.",
|
|
58
|
-
action: createPrunedNamespace,
|
|
59
|
-
},
|
|
60
55
|
]
|
|
61
56
|
|
|
62
57
|
for (const command of commands) {
|
package/src/commands/clean.ts
CHANGED
|
@@ -1,11 +1,8 @@
|
|
|
1
|
-
import glob from "fast-glob"
|
|
2
|
-
import { Parser } from "i18next-scanner"
|
|
3
|
-
import fs from "node:fs"
|
|
4
1
|
import type { Configuration } from "../lib/types"
|
|
5
2
|
import {
|
|
3
|
+
getKeysWithNamespaces,
|
|
6
4
|
getPureKey,
|
|
7
5
|
loadLocalesFile,
|
|
8
|
-
removeDuplicatesFromArray,
|
|
9
6
|
writeLocalesFile,
|
|
10
7
|
} from "../lib/utils"
|
|
11
8
|
|
|
@@ -19,47 +16,37 @@ export const removeUnusedKeys = async (config: Configuration) => {
|
|
|
19
16
|
savePath,
|
|
20
17
|
} = config
|
|
21
18
|
|
|
22
|
-
//
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
19
|
+
// Get all keys with their associated namespaces from the codebase
|
|
20
|
+
const keysWithNamespaces = await getKeysWithNamespaces({
|
|
21
|
+
globPatterns,
|
|
22
|
+
defaultNamespace,
|
|
26
23
|
})
|
|
27
24
|
|
|
28
|
-
// Find all files to scan
|
|
29
|
-
const allPatterns = globPatterns.map((pattern) =>
|
|
30
|
-
typeof pattern === "string" ? pattern : pattern.pattern,
|
|
31
|
-
)
|
|
32
|
-
const files = await glob([...allPatterns, "!**/node_modules/**"])
|
|
33
|
-
|
|
34
|
-
// Extract all translation keys from the codebase
|
|
35
|
-
const extractedKeys = []
|
|
36
|
-
for (const file of files) {
|
|
37
|
-
const content = fs.readFileSync(file, "utf-8")
|
|
38
|
-
parser.parseFuncFromString(content, { list: ["t"] }, (key: string) => {
|
|
39
|
-
extractedKeys.push(key)
|
|
40
|
-
})
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// Remove duplicates
|
|
44
|
-
const uniqueExtractedKeys = removeDuplicatesFromArray(extractedKeys)
|
|
45
|
-
|
|
46
25
|
// Track stats for reporting
|
|
47
26
|
const stats = {
|
|
48
27
|
total: 0,
|
|
49
28
|
removed: 0,
|
|
50
29
|
}
|
|
51
30
|
|
|
52
|
-
//
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
31
|
+
// Group keys by namespace
|
|
32
|
+
const keysByNamespace: Record<string, Set<string>> = {}
|
|
33
|
+
|
|
34
|
+
for (const { key, namespaces: keyNamespaces } of keysWithNamespaces) {
|
|
35
|
+
for (const namespace of keyNamespaces) {
|
|
36
|
+
if (!keysByNamespace[namespace]) {
|
|
37
|
+
keysByNamespace[namespace] = new Set()
|
|
38
|
+
}
|
|
56
39
|
|
|
57
|
-
for (const key of uniqueExtractedKeys) {
|
|
58
40
|
const pureKey = getPureKey(key, namespace, namespace === defaultNamespace)
|
|
59
41
|
if (pureKey) {
|
|
60
|
-
|
|
42
|
+
keysByNamespace[namespace].add(pureKey)
|
|
61
43
|
}
|
|
62
44
|
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Process each namespace and locale
|
|
48
|
+
for (const namespace of namespaces) {
|
|
49
|
+
const usedKeysSet = keysByNamespace[namespace] || new Set()
|
|
63
50
|
|
|
64
51
|
// Process each locale
|
|
65
52
|
for (const locale of locales) {
|
package/src/commands/replace.ts
CHANGED
|
@@ -1,23 +1,30 @@
|
|
|
1
1
|
import type { Configuration } from "../lib/types"
|
|
2
2
|
import {
|
|
3
|
+
getKeysWithNamespaces,
|
|
4
|
+
getPureKey,
|
|
3
5
|
getTextInput,
|
|
4
6
|
loadLocalesFile,
|
|
5
7
|
translateKey,
|
|
6
8
|
writeLocalesFile,
|
|
7
9
|
} from "../lib/utils"
|
|
8
10
|
|
|
9
|
-
const getKeyToReplace = async (
|
|
11
|
+
const getKeyToReplace = async (
|
|
12
|
+
allAvailableKeys: Record<string, { namespace: string; value: string }[]>,
|
|
13
|
+
): Promise<{ key: string; namespaces: string[] }> => {
|
|
10
14
|
const keyToReplace = await getTextInput(
|
|
11
15
|
"Enter the key to replace the translation for: ",
|
|
12
16
|
)
|
|
13
17
|
|
|
14
|
-
if (!
|
|
18
|
+
if (!allAvailableKeys[keyToReplace]) {
|
|
15
19
|
console.log(`The key "${keyToReplace}" does not exist.`)
|
|
16
|
-
return await getKeyToReplace(
|
|
20
|
+
return await getKeyToReplace(allAvailableKeys)
|
|
17
21
|
}
|
|
18
22
|
|
|
19
|
-
|
|
20
|
-
|
|
23
|
+
const namespaces = allAvailableKeys[keyToReplace].map((k) => k.namespace)
|
|
24
|
+
console.log(
|
|
25
|
+
`The key "${keyToReplace}" exists in namespaces: ${namespaces.join(", ")}.`,
|
|
26
|
+
)
|
|
27
|
+
return { key: keyToReplace, namespaces }
|
|
21
28
|
}
|
|
22
29
|
|
|
23
30
|
export const replaceTranslation = async (
|
|
@@ -36,63 +43,107 @@ export const replaceTranslation = async (
|
|
|
36
43
|
openai,
|
|
37
44
|
} = config
|
|
38
45
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
)
|
|
46
|
+
// Find all keys with their namespaces from the codebase
|
|
47
|
+
const keysWithNamespaces = await getKeysWithNamespaces({
|
|
48
|
+
globPatterns,
|
|
49
|
+
defaultNamespace,
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
// Build a map of all available keys across all namespaces
|
|
53
|
+
const allAvailableKeys: Record<
|
|
54
|
+
string,
|
|
55
|
+
{ namespace: string; value: string }[]
|
|
56
|
+
> = {}
|
|
57
|
+
|
|
58
|
+
for (const namespace of namespaces) {
|
|
59
|
+
const keys = await loadLocalesFile(loadPath, defaultLocale, namespace)
|
|
60
|
+
for (const [keyName, value] of Object.entries(keys)) {
|
|
61
|
+
if (!allAvailableKeys[keyName]) {
|
|
62
|
+
allAvailableKeys[keyName] = []
|
|
63
|
+
}
|
|
64
|
+
allAvailableKeys[keyName].push({ namespace, value })
|
|
65
|
+
}
|
|
66
|
+
}
|
|
44
67
|
|
|
45
68
|
let keyToReplace: string
|
|
69
|
+
let targetNamespaces: string[] = []
|
|
46
70
|
|
|
47
71
|
if (key) {
|
|
48
|
-
if (
|
|
72
|
+
if (allAvailableKeys[key]) {
|
|
49
73
|
keyToReplace = key
|
|
50
|
-
|
|
74
|
+
// Determine which namespaces this key should be updated in based on usage
|
|
75
|
+
const keyUsage = keysWithNamespaces.filter((k) => {
|
|
76
|
+
const pureKey = getPureKey(k.key, defaultNamespace, true)
|
|
77
|
+
return pureKey === key || k.key === key
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
if (keyUsage.length > 0) {
|
|
81
|
+
// Use namespaces from actual usage
|
|
82
|
+
const allNamespaces: string[] = []
|
|
83
|
+
for (const k of keyUsage) {
|
|
84
|
+
allNamespaces.push(...k.namespaces)
|
|
85
|
+
}
|
|
86
|
+
targetNamespaces = [...new Set(allNamespaces)]
|
|
87
|
+
} else {
|
|
88
|
+
// Fallback to all namespaces where the key exists
|
|
89
|
+
targetNamespaces = allAvailableKeys[key].map((k) => k.namespace)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
console.log(
|
|
93
|
+
`The key "${keyToReplace}" exists in namespaces: ${targetNamespaces.join(", ")}.`,
|
|
94
|
+
)
|
|
51
95
|
} else {
|
|
52
96
|
console.log(`The key "${key}" does not exist.`)
|
|
53
|
-
|
|
97
|
+
const result = await getKeyToReplace(allAvailableKeys)
|
|
98
|
+
keyToReplace = result.key
|
|
99
|
+
targetNamespaces = result.namespaces
|
|
54
100
|
}
|
|
55
101
|
} else {
|
|
56
|
-
|
|
102
|
+
const result = await getKeyToReplace(allAvailableKeys)
|
|
103
|
+
keyToReplace = result.key
|
|
104
|
+
targetNamespaces = result.namespaces
|
|
57
105
|
}
|
|
58
106
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
107
|
+
// Show current translations across namespaces
|
|
108
|
+
for (const namespace of targetNamespaces) {
|
|
109
|
+
const keys = await loadLocalesFile(loadPath, defaultLocale, namespace)
|
|
110
|
+
if (keys[keyToReplace]) {
|
|
111
|
+
console.log(
|
|
112
|
+
`Current translation in ${defaultLocale} (${namespace}): "${keys[keyToReplace]}"`,
|
|
113
|
+
)
|
|
114
|
+
}
|
|
115
|
+
}
|
|
62
116
|
|
|
63
117
|
const newTranslation = await getTextInput("Enter the new translation: ")
|
|
64
118
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
newValue =
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
}
|
|
119
|
+
// Update the key in all relevant namespaces and locales
|
|
120
|
+
for (const namespace of targetNamespaces) {
|
|
121
|
+
for (const locale of locales) {
|
|
122
|
+
let newValue = ""
|
|
123
|
+
if (locale === defaultLocale) {
|
|
124
|
+
newValue = newTranslation
|
|
125
|
+
} else {
|
|
126
|
+
const translation = await translateKey({
|
|
127
|
+
context,
|
|
128
|
+
inputLanguage: defaultLocale,
|
|
129
|
+
outputLanguage: locale,
|
|
130
|
+
object: {
|
|
131
|
+
[keyToReplace]: newTranslation,
|
|
132
|
+
},
|
|
133
|
+
openai,
|
|
134
|
+
model: config.model,
|
|
135
|
+
})
|
|
83
136
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
locale,
|
|
87
|
-
defaultNamespace,
|
|
88
|
-
)
|
|
137
|
+
newValue = translation[keyToReplace]
|
|
138
|
+
}
|
|
89
139
|
|
|
90
|
-
|
|
140
|
+
const existingKeys = await loadLocalesFile(loadPath, locale, namespace)
|
|
141
|
+
existingKeys[keyToReplace] = newValue
|
|
142
|
+
await writeLocalesFile(savePath, locale, namespace, existingKeys)
|
|
91
143
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
)
|
|
144
|
+
console.log(
|
|
145
|
+
`Updated "${keyToReplace}" in ${locale} (${namespace}): "${newValue}"`,
|
|
146
|
+
)
|
|
147
|
+
}
|
|
97
148
|
}
|
|
98
149
|
}
|
package/src/commands/scan.ts
CHANGED
package/src/index.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
// Export command functions for programmatic usage
|
|
2
2
|
export { checkMissing } from "./commands/check-missing"
|
|
3
3
|
export { removeUnusedKeys } from "./commands/clean"
|
|
4
|
-
|
|
5
|
-
export { createPrunedNamespaceAutomated } from "./commands/create-pruned-namespace-automated"
|
|
4
|
+
|
|
6
5
|
export { replaceTranslation } from "./commands/replace"
|
|
7
6
|
export { translateMissing } from "./commands/scan"
|
|
8
7
|
export { syncLocales } from "./commands/sync-locales"
|
|
@@ -11,11 +10,7 @@ export { syncLocales } from "./commands/sync-locales"
|
|
|
11
10
|
export { loadConfig } from "./lib/utils"
|
|
12
11
|
|
|
13
12
|
// Export types
|
|
14
|
-
|
|
15
|
-
PruneOptions,
|
|
16
|
-
PruneResponse,
|
|
17
|
-
PruneResult,
|
|
18
|
-
} from "./commands/create-pruned-namespace-automated"
|
|
13
|
+
|
|
19
14
|
export type {
|
|
20
15
|
CommandType,
|
|
21
16
|
Configuration,
|
package/src/lib/utils.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import glob from "fast-glob"
|
|
2
2
|
import { Parser } from "i18next-scanner"
|
|
3
|
+
import { minimatch } from "minimatch"
|
|
3
4
|
import fs from "node:fs"
|
|
4
5
|
import path from "node:path"
|
|
5
6
|
import type OpenAI from "openai"
|
|
6
7
|
import prompts from "prompts"
|
|
7
8
|
import { languages } from "./languges"
|
|
8
|
-
import type {
|
|
9
|
+
import type { Configuration, GlobPatternConfig } from "./types"
|
|
9
10
|
|
|
10
11
|
export const loadConfig = ({
|
|
11
12
|
configPath = "i18n-magic.js",
|
|
@@ -197,21 +198,23 @@ export const extractGlobPatterns = (
|
|
|
197
198
|
export const getNamespacesForFile = (
|
|
198
199
|
filePath: string,
|
|
199
200
|
globPatterns: (string | { pattern: string; namespaces: string[] })[],
|
|
201
|
+
defaultNamespace: string,
|
|
200
202
|
): string[] => {
|
|
201
203
|
const matchingNamespaces: string[] = []
|
|
202
204
|
|
|
203
205
|
for (const pattern of globPatterns) {
|
|
204
206
|
if (typeof pattern === "object") {
|
|
205
|
-
// Use minimatch
|
|
206
|
-
|
|
207
|
-
// For now, using a simple includes check - in production you'd want proper glob matching
|
|
208
|
-
if (filePath.includes(globPattern.replace("**/*", "").replace("*", ""))) {
|
|
207
|
+
// Use minimatch for proper glob pattern matching
|
|
208
|
+
if (minimatch(filePath, pattern.pattern)) {
|
|
209
209
|
matchingNamespaces.push(...pattern.namespaces)
|
|
210
210
|
}
|
|
211
211
|
}
|
|
212
212
|
}
|
|
213
213
|
|
|
214
|
-
|
|
214
|
+
// If no specific namespaces found, use default namespace
|
|
215
|
+
return matchingNamespaces.length > 0
|
|
216
|
+
? [...new Set(matchingNamespaces)]
|
|
217
|
+
: [defaultNamespace]
|
|
215
218
|
}
|
|
216
219
|
|
|
217
220
|
/**
|
|
@@ -236,13 +239,13 @@ export const getGlobPatternsForNamespace = (
|
|
|
236
239
|
return patterns
|
|
237
240
|
}
|
|
238
241
|
|
|
239
|
-
|
|
242
|
+
/**
|
|
243
|
+
* Extracts keys with their associated namespaces based on the files they're found in
|
|
244
|
+
*/
|
|
245
|
+
export const getKeysWithNamespaces = async ({
|
|
240
246
|
globPatterns,
|
|
241
|
-
namespaces,
|
|
242
247
|
defaultNamespace,
|
|
243
|
-
|
|
244
|
-
loadPath,
|
|
245
|
-
}: Configuration) => {
|
|
248
|
+
}: Pick<Configuration, "globPatterns" | "defaultNamespace">) => {
|
|
246
249
|
const parser = new Parser({
|
|
247
250
|
nsSeparator: false,
|
|
248
251
|
keySeparator: false,
|
|
@@ -251,19 +254,70 @@ export const getMissingKeys = async ({
|
|
|
251
254
|
const allPatterns = extractGlobPatterns(globPatterns)
|
|
252
255
|
const files = await glob([...allPatterns, "!**/node_modules/**"])
|
|
253
256
|
|
|
254
|
-
const
|
|
257
|
+
const keysWithNamespaces: Array<{
|
|
258
|
+
key: string
|
|
259
|
+
namespaces: string[]
|
|
260
|
+
file: string
|
|
261
|
+
}> = []
|
|
255
262
|
|
|
256
263
|
for (const file of files) {
|
|
257
264
|
const content = fs.readFileSync(file, "utf-8")
|
|
265
|
+
const fileKeys: string[] = []
|
|
266
|
+
|
|
258
267
|
parser.parseFuncFromString(content, { list: ["t"] }, (key: string) => {
|
|
259
|
-
|
|
268
|
+
fileKeys.push(key)
|
|
260
269
|
})
|
|
270
|
+
|
|
271
|
+
// Get namespaces for this file
|
|
272
|
+
const fileNamespaces = getNamespacesForFile(
|
|
273
|
+
file,
|
|
274
|
+
globPatterns,
|
|
275
|
+
defaultNamespace,
|
|
276
|
+
)
|
|
277
|
+
|
|
278
|
+
// Add each key with its associated namespaces
|
|
279
|
+
for (const key of fileKeys) {
|
|
280
|
+
keysWithNamespaces.push({
|
|
281
|
+
key,
|
|
282
|
+
namespaces: fileNamespaces,
|
|
283
|
+
file,
|
|
284
|
+
})
|
|
285
|
+
}
|
|
261
286
|
}
|
|
262
287
|
|
|
263
|
-
|
|
288
|
+
return keysWithNamespaces
|
|
289
|
+
}
|
|
264
290
|
|
|
291
|
+
export const getMissingKeys = async ({
|
|
292
|
+
globPatterns,
|
|
293
|
+
namespaces,
|
|
294
|
+
defaultNamespace,
|
|
295
|
+
defaultLocale,
|
|
296
|
+
loadPath,
|
|
297
|
+
}: Configuration) => {
|
|
298
|
+
const keysWithNamespaces = await getKeysWithNamespaces({
|
|
299
|
+
globPatterns,
|
|
300
|
+
defaultNamespace,
|
|
301
|
+
})
|
|
265
302
|
const newKeys = []
|
|
266
303
|
|
|
304
|
+
// Group keys by namespace
|
|
305
|
+
const keysByNamespace: Record<string, Set<string>> = {}
|
|
306
|
+
|
|
307
|
+
for (const { key, namespaces: keyNamespaces } of keysWithNamespaces) {
|
|
308
|
+
for (const namespace of keyNamespaces) {
|
|
309
|
+
if (!keysByNamespace[namespace]) {
|
|
310
|
+
keysByNamespace[namespace] = new Set()
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
const pureKey = getPureKey(key, namespace, namespace === defaultNamespace)
|
|
314
|
+
if (pureKey) {
|
|
315
|
+
keysByNamespace[namespace].add(pureKey)
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// Check for missing keys in each namespace
|
|
267
321
|
for (const namespace of namespaces) {
|
|
268
322
|
const existingKeys = await loadLocalesFile(
|
|
269
323
|
loadPath,
|
|
@@ -271,17 +325,13 @@ export const getMissingKeys = async ({
|
|
|
271
325
|
namespace,
|
|
272
326
|
)
|
|
273
327
|
|
|
274
|
-
console.log(Object.keys(existingKeys).length, "existing keys")
|
|
275
|
-
|
|
276
|
-
for (const key of uniqueKeys) {
|
|
277
|
-
const pureKey = getPureKey(key, namespace, namespace === defaultNamespace)
|
|
328
|
+
console.log(Object.keys(existingKeys).length, "existing keys in", namespace)
|
|
278
329
|
|
|
279
|
-
|
|
280
|
-
continue
|
|
281
|
-
}
|
|
330
|
+
const keysForNamespace = keysByNamespace[namespace] || new Set()
|
|
282
331
|
|
|
283
|
-
|
|
284
|
-
|
|
332
|
+
for (const key of keysForNamespace) {
|
|
333
|
+
if (!existingKeys[key]) {
|
|
334
|
+
newKeys.push({ key, namespace })
|
|
285
335
|
}
|
|
286
336
|
}
|
|
287
337
|
}
|