@scoutello/i18n-magic 0.57.0 ā 0.57.3
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/dist/commands/check-missing.d.ts.map +1 -1
- package/dist/commands/check-missing.js +100 -3
- package/dist/commands/check-missing.js.map +1 -1
- package/dist/commands/scan.d.ts.map +1 -1
- package/dist/commands/scan.js +6 -0
- package/dist/commands/scan.js.map +1 -1
- package/dist/commands/sync-locales.d.ts.map +1 -1
- package/dist/commands/sync-locales.js +186 -98
- package/dist/commands/sync-locales.js.map +1 -1
- package/dist/lib/utils.d.ts +6 -2
- package/dist/lib/utils.d.ts.map +1 -1
- package/dist/lib/utils.js +33 -12
- package/dist/lib/utils.js.map +1 -1
- package/package.json +10 -8
- package/src/commands/check-missing.ts +154 -3
- package/src/commands/scan.ts +7 -0
- package/src/commands/sync-locales.ts +254 -155
- package/src/lib/utils.ts +37 -10
- package/dist/commands/create-pruned-namespace-automated.d.ts +0 -20
- package/dist/commands/create-pruned-namespace-automated.d.ts.map +0 -1
- package/dist/commands/create-pruned-namespace-automated.js +0 -98
- package/dist/commands/create-pruned-namespace-automated.js.map +0 -1
- package/dist/commands/create-pruned-namespace.d.ts +0 -3
- package/dist/commands/create-pruned-namespace.d.ts.map +0 -1
- package/dist/commands/create-pruned-namespace.js +0 -123
- package/dist/commands/create-pruned-namespace.js.map +0 -1
- package/dist/i18n-magic.cjs.development.js +0 -1787
- package/dist/i18n-magic.cjs.development.js.map +0 -1
- package/dist/i18n-magic.cjs.production.min.js +0 -2
- package/dist/i18n-magic.cjs.production.min.js.map +0 -1
- package/dist/i18n-magic.esm.js +0 -1776
- package/dist/i18n-magic.esm.js.map +0 -1
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import fs from "node:fs"
|
|
2
|
+
import chalk from "chalk"
|
|
3
|
+
import cliProgress from "cli-progress"
|
|
2
4
|
import type { Configuration } from "../lib/types.js"
|
|
3
5
|
import {
|
|
4
6
|
TranslationError,
|
|
@@ -7,6 +9,12 @@ import {
|
|
|
7
9
|
writeLocalesFile,
|
|
8
10
|
findExistingTranslations,
|
|
9
11
|
} from "../lib/utils.js"
|
|
12
|
+
import { languages } from "../lib/languges.js"
|
|
13
|
+
|
|
14
|
+
const getLanguageLabel = (locale: string): string => {
|
|
15
|
+
const lang = languages.find((l) => l.value === locale)
|
|
16
|
+
return lang?.label || locale
|
|
17
|
+
}
|
|
10
18
|
|
|
11
19
|
export const syncLocales = async (config: Configuration) => {
|
|
12
20
|
const {
|
|
@@ -19,9 +27,59 @@ export const syncLocales = async (config: Configuration) => {
|
|
|
19
27
|
openai,
|
|
20
28
|
} = config
|
|
21
29
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
console.log(
|
|
30
|
+
const localesToProcess = locales.filter((l) => l !== defaultLocale)
|
|
31
|
+
|
|
32
|
+
console.log(chalk.cyan("\nš Syncing translations\n"))
|
|
33
|
+
console.log(chalk.dim(` Default locale: ${chalk.white(getLanguageLabel(defaultLocale))}`))
|
|
34
|
+
console.log(chalk.dim(` Namespaces: ${chalk.white(namespaces.join(", "))}`))
|
|
35
|
+
console.log(chalk.dim(` Target languages: ${chalk.white(localesToProcess.length)}\n`))
|
|
36
|
+
|
|
37
|
+
// Create multi-bar container
|
|
38
|
+
const multibar = new cliProgress.MultiBar(
|
|
39
|
+
{
|
|
40
|
+
clearOnComplete: false,
|
|
41
|
+
hideCursor: true,
|
|
42
|
+
format: (options, params, payload) => {
|
|
43
|
+
const lang = payload.language || "unknown"
|
|
44
|
+
const status = payload.status || ""
|
|
45
|
+
const bar = options.barCompleteString?.substring(0, Math.round(params.progress * options.barsize!)) || ""
|
|
46
|
+
const emptyBar = options.barIncompleteString?.substring(0, options.barsize! - bar.length) || ""
|
|
47
|
+
|
|
48
|
+
// Color the bar based on progress
|
|
49
|
+
let coloredBar: string
|
|
50
|
+
if (params.progress >= 1) {
|
|
51
|
+
coloredBar = chalk.green(bar) + chalk.gray(emptyBar)
|
|
52
|
+
} else if (params.progress >= 0.5) {
|
|
53
|
+
coloredBar = chalk.yellow(bar) + chalk.gray(emptyBar)
|
|
54
|
+
} else {
|
|
55
|
+
coloredBar = chalk.cyan(bar) + chalk.gray(emptyBar)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const percentage = Math.round(params.progress * 100)
|
|
59
|
+
const paddedLang = lang.padEnd(12)
|
|
60
|
+
const paddedStatus = status.padEnd(20)
|
|
61
|
+
|
|
62
|
+
return ` ${chalk.white(paddedLang)} ${coloredBar} ${chalk.dim(percentage.toString().padStart(3))}% ${chalk.dim(paddedStatus)}`
|
|
63
|
+
},
|
|
64
|
+
barCompleteChar: "ā",
|
|
65
|
+
barIncompleteChar: "ā",
|
|
66
|
+
barsize: 25,
|
|
67
|
+
},
|
|
68
|
+
cliProgress.Presets.shades_classic,
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
// Create a progress bar for each locale
|
|
72
|
+
const progressBars: Record<string, cliProgress.SingleBar> = {}
|
|
73
|
+
const localeStats: Record<string, { missing: number; reused: number; translated: number }> = {}
|
|
74
|
+
|
|
75
|
+
for (const locale of localesToProcess) {
|
|
76
|
+
const langLabel = getLanguageLabel(locale)
|
|
77
|
+
progressBars[locale] = multibar.create(100, 0, {
|
|
78
|
+
language: langLabel,
|
|
79
|
+
status: "Waiting...",
|
|
80
|
+
})
|
|
81
|
+
localeStats[locale] = { missing: 0, reused: 0, translated: 0 }
|
|
82
|
+
}
|
|
25
83
|
|
|
26
84
|
try {
|
|
27
85
|
// Helper to ensure a locale/namespace file exists before we add keys
|
|
@@ -34,7 +92,6 @@ export const syncLocales = async (config: Configuration) => {
|
|
|
34
92
|
.replace("{{lng}}", locale)
|
|
35
93
|
.replace("{{ns}}", namespace)
|
|
36
94
|
if (!fs.existsSync(filePath)) {
|
|
37
|
-
console.log(`š Creating missing namespace file: ${filePath}`)
|
|
38
95
|
await writeLocalesFile(savePath, locale, namespace, {})
|
|
39
96
|
}
|
|
40
97
|
}
|
|
@@ -48,181 +105,223 @@ export const syncLocales = async (config: Configuration) => {
|
|
|
48
105
|
)
|
|
49
106
|
|
|
50
107
|
// Process all non-default locales in parallel
|
|
51
|
-
const localesToProcess = locales.filter((l) => l !== defaultLocale)
|
|
52
|
-
|
|
53
108
|
await Promise.all(
|
|
54
109
|
localesToProcess.map(async (locale) => {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
+
const bar = progressBars[locale]
|
|
111
|
+
const stats = localeStats[locale]
|
|
112
|
+
|
|
113
|
+
try {
|
|
114
|
+
// Stage 1: Loading (0-20%)
|
|
115
|
+
bar.update(5, { status: "Loading files..." })
|
|
116
|
+
|
|
117
|
+
// Ensure all namespace files for this locale exist before filling keys
|
|
118
|
+
await Promise.all(
|
|
119
|
+
namespaces.map((namespace) =>
|
|
120
|
+
ensureLocaleNamespaceFile(locale, namespace),
|
|
121
|
+
),
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
bar.update(10, { status: "Analyzing keys..." })
|
|
125
|
+
|
|
126
|
+
// Collect all missing keys for this locale across all namespaces
|
|
127
|
+
const allMissingKeys: Record<
|
|
128
|
+
string,
|
|
129
|
+
{ value: string; namespaces: string[] }
|
|
130
|
+
> = {}
|
|
131
|
+
const namespaceKeys: Record<string, Record<string, string>> = {}
|
|
132
|
+
|
|
133
|
+
// Load existing keys for all namespaces in parallel
|
|
134
|
+
const namespaceResults = await Promise.all(
|
|
135
|
+
namespaces.map(async (namespace) => {
|
|
136
|
+
const defaultLocaleKeys = await loadLocalesFile(
|
|
137
|
+
loadPath,
|
|
138
|
+
defaultLocale,
|
|
139
|
+
namespace,
|
|
140
|
+
{ silent: true },
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
const localeKeys = await loadLocalesFile(
|
|
144
|
+
loadPath,
|
|
145
|
+
locale,
|
|
146
|
+
namespace,
|
|
147
|
+
{ silent: true },
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
return {
|
|
151
|
+
namespace,
|
|
152
|
+
defaultLocaleKeys,
|
|
153
|
+
localeKeys,
|
|
154
|
+
}
|
|
155
|
+
}),
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
bar.update(20, { status: "Finding missing..." })
|
|
159
|
+
|
|
160
|
+
// Process results and collect missing keys
|
|
161
|
+
for (const result of namespaceResults) {
|
|
162
|
+
const { namespace, defaultLocaleKeys, localeKeys } = result
|
|
163
|
+
namespaceKeys[namespace] = localeKeys
|
|
164
|
+
|
|
165
|
+
// Check which keys from default locale are missing in current locale
|
|
166
|
+
for (const [key, value] of Object.entries(defaultLocaleKeys)) {
|
|
167
|
+
if (!localeKeys[key]) {
|
|
168
|
+
if (allMissingKeys[key]) {
|
|
169
|
+
// Key already exists, add this namespace to the list
|
|
170
|
+
allMissingKeys[key].namespaces.push(namespace)
|
|
171
|
+
} else {
|
|
172
|
+
// New missing key
|
|
173
|
+
allMissingKeys[key] = {
|
|
174
|
+
value,
|
|
175
|
+
namespaces: [namespace],
|
|
176
|
+
}
|
|
110
177
|
}
|
|
111
178
|
}
|
|
112
179
|
}
|
|
113
180
|
}
|
|
114
|
-
}
|
|
115
181
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
console.log(`ā
No missing keys found for ${locale}`)
|
|
119
|
-
return
|
|
120
|
-
}
|
|
182
|
+
const missingKeysList = Object.keys(allMissingKeys)
|
|
183
|
+
stats.missing = missingKeysList.length
|
|
121
184
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
// Check for existing translations of these keys in other namespaces (parallelized)
|
|
127
|
-
const keysToTranslate: Record<string, string> = {}
|
|
128
|
-
const existingTranslations: Record<string, string> = {}
|
|
129
|
-
|
|
130
|
-
const existingTranslationResults = await findExistingTranslations(
|
|
131
|
-
missingKeysList,
|
|
132
|
-
namespaces,
|
|
133
|
-
locale,
|
|
134
|
-
loadPath,
|
|
135
|
-
)
|
|
136
|
-
|
|
137
|
-
const reusedKeys: string[] = []
|
|
138
|
-
for (const key of missingKeysList) {
|
|
139
|
-
const existingValue = existingTranslationResults[key]
|
|
140
|
-
|
|
141
|
-
// Use explicit null check instead of truthy check to handle empty string values
|
|
142
|
-
if (existingValue !== null) {
|
|
143
|
-
existingTranslations[key] = existingValue
|
|
144
|
-
reusedKeys.push(key)
|
|
145
|
-
} else {
|
|
146
|
-
keysToTranslate[key] = allMissingKeys[key].value
|
|
185
|
+
if (missingKeysList.length === 0) {
|
|
186
|
+
bar.update(100, { status: chalk.green("ā Up to date") })
|
|
187
|
+
return
|
|
147
188
|
}
|
|
148
|
-
}
|
|
149
189
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
console.log(
|
|
153
|
-
`š Reusing ${reusedKeys.length} existing translations for ${locale}`,
|
|
154
|
-
)
|
|
155
|
-
}
|
|
190
|
+
// Stage 2: Check for existing translations (20-40%)
|
|
191
|
+
bar.update(25, { status: `Checking ${missingKeysList.length} keys...` })
|
|
156
192
|
|
|
157
|
-
|
|
193
|
+
const keysToTranslate: Record<string, string> = {}
|
|
194
|
+
const existingTranslations: Record<string, string> = {}
|
|
158
195
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
196
|
+
const existingTranslationResults = await findExistingTranslations(
|
|
197
|
+
missingKeysList,
|
|
198
|
+
namespaces,
|
|
199
|
+
locale,
|
|
200
|
+
loadPath,
|
|
201
|
+
{ silent: true },
|
|
163
202
|
)
|
|
164
203
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
locale,
|
|
178
|
-
undefined,
|
|
179
|
-
error instanceof Error ? error : undefined,
|
|
180
|
-
)
|
|
204
|
+
bar.update(40, { status: "Processing results..." })
|
|
205
|
+
|
|
206
|
+
for (const key of missingKeysList) {
|
|
207
|
+
const existingValue = existingTranslationResults[key]
|
|
208
|
+
|
|
209
|
+
// Use explicit null check instead of truthy check to handle empty string values
|
|
210
|
+
if (existingValue !== null) {
|
|
211
|
+
existingTranslations[key] = existingValue
|
|
212
|
+
stats.reused++
|
|
213
|
+
} else {
|
|
214
|
+
keysToTranslate[key] = allMissingKeys[key].value
|
|
215
|
+
}
|
|
181
216
|
}
|
|
182
|
-
}
|
|
183
217
|
|
|
184
|
-
|
|
185
|
-
const allTranslations = { ...existingTranslations, ...translatedValues }
|
|
218
|
+
let translatedValues: Record<string, string> = {}
|
|
186
219
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
const updatedKeys = { ...namespaceKeys[namespace] }
|
|
220
|
+
// Stage 3: Translate (40-80%)
|
|
221
|
+
if (Object.keys(keysToTranslate).length > 0) {
|
|
222
|
+
const keysCount = Object.keys(keysToTranslate).length
|
|
223
|
+
bar.update(45, { status: `Translating ${keysCount} keys...` })
|
|
192
224
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
225
|
+
try {
|
|
226
|
+
translatedValues = await translateKey({
|
|
227
|
+
inputLanguage: defaultLocale,
|
|
228
|
+
outputLanguage: locale,
|
|
229
|
+
context,
|
|
230
|
+
object: keysToTranslate,
|
|
231
|
+
openai,
|
|
232
|
+
model: config.model,
|
|
233
|
+
})
|
|
234
|
+
stats.translated = Object.keys(translatedValues).length
|
|
235
|
+
} catch (error) {
|
|
236
|
+
bar.update(100, { status: chalk.red("ā Translation failed") })
|
|
237
|
+
throw new TranslationError(
|
|
238
|
+
`Failed to translate keys for locale "${locale}"`,
|
|
239
|
+
locale,
|
|
240
|
+
undefined,
|
|
241
|
+
error instanceof Error ? error : undefined,
|
|
242
|
+
)
|
|
199
243
|
}
|
|
244
|
+
}
|
|
200
245
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
246
|
+
bar.update(80, { status: "Saving files..." })
|
|
247
|
+
|
|
248
|
+
// Stage 4: Save (80-100%)
|
|
249
|
+
// Combine existing translations with new translations
|
|
250
|
+
const allTranslations = { ...existingTranslations, ...translatedValues }
|
|
251
|
+
|
|
252
|
+
// Distribute translations to all relevant namespaces in parallel
|
|
253
|
+
await Promise.all(
|
|
254
|
+
namespaces.map(async (namespace) => {
|
|
255
|
+
let hasChanges = false
|
|
256
|
+
const updatedKeys = { ...namespaceKeys[namespace] }
|
|
257
|
+
|
|
258
|
+
for (const key of missingKeysList) {
|
|
259
|
+
if (allMissingKeys[key].namespaces.includes(namespace)) {
|
|
260
|
+
const translation = allTranslations[key] || ""
|
|
261
|
+
updatedKeys[key] = translation
|
|
262
|
+
hasChanges = true
|
|
263
|
+
}
|
|
219
264
|
}
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
265
|
+
|
|
266
|
+
if (hasChanges) {
|
|
267
|
+
try {
|
|
268
|
+
await writeLocalesFile(savePath, locale, namespace, updatedKeys)
|
|
269
|
+
} catch (error) {
|
|
270
|
+
throw new TranslationError(
|
|
271
|
+
`Failed to save translations for locale "${locale}" (namespace: ${namespace})`,
|
|
272
|
+
locale,
|
|
273
|
+
namespace,
|
|
274
|
+
error instanceof Error ? error : undefined,
|
|
275
|
+
)
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}),
|
|
279
|
+
)
|
|
280
|
+
|
|
281
|
+
// Complete!
|
|
282
|
+
const statusMsg = stats.translated > 0
|
|
283
|
+
? chalk.green(`ā +${stats.translated} translated`)
|
|
284
|
+
: chalk.green(`ā ${stats.reused} reused`)
|
|
285
|
+
bar.update(100, { status: statusMsg })
|
|
286
|
+
|
|
287
|
+
} catch (error) {
|
|
288
|
+
if (error instanceof TranslationError) {
|
|
289
|
+
throw error
|
|
290
|
+
}
|
|
291
|
+
bar.update(100, { status: chalk.red("ā Error") })
|
|
292
|
+
throw error
|
|
293
|
+
}
|
|
223
294
|
}),
|
|
224
295
|
)
|
|
296
|
+
|
|
297
|
+
// Stop the progress bars
|
|
298
|
+
multibar.stop()
|
|
299
|
+
|
|
300
|
+
// Print summary
|
|
301
|
+
console.log("")
|
|
302
|
+
console.log(chalk.green("⨠Sync complete!\n"))
|
|
303
|
+
|
|
304
|
+
// Show detailed stats
|
|
305
|
+
let totalMissing = 0
|
|
306
|
+
let totalReused = 0
|
|
307
|
+
let totalTranslated = 0
|
|
308
|
+
|
|
309
|
+
for (const locale of localesToProcess) {
|
|
310
|
+
const stats = localeStats[locale]
|
|
311
|
+
totalMissing += stats.missing
|
|
312
|
+
totalReused += stats.reused
|
|
313
|
+
totalTranslated += stats.translated
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
if (totalMissing > 0) {
|
|
317
|
+
console.log(chalk.dim(" Summary:"))
|
|
318
|
+
console.log(chalk.dim(` ⢠${chalk.white(totalTranslated)} keys translated`))
|
|
319
|
+
console.log(chalk.dim(` ⢠${chalk.white(totalReused)} keys reused from other namespaces`))
|
|
320
|
+
console.log("")
|
|
321
|
+
}
|
|
322
|
+
|
|
225
323
|
} catch (error) {
|
|
324
|
+
multibar.stop()
|
|
226
325
|
if (error instanceof TranslationError) {
|
|
227
326
|
throw error
|
|
228
327
|
}
|
package/src/lib/utils.ts
CHANGED
|
@@ -138,7 +138,10 @@ export const loadLocalesFile = async (
|
|
|
138
138
|
| ((locale: string, namespace: string) => Promise<Record<string, string>>),
|
|
139
139
|
locale: string,
|
|
140
140
|
namespace: string,
|
|
141
|
+
options?: { silent?: boolean },
|
|
141
142
|
) => {
|
|
143
|
+
const silent = options?.silent ?? false
|
|
144
|
+
|
|
142
145
|
if (typeof loadPath === "string") {
|
|
143
146
|
const resolvedPath = loadPath
|
|
144
147
|
.replace("{{lng}}", locale)
|
|
@@ -146,7 +149,9 @@ export const loadLocalesFile = async (
|
|
|
146
149
|
|
|
147
150
|
// Check if file exists, return empty object if it doesn't
|
|
148
151
|
if (!fs.existsSync(resolvedPath)) {
|
|
149
|
-
|
|
152
|
+
if (!silent) {
|
|
153
|
+
console.log(`š Creating new namespace file: ${resolvedPath}`)
|
|
154
|
+
}
|
|
150
155
|
return {}
|
|
151
156
|
}
|
|
152
157
|
|
|
@@ -474,8 +479,10 @@ export const getMissingKeys = async ({
|
|
|
474
479
|
`š Checking ${keysForNamespace.size} keys for namespace ${namespace}`,
|
|
475
480
|
)
|
|
476
481
|
|
|
482
|
+
const missingInNamespace: string[] = []
|
|
477
483
|
for (const key of keysForNamespace) {
|
|
478
484
|
if (!existingKeys[key]) {
|
|
485
|
+
missingInNamespace.push(key)
|
|
479
486
|
if (uniqueMissingKeys.has(key)) {
|
|
480
487
|
// Add this namespace to the existing entry
|
|
481
488
|
const existing = uniqueMissingKeys.get(key)
|
|
@@ -496,6 +503,11 @@ export const getMissingKeys = async ({
|
|
|
496
503
|
}
|
|
497
504
|
}
|
|
498
505
|
}
|
|
506
|
+
|
|
507
|
+
// Log missing keys for this namespace if any
|
|
508
|
+
if (missingInNamespace.length > 0) {
|
|
509
|
+
console.log(` ā Missing in ${namespace}: ${missingInNamespace.slice(0, 10).join(", ")}${missingInNamespace.length > 10 ? `... and ${missingInNamespace.length - 10} more` : ""}`)
|
|
510
|
+
}
|
|
499
511
|
}
|
|
500
512
|
|
|
501
513
|
// Convert to the expected format
|
|
@@ -511,6 +523,17 @@ export const getMissingKeys = async ({
|
|
|
511
523
|
})
|
|
512
524
|
}
|
|
513
525
|
|
|
526
|
+
// Final summary of all missing keys
|
|
527
|
+
if (newKeys.length > 0) {
|
|
528
|
+
console.log(`\nš Summary: ${newKeys.length} unique missing key(s):`)
|
|
529
|
+
for (const { key, namespaces: ns } of newKeys.slice(0, 20)) {
|
|
530
|
+
console.log(` - "${key}" in [${ns.join(", ")}]`)
|
|
531
|
+
}
|
|
532
|
+
if (newKeys.length > 20) {
|
|
533
|
+
console.log(` ... and ${newKeys.length - 20} more`)
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
|
|
514
537
|
return newKeys
|
|
515
538
|
}
|
|
516
539
|
|
|
@@ -550,12 +573,16 @@ export const findExistingTranslations = async (
|
|
|
550
573
|
loadPath:
|
|
551
574
|
| string
|
|
552
575
|
| ((locale: string, namespace: string) => Promise<Record<string, string>>),
|
|
576
|
+
options?: { silent?: boolean },
|
|
553
577
|
): Promise<Record<string, string | null>> => {
|
|
578
|
+
const silent = options?.silent ?? false
|
|
579
|
+
const log = silent ? () => {} : console.log
|
|
580
|
+
|
|
554
581
|
// Load all namespace files in parallel first
|
|
555
582
|
const namespaceKeys: Record<string, Record<string, string>> = {}
|
|
556
583
|
const loadPromises = namespaces.map(async (namespace) => {
|
|
557
584
|
try {
|
|
558
|
-
const existingKeys = await loadLocalesFile(loadPath, locale, namespace)
|
|
585
|
+
const existingKeys = await loadLocalesFile(loadPath, locale, namespace, { silent })
|
|
559
586
|
namespaceKeys[namespace] = existingKeys
|
|
560
587
|
} catch (error) {
|
|
561
588
|
namespaceKeys[namespace] = {}
|
|
@@ -565,21 +592,21 @@ export const findExistingTranslations = async (
|
|
|
565
592
|
await Promise.all(loadPromises)
|
|
566
593
|
|
|
567
594
|
// Log how many keys were found in each namespace for the default locale
|
|
568
|
-
|
|
595
|
+
log(`\nš Searching for existing translations in ${locale}:`)
|
|
569
596
|
for (const namespace of namespaces) {
|
|
570
597
|
const nsKeys = Object.keys(namespaceKeys[namespace] || {})
|
|
571
|
-
|
|
598
|
+
log(` š ${namespace}.json: ${nsKeys.length} keys available`)
|
|
572
599
|
// Show sample keys from the namespace (first 3)
|
|
573
600
|
if (nsKeys.length > 0) {
|
|
574
601
|
const sampleKeys = nsKeys.slice(0, 3)
|
|
575
|
-
|
|
602
|
+
log(` Sample keys: ${sampleKeys.join(", ")}${nsKeys.length > 3 ? "..." : ""}`)
|
|
576
603
|
}
|
|
577
604
|
}
|
|
578
605
|
|
|
579
606
|
// Show sample of keys we're searching for
|
|
580
607
|
if (keys.length > 0) {
|
|
581
608
|
const sampleSearchKeys = keys.slice(0, 3)
|
|
582
|
-
|
|
609
|
+
log(`\n š Looking for keys like: ${sampleSearchKeys.join(", ")}${keys.length > 3 ? "..." : ""}`)
|
|
583
610
|
}
|
|
584
611
|
|
|
585
612
|
// Now find translations for all keys
|
|
@@ -612,14 +639,14 @@ export const findExistingTranslations = async (
|
|
|
612
639
|
0,
|
|
613
640
|
)
|
|
614
641
|
const notFound = keys.length - totalFound
|
|
615
|
-
|
|
642
|
+
log(`\nš Search results for ${keys.length} missing keys:`)
|
|
616
643
|
for (const [namespace, count] of Object.entries(foundInNamespace)) {
|
|
617
|
-
|
|
644
|
+
log(` ā
Found ${count} keys in ${namespace}.json`)
|
|
618
645
|
}
|
|
619
646
|
if (notFound > 0) {
|
|
620
|
-
|
|
647
|
+
log(` ā ${notFound} keys not found in any namespace`)
|
|
621
648
|
}
|
|
622
|
-
|
|
649
|
+
log("")
|
|
623
650
|
|
|
624
651
|
return results
|
|
625
652
|
}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import type { Configuration } from "../lib/types";
|
|
2
|
-
export interface PruneOptions {
|
|
3
|
-
sourceNamespace: string;
|
|
4
|
-
newNamespace: string;
|
|
5
|
-
globPatterns: string[];
|
|
6
|
-
}
|
|
7
|
-
export interface PruneResult {
|
|
8
|
-
locale: string;
|
|
9
|
-
keyCount: number;
|
|
10
|
-
success: boolean;
|
|
11
|
-
error?: string;
|
|
12
|
-
}
|
|
13
|
-
export interface PruneResponse {
|
|
14
|
-
success: boolean;
|
|
15
|
-
message: string;
|
|
16
|
-
keysCount: number;
|
|
17
|
-
results?: PruneResult[];
|
|
18
|
-
}
|
|
19
|
-
export declare const createPrunedNamespaceAutomated: (config: Configuration, options: PruneOptions) => Promise<PruneResponse>;
|
|
20
|
-
//# sourceMappingURL=create-pruned-namespace-automated.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"create-pruned-namespace-automated.d.ts","sourceRoot":"","sources":["../../src/commands/create-pruned-namespace-automated.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AAQjD,MAAM,WAAW,YAAY;IAC3B,eAAe,EAAE,MAAM,CAAA;IACvB,YAAY,EAAE,MAAM,CAAA;IACpB,YAAY,EAAE,MAAM,EAAE,CAAA;CACvB;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,OAAO,CAAA;IAChB,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,OAAO,CAAA;IAChB,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,CAAC,EAAE,WAAW,EAAE,CAAA;CACxB;AAED,eAAO,MAAM,8BAA8B,GACzC,QAAQ,aAAa,EACrB,SAAS,YAAY,KACpB,OAAO,CAAC,aAAa,CAkIvB,CAAA"}
|