i18next-cli 1.39.7 → 1.39.8

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/cjs/cli.js CHANGED
@@ -28,7 +28,7 @@ const program = new commander.Command();
28
28
  program
29
29
  .name('i18next-cli')
30
30
  .description('A unified, high-performance i18next CLI.')
31
- .version('1.39.7'); // This string is replaced with the actual version at build time by rollup
31
+ .version('1.39.8'); // This string is replaced with the actual version at build time by rollup
32
32
  // new: global config override option
33
33
  program.option('-c, --config <path>', 'Path to i18next-cli config file (overrides detection)');
34
34
  program
@@ -56,13 +56,21 @@ async function runRenameKey(config, oldKey, newKey, options = {}, logger$1 = new
56
56
  // Check for conflicts in translation files
57
57
  const conflicts = await checkConflicts(newParts, config);
58
58
  if (conflicts.length > 0) {
59
- return {
60
- success: false,
61
- sourceFiles: [],
62
- translationFiles: [],
63
- conflicts,
64
- error: 'Target key already exists in translation files'
65
- };
59
+ // If the old key doesn't exist in any translation file, treat this as a
60
+ // no-op (allow the command to succeed). This mirrors previous behavior
61
+ // where renaming a missing key doesn't fail just because the target
62
+ // already exists (it avoids blocking repeated/no-op renames).
63
+ const oldExists = await checkOldKeyExists(parseKeyWithNamespace(oldKey, config), config);
64
+ if (oldExists) {
65
+ return {
66
+ success: false,
67
+ sourceFiles: [],
68
+ translationFiles: [],
69
+ conflicts,
70
+ error: 'Target key already exists in translation files'
71
+ };
72
+ }
73
+ // otherwise: old key not present -> continue (no-op)
66
74
  }
67
75
  // Build a quick map of which namespaces contain which keys (union across locales).
68
76
  // This allows us to decide, per-call, whether an explicit `{ ns: 'x' }` refers to
@@ -155,6 +163,25 @@ async function checkConflicts(newParts, config) {
155
163
  }
156
164
  return conflicts;
157
165
  }
166
+ async function checkOldKeyExists(oldParts, config) {
167
+ const keySeparator = config.extract.keySeparator ?? '.';
168
+ for (const locale of config.locales) {
169
+ const outputPath = fileUtils.getOutputPath(config.extract.output, locale, oldParts.namespace);
170
+ const fullPath = node_path.resolve(process.cwd(), outputPath);
171
+ try {
172
+ const translations = await fileUtils.loadTranslationFile(fullPath);
173
+ if (translations) {
174
+ const val = nestedObject.getNestedValue(translations, oldParts.key, keySeparator);
175
+ if (val !== undefined)
176
+ return true;
177
+ }
178
+ }
179
+ catch {
180
+ // file missing — continue to next locale
181
+ }
182
+ }
183
+ return false;
184
+ }
158
185
  async function buildNamespaceKeyMap(config) {
159
186
  // Map namespace -> set of flattened keys present in that namespace (union across locales)
160
187
  const map = new Map();
@@ -371,14 +398,22 @@ function replaceKeyWithRegex(code, oldParts, newParts, config, namespaceKeyMap)
371
398
  }
372
399
  //
373
400
  // 6) Bare calls without options: fn('key') -> fn('newKey')
401
+ // Apply this replacement only when the old key's namespace is the
402
+ // *effective* default namespace (config.extract.defaultNS ?? 'translation').
403
+ // This preserves previous behaviour: default-namespace bare-calls are
404
+ // considered "key form" and should be rewritten even when the translation
405
+ // file exists but the specific key isn't present.
374
406
  //
375
407
  {
376
- const regexKeyNoOptions = new RegExp(`${prefix}\\s*\\(\\s*(['"\`])${escapeRegex(oldParts.key)}\\1\\s*\\)`, 'g');
377
- newCode = newCode.replace(regexKeyNoOptions, (match, q) => {
378
- changes++;
379
- const replacementKey = newParts.key;
380
- return match.replace(new RegExp(`(['"\`])${escapeRegex(oldParts.key)}\\1`), `${q}${replacementKey}${q}`);
381
- });
408
+ const effectiveDefaultNS = config.extract.defaultNS ?? 'translation';
409
+ if (oldParts.namespace === effectiveDefaultNS) {
410
+ const regexKeyNoOptions = new RegExp(`${prefix}\\s*\\(\\s*(['"\`])${escapeRegex(oldParts.key)}\\1\\s*\\)`, 'g');
411
+ newCode = newCode.replace(regexKeyNoOptions, (match, q) => {
412
+ changes++;
413
+ const replacementKey = newParts.key;
414
+ return match.replace(new RegExp(`(['"\`])${escapeRegex(oldParts.key)}\\1`), `${q}${replacementKey}${q}`);
415
+ });
416
+ }
382
417
  }
383
418
  //
384
419
  // 7) JSX i18nKey attribute (handles both fullKey and key)
package/dist/esm/cli.js CHANGED
@@ -26,7 +26,7 @@ const program = new Command();
26
26
  program
27
27
  .name('i18next-cli')
28
28
  .description('A unified, high-performance i18next CLI.')
29
- .version('1.39.7'); // This string is replaced with the actual version at build time by rollup
29
+ .version('1.39.8'); // This string is replaced with the actual version at build time by rollup
30
30
  // new: global config override option
31
31
  program.option('-c, --config <path>', 'Path to i18next-cli config file (overrides detection)');
32
32
  program
@@ -54,13 +54,21 @@ async function runRenameKey(config, oldKey, newKey, options = {}, logger = new C
54
54
  // Check for conflicts in translation files
55
55
  const conflicts = await checkConflicts(newParts, config);
56
56
  if (conflicts.length > 0) {
57
- return {
58
- success: false,
59
- sourceFiles: [],
60
- translationFiles: [],
61
- conflicts,
62
- error: 'Target key already exists in translation files'
63
- };
57
+ // If the old key doesn't exist in any translation file, treat this as a
58
+ // no-op (allow the command to succeed). This mirrors previous behavior
59
+ // where renaming a missing key doesn't fail just because the target
60
+ // already exists (it avoids blocking repeated/no-op renames).
61
+ const oldExists = await checkOldKeyExists(parseKeyWithNamespace(oldKey, config), config);
62
+ if (oldExists) {
63
+ return {
64
+ success: false,
65
+ sourceFiles: [],
66
+ translationFiles: [],
67
+ conflicts,
68
+ error: 'Target key already exists in translation files'
69
+ };
70
+ }
71
+ // otherwise: old key not present -> continue (no-op)
64
72
  }
65
73
  // Build a quick map of which namespaces contain which keys (union across locales).
66
74
  // This allows us to decide, per-call, whether an explicit `{ ns: 'x' }` refers to
@@ -153,6 +161,25 @@ async function checkConflicts(newParts, config) {
153
161
  }
154
162
  return conflicts;
155
163
  }
164
+ async function checkOldKeyExists(oldParts, config) {
165
+ const keySeparator = config.extract.keySeparator ?? '.';
166
+ for (const locale of config.locales) {
167
+ const outputPath = getOutputPath(config.extract.output, locale, oldParts.namespace);
168
+ const fullPath = resolve(process.cwd(), outputPath);
169
+ try {
170
+ const translations = await loadTranslationFile(fullPath);
171
+ if (translations) {
172
+ const val = getNestedValue(translations, oldParts.key, keySeparator);
173
+ if (val !== undefined)
174
+ return true;
175
+ }
176
+ }
177
+ catch {
178
+ // file missing — continue to next locale
179
+ }
180
+ }
181
+ return false;
182
+ }
156
183
  async function buildNamespaceKeyMap(config) {
157
184
  // Map namespace -> set of flattened keys present in that namespace (union across locales)
158
185
  const map = new Map();
@@ -369,14 +396,22 @@ function replaceKeyWithRegex(code, oldParts, newParts, config, namespaceKeyMap)
369
396
  }
370
397
  //
371
398
  // 6) Bare calls without options: fn('key') -> fn('newKey')
399
+ // Apply this replacement only when the old key's namespace is the
400
+ // *effective* default namespace (config.extract.defaultNS ?? 'translation').
401
+ // This preserves previous behaviour: default-namespace bare-calls are
402
+ // considered "key form" and should be rewritten even when the translation
403
+ // file exists but the specific key isn't present.
372
404
  //
373
405
  {
374
- const regexKeyNoOptions = new RegExp(`${prefix}\\s*\\(\\s*(['"\`])${escapeRegex(oldParts.key)}\\1\\s*\\)`, 'g');
375
- newCode = newCode.replace(regexKeyNoOptions, (match, q) => {
376
- changes++;
377
- const replacementKey = newParts.key;
378
- return match.replace(new RegExp(`(['"\`])${escapeRegex(oldParts.key)}\\1`), `${q}${replacementKey}${q}`);
379
- });
406
+ const effectiveDefaultNS = config.extract.defaultNS ?? 'translation';
407
+ if (oldParts.namespace === effectiveDefaultNS) {
408
+ const regexKeyNoOptions = new RegExp(`${prefix}\\s*\\(\\s*(['"\`])${escapeRegex(oldParts.key)}\\1\\s*\\)`, 'g');
409
+ newCode = newCode.replace(regexKeyNoOptions, (match, q) => {
410
+ changes++;
411
+ const replacementKey = newParts.key;
412
+ return match.replace(new RegExp(`(['"\`])${escapeRegex(oldParts.key)}\\1`), `${q}${replacementKey}${q}`);
413
+ });
414
+ }
380
415
  }
381
416
  //
382
417
  // 7) JSX i18nKey attribute (handles both fullKey and key)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "i18next-cli",
3
- "version": "1.39.7",
3
+ "version": "1.39.8",
4
4
  "description": "A unified, high-performance i18next CLI.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1 +1 @@
1
- {"version":3,"file":"rename-key.d.ts","sourceRoot":"","sources":["../src/rename-key.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AAO5E;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAsB,YAAY,CAChC,MAAM,EAAE,oBAAoB,EAC5B,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,OAAO,GAAE;IACP,MAAM,CAAC,EAAE,OAAO,CAAA;CACZ,EACN,MAAM,GAAE,MAA4B,GACnC,OAAO,CAAC,eAAe,CAAC,CA6D1B"}
1
+ {"version":3,"file":"rename-key.d.ts","sourceRoot":"","sources":["../src/rename-key.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AAO5E;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAsB,YAAY,CAChC,MAAM,EAAE,oBAAoB,EAC5B,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,OAAO,GAAE;IACP,MAAM,CAAC,EAAE,OAAO,CAAA;CACZ,EACN,MAAM,GAAE,MAA4B,GACnC,OAAO,CAAC,eAAe,CAAC,CAqE1B"}