@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/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('The translation key to add (e.g., "welcomeMessage")'),
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('The translation key to add (e.g., "welcomeMessage")'),
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
- const params = ListUntranslatedKeysSchema.parse(
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(filteredKeys.map((item) => item.key)),
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
- filteredKeys.length === 0
672
+ missingKeys.length === 0
714
673
  ? "No missing translation keys found! All keys used in the codebase are defined."
715
- : `Found ${filteredKeys.length} missing translation key${filteredKeys.length === 1 ? "" : "s"}`,
674
+ : `Found ${missingKeys.length} missing translation key${missingKeys.length === 1 ? "" : "s"}`,
716
675
  missingKeys: uniqueKeys,
717
676
  nextSteps:
718
- filteredKeys.length > 0
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
- // If namespace is specified, only check that namespace
790
- if (params.namespace) {
748
+ // Try default namespace first
749
+ try {
791
750
  const keys = await loadLocalesFile(
792
751
  config.loadPath,
793
752
  "en",
794
- params.namespace,
753
+ config.defaultNamespace,
795
754
  )
796
- if (keys[params.key]) {
755
+ if (Object.hasOwn(keys, params.key)) {
797
756
  foundValue = keys[params.key]
798
- foundNamespace = params.namespace
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
- // If not found in default namespace, search all other namespaces
817
- if (!foundValue) {
818
- for (const namespace of config.namespaces) {
819
- if (namespace === config.defaultNamespace) continue // Already checked
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
- try {
822
- const keys = await loadLocalesFile(
823
- config.loadPath,
824
- "en",
825
- namespace,
826
- )
827
- if (keys[params.key]) {
828
- foundValue = keys[params.key]
829
- foundNamespace = namespace
830
- break
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${params.namespace ? ` (namespace: ${params.namespace})` : ""}`,
816
+ error: `Translation key "${params.key}" not found in English locale`,
871
817
  key: params.key,
872
- searchedNamespace: params.namespace || "all namespaces",
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
- if (params.namespace) {
956
- // Check if key exists in specified namespace
957
- const keys = await loadLocalesFile(
958
- config.loadPath,
959
- "en",
960
- params.namespace,
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
- } else {
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
- if (targetNamespaces.length === 0) {
987
- throw new Error(
988
- `Key "${params.key}" does not exist in any namespace. Use add_translation_key to create it.`,
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