i18next-cli 1.39.7 → 1.39.9
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 +1 -1
- package/dist/cjs/rename-key.js +76 -14
- package/dist/esm/cli.js +1 -1
- package/dist/esm/rename-key.js +76 -14
- package/package.json +1 -1
- package/types/rename-key.d.ts.map +1 -1
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.
|
|
31
|
+
.version('1.39.9'); // 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
|
package/dist/cjs/rename-key.js
CHANGED
|
@@ -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
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
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();
|
|
@@ -323,7 +350,34 @@ function replaceKeyWithRegex(code, oldParts, newParts, config, namespaceKeyMap)
|
|
|
323
350
|
});
|
|
324
351
|
}
|
|
325
352
|
//
|
|
326
|
-
//
|
|
353
|
+
// 3a) Replace occurrences where the call uses an explicitly namespaced string
|
|
354
|
+
// literal like t('ns:key') while the CLI rename was invoked with the
|
|
355
|
+
// key without namespace (oldKey='key'). Example: defaultNS = 'ns1',
|
|
356
|
+
// source contains t('ns1:key'), and user called runRenameKey('key', 'key2').
|
|
357
|
+
// We should update 'ns1:key' -> 'ns1:key2' or to a new namespace if the
|
|
358
|
+
// target includes an explicit namespace.
|
|
359
|
+
//
|
|
360
|
+
// Only update explicit "ns:oldKey" string literals when the key name itself
|
|
361
|
+
// is changing. If only the namespace is changing but the key name stays
|
|
362
|
+
// identical (e.g. `key` -> `ns2:key`) we should NOT rewrite explicit
|
|
363
|
+
// `t('ns1:key')` occurrences — keep their explicit namespace intact.
|
|
364
|
+
if (oldParts.namespace && newParts.key !== oldParts.key) {
|
|
365
|
+
// ensure ns separator is a string for regex building (default ':')
|
|
366
|
+
const nsSepStr = nsSeparator === false ? ':' : String(nsSeparator);
|
|
367
|
+
const prefixed = `${escapeRegex(String(oldParts.namespace))}${escapeRegex(nsSepStr)}${escapeRegex(oldParts.key)}`;
|
|
368
|
+
const regexPrefixed = new RegExp(`${prefix}\\s*\\(\\s*(['"\`])${prefixed}\\1`, 'g');
|
|
369
|
+
newCode = newCode.replace(regexPrefixed, (match) => {
|
|
370
|
+
changes++;
|
|
371
|
+
// determine replacement: if newParts is explicitly namespaced, use fullKey;
|
|
372
|
+
// otherwise keep the original namespace but swap the key.
|
|
373
|
+
const replacement = newParts.explicitNamespace
|
|
374
|
+
? newParts.fullKey
|
|
375
|
+
: `${oldParts.namespace}${nsSepStr}${newParts.key}`;
|
|
376
|
+
return match.replace(`${oldParts.namespace}${nsSepStr}${oldParts.key}`, replacement);
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
//
|
|
380
|
+
// 3b) fullKey (explicitly namespaced string in call): only when user supplied a namespaced target
|
|
327
381
|
//
|
|
328
382
|
if (oldParts.fullKey && oldParts.explicitNamespace) {
|
|
329
383
|
const regexFull = new RegExp(`${prefix}\\s*\\(\\s*(['"\`])${escapeRegex(oldParts.fullKey)}\\1`, 'g');
|
|
@@ -371,14 +425,22 @@ function replaceKeyWithRegex(code, oldParts, newParts, config, namespaceKeyMap)
|
|
|
371
425
|
}
|
|
372
426
|
//
|
|
373
427
|
// 6) Bare calls without options: fn('key') -> fn('newKey')
|
|
428
|
+
// Apply this replacement only when the old key's namespace is the
|
|
429
|
+
// *effective* default namespace (config.extract.defaultNS ?? 'translation').
|
|
430
|
+
// This preserves previous behaviour: default-namespace bare-calls are
|
|
431
|
+
// considered "key form" and should be rewritten even when the translation
|
|
432
|
+
// file exists but the specific key isn't present.
|
|
374
433
|
//
|
|
375
434
|
{
|
|
376
|
-
const
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
435
|
+
const effectiveDefaultNS = config.extract.defaultNS ?? 'translation';
|
|
436
|
+
if (oldParts.namespace === effectiveDefaultNS) {
|
|
437
|
+
const regexKeyNoOptions = new RegExp(`${prefix}\\s*\\(\\s*(['"\`])${escapeRegex(oldParts.key)}\\1\\s*\\)`, 'g');
|
|
438
|
+
newCode = newCode.replace(regexKeyNoOptions, (match, q) => {
|
|
439
|
+
changes++;
|
|
440
|
+
const replacementKey = newParts.key;
|
|
441
|
+
return match.replace(new RegExp(`(['"\`])${escapeRegex(oldParts.key)}\\1`), `${q}${replacementKey}${q}`);
|
|
442
|
+
});
|
|
443
|
+
}
|
|
382
444
|
}
|
|
383
445
|
//
|
|
384
446
|
// 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.
|
|
29
|
+
.version('1.39.9'); // 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
|
package/dist/esm/rename-key.js
CHANGED
|
@@ -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
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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();
|
|
@@ -321,7 +348,34 @@ function replaceKeyWithRegex(code, oldParts, newParts, config, namespaceKeyMap)
|
|
|
321
348
|
});
|
|
322
349
|
}
|
|
323
350
|
//
|
|
324
|
-
//
|
|
351
|
+
// 3a) Replace occurrences where the call uses an explicitly namespaced string
|
|
352
|
+
// literal like t('ns:key') while the CLI rename was invoked with the
|
|
353
|
+
// key without namespace (oldKey='key'). Example: defaultNS = 'ns1',
|
|
354
|
+
// source contains t('ns1:key'), and user called runRenameKey('key', 'key2').
|
|
355
|
+
// We should update 'ns1:key' -> 'ns1:key2' or to a new namespace if the
|
|
356
|
+
// target includes an explicit namespace.
|
|
357
|
+
//
|
|
358
|
+
// Only update explicit "ns:oldKey" string literals when the key name itself
|
|
359
|
+
// is changing. If only the namespace is changing but the key name stays
|
|
360
|
+
// identical (e.g. `key` -> `ns2:key`) we should NOT rewrite explicit
|
|
361
|
+
// `t('ns1:key')` occurrences — keep their explicit namespace intact.
|
|
362
|
+
if (oldParts.namespace && newParts.key !== oldParts.key) {
|
|
363
|
+
// ensure ns separator is a string for regex building (default ':')
|
|
364
|
+
const nsSepStr = nsSeparator === false ? ':' : String(nsSeparator);
|
|
365
|
+
const prefixed = `${escapeRegex(String(oldParts.namespace))}${escapeRegex(nsSepStr)}${escapeRegex(oldParts.key)}`;
|
|
366
|
+
const regexPrefixed = new RegExp(`${prefix}\\s*\\(\\s*(['"\`])${prefixed}\\1`, 'g');
|
|
367
|
+
newCode = newCode.replace(regexPrefixed, (match) => {
|
|
368
|
+
changes++;
|
|
369
|
+
// determine replacement: if newParts is explicitly namespaced, use fullKey;
|
|
370
|
+
// otherwise keep the original namespace but swap the key.
|
|
371
|
+
const replacement = newParts.explicitNamespace
|
|
372
|
+
? newParts.fullKey
|
|
373
|
+
: `${oldParts.namespace}${nsSepStr}${newParts.key}`;
|
|
374
|
+
return match.replace(`${oldParts.namespace}${nsSepStr}${oldParts.key}`, replacement);
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
//
|
|
378
|
+
// 3b) fullKey (explicitly namespaced string in call): only when user supplied a namespaced target
|
|
325
379
|
//
|
|
326
380
|
if (oldParts.fullKey && oldParts.explicitNamespace) {
|
|
327
381
|
const regexFull = new RegExp(`${prefix}\\s*\\(\\s*(['"\`])${escapeRegex(oldParts.fullKey)}\\1`, 'g');
|
|
@@ -369,14 +423,22 @@ function replaceKeyWithRegex(code, oldParts, newParts, config, namespaceKeyMap)
|
|
|
369
423
|
}
|
|
370
424
|
//
|
|
371
425
|
// 6) Bare calls without options: fn('key') -> fn('newKey')
|
|
426
|
+
// Apply this replacement only when the old key's namespace is the
|
|
427
|
+
// *effective* default namespace (config.extract.defaultNS ?? 'translation').
|
|
428
|
+
// This preserves previous behaviour: default-namespace bare-calls are
|
|
429
|
+
// considered "key form" and should be rewritten even when the translation
|
|
430
|
+
// file exists but the specific key isn't present.
|
|
372
431
|
//
|
|
373
432
|
{
|
|
374
|
-
const
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
433
|
+
const effectiveDefaultNS = config.extract.defaultNS ?? 'translation';
|
|
434
|
+
if (oldParts.namespace === effectiveDefaultNS) {
|
|
435
|
+
const regexKeyNoOptions = new RegExp(`${prefix}\\s*\\(\\s*(['"\`])${escapeRegex(oldParts.key)}\\1\\s*\\)`, 'g');
|
|
436
|
+
newCode = newCode.replace(regexKeyNoOptions, (match, q) => {
|
|
437
|
+
changes++;
|
|
438
|
+
const replacementKey = newParts.key;
|
|
439
|
+
return match.replace(new RegExp(`(['"\`])${escapeRegex(oldParts.key)}\\1`), `${q}${replacementKey}${q}`);
|
|
440
|
+
});
|
|
441
|
+
}
|
|
380
442
|
}
|
|
381
443
|
//
|
|
382
444
|
// 7) JSX i18nKey attribute (handles both fullKey and key)
|
package/package.json
CHANGED
|
@@ -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,
|
|
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"}
|