@scoutello/i18n-magic 0.64.0 → 0.66.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.
Files changed (58) hide show
  1. package/README.md +13 -7
  2. package/dist/cli.js +20 -15
  3. package/dist/cli.js.map +1 -1
  4. package/dist/commands/check-missing.d.ts +11 -1
  5. package/dist/commands/check-missing.d.ts.map +1 -1
  6. package/dist/commands/check-missing.js +56 -96
  7. package/dist/commands/check-missing.js.map +1 -1
  8. package/dist/commands/replace.d.ts +6 -0
  9. package/dist/commands/replace.d.ts.map +1 -1
  10. package/dist/commands/replace.js +48 -29
  11. package/dist/commands/replace.js.map +1 -1
  12. package/dist/commands/scan.d.ts.map +1 -1
  13. package/dist/commands/scan.js +114 -80
  14. package/dist/commands/scan.js.map +1 -1
  15. package/dist/commands/sync-locales.d.ts.map +1 -1
  16. package/dist/commands/sync-locales.js +90 -50
  17. package/dist/commands/sync-locales.js.map +1 -1
  18. package/dist/index.d.ts +2 -1
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +1 -1
  21. package/dist/index.js.map +1 -1
  22. package/dist/lib/languges.d.ts.map +1 -1
  23. package/dist/lib/languges.js +20 -0
  24. package/dist/lib/languges.js.map +1 -1
  25. package/dist/lib/locale-storage.d.ts +7 -0
  26. package/dist/lib/locale-storage.d.ts.map +1 -0
  27. package/dist/lib/locale-storage.js +49 -0
  28. package/dist/lib/locale-storage.js.map +1 -0
  29. package/dist/lib/translation-response.d.ts +8 -0
  30. package/dist/lib/translation-response.d.ts.map +1 -0
  31. package/dist/lib/translation-response.js +43 -0
  32. package/dist/lib/translation-response.js.map +1 -0
  33. package/dist/lib/types.d.ts +1 -1
  34. package/dist/lib/types.d.ts.map +1 -1
  35. package/dist/lib/update-translation-key.d.ts +16 -0
  36. package/dist/lib/update-translation-key.d.ts.map +1 -0
  37. package/dist/lib/update-translation-key.js +67 -0
  38. package/dist/lib/update-translation-key.js.map +1 -0
  39. package/dist/lib/utils.d.ts +2 -0
  40. package/dist/lib/utils.d.ts.map +1 -1
  41. package/dist/lib/utils.js +69 -51
  42. package/dist/lib/utils.js.map +1 -1
  43. package/dist/mcp-server.js +28 -79
  44. package/dist/mcp-server.js.map +1 -1
  45. package/package.json +5 -9
  46. package/src/cli.ts +26 -18
  47. package/src/commands/check-missing.ts +83 -137
  48. package/src/commands/replace.ts +81 -43
  49. package/src/commands/scan.ts +130 -108
  50. package/src/commands/sync-locales.ts +125 -86
  51. package/src/index.ts +6 -1
  52. package/src/lib/languges.ts +20 -0
  53. package/src/lib/locale-storage.ts +76 -0
  54. package/src/lib/translation-response.ts +59 -0
  55. package/src/lib/types.ts +1 -1
  56. package/src/lib/update-translation-key.ts +119 -0
  57. package/src/lib/utils.ts +92 -76
  58. package/src/mcp-server.ts +31 -112
package/src/mcp-server.ts CHANGED
@@ -10,6 +10,7 @@ import path from "path"
10
10
  import { fileURLToPath } from "url"
11
11
  import { z } from "zod"
12
12
  import type { Configuration } from "./lib/types.js"
13
+ import { updateTranslationKeyOperation } from "./lib/update-translation-key.js"
13
14
  import {
14
15
  addTranslationKey,
15
16
  addTranslationKeys,
@@ -162,7 +163,7 @@ const AddTranslationKeySchema = z.object({
162
163
  .string()
163
164
  .optional()
164
165
  .describe(
165
- 'The language code of the provided value (e.g., "en", "de", "fr"). Defaults to "en" (English) if not specified.',
166
+ "The configured language code of the provided value. Defaults to config.defaultLocale if not specified.",
166
167
  ),
167
168
  })
168
169
 
@@ -181,7 +182,7 @@ const AddTranslationKeysSchema = z.object({
181
182
  .string()
182
183
  .optional()
183
184
  .describe(
184
- 'The language code of the provided value (e.g., "en", "de", "fr"). Defaults to "en" (English) if not specified.',
185
+ "The configured language code of the provided value. Defaults to config.defaultLocale if not specified.",
185
186
  ),
186
187
  }),
187
188
  )
@@ -210,7 +211,7 @@ const UpdateTranslationKeySchema = z.object({
210
211
  .string()
211
212
  .optional()
212
213
  .describe(
213
- 'The language code of the provided value (e.g., "en", "de", "fr"). Defaults to "en" (English) if not specified.',
214
+ "The configured language code of the provided value. Defaults to config.defaultLocale if not specified.",
214
215
  ),
215
216
  })
216
217
 
@@ -299,7 +300,7 @@ class I18nMagicServer {
299
300
  {
300
301
  name: "add_translation_key",
301
302
  description:
302
- "Add a new translation key with a text value. The destination translation file is resolved automatically from project configuration and code usage. 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.",
303
+ "Add a new translation key with a text value. The destination translation file is resolved automatically from project configuration and code usage. The language defaults to the configured defaultLocale. 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.",
303
304
  inputSchema: {
304
305
  type: "object",
305
306
  properties: {
@@ -315,7 +316,7 @@ class I18nMagicServer {
315
316
  language: {
316
317
  type: "string",
317
318
  description:
318
- 'The language code of the provided value (e.g., "en" for English, "de" for German, "fr" for French). Defaults to "en" if not specified.',
319
+ "The configured language code of the provided value. Defaults to config.defaultLocale if not specified.",
319
320
  },
320
321
  },
321
322
  required: ["key", "value"],
@@ -346,7 +347,7 @@ class I18nMagicServer {
346
347
  language: {
347
348
  type: "string",
348
349
  description:
349
- 'The language code of the provided value (e.g., "en" for English, "de" for German, "fr" for French). Defaults to "en" if not specified.',
350
+ "The configured language code of the provided value. Defaults to config.defaultLocale if not specified.",
350
351
  },
351
352
  },
352
353
  required: ["key", "value"],
@@ -385,7 +386,7 @@ class I18nMagicServer {
385
386
  {
386
387
  name: "update_translation_key",
387
388
  description:
388
- "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) everywhere it 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. NOTE: This tool can only UPDATE existing keys, it will NEVER remove any keys.",
389
+ "Update an existing translation key with a new text value. The language defaults to the configured defaultLocale. With a provider, every locale is translated before writes begin. Without a provider, only the supplied/default locale is updated and other locales are returned as pending. NOTE: This tool can only UPDATE existing keys, it will NEVER remove any keys.",
389
390
  inputSchema: {
390
391
  type: "object",
391
392
  properties: {
@@ -401,7 +402,7 @@ class I18nMagicServer {
401
402
  language: {
402
403
  type: "string",
403
404
  description:
404
- 'The language code of the provided value (e.g., "en" for English, "de" for German, "fr" for French). Defaults to "en" if not specified.',
405
+ "The configured language code of the provided value. Defaults to config.defaultLocale if not specified.",
405
406
  },
406
407
  },
407
408
  required: ["key", "value"],
@@ -448,7 +449,7 @@ class I18nMagicServer {
448
449
  return await addTranslationKey({
449
450
  key: params.key,
450
451
  value: params.value,
451
- language: params.language || "en",
452
+ language: params.language,
452
453
  config,
453
454
  })
454
455
  })
@@ -467,7 +468,7 @@ class I18nMagicServer {
467
468
  message: `Successfully added translation key "${result.key}"`,
468
469
  key: result.key,
469
470
  value: result.value,
470
- providedLanguage: params.language || "en",
471
+ providedLanguage: result.providedLanguage,
471
472
  locales: result.locale,
472
473
  nextStep: result.locale.includes(",")
473
474
  ? "Key was automatically translated to multiple locales"
@@ -564,7 +565,7 @@ class I18nMagicServer {
564
565
  keys: params.keys.map((k) => ({
565
566
  key: k.key,
566
567
  value: k.value,
567
- language: k.language || "en",
568
+ language: k.language,
568
569
  })),
569
570
  config,
570
571
  })
@@ -892,102 +893,14 @@ class I18nMagicServer {
892
893
  console.log = () => {}
893
894
 
894
895
  try {
895
- // Use mutex to prevent concurrent writes
896
- await translationMutex.withLock(async () => {
897
- // Find which namespaces contain this key
898
- const targetNamespaces: string[] = []
899
-
900
- // Find all namespaces where this key exists
901
- for (const namespace of config.namespaces) {
902
- try {
903
- const keys = await loadLocalesFile(
904
- config.loadPath,
905
- "en",
906
- namespace,
907
- )
908
- if (Object.hasOwn(keys, params.key)) {
909
- targetNamespaces.push(namespace)
910
- }
911
- } catch (error) {
912
- // Namespace file doesn't exist, continue
913
- }
914
- }
915
-
916
- if (targetNamespaces.length === 0) {
917
- throw new Error(
918
- `Key "${params.key}" does not exist in translation files. Use add_translation_key to create it.`,
919
- )
920
- }
921
-
922
- // Build translation cache with new value
923
- const inputLanguage = params.language || "en"
924
- const translationCache: Record<string, string> = {
925
- [inputLanguage]: params.value,
926
- }
927
-
928
- // Translate to all other locales
929
- const otherLocales = config.locales.filter(
930
- (l) => l !== inputLanguage,
931
- )
932
- if (otherLocales.length > 0 && config.openai) {
933
- const { translateKey } = await import("./lib/utils.js")
934
-
935
- await Promise.all(
936
- otherLocales.map(async (locale) => {
937
- const translation = await translateKey({
938
- context: config.context || "",
939
- inputLanguage: inputLanguage,
940
- outputLanguage: locale,
941
- object: {
942
- [params.key]: params.value,
943
- },
944
- openai: config.openai!,
945
- model: config.model,
946
- })
947
- translationCache[locale] = translation[params.key]
948
- }),
949
- )
950
- }
951
-
952
- // Update the key in all relevant namespaces and locales
953
- // Process sequentially to avoid race conditions
954
- const { writeLocalesFile } = await import("./lib/utils.js")
955
-
956
- for (const namespace of targetNamespaces) {
957
- for (const locale of config.locales) {
958
- const newValue = translationCache[locale] || params.value
959
-
960
- // Load existing keys
961
- const existingKeys = await loadLocalesFile(
962
- config.loadPath,
963
- locale,
964
- namespace,
965
- )
966
-
967
- const originalKeyCount = Object.keys(existingKeys).length
968
-
969
- // Update the specific key
970
- existingKeys[params.key] = newValue
971
-
972
- const newKeyCount = Object.keys(existingKeys).length
973
-
974
- // Safety check: we should only be updating, not removing
975
- // Key count should stay same (update) or increase by 1 (add)
976
- if (newKeyCount < originalKeyCount) {
977
- throw new Error(
978
- `Safety check failed: Updating locale "${locale}" would reduce keys from ${originalKeyCount} to ${newKeyCount}. Aborting.`,
979
- )
980
- }
981
-
982
- await writeLocalesFile(
983
- config.savePath,
984
- locale,
985
- namespace,
986
- existingKeys,
987
- )
988
- }
989
- }
990
- })
896
+ const result = await translationMutex.withLock(async () =>
897
+ updateTranslationKeyOperation({
898
+ config,
899
+ key: params.key,
900
+ value: params.value,
901
+ language: params.language,
902
+ }),
903
+ )
991
904
 
992
905
  return {
993
906
  content: [
@@ -996,11 +909,17 @@ class I18nMagicServer {
996
909
  text: JSON.stringify(
997
910
  {
998
911
  success: true,
999
- message: `Successfully updated translation key "${params.key}" in ${config.locales.length} locale(s)`,
1000
- key: params.key,
1001
- newValue: params.value,
1002
- providedLanguage: params.language || "en",
1003
- locales: config.locales,
912
+ message: `Successfully updated translation key "${result.key}" in ${result.updatedLocales.length} locale(s)`,
913
+ key: result.key,
914
+ newValue: result.value,
915
+ providedLanguage: result.sourceLocale,
916
+ updatedLocales: result.updatedLocales,
917
+ pendingLocales: result.pendingLocales,
918
+ locales: result.updatedLocales,
919
+ nextStep:
920
+ result.pendingLocales.length > 0
921
+ ? "Run 'i18n-magic sync' with a configured provider to translate pending locales."
922
+ : "All configured locales were updated.",
1004
923
  },
1005
924
  null,
1006
925
  2,