i18next-cli 1.41.2 → 1.41.3
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.41.
|
|
31
|
+
.version('1.41.3'); // 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
|
|
@@ -217,16 +217,14 @@ function buildNewTranslationsForNs(nsKeys, existingTranslations, config, locale,
|
|
|
217
217
|
}
|
|
218
218
|
}
|
|
219
219
|
}
|
|
220
|
-
// Get the plural categories for the target language
|
|
220
|
+
// Get the plural categories for the target language (only used for filtering extracted keys)
|
|
221
221
|
const targetLanguagePluralCategories = new Set();
|
|
222
222
|
// Track cardinal plural categories separately so we can special-case single-"other" languages
|
|
223
223
|
let cardinalCategories = [];
|
|
224
|
-
let ordinalCategories = [];
|
|
225
224
|
try {
|
|
226
225
|
const cardinalRules = new Intl.PluralRules(locale, { type: 'cardinal' });
|
|
227
226
|
const ordinalRules = new Intl.PluralRules(locale, { type: 'ordinal' });
|
|
228
227
|
cardinalCategories = cardinalRules.resolvedOptions().pluralCategories;
|
|
229
|
-
ordinalCategories = ordinalRules.resolvedOptions().pluralCategories;
|
|
230
228
|
cardinalCategories.forEach(cat => targetLanguagePluralCategories.add(cat));
|
|
231
229
|
ordinalRules.resolvedOptions().pluralCategories.forEach(cat => targetLanguagePluralCategories.add(`ordinal_${cat}`));
|
|
232
230
|
}
|
|
@@ -236,7 +234,6 @@ function buildNewTranslationsForNs(nsKeys, existingTranslations, config, locale,
|
|
|
236
234
|
const cardinalRules = new Intl.PluralRules(fallbackLang, { type: 'cardinal' });
|
|
237
235
|
const ordinalRules = new Intl.PluralRules(fallbackLang, { type: 'ordinal' });
|
|
238
236
|
cardinalCategories = cardinalRules.resolvedOptions().pluralCategories;
|
|
239
|
-
ordinalCategories = ordinalRules.resolvedOptions().pluralCategories;
|
|
240
237
|
cardinalCategories.forEach(cat => targetLanguagePluralCategories.add(cat));
|
|
241
238
|
ordinalRules.resolvedOptions().pluralCategories.forEach(cat => targetLanguagePluralCategories.add(`ordinal_${cat}`));
|
|
242
239
|
}
|
|
@@ -450,49 +447,104 @@ function buildNewTranslationsForNs(nsKeys, existingTranslations, config, locale,
|
|
|
450
447
|
continue;
|
|
451
448
|
}
|
|
452
449
|
}
|
|
453
|
-
// If this is a base plural key (no explicit suffix)
|
|
454
|
-
//
|
|
455
|
-
//
|
|
450
|
+
// If this is a base plural key (no explicit suffix), expand it into locale-specific plural variants.
|
|
451
|
+
// For non-primary locales, we generate forms for that specific locale from CLDR.
|
|
452
|
+
// Additionally, we generate empty placeholders for ALL other CLDR forms not in the target locale
|
|
453
|
+
// (so translators can add them manually if needed).
|
|
456
454
|
if (hasCount && !isExpandedPlural) {
|
|
457
455
|
const parts = String(key).split(pluralSeparator);
|
|
458
456
|
const isBaseKey = parts.length === 1;
|
|
459
|
-
if (isBaseKey
|
|
457
|
+
if (isBaseKey) {
|
|
460
458
|
// If explicit expanded variants exist, do not expand the base.
|
|
461
459
|
const base = key;
|
|
462
460
|
if (expandedBases.has(base)) ;
|
|
463
461
|
else {
|
|
464
|
-
//
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
//
|
|
471
|
-
//
|
|
472
|
-
const
|
|
473
|
-
//
|
|
474
|
-
const
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
462
|
+
// Determine which plural forms to generate
|
|
463
|
+
let formsToGenerate;
|
|
464
|
+
if (locale !== primaryLanguage) {
|
|
465
|
+
// For non-primary locales:
|
|
466
|
+
// 1. Generate the forms that locale actually needs
|
|
467
|
+
formsToGenerate = cardinalCategories;
|
|
468
|
+
// 2. Also prepare empty placeholders for all OTHER CLDR forms not in this locale
|
|
469
|
+
// so translators can add them manually without --sync-primary removing them
|
|
470
|
+
const otherForms = pluralForms.filter(f => !cardinalCategories.includes(f));
|
|
471
|
+
// Process the locale-specific forms normally
|
|
472
|
+
for (const form of formsToGenerate) {
|
|
473
|
+
const finalKey = isOrdinal
|
|
474
|
+
? `${base}${pluralSeparator}${form}`
|
|
475
|
+
: `${base}${pluralSeparator}${form}`;
|
|
476
|
+
const separator = finalKey.startsWith('<') ? false : (keySeparator ?? '.');
|
|
477
|
+
const existingVariantValue = nestedObject.getNestedValue(existingTranslations, finalKey, separator);
|
|
478
|
+
if (existingVariantValue === undefined) {
|
|
479
|
+
// Use the default value for secondary locale forms
|
|
480
|
+
let resolvedValue;
|
|
481
|
+
if (typeof defaultValue$1 === 'string') {
|
|
482
|
+
resolvedValue = defaultValue$1;
|
|
483
|
+
}
|
|
484
|
+
else {
|
|
485
|
+
resolvedValue = defaultValue.resolveDefaultValue(emptyDefaultValue, String(base), namespace || config?.extract?.defaultNS || 'translation', locale, defaultValue$1);
|
|
486
|
+
}
|
|
487
|
+
nestedObject.setNestedValue(newTranslations, finalKey, resolvedValue, separator);
|
|
481
488
|
}
|
|
482
489
|
else {
|
|
483
|
-
|
|
484
|
-
resolvedValue = defaultValue.resolveDefaultValue(emptyDefaultValue, String(base), namespace || config?.extract?.defaultNS || 'translation', locale, defaultValue$1);
|
|
490
|
+
nestedObject.setNestedValue(newTranslations, finalKey, existingVariantValue, separator);
|
|
485
491
|
}
|
|
486
|
-
|
|
492
|
+
}
|
|
493
|
+
// Now process other CLDR forms: set empty placeholders for forms this locale doesn't use
|
|
494
|
+
// but preserve any that were manually added by translators
|
|
495
|
+
for (const form of otherForms) {
|
|
496
|
+
const finalKey = isOrdinal
|
|
497
|
+
? `${base}${pluralSeparator}${form}`
|
|
498
|
+
: `${base}${pluralSeparator}${form}`;
|
|
499
|
+
const separator = finalKey.startsWith('<') ? false : (keySeparator ?? '.');
|
|
500
|
+
const existingVariantValue = nestedObject.getNestedValue(existingTranslations, finalKey, separator);
|
|
501
|
+
if (existingVariantValue !== undefined) {
|
|
502
|
+
// Preserve manually-added forms
|
|
503
|
+
nestedObject.setNestedValue(newTranslations, finalKey, existingVariantValue, separator);
|
|
504
|
+
}
|
|
505
|
+
// Don't generate empty placeholders - only generate what the locale needs and preserve what's manual
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
else {
|
|
509
|
+
// For primary language, only expand if it has multiple plural forms
|
|
510
|
+
// Single-"other" languages (ja, zh, ko) should NOT expand the base key
|
|
511
|
+
if (cardinalCategories.length === 1 && cardinalCategories[0] === 'other') {
|
|
512
|
+
// Single-"other" language - don't expand, keep just the base key
|
|
513
|
+
formsToGenerate = [];
|
|
487
514
|
}
|
|
488
515
|
else {
|
|
489
|
-
//
|
|
490
|
-
|
|
516
|
+
// Multi-form language - expand to its plural forms
|
|
517
|
+
formsToGenerate = cardinalCategories;
|
|
518
|
+
for (const form of formsToGenerate) {
|
|
519
|
+
const finalKey = isOrdinal
|
|
520
|
+
? `${base}${pluralSeparator}${form}`
|
|
521
|
+
: `${base}${pluralSeparator}${form}`;
|
|
522
|
+
const separator = finalKey.startsWith('<') ? false : (keySeparator ?? '.');
|
|
523
|
+
const existingVariantValue = nestedObject.getNestedValue(existingTranslations, finalKey, separator);
|
|
524
|
+
if (existingVariantValue === undefined) {
|
|
525
|
+
// Prefer explicit defaultValue extracted for this key; fall back to configured defaultValue
|
|
526
|
+
let resolvedValue;
|
|
527
|
+
if (typeof defaultValue$1 === 'string') {
|
|
528
|
+
resolvedValue = defaultValue$1;
|
|
529
|
+
}
|
|
530
|
+
else {
|
|
531
|
+
resolvedValue = defaultValue.resolveDefaultValue(emptyDefaultValue, String(base), namespace || config?.extract?.defaultNS || 'translation', locale, defaultValue$1);
|
|
532
|
+
}
|
|
533
|
+
nestedObject.setNestedValue(newTranslations, finalKey, resolvedValue, separator);
|
|
534
|
+
}
|
|
535
|
+
else {
|
|
536
|
+
nestedObject.setNestedValue(newTranslations, finalKey, existingVariantValue, separator);
|
|
537
|
+
}
|
|
538
|
+
}
|
|
491
539
|
}
|
|
492
540
|
}
|
|
541
|
+
if (formsToGenerate && formsToGenerate.length > 0) {
|
|
542
|
+
// We've handled expansion for this base key; skip the normal single-key handling.
|
|
543
|
+
continue;
|
|
544
|
+
}
|
|
545
|
+
// else: formsToGenerate is empty (single-"other" primary language)
|
|
546
|
+
// Fall through to normal key handling below
|
|
493
547
|
}
|
|
494
|
-
// We've expanded variants for this base key; skip the normal single-key handling.
|
|
495
|
-
continue;
|
|
496
548
|
}
|
|
497
549
|
}
|
|
498
550
|
// If the key looks like a serialized Trans component (starts with <), treat it as a flat 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.41.
|
|
29
|
+
.version('1.41.3'); // 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
|
|
@@ -215,16 +215,14 @@ function buildNewTranslationsForNs(nsKeys, existingTranslations, config, locale,
|
|
|
215
215
|
}
|
|
216
216
|
}
|
|
217
217
|
}
|
|
218
|
-
// Get the plural categories for the target language
|
|
218
|
+
// Get the plural categories for the target language (only used for filtering extracted keys)
|
|
219
219
|
const targetLanguagePluralCategories = new Set();
|
|
220
220
|
// Track cardinal plural categories separately so we can special-case single-"other" languages
|
|
221
221
|
let cardinalCategories = [];
|
|
222
|
-
let ordinalCategories = [];
|
|
223
222
|
try {
|
|
224
223
|
const cardinalRules = new Intl.PluralRules(locale, { type: 'cardinal' });
|
|
225
224
|
const ordinalRules = new Intl.PluralRules(locale, { type: 'ordinal' });
|
|
226
225
|
cardinalCategories = cardinalRules.resolvedOptions().pluralCategories;
|
|
227
|
-
ordinalCategories = ordinalRules.resolvedOptions().pluralCategories;
|
|
228
226
|
cardinalCategories.forEach(cat => targetLanguagePluralCategories.add(cat));
|
|
229
227
|
ordinalRules.resolvedOptions().pluralCategories.forEach(cat => targetLanguagePluralCategories.add(`ordinal_${cat}`));
|
|
230
228
|
}
|
|
@@ -234,7 +232,6 @@ function buildNewTranslationsForNs(nsKeys, existingTranslations, config, locale,
|
|
|
234
232
|
const cardinalRules = new Intl.PluralRules(fallbackLang, { type: 'cardinal' });
|
|
235
233
|
const ordinalRules = new Intl.PluralRules(fallbackLang, { type: 'ordinal' });
|
|
236
234
|
cardinalCategories = cardinalRules.resolvedOptions().pluralCategories;
|
|
237
|
-
ordinalCategories = ordinalRules.resolvedOptions().pluralCategories;
|
|
238
235
|
cardinalCategories.forEach(cat => targetLanguagePluralCategories.add(cat));
|
|
239
236
|
ordinalRules.resolvedOptions().pluralCategories.forEach(cat => targetLanguagePluralCategories.add(`ordinal_${cat}`));
|
|
240
237
|
}
|
|
@@ -448,49 +445,104 @@ function buildNewTranslationsForNs(nsKeys, existingTranslations, config, locale,
|
|
|
448
445
|
continue;
|
|
449
446
|
}
|
|
450
447
|
}
|
|
451
|
-
// If this is a base plural key (no explicit suffix)
|
|
452
|
-
//
|
|
453
|
-
//
|
|
448
|
+
// If this is a base plural key (no explicit suffix), expand it into locale-specific plural variants.
|
|
449
|
+
// For non-primary locales, we generate forms for that specific locale from CLDR.
|
|
450
|
+
// Additionally, we generate empty placeholders for ALL other CLDR forms not in the target locale
|
|
451
|
+
// (so translators can add them manually if needed).
|
|
454
452
|
if (hasCount && !isExpandedPlural) {
|
|
455
453
|
const parts = String(key).split(pluralSeparator);
|
|
456
454
|
const isBaseKey = parts.length === 1;
|
|
457
|
-
if (isBaseKey
|
|
455
|
+
if (isBaseKey) {
|
|
458
456
|
// If explicit expanded variants exist, do not expand the base.
|
|
459
457
|
const base = key;
|
|
460
458
|
if (expandedBases.has(base)) ;
|
|
461
459
|
else {
|
|
462
|
-
//
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
//
|
|
469
|
-
//
|
|
470
|
-
const
|
|
471
|
-
//
|
|
472
|
-
const
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
460
|
+
// Determine which plural forms to generate
|
|
461
|
+
let formsToGenerate;
|
|
462
|
+
if (locale !== primaryLanguage) {
|
|
463
|
+
// For non-primary locales:
|
|
464
|
+
// 1. Generate the forms that locale actually needs
|
|
465
|
+
formsToGenerate = cardinalCategories;
|
|
466
|
+
// 2. Also prepare empty placeholders for all OTHER CLDR forms not in this locale
|
|
467
|
+
// so translators can add them manually without --sync-primary removing them
|
|
468
|
+
const otherForms = pluralForms.filter(f => !cardinalCategories.includes(f));
|
|
469
|
+
// Process the locale-specific forms normally
|
|
470
|
+
for (const form of formsToGenerate) {
|
|
471
|
+
const finalKey = isOrdinal
|
|
472
|
+
? `${base}${pluralSeparator}${form}`
|
|
473
|
+
: `${base}${pluralSeparator}${form}`;
|
|
474
|
+
const separator = finalKey.startsWith('<') ? false : (keySeparator ?? '.');
|
|
475
|
+
const existingVariantValue = getNestedValue(existingTranslations, finalKey, separator);
|
|
476
|
+
if (existingVariantValue === undefined) {
|
|
477
|
+
// Use the default value for secondary locale forms
|
|
478
|
+
let resolvedValue;
|
|
479
|
+
if (typeof defaultValue === 'string') {
|
|
480
|
+
resolvedValue = defaultValue;
|
|
481
|
+
}
|
|
482
|
+
else {
|
|
483
|
+
resolvedValue = resolveDefaultValue(emptyDefaultValue, String(base), namespace || config?.extract?.defaultNS || 'translation', locale, defaultValue);
|
|
484
|
+
}
|
|
485
|
+
setNestedValue(newTranslations, finalKey, resolvedValue, separator);
|
|
479
486
|
}
|
|
480
487
|
else {
|
|
481
|
-
|
|
482
|
-
resolvedValue = resolveDefaultValue(emptyDefaultValue, String(base), namespace || config?.extract?.defaultNS || 'translation', locale, defaultValue);
|
|
488
|
+
setNestedValue(newTranslations, finalKey, existingVariantValue, separator);
|
|
483
489
|
}
|
|
484
|
-
|
|
490
|
+
}
|
|
491
|
+
// Now process other CLDR forms: set empty placeholders for forms this locale doesn't use
|
|
492
|
+
// but preserve any that were manually added by translators
|
|
493
|
+
for (const form of otherForms) {
|
|
494
|
+
const finalKey = isOrdinal
|
|
495
|
+
? `${base}${pluralSeparator}${form}`
|
|
496
|
+
: `${base}${pluralSeparator}${form}`;
|
|
497
|
+
const separator = finalKey.startsWith('<') ? false : (keySeparator ?? '.');
|
|
498
|
+
const existingVariantValue = getNestedValue(existingTranslations, finalKey, separator);
|
|
499
|
+
if (existingVariantValue !== undefined) {
|
|
500
|
+
// Preserve manually-added forms
|
|
501
|
+
setNestedValue(newTranslations, finalKey, existingVariantValue, separator);
|
|
502
|
+
}
|
|
503
|
+
// Don't generate empty placeholders - only generate what the locale needs and preserve what's manual
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
else {
|
|
507
|
+
// For primary language, only expand if it has multiple plural forms
|
|
508
|
+
// Single-"other" languages (ja, zh, ko) should NOT expand the base key
|
|
509
|
+
if (cardinalCategories.length === 1 && cardinalCategories[0] === 'other') {
|
|
510
|
+
// Single-"other" language - don't expand, keep just the base key
|
|
511
|
+
formsToGenerate = [];
|
|
485
512
|
}
|
|
486
513
|
else {
|
|
487
|
-
//
|
|
488
|
-
|
|
514
|
+
// Multi-form language - expand to its plural forms
|
|
515
|
+
formsToGenerate = cardinalCategories;
|
|
516
|
+
for (const form of formsToGenerate) {
|
|
517
|
+
const finalKey = isOrdinal
|
|
518
|
+
? `${base}${pluralSeparator}${form}`
|
|
519
|
+
: `${base}${pluralSeparator}${form}`;
|
|
520
|
+
const separator = finalKey.startsWith('<') ? false : (keySeparator ?? '.');
|
|
521
|
+
const existingVariantValue = getNestedValue(existingTranslations, finalKey, separator);
|
|
522
|
+
if (existingVariantValue === undefined) {
|
|
523
|
+
// Prefer explicit defaultValue extracted for this key; fall back to configured defaultValue
|
|
524
|
+
let resolvedValue;
|
|
525
|
+
if (typeof defaultValue === 'string') {
|
|
526
|
+
resolvedValue = defaultValue;
|
|
527
|
+
}
|
|
528
|
+
else {
|
|
529
|
+
resolvedValue = resolveDefaultValue(emptyDefaultValue, String(base), namespace || config?.extract?.defaultNS || 'translation', locale, defaultValue);
|
|
530
|
+
}
|
|
531
|
+
setNestedValue(newTranslations, finalKey, resolvedValue, separator);
|
|
532
|
+
}
|
|
533
|
+
else {
|
|
534
|
+
setNestedValue(newTranslations, finalKey, existingVariantValue, separator);
|
|
535
|
+
}
|
|
536
|
+
}
|
|
489
537
|
}
|
|
490
538
|
}
|
|
539
|
+
if (formsToGenerate && formsToGenerate.length > 0) {
|
|
540
|
+
// We've handled expansion for this base key; skip the normal single-key handling.
|
|
541
|
+
continue;
|
|
542
|
+
}
|
|
543
|
+
// else: formsToGenerate is empty (single-"other" primary language)
|
|
544
|
+
// Fall through to normal key handling below
|
|
491
545
|
}
|
|
492
|
-
// We've expanded variants for this base key; skip the normal single-key handling.
|
|
493
|
-
continue;
|
|
494
546
|
}
|
|
495
547
|
}
|
|
496
548
|
// If the key looks like a serialized Trans component (starts with <), treat it as a flat key
|
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,aAAa,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,aAAa,CAAA;AAgyBnF;;;;;;;;;;;;;;;;;;;;;;;;;;;;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,EAChB,GAAE;IACD,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,OAAO,CAAC,EAAE,OAAO,CAAA;CACb,GACL,OAAO,CAAC,iBAAiB,EAAE,CAAC,CA0I9B"}
|