@scoutello/i18n-magic 0.60.0 → 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 +12 -9
- 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/lib/utils.ts +102 -74
- package/src/mcp-server.ts +61 -131
package/src/lib/utils.ts
CHANGED
|
@@ -872,34 +872,78 @@ export const addTranslationKeys = async ({
|
|
|
872
872
|
log(`⏱️ Codebase scan completed in ${scanTime.toFixed(2)}ms`)
|
|
873
873
|
|
|
874
874
|
// Step 2: Determine namespaces for each key
|
|
875
|
-
|
|
875
|
+
// Resolution order:
|
|
876
|
+
// 1) Explicit namespace prefix (e.g. "dashboard:welcome")
|
|
877
|
+
// 2) Code usage scan matches
|
|
878
|
+
// 3) Existing key in default locale namespace files
|
|
879
|
+
// 4) Key prefix matching a namespace (e.g. "dashboard.title")
|
|
880
|
+
// 5) Default namespace fallback
|
|
881
|
+
const defaultLocaleKeysByNamespace = new Map<string, Record<string, string>>()
|
|
882
|
+
await Promise.all(
|
|
883
|
+
namespaces.map(async (namespace) => {
|
|
884
|
+
try {
|
|
885
|
+
const nsKeys = await loadLocalesFile(loadPath, defaultLocale, namespace, {
|
|
886
|
+
silent: true,
|
|
887
|
+
})
|
|
888
|
+
defaultLocaleKeysByNamespace.set(namespace, nsKeys)
|
|
889
|
+
} catch {
|
|
890
|
+
defaultLocaleKeysByNamespace.set(namespace, {})
|
|
891
|
+
}
|
|
892
|
+
}),
|
|
893
|
+
)
|
|
876
894
|
|
|
877
|
-
|
|
878
|
-
const
|
|
895
|
+
const preparedKeys = keys.map(({ key, value, language = "en" }) => {
|
|
896
|
+
const splitKey = key.split(":")
|
|
897
|
+
const hasExplicitNamespace =
|
|
898
|
+
splitKey.length > 1 && namespaces.includes(splitKey[0])
|
|
899
|
+
const normalizedKey = hasExplicitNamespace ? splitKey.slice(1).join(":") : key
|
|
900
|
+
const explicitNamespace = hasExplicitNamespace ? splitKey[0] : null
|
|
901
|
+
const foundNamespaces = new Set<string>()
|
|
879
902
|
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
903
|
+
if (explicitNamespace) {
|
|
904
|
+
foundNamespaces.add(explicitNamespace)
|
|
905
|
+
} else {
|
|
906
|
+
for (const entry of keysWithNamespaces) {
|
|
907
|
+
for (const namespace of entry.namespaces) {
|
|
908
|
+
const pureKey = getPureKey(
|
|
909
|
+
entry.key,
|
|
910
|
+
namespace,
|
|
911
|
+
namespace === defaultNamespace,
|
|
912
|
+
)
|
|
913
|
+
if (entry.key === normalizedKey || pureKey === normalizedKey) {
|
|
914
|
+
foundNamespaces.add(namespace)
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
}
|
|
885
918
|
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
919
|
+
if (foundNamespaces.size === 0) {
|
|
920
|
+
for (const namespace of namespaces) {
|
|
921
|
+
const namespaceKeys = defaultLocaleKeysByNamespace.get(namespace) || {}
|
|
922
|
+
if (Object.hasOwn(namespaceKeys, normalizedKey)) {
|
|
923
|
+
foundNamespaces.add(namespace)
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
if (foundNamespaces.size === 0) {
|
|
929
|
+
const keyPrefix = normalizedKey.split(".")[0]
|
|
930
|
+
if (namespaces.includes(keyPrefix)) {
|
|
931
|
+
foundNamespaces.add(keyPrefix)
|
|
932
|
+
}
|
|
891
933
|
}
|
|
892
934
|
}
|
|
893
935
|
|
|
894
|
-
if (foundNamespaces.size
|
|
895
|
-
|
|
896
|
-
} else {
|
|
897
|
-
// If the key is not found in the codebase, use the default namespace
|
|
898
|
-
affectedNamespaces.push(defaultNamespace)
|
|
936
|
+
if (foundNamespaces.size === 0) {
|
|
937
|
+
foundNamespaces.add(defaultNamespace)
|
|
899
938
|
}
|
|
900
939
|
|
|
901
|
-
|
|
902
|
-
|
|
940
|
+
return {
|
|
941
|
+
key: normalizedKey,
|
|
942
|
+
value,
|
|
943
|
+
language,
|
|
944
|
+
namespaces: foundNamespaces,
|
|
945
|
+
}
|
|
946
|
+
})
|
|
903
947
|
|
|
904
948
|
// Step 3: Group keys by namespace and locale
|
|
905
949
|
const namespaceLocaleToKeys = new Map<
|
|
@@ -907,21 +951,24 @@ export const addTranslationKeys = async ({
|
|
|
907
951
|
Array<{ key: string; value: string; language: string }>
|
|
908
952
|
>()
|
|
909
953
|
|
|
910
|
-
for (const
|
|
911
|
-
const
|
|
912
|
-
|
|
913
|
-
const mapKey = `${namespace}:${language}`
|
|
954
|
+
for (const keyEntry of preparedKeys) {
|
|
955
|
+
for (const namespace of keyEntry.namespaces) {
|
|
956
|
+
const mapKey = `${namespace}:${keyEntry.language}`
|
|
914
957
|
if (!namespaceLocaleToKeys.has(mapKey)) {
|
|
915
958
|
namespaceLocaleToKeys.set(mapKey, [])
|
|
916
959
|
}
|
|
917
|
-
namespaceLocaleToKeys.get(mapKey)!.push({
|
|
960
|
+
namespaceLocaleToKeys.get(mapKey)!.push({
|
|
961
|
+
key: keyEntry.key,
|
|
962
|
+
value: keyEntry.value,
|
|
963
|
+
language: keyEntry.language,
|
|
964
|
+
})
|
|
918
965
|
}
|
|
919
966
|
}
|
|
920
967
|
|
|
921
968
|
// Step 4: Collect all unique namespaces that will be affected
|
|
922
969
|
const affectedNamespaces = new Set<string>()
|
|
923
|
-
for (const
|
|
924
|
-
for (const ns of namespaces) {
|
|
970
|
+
for (const keyEntry of preparedKeys) {
|
|
971
|
+
for (const ns of keyEntry.namespaces) {
|
|
925
972
|
affectedNamespaces.add(ns)
|
|
926
973
|
}
|
|
927
974
|
}
|
|
@@ -930,32 +977,31 @@ export const addTranslationKeys = async ({
|
|
|
930
977
|
// This ensures we preserve existing keys in all locales
|
|
931
978
|
const fileIOStartTime = performance.now()
|
|
932
979
|
const localeFiles = new Map<string, Record<string, string>>()
|
|
980
|
+
const originalKeyCounts = new Map<string, number>()
|
|
933
981
|
const loadErrors: Array<{ fileKey: string; error: string }> = []
|
|
934
982
|
|
|
935
|
-
// Load files for all locales × all affected namespaces
|
|
983
|
+
// Load files for all locales × all affected namespaces in parallel (read-only)
|
|
984
|
+
const localeLoadPromises: Promise<void>[] = []
|
|
936
985
|
for (const namespace of affectedNamespaces) {
|
|
937
986
|
for (const locale of config.locales) {
|
|
938
987
|
const fileKey = `${locale}:${namespace}`
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
loadErrors.push({ fileKey, error: errorMsg })
|
|
954
|
-
log(`⚠️ Failed to load ${fileKey}: ${errorMsg}`)
|
|
955
|
-
}
|
|
956
|
-
}
|
|
988
|
+
localeLoadPromises.push(
|
|
989
|
+
loadLocalesFile(loadPath, locale, namespace, { silent: true })
|
|
990
|
+
.then((existingKeys) => {
|
|
991
|
+
localeFiles.set(fileKey, existingKeys)
|
|
992
|
+
originalKeyCounts.set(fileKey, Object.keys(existingKeys).length)
|
|
993
|
+
})
|
|
994
|
+
.catch((error) => {
|
|
995
|
+
// Don't silently ignore - track the error and DO NOT set empty object
|
|
996
|
+
const errorMsg =
|
|
997
|
+
error instanceof Error ? error.message : String(error)
|
|
998
|
+
loadErrors.push({ fileKey, error: errorMsg })
|
|
999
|
+
log(`⚠️ Failed to load ${fileKey}: ${errorMsg}`)
|
|
1000
|
+
}),
|
|
1001
|
+
)
|
|
957
1002
|
}
|
|
958
1003
|
}
|
|
1004
|
+
await Promise.all(localeLoadPromises)
|
|
959
1005
|
|
|
960
1006
|
// If any files failed to load, abort to prevent data loss
|
|
961
1007
|
if (loadErrors.length > 0) {
|
|
@@ -992,14 +1038,14 @@ export const addTranslationKeys = async ({
|
|
|
992
1038
|
Array<{ key: string; value: string; namespaces: Set<string> }>
|
|
993
1039
|
>()
|
|
994
1040
|
|
|
995
|
-
for (const
|
|
996
|
-
if (!keysByLanguage.has(language)) {
|
|
997
|
-
keysByLanguage.set(language, [])
|
|
1041
|
+
for (const keyEntry of preparedKeys) {
|
|
1042
|
+
if (!keysByLanguage.has(keyEntry.language)) {
|
|
1043
|
+
keysByLanguage.set(keyEntry.language, [])
|
|
998
1044
|
}
|
|
999
|
-
keysByLanguage.get(language)!.push({
|
|
1000
|
-
key,
|
|
1001
|
-
value,
|
|
1002
|
-
namespaces:
|
|
1045
|
+
keysByLanguage.get(keyEntry.language)!.push({
|
|
1046
|
+
key: keyEntry.key,
|
|
1047
|
+
value: keyEntry.value,
|
|
1048
|
+
namespaces: keyEntry.namespaces,
|
|
1003
1049
|
})
|
|
1004
1050
|
}
|
|
1005
1051
|
|
|
@@ -1078,22 +1124,6 @@ export const addTranslationKeys = async ({
|
|
|
1078
1124
|
// Step 8: Validate and write all files
|
|
1079
1125
|
// Safety check: ensure we're not accidentally removing keys (only adding)
|
|
1080
1126
|
const writeStartTime = performance.now()
|
|
1081
|
-
const originalKeyCounts = new Map<string, number>()
|
|
1082
|
-
|
|
1083
|
-
// Store original key counts for validation
|
|
1084
|
-
for (const namespace of affectedNamespaces) {
|
|
1085
|
-
for (const locale of config.locales) {
|
|
1086
|
-
const fileKey = `${locale}:${namespace}`
|
|
1087
|
-
try {
|
|
1088
|
-
const original = await loadLocalesFile(loadPath, locale, namespace, {
|
|
1089
|
-
silent: true,
|
|
1090
|
-
})
|
|
1091
|
-
originalKeyCounts.set(fileKey, Object.keys(original).length)
|
|
1092
|
-
} catch {
|
|
1093
|
-
originalKeyCounts.set(fileKey, 0)
|
|
1094
|
-
}
|
|
1095
|
-
}
|
|
1096
|
-
}
|
|
1097
1127
|
|
|
1098
1128
|
// Validate: new file should have at least as many keys as original
|
|
1099
1129
|
for (const [fileKey, newKeys] of localeFiles) {
|
|
@@ -1123,10 +1153,8 @@ export const addTranslationKeys = async ({
|
|
|
1123
1153
|
)
|
|
1124
1154
|
|
|
1125
1155
|
// Build results
|
|
1126
|
-
const results =
|
|
1127
|
-
const
|
|
1128
|
-
keyToNamespaces.get(key) || new Set([defaultNamespace]),
|
|
1129
|
-
)
|
|
1156
|
+
const results = preparedKeys.map(({ key, value, language, namespaces }) => {
|
|
1157
|
+
const resolvedNamespaces = Array.from(namespaces)
|
|
1130
1158
|
const savedLocales = new Set<string>([language])
|
|
1131
1159
|
|
|
1132
1160
|
if (openai) {
|
|
@@ -1143,7 +1171,7 @@ export const addTranslationKeys = async ({
|
|
|
1143
1171
|
return {
|
|
1144
1172
|
key,
|
|
1145
1173
|
value,
|
|
1146
|
-
namespace:
|
|
1174
|
+
namespace: resolvedNamespaces.join(", "),
|
|
1147
1175
|
locale: Array.from(savedLocales).sort().join(", "),
|
|
1148
1176
|
}
|
|
1149
1177
|
})
|
package/src/mcp-server.ts
CHANGED
|
@@ -120,7 +120,9 @@ function resolveProjectRoot(): string {
|
|
|
120
120
|
const AddTranslationKeySchema = z.object({
|
|
121
121
|
key: z
|
|
122
122
|
.string()
|
|
123
|
-
.describe(
|
|
123
|
+
.describe(
|
|
124
|
+
'The translation key to add (e.g., "welcomeMessage"). Namespace is auto-detected; optionally use "namespace:key" to force a namespace.',
|
|
125
|
+
),
|
|
124
126
|
value: z.string().describe("The text value for this translation key"),
|
|
125
127
|
language: z
|
|
126
128
|
.string()
|
|
@@ -137,7 +139,9 @@ const AddTranslationKeysSchema = z.object({
|
|
|
137
139
|
z.object({
|
|
138
140
|
key: z
|
|
139
141
|
.string()
|
|
140
|
-
.describe(
|
|
142
|
+
.describe(
|
|
143
|
+
'The translation key to add (e.g., "welcomeMessage"). Namespace is auto-detected; optionally use "namespace:key" to force a namespace.',
|
|
144
|
+
),
|
|
141
145
|
value: z.string().describe("The text value for this translation key"),
|
|
142
146
|
language: z
|
|
143
147
|
.string()
|
|
@@ -153,26 +157,13 @@ const AddTranslationKeysSchema = z.object({
|
|
|
153
157
|
})
|
|
154
158
|
|
|
155
159
|
// Zod schema for the list_untranslated_keys tool parameters
|
|
156
|
-
const ListUntranslatedKeysSchema = z.object({
|
|
157
|
-
namespace: z
|
|
158
|
-
.string()
|
|
159
|
-
.optional()
|
|
160
|
-
.describe(
|
|
161
|
-
"Optional namespace to check. If not provided, checks all namespaces.",
|
|
162
|
-
),
|
|
163
|
-
})
|
|
160
|
+
const ListUntranslatedKeysSchema = z.object({})
|
|
164
161
|
|
|
165
162
|
// Zod schema for the get_translation_key tool parameters
|
|
166
163
|
const GetTranslationKeySchema = z.object({
|
|
167
164
|
key: z
|
|
168
165
|
.string()
|
|
169
166
|
.describe('The translation key to retrieve (e.g., "welcomeMessage")'),
|
|
170
|
-
namespace: z
|
|
171
|
-
.string()
|
|
172
|
-
.optional()
|
|
173
|
-
.describe(
|
|
174
|
-
"Optional namespace to search in. If not provided, searches in default namespace first, then all namespaces.",
|
|
175
|
-
),
|
|
176
167
|
})
|
|
177
168
|
|
|
178
169
|
// Zod schema for the update_translation_key tool parameters
|
|
@@ -187,12 +178,6 @@ const UpdateTranslationKeySchema = z.object({
|
|
|
187
178
|
.describe(
|
|
188
179
|
'The language code of the provided value (e.g., "en", "de", "fr"). Defaults to "en" (English) if not specified.',
|
|
189
180
|
),
|
|
190
|
-
namespace: z
|
|
191
|
-
.string()
|
|
192
|
-
.optional()
|
|
193
|
-
.describe(
|
|
194
|
-
"Optional namespace to update. If not provided, updates the key in all namespaces where it exists.",
|
|
195
|
-
),
|
|
196
181
|
})
|
|
197
182
|
|
|
198
183
|
// Zod schema for the search_translations tool parameters
|
|
@@ -280,14 +265,14 @@ class I18nMagicServer {
|
|
|
280
265
|
{
|
|
281
266
|
name: "add_translation_key",
|
|
282
267
|
description:
|
|
283
|
-
"Add a new translation key with a text value. You can optionally specify the language of the value you're providing (defaults to English). For adding multiple keys at once, use add_translation_keys instead for better performance. NOTE: This tool can only ADD keys, it will NEVER remove any existing keys.",
|
|
268
|
+
"Add a new translation key with a text value. Namespace is resolved automatically from code usage and existing locale files. Optionally, force a namespace by prefixing the key as namespace:key. You can optionally specify the language of the value you're providing (defaults to English). For adding multiple keys at once, use add_translation_keys instead for better performance. NOTE: This tool can only ADD keys, it will NEVER remove any existing keys.",
|
|
284
269
|
inputSchema: {
|
|
285
270
|
type: "object",
|
|
286
271
|
properties: {
|
|
287
272
|
key: {
|
|
288
273
|
type: "string",
|
|
289
274
|
description:
|
|
290
|
-
'The translation key to add (e.g., "welcomeMessage")',
|
|
275
|
+
'The translation key to add (e.g., "welcomeMessage"). Namespace is auto-detected; optionally use "namespace:key" to force a namespace.',
|
|
291
276
|
},
|
|
292
277
|
value: {
|
|
293
278
|
type: "string",
|
|
@@ -305,7 +290,7 @@ class I18nMagicServer {
|
|
|
305
290
|
{
|
|
306
291
|
name: "add_translation_keys",
|
|
307
292
|
description:
|
|
308
|
-
"Add multiple translation keys in batch. This is optimized for performance - when adding 2 or more keys, prefer this over multiple add_translation_key calls. It performs a single codebase scan, batches file I/O operations, and batches translations for much better performance. NOTE: This tool can only ADD keys, it will NEVER remove any existing keys.",
|
|
293
|
+
"Add multiple translation keys in batch. Namespaces are resolved automatically per key from code usage and existing locale files; optionally force per-key namespace with namespace:key. This is optimized for performance - when adding 2 or more keys, prefer this over multiple add_translation_key calls. It performs a single codebase scan, batches file I/O operations, and batches translations for much better performance. NOTE: This tool can only ADD keys, it will NEVER remove any existing keys.",
|
|
309
294
|
inputSchema: {
|
|
310
295
|
type: "object",
|
|
311
296
|
properties: {
|
|
@@ -318,7 +303,7 @@ class I18nMagicServer {
|
|
|
318
303
|
key: {
|
|
319
304
|
type: "string",
|
|
320
305
|
description:
|
|
321
|
-
'The translation key to add (e.g., "welcomeMessage")',
|
|
306
|
+
'The translation key to add (e.g., "welcomeMessage"). Namespace is auto-detected; optionally use "namespace:key" to force a namespace.',
|
|
322
307
|
},
|
|
323
308
|
value: {
|
|
324
309
|
type: "string",
|
|
@@ -340,16 +325,10 @@ class I18nMagicServer {
|
|
|
340
325
|
{
|
|
341
326
|
name: "list_untranslated_keys",
|
|
342
327
|
description:
|
|
343
|
-
"List all translation keys that are used in the codebase but are not yet defined in the locale files. This helps identify missing translations that need to be added. The tool scans the codebase for translation keys and compares them against existing locale files. NOTE: This is a read-only tool that does not modify any files.",
|
|
328
|
+
"List all translation keys that are used in the codebase but are not yet defined in the locale files. This helps identify missing translations that need to be added. The tool scans the codebase for translation keys and compares them against existing locale files across all namespaces automatically. NOTE: This is a read-only tool that does not modify any files.",
|
|
344
329
|
inputSchema: {
|
|
345
330
|
type: "object",
|
|
346
|
-
properties: {
|
|
347
|
-
namespace: {
|
|
348
|
-
type: "string",
|
|
349
|
-
description:
|
|
350
|
-
"Optional namespace to check. If not provided, checks all namespaces.",
|
|
351
|
-
},
|
|
352
|
-
},
|
|
331
|
+
properties: {},
|
|
353
332
|
required: [],
|
|
354
333
|
},
|
|
355
334
|
},
|
|
@@ -365,11 +344,6 @@ class I18nMagicServer {
|
|
|
365
344
|
description:
|
|
366
345
|
'The translation key to retrieve (e.g., "welcomeMessage")',
|
|
367
346
|
},
|
|
368
|
-
namespace: {
|
|
369
|
-
type: "string",
|
|
370
|
-
description:
|
|
371
|
-
"Optional namespace to search in. If not provided, searches in default namespace first, then all namespaces.",
|
|
372
|
-
},
|
|
373
347
|
},
|
|
374
348
|
required: ["key"],
|
|
375
349
|
},
|
|
@@ -395,11 +369,6 @@ class I18nMagicServer {
|
|
|
395
369
|
description:
|
|
396
370
|
'The language code of the provided value (e.g., "en" for English, "de" for German, "fr" for French). Defaults to "en" if not specified.',
|
|
397
371
|
},
|
|
398
|
-
namespace: {
|
|
399
|
-
type: "string",
|
|
400
|
-
description:
|
|
401
|
-
"Optional namespace to update. If not provided, updates the key in all namespaces where it exists.",
|
|
402
|
-
},
|
|
403
372
|
},
|
|
404
373
|
required: ["key", "value"],
|
|
405
374
|
},
|
|
@@ -669,9 +638,7 @@ class I18nMagicServer {
|
|
|
669
638
|
if (request.params.name === "list_untranslated_keys") {
|
|
670
639
|
try {
|
|
671
640
|
// Validate parameters
|
|
672
|
-
|
|
673
|
-
request.params.arguments,
|
|
674
|
-
)
|
|
641
|
+
ListUntranslatedKeysSchema.parse(request.params.arguments)
|
|
675
642
|
|
|
676
643
|
// Ensure config is loaded
|
|
677
644
|
const config = await this.ensureConfig()
|
|
@@ -689,17 +656,9 @@ class I18nMagicServer {
|
|
|
689
656
|
console.log = originalConsoleLog
|
|
690
657
|
}
|
|
691
658
|
|
|
692
|
-
// Filter by namespace if specified
|
|
693
|
-
let filteredKeys = missingKeys
|
|
694
|
-
if (params.namespace) {
|
|
695
|
-
filteredKeys = missingKeys.filter((item) =>
|
|
696
|
-
item.namespaces.includes(params.namespace!),
|
|
697
|
-
)
|
|
698
|
-
}
|
|
699
|
-
|
|
700
659
|
// Extract just the keys (sorted and unique)
|
|
701
660
|
const uniqueKeys = Array.from(
|
|
702
|
-
new Set(
|
|
661
|
+
new Set(missingKeys.map((item) => item.key)),
|
|
703
662
|
).sort()
|
|
704
663
|
|
|
705
664
|
return {
|
|
@@ -710,12 +669,12 @@ class I18nMagicServer {
|
|
|
710
669
|
{
|
|
711
670
|
success: true,
|
|
712
671
|
message:
|
|
713
|
-
|
|
672
|
+
missingKeys.length === 0
|
|
714
673
|
? "No missing translation keys found! All keys used in the codebase are defined."
|
|
715
|
-
: `Found ${
|
|
674
|
+
: `Found ${missingKeys.length} missing translation key${missingKeys.length === 1 ? "" : "s"}`,
|
|
716
675
|
missingKeys: uniqueKeys,
|
|
717
676
|
nextSteps:
|
|
718
|
-
|
|
677
|
+
missingKeys.length > 0
|
|
719
678
|
? [
|
|
720
679
|
"Use add_translation_key to add these keys with English values",
|
|
721
680
|
"Or run 'i18n-magic scan' to add them interactively",
|
|
@@ -786,52 +745,39 @@ class I18nMagicServer {
|
|
|
786
745
|
let foundNamespace: string | null = null
|
|
787
746
|
|
|
788
747
|
try {
|
|
789
|
-
//
|
|
790
|
-
|
|
748
|
+
// Try default namespace first
|
|
749
|
+
try {
|
|
791
750
|
const keys = await loadLocalesFile(
|
|
792
751
|
config.loadPath,
|
|
793
752
|
"en",
|
|
794
|
-
|
|
753
|
+
config.defaultNamespace,
|
|
795
754
|
)
|
|
796
|
-
if (keys
|
|
755
|
+
if (Object.hasOwn(keys, params.key)) {
|
|
797
756
|
foundValue = keys[params.key]
|
|
798
|
-
foundNamespace =
|
|
799
|
-
}
|
|
800
|
-
} else {
|
|
801
|
-
// Try default namespace first
|
|
802
|
-
try {
|
|
803
|
-
const keys = await loadLocalesFile(
|
|
804
|
-
config.loadPath,
|
|
805
|
-
"en",
|
|
806
|
-
config.defaultNamespace,
|
|
807
|
-
)
|
|
808
|
-
if (keys[params.key]) {
|
|
809
|
-
foundValue = keys[params.key]
|
|
810
|
-
foundNamespace = config.defaultNamespace
|
|
811
|
-
}
|
|
812
|
-
} catch (error) {
|
|
813
|
-
// Default namespace file doesn't exist or has issues, continue to search other namespaces
|
|
757
|
+
foundNamespace = config.defaultNamespace
|
|
814
758
|
}
|
|
759
|
+
} catch (error) {
|
|
760
|
+
// Default namespace file doesn't exist or has issues, continue to search other namespaces
|
|
761
|
+
}
|
|
815
762
|
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
763
|
+
// If not found in default namespace, search all other namespaces
|
|
764
|
+
if (foundValue === null) {
|
|
765
|
+
for (const namespace of config.namespaces) {
|
|
766
|
+
if (namespace === config.defaultNamespace) continue // Already checked
|
|
820
767
|
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
}
|
|
832
|
-
} catch (error) {
|
|
833
|
-
// Namespace file doesn't exist or has issues, continue
|
|
768
|
+
try {
|
|
769
|
+
const keys = await loadLocalesFile(
|
|
770
|
+
config.loadPath,
|
|
771
|
+
"en",
|
|
772
|
+
namespace,
|
|
773
|
+
)
|
|
774
|
+
if (Object.hasOwn(keys, params.key)) {
|
|
775
|
+
foundValue = keys[params.key]
|
|
776
|
+
foundNamespace = namespace
|
|
777
|
+
break
|
|
834
778
|
}
|
|
779
|
+
} catch (error) {
|
|
780
|
+
// Namespace file doesn't exist or has issues, continue
|
|
835
781
|
}
|
|
836
782
|
}
|
|
837
783
|
}
|
|
@@ -840,7 +786,7 @@ class I18nMagicServer {
|
|
|
840
786
|
console.log = originalConsoleLog
|
|
841
787
|
}
|
|
842
788
|
|
|
843
|
-
if (foundValue) {
|
|
789
|
+
if (foundValue !== null) {
|
|
844
790
|
return {
|
|
845
791
|
content: [
|
|
846
792
|
{
|
|
@@ -867,9 +813,9 @@ class I18nMagicServer {
|
|
|
867
813
|
text: JSON.stringify(
|
|
868
814
|
{
|
|
869
815
|
success: false,
|
|
870
|
-
error: `Translation key "${params.key}" not found in English locale
|
|
816
|
+
error: `Translation key "${params.key}" not found in English locale`,
|
|
871
817
|
key: params.key,
|
|
872
|
-
searchedNamespace:
|
|
818
|
+
searchedNamespace: "all namespaces",
|
|
873
819
|
suggestion:
|
|
874
820
|
"Use list_untranslated_keys to see all missing keys or add_translation_key to add this key",
|
|
875
821
|
},
|
|
@@ -952,42 +898,26 @@ class I18nMagicServer {
|
|
|
952
898
|
// Find which namespaces contain this key
|
|
953
899
|
const targetNamespaces: string[] = []
|
|
954
900
|
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
if (keys[params.key]) {
|
|
963
|
-
targetNamespaces.push(params.namespace)
|
|
964
|
-
} else {
|
|
965
|
-
throw new Error(
|
|
966
|
-
`Key "${params.key}" does not exist in namespace "${params.namespace}"`,
|
|
901
|
+
// Find all namespaces where this key exists
|
|
902
|
+
for (const namespace of config.namespaces) {
|
|
903
|
+
try {
|
|
904
|
+
const keys = await loadLocalesFile(
|
|
905
|
+
config.loadPath,
|
|
906
|
+
"en",
|
|
907
|
+
namespace,
|
|
967
908
|
)
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
// Find all namespaces where this key exists
|
|
971
|
-
for (const namespace of config.namespaces) {
|
|
972
|
-
try {
|
|
973
|
-
const keys = await loadLocalesFile(
|
|
974
|
-
config.loadPath,
|
|
975
|
-
"en",
|
|
976
|
-
namespace,
|
|
977
|
-
)
|
|
978
|
-
if (keys[params.key]) {
|
|
979
|
-
targetNamespaces.push(namespace)
|
|
980
|
-
}
|
|
981
|
-
} catch (error) {
|
|
982
|
-
// Namespace file doesn't exist, continue
|
|
909
|
+
if (Object.hasOwn(keys, params.key)) {
|
|
910
|
+
targetNamespaces.push(namespace)
|
|
983
911
|
}
|
|
912
|
+
} catch (error) {
|
|
913
|
+
// Namespace file doesn't exist, continue
|
|
984
914
|
}
|
|
915
|
+
}
|
|
985
916
|
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
}
|
|
917
|
+
if (targetNamespaces.length === 0) {
|
|
918
|
+
throw new Error(
|
|
919
|
+
`Key "${params.key}" does not exist in any namespace. Use add_translation_key to create it.`,
|
|
920
|
+
)
|
|
991
921
|
}
|
|
992
922
|
|
|
993
923
|
// Build translation cache with new value
|