i18next-cli 1.51.0 → 1.51.2
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/README.md +4 -4
- package/dist/cjs/cli.js +1 -1
- package/dist/cjs/extractor/core/translation-manager.js +25 -11
- package/dist/esm/cli.js +1 -1
- package/dist/esm/extractor/core/translation-manager.js +25 -11
- package/package.json +1 -1
- package/types/extractor/core/translation-manager.d.ts.map +1 -1
- package/types/types.d.ts +4 -4
package/README.md
CHANGED
|
@@ -649,10 +649,10 @@ export default defineConfig({
|
|
|
649
649
|
// The count option can still be used for {{count}} interpolation in the translation value
|
|
650
650
|
disablePlurals: false, // Default: false
|
|
651
651
|
|
|
652
|
-
//
|
|
653
|
-
//
|
|
654
|
-
//
|
|
655
|
-
//
|
|
652
|
+
// Generate the union of all configured locales' plural forms for every language.
|
|
653
|
+
// For example, if your locales are ['en', 'pl'], English normally only gets _one/_other,
|
|
654
|
+
// but with this option it also gets _few/_many (needed by Polish).
|
|
655
|
+
// Useful when you want a consistent set of plural keys across all locales.
|
|
656
656
|
allPluralForms: false, // Default: false
|
|
657
657
|
|
|
658
658
|
// Prefix for nested translations.
|
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.51.
|
|
34
|
+
.version('1.51.2'); // 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
|
|
@@ -273,6 +273,17 @@ function buildNewTranslationsForNs(nsKeys, existingTranslations, config, locale,
|
|
|
273
273
|
const cardinalCategories = cardinalRules.resolvedOptions().pluralCategories;
|
|
274
274
|
cardinalCategories.forEach(cat => targetLanguagePluralCategories.add(cat));
|
|
275
275
|
ordinalRules.resolvedOptions().pluralCategories.forEach(cat => targetLanguagePluralCategories.add(`ordinal_${cat}`));
|
|
276
|
+
// When allPluralForms is enabled, compute the union of cardinal categories across all configured locales.
|
|
277
|
+
// This ensures every locale gets the same set of plural keys — but only the forms actually needed by at least one locale.
|
|
278
|
+
const allLocalesCardinalCategories = config.extract.allPluralForms
|
|
279
|
+
? (() => {
|
|
280
|
+
const union = new Set();
|
|
281
|
+
for (const loc of config.locales) {
|
|
282
|
+
pluralRules.safePluralRules(loc, { type: 'cardinal' }).resolvedOptions().pluralCategories.forEach(c => union.add(c));
|
|
283
|
+
}
|
|
284
|
+
return [...union];
|
|
285
|
+
})()
|
|
286
|
+
: null;
|
|
276
287
|
// Prepare namespace pattern checking helpers
|
|
277
288
|
const rawPreserve = config.extract.preservePatterns || [];
|
|
278
289
|
// Helper to check if a key should be filtered out during extraction
|
|
@@ -364,7 +375,7 @@ function buildNewTranslationsForNs(nsKeys, existingTranslations, config, locale,
|
|
|
364
375
|
return true;
|
|
365
376
|
}
|
|
366
377
|
// When allPluralForms is enabled, include all CLDR plural forms regardless of the target language
|
|
367
|
-
if (
|
|
378
|
+
if (allLocalesCardinalCategories && allLocalesCardinalCategories.includes(lastPart)) {
|
|
368
379
|
return true;
|
|
369
380
|
}
|
|
370
381
|
if (isOrdinal && keyParts.includes('ordinal')) {
|
|
@@ -511,8 +522,8 @@ function buildNewTranslationsForNs(nsKeys, existingTranslations, config, locale,
|
|
|
511
522
|
let formsToGenerate;
|
|
512
523
|
if (locale !== primaryLanguage) {
|
|
513
524
|
// For non-primary locales:
|
|
514
|
-
// 1. Generate the forms that locale actually needs (or all forms if allPluralForms is enabled)
|
|
515
|
-
formsToGenerate =
|
|
525
|
+
// 1. Generate the forms that locale actually needs (or union of all locales' forms if allPluralForms is enabled)
|
|
526
|
+
formsToGenerate = allLocalesCardinalCategories ?? cardinalCategories;
|
|
516
527
|
// 2. Also prepare empty placeholders for all OTHER CLDR forms not in this locale
|
|
517
528
|
// so translators can add them manually without --sync-primary removing them
|
|
518
529
|
const otherForms = pluralForms.filter(f => !cardinalCategories.includes(f));
|
|
@@ -556,13 +567,13 @@ function buildNewTranslationsForNs(nsKeys, existingTranslations, config, locale,
|
|
|
556
567
|
else {
|
|
557
568
|
// For primary language, only expand if it has multiple plural forms
|
|
558
569
|
// Single-"other" languages (ja, zh, ko) should NOT expand the base key (unless allPluralForms is enabled)
|
|
559
|
-
if (cardinalCategories.length === 1 && cardinalCategories[0] === 'other' && !
|
|
570
|
+
if (cardinalCategories.length === 1 && cardinalCategories[0] === 'other' && !allLocalesCardinalCategories) {
|
|
560
571
|
// Single-"other" language - don't expand, keep just the base key
|
|
561
572
|
formsToGenerate = [];
|
|
562
573
|
}
|
|
563
574
|
else {
|
|
564
|
-
// Multi-form language - expand to its plural forms (or all forms if allPluralForms is enabled)
|
|
565
|
-
formsToGenerate =
|
|
575
|
+
// Multi-form language - expand to its plural forms (or union of all locales' forms if allPluralForms is enabled)
|
|
576
|
+
formsToGenerate = allLocalesCardinalCategories ?? cardinalCategories;
|
|
566
577
|
for (const form of formsToGenerate) {
|
|
567
578
|
const finalKey = isOrdinal
|
|
568
579
|
? `${base}${pluralSeparator}${form}`
|
|
@@ -710,11 +721,11 @@ function buildNewTranslationsForNs(nsKeys, existingTranslations, config, locale,
|
|
|
710
721
|
}
|
|
711
722
|
nestedObject.setNestedValue(newTranslations, key, valueToSet, separator);
|
|
712
723
|
}
|
|
713
|
-
// When allPluralForms is enabled, ensure all
|
|
724
|
+
// When allPluralForms is enabled, ensure all union plural forms exist for every plural base key.
|
|
714
725
|
// The extractor only generates forms for the configured locales' categories, so we need to fill in the rest.
|
|
715
|
-
if (
|
|
726
|
+
if (allLocalesCardinalCategories && !config.extract.disablePlurals) {
|
|
716
727
|
for (const base of expandedBases) {
|
|
717
|
-
for (const form of
|
|
728
|
+
for (const form of allLocalesCardinalCategories) {
|
|
718
729
|
const finalKey = `${base}${pluralSeparator}${form}`;
|
|
719
730
|
const separator = finalKey.startsWith('<') ? false : (keySeparator ?? '.');
|
|
720
731
|
const existingInNew = nestedObject.getNestedValue(newTranslations, finalKey, separator);
|
|
@@ -904,9 +915,12 @@ async function getTranslations(keys, objectKeys, config, { syncPrimaryWithDefaul
|
|
|
904
915
|
}
|
|
905
916
|
// When nsSeparator is false, keys resolved to the defaultNS (e.g. from
|
|
906
917
|
// useTranslation() with no args) should be treated as top-level, not
|
|
907
|
-
// wrapped under the namespace name
|
|
918
|
+
// wrapped under the namespace name — but only when there are no other
|
|
919
|
+
// explicit namespaces. If multiple namespaces exist, we must keep the
|
|
920
|
+
// default namespace wrapper to avoid flattening it into the top level (#227).
|
|
908
921
|
const defaultNs = String(config.extract.defaultNS ?? 'translation');
|
|
909
|
-
const
|
|
922
|
+
const hasOtherNamespaces = [...keysByNS.keys()].some(k => k !== NO_NS_TOKEN && k !== defaultNs);
|
|
923
|
+
const isTopLevel = (nsKey) => nsKey === NO_NS_TOKEN || (config.extract.nsSeparator === false && nsKey === defaultNs && !hasOtherNamespaces);
|
|
910
924
|
for (const nsKey of namespacesToProcess) {
|
|
911
925
|
const nsKeys = keysByNS.get(nsKey) || [];
|
|
912
926
|
if (isTopLevel(nsKey)) {
|
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.51.
|
|
32
|
+
.version('1.51.2'); // 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
|
|
@@ -271,6 +271,17 @@ function buildNewTranslationsForNs(nsKeys, existingTranslations, config, locale,
|
|
|
271
271
|
const cardinalCategories = cardinalRules.resolvedOptions().pluralCategories;
|
|
272
272
|
cardinalCategories.forEach(cat => targetLanguagePluralCategories.add(cat));
|
|
273
273
|
ordinalRules.resolvedOptions().pluralCategories.forEach(cat => targetLanguagePluralCategories.add(`ordinal_${cat}`));
|
|
274
|
+
// When allPluralForms is enabled, compute the union of cardinal categories across all configured locales.
|
|
275
|
+
// This ensures every locale gets the same set of plural keys — but only the forms actually needed by at least one locale.
|
|
276
|
+
const allLocalesCardinalCategories = config.extract.allPluralForms
|
|
277
|
+
? (() => {
|
|
278
|
+
const union = new Set();
|
|
279
|
+
for (const loc of config.locales) {
|
|
280
|
+
safePluralRules(loc, { type: 'cardinal' }).resolvedOptions().pluralCategories.forEach(c => union.add(c));
|
|
281
|
+
}
|
|
282
|
+
return [...union];
|
|
283
|
+
})()
|
|
284
|
+
: null;
|
|
274
285
|
// Prepare namespace pattern checking helpers
|
|
275
286
|
const rawPreserve = config.extract.preservePatterns || [];
|
|
276
287
|
// Helper to check if a key should be filtered out during extraction
|
|
@@ -362,7 +373,7 @@ function buildNewTranslationsForNs(nsKeys, existingTranslations, config, locale,
|
|
|
362
373
|
return true;
|
|
363
374
|
}
|
|
364
375
|
// When allPluralForms is enabled, include all CLDR plural forms regardless of the target language
|
|
365
|
-
if (
|
|
376
|
+
if (allLocalesCardinalCategories && allLocalesCardinalCategories.includes(lastPart)) {
|
|
366
377
|
return true;
|
|
367
378
|
}
|
|
368
379
|
if (isOrdinal && keyParts.includes('ordinal')) {
|
|
@@ -509,8 +520,8 @@ function buildNewTranslationsForNs(nsKeys, existingTranslations, config, locale,
|
|
|
509
520
|
let formsToGenerate;
|
|
510
521
|
if (locale !== primaryLanguage) {
|
|
511
522
|
// For non-primary locales:
|
|
512
|
-
// 1. Generate the forms that locale actually needs (or all forms if allPluralForms is enabled)
|
|
513
|
-
formsToGenerate =
|
|
523
|
+
// 1. Generate the forms that locale actually needs (or union of all locales' forms if allPluralForms is enabled)
|
|
524
|
+
formsToGenerate = allLocalesCardinalCategories ?? cardinalCategories;
|
|
514
525
|
// 2. Also prepare empty placeholders for all OTHER CLDR forms not in this locale
|
|
515
526
|
// so translators can add them manually without --sync-primary removing them
|
|
516
527
|
const otherForms = pluralForms.filter(f => !cardinalCategories.includes(f));
|
|
@@ -554,13 +565,13 @@ function buildNewTranslationsForNs(nsKeys, existingTranslations, config, locale,
|
|
|
554
565
|
else {
|
|
555
566
|
// For primary language, only expand if it has multiple plural forms
|
|
556
567
|
// Single-"other" languages (ja, zh, ko) should NOT expand the base key (unless allPluralForms is enabled)
|
|
557
|
-
if (cardinalCategories.length === 1 && cardinalCategories[0] === 'other' && !
|
|
568
|
+
if (cardinalCategories.length === 1 && cardinalCategories[0] === 'other' && !allLocalesCardinalCategories) {
|
|
558
569
|
// Single-"other" language - don't expand, keep just the base key
|
|
559
570
|
formsToGenerate = [];
|
|
560
571
|
}
|
|
561
572
|
else {
|
|
562
|
-
// Multi-form language - expand to its plural forms (or all forms if allPluralForms is enabled)
|
|
563
|
-
formsToGenerate =
|
|
573
|
+
// Multi-form language - expand to its plural forms (or union of all locales' forms if allPluralForms is enabled)
|
|
574
|
+
formsToGenerate = allLocalesCardinalCategories ?? cardinalCategories;
|
|
564
575
|
for (const form of formsToGenerate) {
|
|
565
576
|
const finalKey = isOrdinal
|
|
566
577
|
? `${base}${pluralSeparator}${form}`
|
|
@@ -708,11 +719,11 @@ function buildNewTranslationsForNs(nsKeys, existingTranslations, config, locale,
|
|
|
708
719
|
}
|
|
709
720
|
setNestedValue(newTranslations, key, valueToSet, separator);
|
|
710
721
|
}
|
|
711
|
-
// When allPluralForms is enabled, ensure all
|
|
722
|
+
// When allPluralForms is enabled, ensure all union plural forms exist for every plural base key.
|
|
712
723
|
// The extractor only generates forms for the configured locales' categories, so we need to fill in the rest.
|
|
713
|
-
if (
|
|
724
|
+
if (allLocalesCardinalCategories && !config.extract.disablePlurals) {
|
|
714
725
|
for (const base of expandedBases) {
|
|
715
|
-
for (const form of
|
|
726
|
+
for (const form of allLocalesCardinalCategories) {
|
|
716
727
|
const finalKey = `${base}${pluralSeparator}${form}`;
|
|
717
728
|
const separator = finalKey.startsWith('<') ? false : (keySeparator ?? '.');
|
|
718
729
|
const existingInNew = getNestedValue(newTranslations, finalKey, separator);
|
|
@@ -902,9 +913,12 @@ async function getTranslations(keys, objectKeys, config, { syncPrimaryWithDefaul
|
|
|
902
913
|
}
|
|
903
914
|
// When nsSeparator is false, keys resolved to the defaultNS (e.g. from
|
|
904
915
|
// useTranslation() with no args) should be treated as top-level, not
|
|
905
|
-
// wrapped under the namespace name
|
|
916
|
+
// wrapped under the namespace name — but only when there are no other
|
|
917
|
+
// explicit namespaces. If multiple namespaces exist, we must keep the
|
|
918
|
+
// default namespace wrapper to avoid flattening it into the top level (#227).
|
|
906
919
|
const defaultNs = String(config.extract.defaultNS ?? 'translation');
|
|
907
|
-
const
|
|
920
|
+
const hasOtherNamespaces = [...keysByNS.keys()].some(k => k !== NO_NS_TOKEN && k !== defaultNs);
|
|
921
|
+
const isTopLevel = (nsKey) => nsKey === NO_NS_TOKEN || (config.extract.nsSeparator === false && nsKey === defaultNs && !hasOtherNamespaces);
|
|
908
922
|
for (const nsKey of namespacesToProcess) {
|
|
909
923
|
const nsKeys = keysByNS.get(nsKey) || [];
|
|
910
924
|
if (isTopLevel(nsKey)) {
|
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;AA66B9F;;;;;;;;;;;;;;;;;;;;;;;;;;;;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,CAuJ9B"}
|
package/types/types.d.ts
CHANGED
|
@@ -135,10 +135,10 @@ export interface I18nextToolkitConfig {
|
|
|
135
135
|
generateBasePluralForms?: boolean;
|
|
136
136
|
disablePlurals?: boolean;
|
|
137
137
|
/**
|
|
138
|
-
* When true, generates all
|
|
139
|
-
*
|
|
140
|
-
*
|
|
141
|
-
*
|
|
138
|
+
* When true, generates the union of all configured locales' plural forms for every language.
|
|
139
|
+
* For example, with locales ['en', 'pl'], English normally only gets _one/_other,
|
|
140
|
+
* but with this option it also gets _few/_many (needed by Polish).
|
|
141
|
+
* Only forms required by at least one configured locale are generated.
|
|
142
142
|
* (default: false)
|
|
143
143
|
*/
|
|
144
144
|
allPluralForms?: boolean;
|