i18next-cli 1.46.1 → 1.46.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 +1 -1
- package/dist/cjs/extractor/core/translation-manager.js +68 -0
- package/dist/cjs/extractor/parsers/call-expression-handler.js +19 -16
- package/dist/esm/cli.js +1 -1
- package/dist/esm/extractor/core/translation-manager.js +68 -0
- package/dist/esm/extractor/parsers/call-expression-handler.js +19 -16
- 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
|
@@ -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.46.
|
|
31
|
+
.version('1.46.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
|
|
@@ -87,6 +87,51 @@ function looksLikeObjectPath(key, separator, regex) {
|
|
|
87
87
|
}
|
|
88
88
|
return matched;
|
|
89
89
|
}
|
|
90
|
+
/**
|
|
91
|
+
* Returns true when splitting `key` by `separator` would produce at least one
|
|
92
|
+
* empty string segment (e.g. "Loading..." split by "." → ["Loading","","",""]).
|
|
93
|
+
* Keys with empty segments must be treated as flat keys, not nested paths,
|
|
94
|
+
* otherwise they create `{ "": { "": "..." } }` entries in the JSON output.
|
|
95
|
+
*/
|
|
96
|
+
function hasEmptySegments(key, separator) {
|
|
97
|
+
return key.split(separator).some(s => s === '');
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Detects a nesting conflict for `key` against the object being built.
|
|
101
|
+
* Returns the conflicting ancestor/descendant key path as a string when a
|
|
102
|
+
* conflict is found, or `null` when there is no conflict.
|
|
103
|
+
*
|
|
104
|
+
* The returned string lets callers produce an actionable error message
|
|
105
|
+
* pointing to the specific key that is already occupying the conflicting path.
|
|
106
|
+
*/
|
|
107
|
+
function findNestingConflict(obj, key, separator) {
|
|
108
|
+
const parts = key.split(separator);
|
|
109
|
+
let current = obj;
|
|
110
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
111
|
+
const part = parts[i];
|
|
112
|
+
const value = current[part];
|
|
113
|
+
if (value === undefined || value === null) {
|
|
114
|
+
// Path does not exist yet — no conflict
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
if (typeof value !== 'object' || Array.isArray(value)) {
|
|
118
|
+
// A non-object value already occupies an ancestor segment.
|
|
119
|
+
// We cannot nest inside a string/number/array.
|
|
120
|
+
return parts.slice(0, i + 1).join(separator);
|
|
121
|
+
}
|
|
122
|
+
current = value;
|
|
123
|
+
}
|
|
124
|
+
// Check the final segment: if the existing value is a non-empty object and we are
|
|
125
|
+
// about to overwrite it with a string, that is also a conflict (the deeper keys
|
|
126
|
+
// that populate this object came from other extracted keys and would be silently lost).
|
|
127
|
+
const leafPart = parts[parts.length - 1];
|
|
128
|
+
const leafValue = current[leafPart];
|
|
129
|
+
if (typeof leafValue === 'object' && leafValue !== null && !Array.isArray(leafValue) && Object.keys(leafValue).length > 0) {
|
|
130
|
+
// The conflicting path is the key itself (it already has nested children)
|
|
131
|
+
return key;
|
|
132
|
+
}
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
90
135
|
/**
|
|
91
136
|
* Recursively sorts the keys of an object.
|
|
92
137
|
*/
|
|
@@ -551,6 +596,13 @@ function buildNewTranslationsForNs(nsKeys, existingTranslations, config, locale,
|
|
|
551
596
|
let separator = key.startsWith('<') ? false : (keySeparator ?? '.');
|
|
552
597
|
if (separator && typeof separator === 'string') {
|
|
553
598
|
if (!looksLikeObjectPath(key, separator, naturalLanguageRegex)) {
|
|
599
|
+
// Natural-language key — treat as flat
|
|
600
|
+
separator = false;
|
|
601
|
+
}
|
|
602
|
+
else if (hasEmptySegments(key, separator)) {
|
|
603
|
+
// Splitting would produce empty-string segments (e.g. "Loading..." split by "."
|
|
604
|
+
// yields ["Loading","","",""]). Storing those creates { "": { "": "…" } }
|
|
605
|
+
// noise in the JSON, so treat the whole key as a flat leaf instead.
|
|
554
606
|
separator = false;
|
|
555
607
|
}
|
|
556
608
|
}
|
|
@@ -637,6 +689,22 @@ function buildNewTranslationsForNs(nsKeys, existingTranslations, config, locale,
|
|
|
637
689
|
}
|
|
638
690
|
}
|
|
639
691
|
}
|
|
692
|
+
// Guard against nesting conflicts before writing to the output object.
|
|
693
|
+
// A conflict arises when one extracted key would clobber an ancestor/descendant
|
|
694
|
+
// that was already written by a different extracted key, e.g.:
|
|
695
|
+
// t("a.b") => sets a.b = string
|
|
696
|
+
// t("a.b.c") => tries to descend into a.b which is already a string
|
|
697
|
+
// In that situation we skip the conflicting key and emit a console.error so
|
|
698
|
+
// developers see the problem immediately — a skipped key becomes a missing
|
|
699
|
+
// translation at runtime.
|
|
700
|
+
if (separator && typeof separator === 'string') {
|
|
701
|
+
const conflictingPath = findNestingConflict(newTranslations, key, separator);
|
|
702
|
+
if (conflictingPath !== null) {
|
|
703
|
+
console.error(`[i18next-toolkit] Nesting conflict: key "${key}" conflicts with existing key "${conflictingPath}". ` +
|
|
704
|
+
`"${key}" will be skipped — fix the overlapping key paths in your source code to avoid missing translations at runtime.`);
|
|
705
|
+
continue;
|
|
706
|
+
}
|
|
707
|
+
}
|
|
640
708
|
nestedObject.setNestedValue(newTranslations, key, valueToSet, separator);
|
|
641
709
|
}
|
|
642
710
|
// 2. If sorting is enabled, recursively sort the entire object.
|
|
@@ -216,6 +216,12 @@ class CallExpressionHandler {
|
|
|
216
216
|
: (nsSeparator && originalKey.includes(nsSeparator || ':') ? originalKey : key))
|
|
217
217
|
: key;
|
|
218
218
|
// Handle plurals, context, and returnObjects
|
|
219
|
+
// Compute location once here so it is available to ALL paths below
|
|
220
|
+
// (plural, context, single-other fast paths, and the default leaf path).
|
|
221
|
+
const location = this.getLocationFromNode(node);
|
|
222
|
+
const locationEntry = location
|
|
223
|
+
? [{ file: this.getCurrentFile(), line: location.line, column: location.column }]
|
|
224
|
+
: undefined;
|
|
219
225
|
if (options) {
|
|
220
226
|
const contextPropValue = astUtils.getObjectPropValueExpression(options, 'context');
|
|
221
227
|
const keysWithContext = [];
|
|
@@ -345,7 +351,8 @@ class CallExpressionHandler {
|
|
|
345
351
|
ns: k.ns,
|
|
346
352
|
defaultValue: k.defaultValue,
|
|
347
353
|
hasCount: true,
|
|
348
|
-
isOrdinal: isOrdinalByKey
|
|
354
|
+
isOrdinal: isOrdinalByKey,
|
|
355
|
+
locations: locationEntry
|
|
349
356
|
});
|
|
350
357
|
}
|
|
351
358
|
}
|
|
@@ -355,7 +362,8 @@ class CallExpressionHandler {
|
|
|
355
362
|
ns,
|
|
356
363
|
defaultValue: dv,
|
|
357
364
|
hasCount: true,
|
|
358
|
-
isOrdinal: isOrdinalByKey
|
|
365
|
+
isOrdinal: isOrdinalByKey,
|
|
366
|
+
locations: locationEntry
|
|
359
367
|
});
|
|
360
368
|
}
|
|
361
369
|
continue;
|
|
@@ -381,7 +389,7 @@ class CallExpressionHandler {
|
|
|
381
389
|
// Pass explicitDefaultForBase so that when a call-site provided an explicit
|
|
382
390
|
// base default (e.g. t('key', 'Default', { count })), plural variant keys
|
|
383
391
|
// are treated as explicit and may be synced to that default.
|
|
384
|
-
this.handlePluralKeys(finalKey, ns, options, isOrdinalByOption || isOrdinalByKey, finalDefaultValue, explicitPluralForVariants);
|
|
392
|
+
this.handlePluralKeys(finalKey, ns, options, isOrdinalByOption || isOrdinalByKey, finalDefaultValue, explicitPluralForVariants, locationEntry);
|
|
385
393
|
}
|
|
386
394
|
continue; // This key is fully handled
|
|
387
395
|
}
|
|
@@ -401,21 +409,14 @@ class CallExpressionHandler {
|
|
|
401
409
|
// Fall through to add the base key itself
|
|
402
410
|
}
|
|
403
411
|
// 5. Default case: Add the simple key
|
|
412
|
+
// eslint-disable-next-line no-lone-blocks
|
|
404
413
|
{
|
|
405
|
-
// ✅ Use the helper method to find location by searching the code
|
|
406
|
-
const location = this.getLocationFromNode(node);
|
|
407
414
|
this.pluginContext.addKey({
|
|
408
415
|
key: finalKey,
|
|
409
416
|
ns,
|
|
410
417
|
defaultValue: dv,
|
|
411
418
|
explicitDefault: explicitDefaultForBase,
|
|
412
|
-
locations:
|
|
413
|
-
? [{
|
|
414
|
-
file: this.getCurrentFile(),
|
|
415
|
-
line: location.line,
|
|
416
|
-
column: location.column
|
|
417
|
-
}]
|
|
418
|
-
: undefined
|
|
419
|
+
locations: locationEntry
|
|
419
420
|
});
|
|
420
421
|
// Check for nested translations in the key itself
|
|
421
422
|
this.extractNestedKeys(finalKey, ns);
|
|
@@ -686,7 +687,7 @@ class CallExpressionHandler {
|
|
|
686
687
|
* @param options - object expression options
|
|
687
688
|
* @param isOrdinal - isOrdinal flag
|
|
688
689
|
*/
|
|
689
|
-
handlePluralKeys(key, ns, options, isOrdinal, defaultValueFromCall, explicitDefaultFromSource) {
|
|
690
|
+
handlePluralKeys(key, ns, options, isOrdinal, defaultValueFromCall, explicitDefaultFromSource, locations) {
|
|
690
691
|
try {
|
|
691
692
|
const type = isOrdinal ? 'ordinal' : 'cardinal';
|
|
692
693
|
// Generate plural forms for ALL target languages to ensure we have all necessary keys
|
|
@@ -794,7 +795,8 @@ class CallExpressionHandler {
|
|
|
794
795
|
defaultValue: finalDefaultValue,
|
|
795
796
|
hasCount: true,
|
|
796
797
|
isOrdinal,
|
|
797
|
-
explicitDefault: Boolean(explicitDefaultFromSource || typeof specificOther === 'string')
|
|
798
|
+
explicitDefault: Boolean(explicitDefaultFromSource || typeof specificOther === 'string'),
|
|
799
|
+
locations
|
|
798
800
|
});
|
|
799
801
|
}
|
|
800
802
|
return;
|
|
@@ -864,7 +866,8 @@ class CallExpressionHandler {
|
|
|
864
866
|
// Do NOT treat the presence of a general base defaultValueFromCall as making variants explicit.
|
|
865
867
|
explicitDefault: Boolean(explicitDefaultFromSource || typeof specificDefault === 'string' || typeof otherDefault === 'string'),
|
|
866
868
|
// If this is a context variant, track the base key (without context or plural suffixes)
|
|
867
|
-
keyAcceptingContext: context !== undefined ? key : undefined
|
|
869
|
+
keyAcceptingContext: context !== undefined ? key : undefined,
|
|
870
|
+
locations
|
|
868
871
|
});
|
|
869
872
|
}
|
|
870
873
|
}
|
|
@@ -873,7 +876,7 @@ class CallExpressionHandler {
|
|
|
873
876
|
this.logger.warn(`Could not determine plural rules for language "${this.config.extract?.primaryLanguage}". Falling back to simple key extraction.`);
|
|
874
877
|
// Fallback to a simple key if Intl API fails
|
|
875
878
|
const defaultValue = defaultValueFromCall || astUtils.getObjectPropValue(options, 'defaultValue');
|
|
876
|
-
this.pluginContext.addKey({ key, ns, defaultValue: typeof defaultValue === 'string' ? defaultValue : key });
|
|
879
|
+
this.pluginContext.addKey({ key, ns, defaultValue: typeof defaultValue === 'string' ? defaultValue : key, locations });
|
|
877
880
|
}
|
|
878
881
|
}
|
|
879
882
|
/**
|
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.46.
|
|
29
|
+
.version('1.46.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
|
|
@@ -85,6 +85,51 @@ function looksLikeObjectPath(key, separator, regex) {
|
|
|
85
85
|
}
|
|
86
86
|
return matched;
|
|
87
87
|
}
|
|
88
|
+
/**
|
|
89
|
+
* Returns true when splitting `key` by `separator` would produce at least one
|
|
90
|
+
* empty string segment (e.g. "Loading..." split by "." → ["Loading","","",""]).
|
|
91
|
+
* Keys with empty segments must be treated as flat keys, not nested paths,
|
|
92
|
+
* otherwise they create `{ "": { "": "..." } }` entries in the JSON output.
|
|
93
|
+
*/
|
|
94
|
+
function hasEmptySegments(key, separator) {
|
|
95
|
+
return key.split(separator).some(s => s === '');
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Detects a nesting conflict for `key` against the object being built.
|
|
99
|
+
* Returns the conflicting ancestor/descendant key path as a string when a
|
|
100
|
+
* conflict is found, or `null` when there is no conflict.
|
|
101
|
+
*
|
|
102
|
+
* The returned string lets callers produce an actionable error message
|
|
103
|
+
* pointing to the specific key that is already occupying the conflicting path.
|
|
104
|
+
*/
|
|
105
|
+
function findNestingConflict(obj, key, separator) {
|
|
106
|
+
const parts = key.split(separator);
|
|
107
|
+
let current = obj;
|
|
108
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
109
|
+
const part = parts[i];
|
|
110
|
+
const value = current[part];
|
|
111
|
+
if (value === undefined || value === null) {
|
|
112
|
+
// Path does not exist yet — no conflict
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
if (typeof value !== 'object' || Array.isArray(value)) {
|
|
116
|
+
// A non-object value already occupies an ancestor segment.
|
|
117
|
+
// We cannot nest inside a string/number/array.
|
|
118
|
+
return parts.slice(0, i + 1).join(separator);
|
|
119
|
+
}
|
|
120
|
+
current = value;
|
|
121
|
+
}
|
|
122
|
+
// Check the final segment: if the existing value is a non-empty object and we are
|
|
123
|
+
// about to overwrite it with a string, that is also a conflict (the deeper keys
|
|
124
|
+
// that populate this object came from other extracted keys and would be silently lost).
|
|
125
|
+
const leafPart = parts[parts.length - 1];
|
|
126
|
+
const leafValue = current[leafPart];
|
|
127
|
+
if (typeof leafValue === 'object' && leafValue !== null && !Array.isArray(leafValue) && Object.keys(leafValue).length > 0) {
|
|
128
|
+
// The conflicting path is the key itself (it already has nested children)
|
|
129
|
+
return key;
|
|
130
|
+
}
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
88
133
|
/**
|
|
89
134
|
* Recursively sorts the keys of an object.
|
|
90
135
|
*/
|
|
@@ -549,6 +594,13 @@ function buildNewTranslationsForNs(nsKeys, existingTranslations, config, locale,
|
|
|
549
594
|
let separator = key.startsWith('<') ? false : (keySeparator ?? '.');
|
|
550
595
|
if (separator && typeof separator === 'string') {
|
|
551
596
|
if (!looksLikeObjectPath(key, separator, naturalLanguageRegex)) {
|
|
597
|
+
// Natural-language key — treat as flat
|
|
598
|
+
separator = false;
|
|
599
|
+
}
|
|
600
|
+
else if (hasEmptySegments(key, separator)) {
|
|
601
|
+
// Splitting would produce empty-string segments (e.g. "Loading..." split by "."
|
|
602
|
+
// yields ["Loading","","",""]). Storing those creates { "": { "": "…" } }
|
|
603
|
+
// noise in the JSON, so treat the whole key as a flat leaf instead.
|
|
552
604
|
separator = false;
|
|
553
605
|
}
|
|
554
606
|
}
|
|
@@ -635,6 +687,22 @@ function buildNewTranslationsForNs(nsKeys, existingTranslations, config, locale,
|
|
|
635
687
|
}
|
|
636
688
|
}
|
|
637
689
|
}
|
|
690
|
+
// Guard against nesting conflicts before writing to the output object.
|
|
691
|
+
// A conflict arises when one extracted key would clobber an ancestor/descendant
|
|
692
|
+
// that was already written by a different extracted key, e.g.:
|
|
693
|
+
// t("a.b") => sets a.b = string
|
|
694
|
+
// t("a.b.c") => tries to descend into a.b which is already a string
|
|
695
|
+
// In that situation we skip the conflicting key and emit a console.error so
|
|
696
|
+
// developers see the problem immediately — a skipped key becomes a missing
|
|
697
|
+
// translation at runtime.
|
|
698
|
+
if (separator && typeof separator === 'string') {
|
|
699
|
+
const conflictingPath = findNestingConflict(newTranslations, key, separator);
|
|
700
|
+
if (conflictingPath !== null) {
|
|
701
|
+
console.error(`[i18next-toolkit] Nesting conflict: key "${key}" conflicts with existing key "${conflictingPath}". ` +
|
|
702
|
+
`"${key}" will be skipped — fix the overlapping key paths in your source code to avoid missing translations at runtime.`);
|
|
703
|
+
continue;
|
|
704
|
+
}
|
|
705
|
+
}
|
|
638
706
|
setNestedValue(newTranslations, key, valueToSet, separator);
|
|
639
707
|
}
|
|
640
708
|
// 2. If sorting is enabled, recursively sort the entire object.
|
|
@@ -214,6 +214,12 @@ class CallExpressionHandler {
|
|
|
214
214
|
: (nsSeparator && originalKey.includes(nsSeparator || ':') ? originalKey : key))
|
|
215
215
|
: key;
|
|
216
216
|
// Handle plurals, context, and returnObjects
|
|
217
|
+
// Compute location once here so it is available to ALL paths below
|
|
218
|
+
// (plural, context, single-other fast paths, and the default leaf path).
|
|
219
|
+
const location = this.getLocationFromNode(node);
|
|
220
|
+
const locationEntry = location
|
|
221
|
+
? [{ file: this.getCurrentFile(), line: location.line, column: location.column }]
|
|
222
|
+
: undefined;
|
|
217
223
|
if (options) {
|
|
218
224
|
const contextPropValue = getObjectPropValueExpression(options, 'context');
|
|
219
225
|
const keysWithContext = [];
|
|
@@ -343,7 +349,8 @@ class CallExpressionHandler {
|
|
|
343
349
|
ns: k.ns,
|
|
344
350
|
defaultValue: k.defaultValue,
|
|
345
351
|
hasCount: true,
|
|
346
|
-
isOrdinal: isOrdinalByKey
|
|
352
|
+
isOrdinal: isOrdinalByKey,
|
|
353
|
+
locations: locationEntry
|
|
347
354
|
});
|
|
348
355
|
}
|
|
349
356
|
}
|
|
@@ -353,7 +360,8 @@ class CallExpressionHandler {
|
|
|
353
360
|
ns,
|
|
354
361
|
defaultValue: dv,
|
|
355
362
|
hasCount: true,
|
|
356
|
-
isOrdinal: isOrdinalByKey
|
|
363
|
+
isOrdinal: isOrdinalByKey,
|
|
364
|
+
locations: locationEntry
|
|
357
365
|
});
|
|
358
366
|
}
|
|
359
367
|
continue;
|
|
@@ -379,7 +387,7 @@ class CallExpressionHandler {
|
|
|
379
387
|
// Pass explicitDefaultForBase so that when a call-site provided an explicit
|
|
380
388
|
// base default (e.g. t('key', 'Default', { count })), plural variant keys
|
|
381
389
|
// are treated as explicit and may be synced to that default.
|
|
382
|
-
this.handlePluralKeys(finalKey, ns, options, isOrdinalByOption || isOrdinalByKey, finalDefaultValue, explicitPluralForVariants);
|
|
390
|
+
this.handlePluralKeys(finalKey, ns, options, isOrdinalByOption || isOrdinalByKey, finalDefaultValue, explicitPluralForVariants, locationEntry);
|
|
383
391
|
}
|
|
384
392
|
continue; // This key is fully handled
|
|
385
393
|
}
|
|
@@ -399,21 +407,14 @@ class CallExpressionHandler {
|
|
|
399
407
|
// Fall through to add the base key itself
|
|
400
408
|
}
|
|
401
409
|
// 5. Default case: Add the simple key
|
|
410
|
+
// eslint-disable-next-line no-lone-blocks
|
|
402
411
|
{
|
|
403
|
-
// ✅ Use the helper method to find location by searching the code
|
|
404
|
-
const location = this.getLocationFromNode(node);
|
|
405
412
|
this.pluginContext.addKey({
|
|
406
413
|
key: finalKey,
|
|
407
414
|
ns,
|
|
408
415
|
defaultValue: dv,
|
|
409
416
|
explicitDefault: explicitDefaultForBase,
|
|
410
|
-
locations:
|
|
411
|
-
? [{
|
|
412
|
-
file: this.getCurrentFile(),
|
|
413
|
-
line: location.line,
|
|
414
|
-
column: location.column
|
|
415
|
-
}]
|
|
416
|
-
: undefined
|
|
417
|
+
locations: locationEntry
|
|
417
418
|
});
|
|
418
419
|
// Check for nested translations in the key itself
|
|
419
420
|
this.extractNestedKeys(finalKey, ns);
|
|
@@ -684,7 +685,7 @@ class CallExpressionHandler {
|
|
|
684
685
|
* @param options - object expression options
|
|
685
686
|
* @param isOrdinal - isOrdinal flag
|
|
686
687
|
*/
|
|
687
|
-
handlePluralKeys(key, ns, options, isOrdinal, defaultValueFromCall, explicitDefaultFromSource) {
|
|
688
|
+
handlePluralKeys(key, ns, options, isOrdinal, defaultValueFromCall, explicitDefaultFromSource, locations) {
|
|
688
689
|
try {
|
|
689
690
|
const type = isOrdinal ? 'ordinal' : 'cardinal';
|
|
690
691
|
// Generate plural forms for ALL target languages to ensure we have all necessary keys
|
|
@@ -792,7 +793,8 @@ class CallExpressionHandler {
|
|
|
792
793
|
defaultValue: finalDefaultValue,
|
|
793
794
|
hasCount: true,
|
|
794
795
|
isOrdinal,
|
|
795
|
-
explicitDefault: Boolean(explicitDefaultFromSource || typeof specificOther === 'string')
|
|
796
|
+
explicitDefault: Boolean(explicitDefaultFromSource || typeof specificOther === 'string'),
|
|
797
|
+
locations
|
|
796
798
|
});
|
|
797
799
|
}
|
|
798
800
|
return;
|
|
@@ -862,7 +864,8 @@ class CallExpressionHandler {
|
|
|
862
864
|
// Do NOT treat the presence of a general base defaultValueFromCall as making variants explicit.
|
|
863
865
|
explicitDefault: Boolean(explicitDefaultFromSource || typeof specificDefault === 'string' || typeof otherDefault === 'string'),
|
|
864
866
|
// If this is a context variant, track the base key (without context or plural suffixes)
|
|
865
|
-
keyAcceptingContext: context !== undefined ? key : undefined
|
|
867
|
+
keyAcceptingContext: context !== undefined ? key : undefined,
|
|
868
|
+
locations
|
|
866
869
|
});
|
|
867
870
|
}
|
|
868
871
|
}
|
|
@@ -871,7 +874,7 @@ class CallExpressionHandler {
|
|
|
871
874
|
this.logger.warn(`Could not determine plural rules for language "${this.config.extract?.primaryLanguage}". Falling back to simple key extraction.`);
|
|
872
875
|
// Fallback to a simple key if Intl API fails
|
|
873
876
|
const defaultValue = defaultValueFromCall || getObjectPropValue(options, 'defaultValue');
|
|
874
|
-
this.pluginContext.addKey({ key, ns, defaultValue: typeof defaultValue === 'string' ? defaultValue : key });
|
|
877
|
+
this.pluginContext.addKey({ key, ns, defaultValue: typeof defaultValue === 'string' ? defaultValue : key, locations });
|
|
875
878
|
}
|
|
876
879
|
}
|
|
877
880
|
/**
|
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;AA82BnF;;;;;;;;;;;;;;;;;;;;;;;;;;;;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,CA8J9B"}
|
|
@@ -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,aAAa,CAAA;AACvG,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAA;AAM1D,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;gBAGlC,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;IAU9B;;;;;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;IAgYxG;;OAEG;IACH,OAAO,CAAC,iBAAiB;IA4BzB,OAAO,CAAC,oBAAoB;IA6E5B,OAAO,CAAC,wBAAwB;IAyEhC;;;;;;OAMG;IACH,OAAO,CAAC,4BAA4B;IA8BpC;;;;;;;;;;;;;OAaG;IACH,OAAO,CAAC,sBAAsB;IA2C9B;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,gBAAgB;
|
|
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,aAAa,CAAA;AACvG,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAA;AAM1D,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;gBAGlC,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;IAU9B;;;;;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;IAgYxG;;OAEG;IACH,OAAO,CAAC,iBAAiB;IA4BzB,OAAO,CAAC,oBAAoB;IA6E5B,OAAO,CAAC,wBAAwB;IAyEhC;;;;;;OAMG;IACH,OAAO,CAAC,4BAA4B;IA8BpC;;;;;;;;;;;;;OAaG;IACH,OAAO,CAAC,sBAAsB;IA2C9B;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,gBAAgB;IA+LxB;;;;;;;;;OASG;IACH,OAAO,CAAC,eAAe;CA2BxB"}
|