i18next-cli 1.50.4 → 1.50.6
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/extractor/core/extractor.js +1 -1
- package/dist/cjs/extractor/core/translation-manager.js +28 -3
- package/dist/cjs/syncer.js +2 -2
- package/dist/esm/cli.js +1 -1
- package/dist/esm/extractor/core/extractor.js +1 -1
- package/dist/esm/extractor/core/translation-manager.js +28 -3
- package/dist/esm/syncer.js +2 -2
- package/package.json +3 -2
- package/types/extractor/core/translation-manager.d.ts.map +1 -1
package/dist/cjs/cli.js
CHANGED
|
@@ -31,7 +31,7 @@ const program = new commander.Command();
|
|
|
31
31
|
program
|
|
32
32
|
.name('i18next-cli')
|
|
33
33
|
.description('A unified, high-performance i18next CLI.')
|
|
34
|
-
.version('1.50.
|
|
34
|
+
.version('1.50.6'); // This string is replaced with the actual version at build time by rollup
|
|
35
35
|
// new: global config override option
|
|
36
36
|
program.option('-c, --config <path>', 'Path to i18next-cli config file (overrides detection)');
|
|
37
37
|
program
|
|
@@ -96,7 +96,7 @@ async function runExtractor(config, options = {}) {
|
|
|
96
96
|
// Show the funnel message only if files were actually changed.
|
|
97
97
|
// When new translation files are created (new namespace or first extraction),
|
|
98
98
|
// always show the funnel regardless of cooldown.
|
|
99
|
-
if (anyFileUpdated && !options.isDryRun)
|
|
99
|
+
if (anyFileUpdated && !options.isDryRun && !options.quiet)
|
|
100
100
|
await printLocizeFunnel(options.logger, anyNewFile);
|
|
101
101
|
return { anyFileUpdated, hasErrors: fileErrors.length > 0 };
|
|
102
102
|
}
|
|
@@ -716,7 +716,32 @@ function buildNewTranslationsForNs(nsKeys, existingTranslations, config, locale,
|
|
|
716
716
|
}
|
|
717
717
|
nestedObject.setNestedValue(newTranslations, key, valueToSet, separator);
|
|
718
718
|
}
|
|
719
|
-
//
|
|
719
|
+
// 2a. When sort is disabled but removeUnusedKeys is on, the rebuild from `{}`
|
|
720
|
+
// lost the original key order. Reorder to match existingTranslations, with new keys at the end.
|
|
721
|
+
if (sort === false && removeUnusedKeys) {
|
|
722
|
+
const reorderToMatch = (newObj, refObj) => {
|
|
723
|
+
if (typeof newObj !== 'object' || newObj === null || typeof refObj !== 'object' || refObj === null)
|
|
724
|
+
return newObj;
|
|
725
|
+
const ordered = {};
|
|
726
|
+
// First: keys from refObj in original order
|
|
727
|
+
for (const key of Object.keys(refObj)) {
|
|
728
|
+
if (key in newObj) {
|
|
729
|
+
ordered[key] = (typeof newObj[key] === 'object' && newObj[key] !== null && typeof refObj[key] === 'object' && refObj[key] !== null)
|
|
730
|
+
? reorderToMatch(newObj[key], refObj[key])
|
|
731
|
+
: newObj[key];
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
// Then: new keys not in refObj
|
|
735
|
+
for (const key of Object.keys(newObj)) {
|
|
736
|
+
if (!(key in ordered)) {
|
|
737
|
+
ordered[key] = newObj[key];
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
return ordered;
|
|
741
|
+
};
|
|
742
|
+
return reorderToMatch(newTranslations, existingTranslations);
|
|
743
|
+
}
|
|
744
|
+
// 2b. If sorting is enabled, recursively sort the entire object.
|
|
720
745
|
// This correctly handles both top-level and nested keys.
|
|
721
746
|
if (sort === true) {
|
|
722
747
|
return sortObject(newTranslations, config);
|
|
@@ -811,7 +836,7 @@ async function getTranslations(keys, objectKeys, config, { syncPrimaryWithDefaul
|
|
|
811
836
|
const keysByNS = new Map();
|
|
812
837
|
for (const k of keys.values()) {
|
|
813
838
|
const ns = k.ns;
|
|
814
|
-
const nsKey = (k.nsIsImplicit && config.extract.defaultNS === false)
|
|
839
|
+
const nsKey = (k.nsIsImplicit && (config.extract.defaultNS === false || config.extract.nsSeparator === false))
|
|
815
840
|
? NO_NS_TOKEN
|
|
816
841
|
: String(ns ?? (config.extract.defaultNS ?? 'translation'));
|
|
817
842
|
if (!keysByNS.has(nsKey))
|
|
@@ -848,7 +873,7 @@ async function getTranslations(keys, objectKeys, config, { syncPrimaryWithDefaul
|
|
|
848
873
|
// (possibly as nested objects when keySeparator is '.'), which should NOT
|
|
849
874
|
// be interpreted as "namespaced files". This avoids splitting a single
|
|
850
875
|
// merged translations file into artificial namespace buckets on re-extract.
|
|
851
|
-
const existingIsNamespaced = (config.extract.defaultNS !== false) && existingKeys.some(k => {
|
|
876
|
+
const existingIsNamespaced = (config.extract.defaultNS !== false) && (config.extract.nsSeparator !== false) && existingKeys.some(k => {
|
|
852
877
|
const v = existingMergedFile[k];
|
|
853
878
|
return typeof v === 'object' && v !== null && !Array.isArray(v);
|
|
854
879
|
});
|
package/dist/cjs/syncer.js
CHANGED
|
@@ -204,10 +204,10 @@ async function runSyncer(config, options = {}) {
|
|
|
204
204
|
}
|
|
205
205
|
spinner.succeed(node_util.styleText('bold', 'Synchronization complete!'));
|
|
206
206
|
logMessages.forEach(msg => internalLogger.info ? internalLogger.info(msg) : console.log(msg));
|
|
207
|
-
if (wasAnythingSynced) {
|
|
207
|
+
if (wasAnythingSynced && !options.quiet) {
|
|
208
208
|
await printLocizeFunnel();
|
|
209
209
|
}
|
|
210
|
-
else {
|
|
210
|
+
else if (!wasAnythingSynced) {
|
|
211
211
|
if (typeof internalLogger.info === 'function')
|
|
212
212
|
internalLogger.info(node_util.styleText(['green', 'bold'], '\n✅ All locales are already in sync.'));
|
|
213
213
|
else
|
package/dist/esm/cli.js
CHANGED
|
@@ -29,7 +29,7 @@ const program = new Command();
|
|
|
29
29
|
program
|
|
30
30
|
.name('i18next-cli')
|
|
31
31
|
.description('A unified, high-performance i18next CLI.')
|
|
32
|
-
.version('1.50.
|
|
32
|
+
.version('1.50.6'); // This string is replaced with the actual version at build time by rollup
|
|
33
33
|
// new: global config override option
|
|
34
34
|
program.option('-c, --config <path>', 'Path to i18next-cli config file (overrides detection)');
|
|
35
35
|
program
|
|
@@ -94,7 +94,7 @@ async function runExtractor(config, options = {}) {
|
|
|
94
94
|
// Show the funnel message only if files were actually changed.
|
|
95
95
|
// When new translation files are created (new namespace or first extraction),
|
|
96
96
|
// always show the funnel regardless of cooldown.
|
|
97
|
-
if (anyFileUpdated && !options.isDryRun)
|
|
97
|
+
if (anyFileUpdated && !options.isDryRun && !options.quiet)
|
|
98
98
|
await printLocizeFunnel(options.logger, anyNewFile);
|
|
99
99
|
return { anyFileUpdated, hasErrors: fileErrors.length > 0 };
|
|
100
100
|
}
|
|
@@ -714,7 +714,32 @@ function buildNewTranslationsForNs(nsKeys, existingTranslations, config, locale,
|
|
|
714
714
|
}
|
|
715
715
|
setNestedValue(newTranslations, key, valueToSet, separator);
|
|
716
716
|
}
|
|
717
|
-
//
|
|
717
|
+
// 2a. When sort is disabled but removeUnusedKeys is on, the rebuild from `{}`
|
|
718
|
+
// lost the original key order. Reorder to match existingTranslations, with new keys at the end.
|
|
719
|
+
if (sort === false && removeUnusedKeys) {
|
|
720
|
+
const reorderToMatch = (newObj, refObj) => {
|
|
721
|
+
if (typeof newObj !== 'object' || newObj === null || typeof refObj !== 'object' || refObj === null)
|
|
722
|
+
return newObj;
|
|
723
|
+
const ordered = {};
|
|
724
|
+
// First: keys from refObj in original order
|
|
725
|
+
for (const key of Object.keys(refObj)) {
|
|
726
|
+
if (key in newObj) {
|
|
727
|
+
ordered[key] = (typeof newObj[key] === 'object' && newObj[key] !== null && typeof refObj[key] === 'object' && refObj[key] !== null)
|
|
728
|
+
? reorderToMatch(newObj[key], refObj[key])
|
|
729
|
+
: newObj[key];
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
// Then: new keys not in refObj
|
|
733
|
+
for (const key of Object.keys(newObj)) {
|
|
734
|
+
if (!(key in ordered)) {
|
|
735
|
+
ordered[key] = newObj[key];
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
return ordered;
|
|
739
|
+
};
|
|
740
|
+
return reorderToMatch(newTranslations, existingTranslations);
|
|
741
|
+
}
|
|
742
|
+
// 2b. If sorting is enabled, recursively sort the entire object.
|
|
718
743
|
// This correctly handles both top-level and nested keys.
|
|
719
744
|
if (sort === true) {
|
|
720
745
|
return sortObject(newTranslations, config);
|
|
@@ -809,7 +834,7 @@ async function getTranslations(keys, objectKeys, config, { syncPrimaryWithDefaul
|
|
|
809
834
|
const keysByNS = new Map();
|
|
810
835
|
for (const k of keys.values()) {
|
|
811
836
|
const ns = k.ns;
|
|
812
|
-
const nsKey = (k.nsIsImplicit && config.extract.defaultNS === false)
|
|
837
|
+
const nsKey = (k.nsIsImplicit && (config.extract.defaultNS === false || config.extract.nsSeparator === false))
|
|
813
838
|
? NO_NS_TOKEN
|
|
814
839
|
: String(ns ?? (config.extract.defaultNS ?? 'translation'));
|
|
815
840
|
if (!keysByNS.has(nsKey))
|
|
@@ -846,7 +871,7 @@ async function getTranslations(keys, objectKeys, config, { syncPrimaryWithDefaul
|
|
|
846
871
|
// (possibly as nested objects when keySeparator is '.'), which should NOT
|
|
847
872
|
// be interpreted as "namespaced files". This avoids splitting a single
|
|
848
873
|
// merged translations file into artificial namespace buckets on re-extract.
|
|
849
|
-
const existingIsNamespaced = (config.extract.defaultNS !== false) && existingKeys.some(k => {
|
|
874
|
+
const existingIsNamespaced = (config.extract.defaultNS !== false) && (config.extract.nsSeparator !== false) && existingKeys.some(k => {
|
|
850
875
|
const v = existingMergedFile[k];
|
|
851
876
|
return typeof v === 'object' && v !== null && !Array.isArray(v);
|
|
852
877
|
});
|
package/dist/esm/syncer.js
CHANGED
|
@@ -202,10 +202,10 @@ async function runSyncer(config, options = {}) {
|
|
|
202
202
|
}
|
|
203
203
|
spinner.succeed(styleText('bold', 'Synchronization complete!'));
|
|
204
204
|
logMessages.forEach(msg => internalLogger.info ? internalLogger.info(msg) : console.log(msg));
|
|
205
|
-
if (wasAnythingSynced) {
|
|
205
|
+
if (wasAnythingSynced && !options.quiet) {
|
|
206
206
|
await printLocizeFunnel();
|
|
207
207
|
}
|
|
208
|
-
else {
|
|
208
|
+
else if (!wasAnythingSynced) {
|
|
209
209
|
if (typeof internalLogger.info === 'function')
|
|
210
210
|
internalLogger.info(styleText(['green', 'bold'], '\n✅ All locales are already in sync.'));
|
|
211
211
|
else
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "i18next-cli",
|
|
3
|
-
"version": "1.50.
|
|
3
|
+
"version": "1.50.6",
|
|
4
4
|
"description": "A unified, high-performance i18next CLI.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -54,6 +54,7 @@
|
|
|
54
54
|
"devDependencies": {
|
|
55
55
|
"@rollup/plugin-replace": "^6.0.3",
|
|
56
56
|
"@rollup/plugin-terser": "^1.0.0",
|
|
57
|
+
"@rollup/plugin-typescript": "^12.3.0",
|
|
57
58
|
"@types/inquirer": "^9.0.9",
|
|
58
59
|
"@types/node": "^25.5.0",
|
|
59
60
|
"@types/react": "^19.2.14",
|
|
@@ -64,7 +65,7 @@
|
|
|
64
65
|
"eslint-plugin-import": "^2.32.0",
|
|
65
66
|
"memfs": "^4.57.1",
|
|
66
67
|
"neostandard": "^0.13.0",
|
|
67
|
-
"rollup
|
|
68
|
+
"rollup": "^4.60.0",
|
|
68
69
|
"typescript": "^5.9.3",
|
|
69
70
|
"unplugin-swc": "^1.5.9",
|
|
70
71
|
"vitest": "^4.1.0"
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"translation-manager.d.ts","sourceRoot":"","sources":["../../../src/extractor/core/translation-manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,oBAAoB,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAA;
|
|
1
|
+
{"version":3,"file":"translation-manager.d.ts","sourceRoot":"","sources":["../../../src/extractor/core/translation-manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,oBAAoB,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAA;AAk5B9F;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAsB,eAAe,CACnC,IAAI,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,EAC/B,UAAU,EAAE,GAAG,CAAC,MAAM,CAAC,EACvB,MAAM,EAAE,oBAAoB,EAC5B,EACE,uBAA+B,EAC/B,OAAe,EACf,MAA4B,EAC7B,GAAE;IACD,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAA;CACX,GACL,OAAO,CAAC,iBAAiB,EAAE,CAAC,CA6I9B"}
|