@scoutello/i18n-magic 0.53.0 → 0.55.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
@@ -5,7 +5,7 @@ import {
5
5
  ListToolsRequestSchema,
6
6
  } from "@modelcontextprotocol/sdk/types.js"
7
7
  import { z } from "zod"
8
- import { addTranslationKey, getMissingKeys, loadConfig, loadLocalesFile } from "./lib/utils.js"
8
+ import { addTranslationKey, addTranslationKeys, getMissingKeys, loadConfig, loadLocalesFile } from "./lib/utils.js"
9
9
  import type { Configuration } from "./lib/types.js"
10
10
  import path from "path"
11
11
  import fs from "fs"
@@ -67,7 +67,17 @@ function resolveProjectRoot(): string {
67
67
  // Zod schema for the add_translation_key tool parameters
68
68
  const AddTranslationKeySchema = z.object({
69
69
  key: z.string().describe("The translation key to add (e.g., \"welcomeMessage\")"),
70
- value: z.string().describe("The English text value for this translation key"),
70
+ value: z.string().describe("The text value for this translation key"),
71
+ language: z.string().optional().describe("The language code of the provided value (e.g., \"en\", \"de\", \"fr\"). Defaults to \"en\" (English) if not specified."),
72
+ })
73
+
74
+ // Zod schema for the add_translation_keys (batch) tool parameters
75
+ const AddTranslationKeysSchema = z.object({
76
+ keys: z.array(z.object({
77
+ key: z.string().describe("The translation key to add (e.g., \"welcomeMessage\")"),
78
+ value: z.string().describe("The text value for this translation key"),
79
+ language: z.string().optional().describe("The language code of the provided value (e.g., \"en\", \"de\", \"fr\"). Defaults to \"en\" (English) if not specified."),
80
+ })).describe("Array of translation keys to add in batch. Use this for adding multiple keys at once for better performance."),
71
81
  })
72
82
 
73
83
  // Zod schema for the list_untranslated_keys tool parameters
@@ -94,7 +104,8 @@ const GetTranslationKeySchema = z.object({
94
104
  // Zod schema for the update_translation_key tool parameters
95
105
  const UpdateTranslationKeySchema = z.object({
96
106
  key: z.string().describe("The translation key to update (e.g., \"welcomeMessage\")"),
97
- value: z.string().describe("The new English text value for this translation key"),
107
+ value: z.string().describe("The new text value for this translation key"),
108
+ language: z.string().optional().describe("The language code of the provided value (e.g., \"en\", \"de\", \"fr\"). Defaults to \"en\" (English) if not specified."),
98
109
  namespace: z
99
110
  .string()
100
111
  .optional()
@@ -182,7 +193,7 @@ class I18nMagicServer {
182
193
  tools: [
183
194
  {
184
195
  name: "add_translation_key",
185
- description: "Add a new translation key with an English value.",
196
+ description: "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.",
186
197
  inputSchema: {
187
198
  type: "object",
188
199
  properties: {
@@ -194,12 +205,49 @@ class I18nMagicServer {
194
205
  value: {
195
206
  type: "string",
196
207
  description:
197
- "The English text value for this translation key",
208
+ "The text value for this translation key",
209
+ },
210
+ language: {
211
+ type: "string",
212
+ description:
213
+ "The language code of the provided value (e.g., \"en\" for English, \"de\" for German, \"fr\" for French). Defaults to \"en\" if not specified.",
198
214
  },
199
215
  },
200
216
  required: ["key", "value"],
201
217
  },
202
218
  },
219
+ {
220
+ name: "add_translation_keys",
221
+ description: "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.",
222
+ inputSchema: {
223
+ type: "object",
224
+ properties: {
225
+ keys: {
226
+ type: "array",
227
+ description: "Array of translation keys to add in batch",
228
+ items: {
229
+ type: "object",
230
+ properties: {
231
+ key: {
232
+ type: "string",
233
+ description: "The translation key to add (e.g., \"welcomeMessage\")",
234
+ },
235
+ value: {
236
+ type: "string",
237
+ description: "The text value for this translation key",
238
+ },
239
+ language: {
240
+ type: "string",
241
+ description: "The language code of the provided value (e.g., \"en\" for English, \"de\" for German, \"fr\" for French). Defaults to \"en\" if not specified.",
242
+ },
243
+ },
244
+ required: ["key", "value"],
245
+ },
246
+ },
247
+ },
248
+ required: ["keys"],
249
+ },
250
+ },
203
251
  {
204
252
  name: "list_untranslated_keys",
205
253
  description:
@@ -240,7 +288,7 @@ class I18nMagicServer {
240
288
  {
241
289
  name: "update_translation_key",
242
290
  description:
243
- "Update an existing translation key with a new English value. This will update the key across all locales (translating automatically to other languages) and all namespaces where the key exists. Use this when you need to fix typos, improve wording, or change the text of an existing translation. If you're not sure if a key exists, use get_translation_key or search_translations first.",
291
+ "Update an existing translation key with a new text value. You can optionally specify the language of the value you're providing (defaults to English). This will update the key across all locales (translating automatically to other languages) and all namespaces where the key exists. Use this when you need to fix typos, improve wording, or change the text of an existing translation. If you're not sure if a key exists, use get_translation_key or search_translations first.",
244
292
  inputSchema: {
245
293
  type: "object",
246
294
  properties: {
@@ -252,7 +300,12 @@ class I18nMagicServer {
252
300
  value: {
253
301
  type: "string",
254
302
  description:
255
- "The new English text value for this translation key",
303
+ "The new text value for this translation key",
304
+ },
305
+ language: {
306
+ type: "string",
307
+ description:
308
+ "The language code of the provided value (e.g., \"en\" for English, \"de\" for German, \"fr\" for French). Defaults to \"en\" if not specified.",
256
309
  },
257
310
  namespace: {
258
311
  type: "string",
@@ -311,6 +364,7 @@ class I18nMagicServer {
311
364
  result = await addTranslationKey({
312
365
  key: params.key,
313
366
  value: params.value,
367
+ language: params.language || "en",
314
368
  config,
315
369
  })
316
370
  } finally {
@@ -325,14 +379,15 @@ class I18nMagicServer {
325
379
  text: JSON.stringify(
326
380
  {
327
381
  success: true,
328
- message: `Successfully added translation key "${result.key}" to affected namespaces: ${result.namespace} (${result.locale})`,
382
+ message: `Successfully added translation key "${result.key}" to affected namespaces: ${result.namespace} (locales: ${result.locale})`,
329
383
  key: result.key,
330
384
  value: result.value,
385
+ providedLanguage: params.language || "en",
331
386
  namespace: result.namespace,
332
- locale: result.locale,
387
+ locales: result.locale,
333
388
  nextStep: result.locale.includes(',')
334
- ? "Run 'i18n-magic sync' to translate this key to other locales"
335
- : "Key was translated to default locale. Run 'i18n-magic sync' to translate to other locales",
389
+ ? "Key was automatically translated to multiple locales"
390
+ : "Run 'i18n-magic sync' to translate this key to other locales",
336
391
  diagnostics: logMessages.join('\n'),
337
392
  },
338
393
  null,
@@ -385,6 +440,131 @@ class I18nMagicServer {
385
440
  }
386
441
  }
387
442
 
443
+ if (request.params.name === "add_translation_keys") {
444
+ try {
445
+ // Validate parameters
446
+ const params = AddTranslationKeysSchema.parse(request.params.arguments)
447
+
448
+ if (!params.keys || params.keys.length === 0) {
449
+ return {
450
+ content: [
451
+ {
452
+ type: "text",
453
+ text: JSON.stringify(
454
+ {
455
+ success: false,
456
+ error: "No keys provided. The 'keys' array must contain at least one key-value pair.",
457
+ },
458
+ null,
459
+ 2,
460
+ ),
461
+ },
462
+ ],
463
+ isError: true,
464
+ }
465
+ }
466
+
467
+ // Ensure config is loaded
468
+ const config = await this.ensureConfig()
469
+
470
+ // Capture console.log output for diagnostics
471
+ const originalConsoleLog = console.log
472
+ const logMessages: string[] = []
473
+ console.log = (...args: any[]) => {
474
+ const message = args.map(arg =>
475
+ typeof arg === 'object' ? JSON.stringify(arg) : String(arg)
476
+ ).join(' ')
477
+ logMessages.push(message)
478
+ // Also log to stderr for debugging
479
+ console.error(`[i18n-magic] ${message}`)
480
+ }
481
+
482
+ let result
483
+ try {
484
+ // Add the translation keys in batch
485
+ result = await addTranslationKeys({
486
+ keys: params.keys.map(k => ({
487
+ key: k.key,
488
+ value: k.value,
489
+ language: k.language || "en",
490
+ })),
491
+ config,
492
+ })
493
+ } finally {
494
+ // Restore console.log
495
+ console.log = originalConsoleLog
496
+ }
497
+
498
+ return {
499
+ content: [
500
+ {
501
+ type: "text",
502
+ text: JSON.stringify(
503
+ {
504
+ success: true,
505
+ message: `Successfully added ${result.results.length} translation key(s) in batch`,
506
+ results: result.results,
507
+ performance: result.performance,
508
+ summary: {
509
+ totalKeys: result.results.length,
510
+ totalTime: `${result.performance.totalTime.toFixed(2)}ms`,
511
+ averageTimePerKey: `${(result.performance.totalTime / result.results.length).toFixed(2)}ms`,
512
+ scanTime: `${result.performance.scanTime.toFixed(2)}ms`,
513
+ translationTime: `${result.performance.translationTime.toFixed(2)}ms`,
514
+ fileIOTime: `${result.performance.fileIOTime.toFixed(2)}ms`,
515
+ },
516
+ diagnostics: logMessages.join('\n'),
517
+ },
518
+ null,
519
+ 2,
520
+ ),
521
+ },
522
+ ],
523
+ }
524
+ } catch (error) {
525
+ const errorMessage =
526
+ error instanceof Error ? error.message : "Unknown error occurred"
527
+
528
+ // Get more detailed error information
529
+ let errorDetails = errorMessage
530
+ if (error instanceof Error) {
531
+ // Check if there's a cause
532
+ const cause = (error as any).cause
533
+ if (cause instanceof Error) {
534
+ errorDetails = `${errorMessage}\nCause: ${cause.message}\nStack: ${cause.stack}`
535
+ } else if (cause) {
536
+ errorDetails = `${errorMessage}\nCause: ${JSON.stringify(cause)}`
537
+ }
538
+ // Include stack trace
539
+ if (error.stack) {
540
+ errorDetails = `${errorDetails}\nStack: ${error.stack}`
541
+ }
542
+ }
543
+
544
+ // Log detailed error to stderr for debugging
545
+ console.error(`[i18n-magic MCP] Error adding translation keys in batch:`)
546
+ console.error(errorDetails)
547
+
548
+ return {
549
+ content: [
550
+ {
551
+ type: "text",
552
+ text: JSON.stringify(
553
+ {
554
+ success: false,
555
+ error: errorMessage,
556
+ details: errorDetails,
557
+ },
558
+ null,
559
+ 2,
560
+ ),
561
+ },
562
+ ],
563
+ isError: true,
564
+ }
565
+ }
566
+ }
567
+
388
568
  if (request.params.name === "list_untranslated_keys") {
389
569
  try {
390
570
  // Validate parameters
@@ -681,20 +861,21 @@ class I18nMagicServer {
681
861
  }
682
862
 
683
863
  // Build translation cache with new value
864
+ const inputLanguage = params.language || "en"
684
865
  const translationCache: Record<string, string> = {
685
- en: params.value,
866
+ [inputLanguage]: params.value,
686
867
  }
687
868
 
688
869
  // Translate to all other locales
689
- const nonEnglishLocales = config.locales.filter((l) => l !== "en")
690
- if (nonEnglishLocales.length > 0 && config.openai) {
870
+ const otherLocales = config.locales.filter((l) => l !== inputLanguage)
871
+ if (otherLocales.length > 0 && config.openai) {
691
872
  const { translateKey } = await import("./lib/utils.js")
692
873
 
693
874
  await Promise.all(
694
- nonEnglishLocales.map(async (locale) => {
875
+ otherLocales.map(async (locale) => {
695
876
  const translation = await translateKey({
696
877
  context: config.context || "",
697
- inputLanguage: "en",
878
+ inputLanguage: inputLanguage,
698
879
  outputLanguage: locale,
699
880
  object: {
700
881
  [params.key]: params.value,
@@ -730,6 +911,7 @@ class I18nMagicServer {
730
911
  message: `Successfully updated translation key "${params.key}" in ${targetNamespaces.length} namespace(s) and ${config.locales.length} locale(s)`,
731
912
  key: params.key,
732
913
  newValue: params.value,
914
+ providedLanguage: params.language || "en",
733
915
  namespaces: targetNamespaces,
734
916
  locales: config.locales,
735
917
  diagnostics: logMessages.join('\n'),