i18next-cli 1.56.5 → 1.56.7
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/translation-manager.js +23 -0
- package/dist/cjs/extractor/parsers/call-expression-handler.js +17 -15
- package/dist/esm/cli.js +1 -1
- package/dist/esm/extractor/core/translation-manager.js +23 -0
- package/dist/esm/extractor/parsers/call-expression-handler.js +17 -15
- package/package.json +1 -1
- package/types/extractor/core/translation-manager.d.ts.map +1 -1
- package/types/extractor/parsers/call-expression-handler.d.ts.map +1 -1
package/dist/cjs/cli.js
CHANGED
|
@@ -32,7 +32,7 @@ const program = new commander.Command();
|
|
|
32
32
|
program
|
|
33
33
|
.name('i18next-cli')
|
|
34
34
|
.description('A unified, high-performance i18next CLI.')
|
|
35
|
-
.version('1.56.
|
|
35
|
+
.version('1.56.7'); // This string is replaced with the actual version at build time by rollup
|
|
36
36
|
// new: global config override option
|
|
37
37
|
program.option('-c, --config <path>', 'Path to i18next-cli config file (overrides detection)');
|
|
38
38
|
program
|
|
@@ -224,6 +224,11 @@ function buildNewTranslationsForNs(nsKeys, existingTranslations, config, locale,
|
|
|
224
224
|
const cardinalCategories = cardinalRules.resolvedOptions().pluralCategories;
|
|
225
225
|
cardinalCategories.forEach(cat => targetLanguagePluralCategories.add(cat));
|
|
226
226
|
ordinalRules.resolvedOptions().pluralCategories.forEach(cat => targetLanguagePluralCategories.add(`ordinal_${cat}`));
|
|
227
|
+
// Plural categories of the primary language — used to recognise locale-specific
|
|
228
|
+
// plural variants (e.g. French `_many` when primary is English) so we don't
|
|
229
|
+
// treat their absence from the primary file as a "divergence" during --sync-all.
|
|
230
|
+
const primaryCardinalCategoriesSet = new Set(pluralRules.safePluralRules(primaryLanguage, { type: 'cardinal' }).resolvedOptions().pluralCategories);
|
|
231
|
+
const primaryOrdinalCategoriesSet = new Set(pluralRules.safePluralRules(primaryLanguage, { type: 'ordinal' }).resolvedOptions().pluralCategories);
|
|
227
232
|
// When allPluralForms is enabled, compute the union of cardinal categories across all configured locales.
|
|
228
233
|
// This ensures every locale gets the same set of plural keys — but only the forms actually needed by at least one locale.
|
|
229
234
|
const allLocalesCardinalCategories = config.extract.allPluralForms
|
|
@@ -762,8 +767,26 @@ function buildNewTranslationsForNs(nsKeys, existingTranslations, config, locale,
|
|
|
762
767
|
else {
|
|
763
768
|
// Non-primary locale behavior
|
|
764
769
|
const isVariantKey = key.includes(pluralSeparator) || key.includes(contextSeparator);
|
|
770
|
+
// A plural variant whose category exists in the current locale but not in the
|
|
771
|
+
// primary language (e.g. French `_many` vs English `one`/`other`) will always be
|
|
772
|
+
// absent from the primary file by CLDR design. Treat that absence as expected —
|
|
773
|
+
// not as the primary "diverging" from the default — so --sync-all preserves the
|
|
774
|
+
// locale-specific translation instead of clearing it on every run. (issue #248)
|
|
775
|
+
const isLocaleSpecificPluralVariant = (() => {
|
|
776
|
+
if (!hasCount)
|
|
777
|
+
return false;
|
|
778
|
+
const parts = String(key).split(pluralSeparator);
|
|
779
|
+
if (parts.length < 2)
|
|
780
|
+
return false;
|
|
781
|
+
const lastPart = parts[parts.length - 1];
|
|
782
|
+
if (isOrdinal && parts.length >= 3 && parts[parts.length - 2] === 'ordinal') {
|
|
783
|
+
return !primaryOrdinalCategoriesSet.has(lastPart);
|
|
784
|
+
}
|
|
785
|
+
return !primaryCardinalCategoriesSet.has(lastPart);
|
|
786
|
+
})();
|
|
765
787
|
const primaryDivergedFromDefault = Boolean(defaultValue$1 &&
|
|
766
788
|
!primaryShouldPreserveObject &&
|
|
789
|
+
!isLocaleSpecificPluralVariant &&
|
|
767
790
|
(primaryExistingValue === undefined ||
|
|
768
791
|
primaryIsStaleObject ||
|
|
769
792
|
((!isVariantKey || explicitDefault) &&
|
|
@@ -68,21 +68,6 @@ class CallExpressionHandler {
|
|
|
68
68
|
return;
|
|
69
69
|
// The scope lookup will only work for simple identifiers, which is okay for this fix.
|
|
70
70
|
let scopeInfo = getScopeInfo(functionName);
|
|
71
|
-
// For member expressions like `emailError.t`, also try the object part(s) as a
|
|
72
|
-
// fallback. This lets custom hooks that return an object with a `t` method
|
|
73
|
-
// (e.g. `const emailError = useTranslateKeyState('auth')`) propagate their
|
|
74
|
-
// namespace/keyPrefix to the inner `t` call.
|
|
75
|
-
if (!scopeInfo && functionName.includes('.')) {
|
|
76
|
-
const parts = functionName.split('.');
|
|
77
|
-
for (let i = parts.length - 1; i > 0; i--) {
|
|
78
|
-
const candidate = parts.slice(0, i).join('.');
|
|
79
|
-
const candidateScope = getScopeInfo(candidate);
|
|
80
|
-
if (candidateScope) {
|
|
81
|
-
scopeInfo = candidateScope;
|
|
82
|
-
break;
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
71
|
const configuredFunctions = this.config.extract.functions || ['t', '*.t'];
|
|
87
72
|
let isFunctionToParse = scopeInfo !== undefined; // A scoped variable (from useTranslation, etc.) is always parsed.
|
|
88
73
|
if (!isFunctionToParse) {
|
|
@@ -105,6 +90,23 @@ class CallExpressionHandler {
|
|
|
105
90
|
}
|
|
106
91
|
if (!isFunctionToParse || node.arguments.length === 0)
|
|
107
92
|
return;
|
|
93
|
+
// For member expressions like `emailError.t`, also try the object part(s) as a
|
|
94
|
+
// fallback. This lets custom hooks that return an object with a `t` method
|
|
95
|
+
// (e.g. `const emailError = useTranslateKeyState('auth')`) propagate their
|
|
96
|
+
// namespace/keyPrefix to the inner `t` call. This runs AFTER the pattern
|
|
97
|
+
// check so an in-scope prefix cannot by itself promote an arbitrary method
|
|
98
|
+
// call (e.g. `i18n.language.substring(...)`) into a translation call.
|
|
99
|
+
if (!scopeInfo && functionName.includes('.')) {
|
|
100
|
+
const parts = functionName.split('.');
|
|
101
|
+
for (let i = parts.length - 1; i > 0; i--) {
|
|
102
|
+
const candidate = parts.slice(0, i).join('.');
|
|
103
|
+
const candidateScope = getScopeInfo(candidate);
|
|
104
|
+
if (candidateScope) {
|
|
105
|
+
scopeInfo = candidateScope;
|
|
106
|
+
break;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
108
110
|
const { keysToProcess, isSelectorAPI } = this.handleCallExpressionArgument(node, 0);
|
|
109
111
|
if (keysToProcess.length === 0)
|
|
110
112
|
return;
|
package/dist/esm/cli.js
CHANGED
|
@@ -30,7 +30,7 @@ const program = new Command();
|
|
|
30
30
|
program
|
|
31
31
|
.name('i18next-cli')
|
|
32
32
|
.description('A unified, high-performance i18next CLI.')
|
|
33
|
-
.version('1.56.
|
|
33
|
+
.version('1.56.7'); // This string is replaced with the actual version at build time by rollup
|
|
34
34
|
// new: global config override option
|
|
35
35
|
program.option('-c, --config <path>', 'Path to i18next-cli config file (overrides detection)');
|
|
36
36
|
program
|
|
@@ -222,6 +222,11 @@ function buildNewTranslationsForNs(nsKeys, existingTranslations, config, locale,
|
|
|
222
222
|
const cardinalCategories = cardinalRules.resolvedOptions().pluralCategories;
|
|
223
223
|
cardinalCategories.forEach(cat => targetLanguagePluralCategories.add(cat));
|
|
224
224
|
ordinalRules.resolvedOptions().pluralCategories.forEach(cat => targetLanguagePluralCategories.add(`ordinal_${cat}`));
|
|
225
|
+
// Plural categories of the primary language — used to recognise locale-specific
|
|
226
|
+
// plural variants (e.g. French `_many` when primary is English) so we don't
|
|
227
|
+
// treat their absence from the primary file as a "divergence" during --sync-all.
|
|
228
|
+
const primaryCardinalCategoriesSet = new Set(safePluralRules(primaryLanguage, { type: 'cardinal' }).resolvedOptions().pluralCategories);
|
|
229
|
+
const primaryOrdinalCategoriesSet = new Set(safePluralRules(primaryLanguage, { type: 'ordinal' }).resolvedOptions().pluralCategories);
|
|
225
230
|
// When allPluralForms is enabled, compute the union of cardinal categories across all configured locales.
|
|
226
231
|
// This ensures every locale gets the same set of plural keys — but only the forms actually needed by at least one locale.
|
|
227
232
|
const allLocalesCardinalCategories = config.extract.allPluralForms
|
|
@@ -760,8 +765,26 @@ function buildNewTranslationsForNs(nsKeys, existingTranslations, config, locale,
|
|
|
760
765
|
else {
|
|
761
766
|
// Non-primary locale behavior
|
|
762
767
|
const isVariantKey = key.includes(pluralSeparator) || key.includes(contextSeparator);
|
|
768
|
+
// A plural variant whose category exists in the current locale but not in the
|
|
769
|
+
// primary language (e.g. French `_many` vs English `one`/`other`) will always be
|
|
770
|
+
// absent from the primary file by CLDR design. Treat that absence as expected —
|
|
771
|
+
// not as the primary "diverging" from the default — so --sync-all preserves the
|
|
772
|
+
// locale-specific translation instead of clearing it on every run. (issue #248)
|
|
773
|
+
const isLocaleSpecificPluralVariant = (() => {
|
|
774
|
+
if (!hasCount)
|
|
775
|
+
return false;
|
|
776
|
+
const parts = String(key).split(pluralSeparator);
|
|
777
|
+
if (parts.length < 2)
|
|
778
|
+
return false;
|
|
779
|
+
const lastPart = parts[parts.length - 1];
|
|
780
|
+
if (isOrdinal && parts.length >= 3 && parts[parts.length - 2] === 'ordinal') {
|
|
781
|
+
return !primaryOrdinalCategoriesSet.has(lastPart);
|
|
782
|
+
}
|
|
783
|
+
return !primaryCardinalCategoriesSet.has(lastPart);
|
|
784
|
+
})();
|
|
763
785
|
const primaryDivergedFromDefault = Boolean(defaultValue &&
|
|
764
786
|
!primaryShouldPreserveObject &&
|
|
787
|
+
!isLocaleSpecificPluralVariant &&
|
|
765
788
|
(primaryExistingValue === undefined ||
|
|
766
789
|
primaryIsStaleObject ||
|
|
767
790
|
((!isVariantKey || explicitDefault) &&
|
|
@@ -66,21 +66,6 @@ class CallExpressionHandler {
|
|
|
66
66
|
return;
|
|
67
67
|
// The scope lookup will only work for simple identifiers, which is okay for this fix.
|
|
68
68
|
let scopeInfo = getScopeInfo(functionName);
|
|
69
|
-
// For member expressions like `emailError.t`, also try the object part(s) as a
|
|
70
|
-
// fallback. This lets custom hooks that return an object with a `t` method
|
|
71
|
-
// (e.g. `const emailError = useTranslateKeyState('auth')`) propagate their
|
|
72
|
-
// namespace/keyPrefix to the inner `t` call.
|
|
73
|
-
if (!scopeInfo && functionName.includes('.')) {
|
|
74
|
-
const parts = functionName.split('.');
|
|
75
|
-
for (let i = parts.length - 1; i > 0; i--) {
|
|
76
|
-
const candidate = parts.slice(0, i).join('.');
|
|
77
|
-
const candidateScope = getScopeInfo(candidate);
|
|
78
|
-
if (candidateScope) {
|
|
79
|
-
scopeInfo = candidateScope;
|
|
80
|
-
break;
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
69
|
const configuredFunctions = this.config.extract.functions || ['t', '*.t'];
|
|
85
70
|
let isFunctionToParse = scopeInfo !== undefined; // A scoped variable (from useTranslation, etc.) is always parsed.
|
|
86
71
|
if (!isFunctionToParse) {
|
|
@@ -103,6 +88,23 @@ class CallExpressionHandler {
|
|
|
103
88
|
}
|
|
104
89
|
if (!isFunctionToParse || node.arguments.length === 0)
|
|
105
90
|
return;
|
|
91
|
+
// For member expressions like `emailError.t`, also try the object part(s) as a
|
|
92
|
+
// fallback. This lets custom hooks that return an object with a `t` method
|
|
93
|
+
// (e.g. `const emailError = useTranslateKeyState('auth')`) propagate their
|
|
94
|
+
// namespace/keyPrefix to the inner `t` call. This runs AFTER the pattern
|
|
95
|
+
// check so an in-scope prefix cannot by itself promote an arbitrary method
|
|
96
|
+
// call (e.g. `i18n.language.substring(...)`) into a translation call.
|
|
97
|
+
if (!scopeInfo && functionName.includes('.')) {
|
|
98
|
+
const parts = functionName.split('.');
|
|
99
|
+
for (let i = parts.length - 1; i > 0; i--) {
|
|
100
|
+
const candidate = parts.slice(0, i).join('.');
|
|
101
|
+
const candidateScope = getScopeInfo(candidate);
|
|
102
|
+
if (candidateScope) {
|
|
103
|
+
scopeInfo = candidateScope;
|
|
104
|
+
break;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
106
108
|
const { keysToProcess, isSelectorAPI } = this.handleCallExpressionArgument(node, 0);
|
|
107
109
|
if (keysToProcess.length === 0)
|
|
108
110
|
return;
|
package/package.json
CHANGED
|
@@ -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;AA8iC9F;;;;;;;;;;;;;;;;;;;;;;;;;;;;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,oBAA4B,EAC5B,MAA4B,EAC7B,GAAE;IACD,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,MAAM,CAAC,EAAE,MAAM,CAAA;CACX,GACL,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAiK9B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"call-expression-handler.d.ts","sourceRoot":"","sources":["../../../src/extractor/parsers/call-expression-handler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAA6C,MAAM,WAAW,CAAA;AAC1F,OAAO,KAAK,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,EAAgB,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAC1G,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAA;AAa7D,qBAAa,qBAAqB;IAChC,OAAO,CAAC,aAAa,CAAe;IACpC,OAAO,CAAC,MAAM,CAAuC;IACrD,OAAO,CAAC,MAAM,CAAQ;IACtB,OAAO,CAAC,kBAAkB,CAAoB;IACvC,UAAU,cAAoB;IACrC,OAAO,CAAC,cAAc,CAAc;IACpC,OAAO,CAAC,cAAc,CAAc;IACpC,OAAO,CAAC,iBAAiB,CAAsC;gBAG7D,MAAM,EAAE,IAAI,CAAC,oBAAoB,EAAE,SAAS,CAAC,EAC7C,aAAa,EAAE,aAAa,EAC5B,MAAM,EAAE,MAAM,EACd,kBAAkB,EAAE,kBAAkB,EACtC,cAAc,EAAE,MAAM,MAAM,EAC5B,cAAc,EAAE,MAAM,MAAM,EAC5B,iBAAiB,GAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,GAAG,SAA2B;IAW3E;;;;;OAKG;IACH,OAAO,CAAC,mBAAmB;IAiB3B;;;;;;;;;;;;;;OAcG;IACH,oBAAoB,CAAE,IAAI,EAAE,cAAc,EAAE,YAAY,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,SAAS,GAAG,SAAS,GAAG,IAAI;
|
|
1
|
+
{"version":3,"file":"call-expression-handler.d.ts","sourceRoot":"","sources":["../../../src/extractor/parsers/call-expression-handler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAA6C,MAAM,WAAW,CAAA;AAC1F,OAAO,KAAK,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,EAAgB,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAC1G,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAA;AAa7D,qBAAa,qBAAqB;IAChC,OAAO,CAAC,aAAa,CAAe;IACpC,OAAO,CAAC,MAAM,CAAuC;IACrD,OAAO,CAAC,MAAM,CAAQ;IACtB,OAAO,CAAC,kBAAkB,CAAoB;IACvC,UAAU,cAAoB;IACrC,OAAO,CAAC,cAAc,CAAc;IACpC,OAAO,CAAC,cAAc,CAAc;IACpC,OAAO,CAAC,iBAAiB,CAAsC;gBAG7D,MAAM,EAAE,IAAI,CAAC,oBAAoB,EAAE,SAAS,CAAC,EAC7C,aAAa,EAAE,aAAa,EAC5B,MAAM,EAAE,MAAM,EACd,kBAAkB,EAAE,kBAAkB,EACtC,cAAc,EAAE,MAAM,MAAM,EAC5B,cAAc,EAAE,MAAM,MAAM,EAC5B,iBAAiB,GAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,GAAG,SAA2B;IAW3E;;;;;OAKG;IACH,OAAO,CAAC,mBAAmB;IAiB3B;;;;;;;;;;;;;;OAcG;IACH,oBAAoB,CAAE,IAAI,EAAE,cAAc,EAAE,YAAY,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,SAAS,GAAG,SAAS,GAAG,IAAI;IA4ZxG;;;;OAIG;IACH,OAAO,CAAC,iBAAiB;IAmBzB,OAAO,CAAC,wBAAwB;IAyEhC;;;;;;OAMG;IACH,OAAO,CAAC,4BAA4B;IA8BpC;;;;;;;;;;;;;;;;OAgBG;IACH,OAAO,CAAC,uBAAuB;IAgB/B;;;;;;;;OAQG;IACH,OAAO,CAAC,iCAAiC;IAwFzC;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,gBAAgB;IAyMxB;;;;;;;;;OASG;IACH,OAAO,CAAC,eAAe;CA2BxB"}
|