i18next-cli 1.55.0 → 1.56.0
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 +10 -3
- package/dist/cjs/cli.js +1 -1
- package/dist/cjs/extractor/core/translation-manager.js +127 -54
- package/dist/cjs/extractor/parsers/call-expression-handler.js +17 -93
- package/dist/cjs/status.js +73 -12
- package/dist/cjs/utils/context-variants.js +59 -0
- package/dist/cjs/utils/nesting.js +100 -0
- package/dist/esm/cli.js +1 -1
- package/dist/esm/extractor/core/translation-manager.js +126 -53
- package/dist/esm/extractor/parsers/call-expression-handler.js +17 -93
- package/dist/esm/status.js +74 -13
- package/dist/esm/utils/context-variants.js +57 -0
- package/dist/esm/utils/nesting.js +98 -0
- 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 +3 -2
- package/types/extractor/parsers/call-expression-handler.d.ts.map +1 -1
- package/types/status.d.ts.map +1 -1
- package/types/utils/context-variants.d.ts +22 -0
- package/types/utils/context-variants.d.ts.map +1 -0
- package/types/utils/nesting.d.ts +36 -0
- package/types/utils/nesting.d.ts.map +1 -0
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared parser for i18next-style nested translation references of the form
|
|
3
|
+
* `$t(key, { options })`.
|
|
4
|
+
*
|
|
5
|
+
* Used in two places:
|
|
6
|
+
* 1. The extractor's AST pass scans source-code keys and default values for
|
|
7
|
+
* nested references and registers the referenced keys (so they show up in
|
|
8
|
+
* output translation files).
|
|
9
|
+
* 2. The translation-manager uses it during `removeUnusedKeys` cleanup so
|
|
10
|
+
* keys that are only referenced from inside a translation value (and thus
|
|
11
|
+
* invisible to the AST pass) are preserved instead of being deleted.
|
|
12
|
+
*/
|
|
13
|
+
const naturalLanguageChars = /[ ,?!;]/;
|
|
14
|
+
const looksLikeNaturalLanguage = (s) => naturalLanguageChars.test(s);
|
|
15
|
+
const escapeRegex = (str) => str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
16
|
+
/**
|
|
17
|
+
* Scans a string for `$t(...)` references and returns metadata about each one.
|
|
18
|
+
* The implementation mirrors the behaviour of i18next's Interpolator so that
|
|
19
|
+
* the extractor and translation manager agree on what counts as a reference.
|
|
20
|
+
*/
|
|
21
|
+
function parseNestedReferences(text, config) {
|
|
22
|
+
if (!text || typeof text !== 'string')
|
|
23
|
+
return [];
|
|
24
|
+
const prefix = config.nestingPrefix ?? '$t(';
|
|
25
|
+
const suffix = config.nestingSuffix ?? ')';
|
|
26
|
+
const separator = config.nestingOptionsSeparator ?? ',';
|
|
27
|
+
const nsSeparator = config.nsSeparator ?? ':';
|
|
28
|
+
const escapedPrefix = escapeRegex(prefix);
|
|
29
|
+
const escapedSuffix = escapeRegex(suffix);
|
|
30
|
+
// Regex adapted from i18next Interpolator.js — matches `$t(key)` or
|
|
31
|
+
// `$t(key, { options })` with (limited) support for balanced parens and
|
|
32
|
+
// quoted strings.
|
|
33
|
+
const nestingRegexp = new RegExp(`${escapedPrefix}((?:[^()"']+|"[^"]*"|'[^']*'|\\((?:[^()]|"[^"]*"|'[^']*')*\\))*?)${escapedSuffix}`, 'g');
|
|
34
|
+
const results = [];
|
|
35
|
+
let match;
|
|
36
|
+
while ((match = nestingRegexp.exec(text)) !== null) {
|
|
37
|
+
const content = match[1];
|
|
38
|
+
if (!content)
|
|
39
|
+
continue;
|
|
40
|
+
let key = content;
|
|
41
|
+
let optionsString = '';
|
|
42
|
+
if (content.indexOf(separator) < 0) {
|
|
43
|
+
key = content.trim();
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
// i18next does: const c = key.split(new RegExp(`${sep}[ ]*{`));
|
|
47
|
+
// This assumes options start with `{`.
|
|
48
|
+
const sepRegex = new RegExp(`${escapeRegex(separator)}[ ]*{`);
|
|
49
|
+
const parts = content.split(sepRegex);
|
|
50
|
+
if (parts.length > 1) {
|
|
51
|
+
key = parts[0].trim();
|
|
52
|
+
optionsString = `{${parts.slice(1).join(separator + ' {')}`;
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
const sepIdx = content.indexOf(separator);
|
|
56
|
+
key = content.substring(0, sepIdx).trim();
|
|
57
|
+
optionsString = content.substring(sepIdx + 1).trim();
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if ((key.startsWith("'") && key.endsWith("'")) || (key.startsWith('"') && key.endsWith('"'))) {
|
|
61
|
+
key = key.slice(1, -1);
|
|
62
|
+
}
|
|
63
|
+
if (!key)
|
|
64
|
+
continue;
|
|
65
|
+
let ns;
|
|
66
|
+
if (nsSeparator && typeof nsSeparator === 'string' && key.includes(nsSeparator)) {
|
|
67
|
+
const parts = key.split(nsSeparator);
|
|
68
|
+
const candidateNs = parts[0];
|
|
69
|
+
if (!looksLikeNaturalLanguage(candidateNs)) {
|
|
70
|
+
ns = parts.shift();
|
|
71
|
+
key = parts.join(nsSeparator);
|
|
72
|
+
if (!key || key.trim() === '')
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
ns = config.defaultNS;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
ns = config.defaultNS;
|
|
81
|
+
}
|
|
82
|
+
let hasCount = false;
|
|
83
|
+
let context;
|
|
84
|
+
if (optionsString) {
|
|
85
|
+
if (/['"]?count['"]?\s*:/.test(optionsString)) {
|
|
86
|
+
hasCount = true;
|
|
87
|
+
}
|
|
88
|
+
const contextMatch = /['"]?context['"]?\s*:\s*(['"])(.*?)\1/.exec(optionsString);
|
|
89
|
+
if (contextMatch) {
|
|
90
|
+
context = contextMatch[2];
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
results.push({ key, ns, hasCount, context });
|
|
94
|
+
}
|
|
95
|
+
return results;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export { parseNestedReferences };
|
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;AAohC9F;;;;;;;;;;;;;;;;;;;;;;;;;;;;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"}
|
|
@@ -35,10 +35,11 @@ export declare class CallExpressionHandler {
|
|
|
35
35
|
*/
|
|
36
36
|
handleCallExpression(node: CallExpression, getScopeInfo: (name: string) => ScopeInfo | undefined): void;
|
|
37
37
|
/**
|
|
38
|
-
* Scans a string for nested translations like $t(key, options) and
|
|
38
|
+
* Scans a string for nested translations like $t(key, options) and registers
|
|
39
|
+
* the referenced keys (plus their plural / context variants) on the current
|
|
40
|
+
* plugin context.
|
|
39
41
|
*/
|
|
40
42
|
private extractNestedKeys;
|
|
41
|
-
private processNestedContent;
|
|
42
43
|
private generateNestedPluralKeys;
|
|
43
44
|
/**
|
|
44
45
|
* Processed a call expression to extract keys from the specified argument.
|
|
@@ -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;
|
|
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;IA0ZxG;;;;OAIG;IACH,OAAO,CAAC,iBAAiB;IAmBzB,OAAO,CAAC,wBAAwB;IAyEhC;;;;;;OAMG;IACH,OAAO,CAAC,4BAA4B;IA8BpC;;;;;;;;;;;;;;;;OAgBG;IACH,OAAO,CAAC,uBAAuB;IAgE/B;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,gBAAgB;IAyMxB;;;;;;;;;OASG;IACH,OAAO,CAAC,eAAe;CA2BxB"}
|
package/types/status.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../src/status.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,oBAAoB,EAAgB,MAAM,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../src/status.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,oBAAoB,EAAgB,MAAM,YAAY,CAAA;AAMpE;;GAEG;AACH,UAAU,aAAa;IACrB,0EAA0E;IAC1E,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,wCAAwC;IACxC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uEAAuE;IACvE,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAqDD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,SAAS,CAAE,MAAM,EAAE,oBAAoB,EAAE,OAAO,GAAE,aAAkB,iBAuBzF"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Helpers for reasoning about context variants of translation keys.
|
|
3
|
+
*
|
|
4
|
+
* A "key accepting context" is a base key that was called with a `context`
|
|
5
|
+
* option in source code (e.g. `t('friend', { context: gender })`). Its
|
|
6
|
+
* variants in the translation file look like `<base><contextSeparator><ctx>`
|
|
7
|
+
* (optionally suffixed with a CLDR plural form).
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Checks if an existing key is a context variant of a base key that accepts context.
|
|
11
|
+
* Handles:
|
|
12
|
+
* - Keys suffixed with a CLDR plural form (e.g. `friend_male_one`).
|
|
13
|
+
* - Context values that contain the separator (e.g. `mc_laren`).
|
|
14
|
+
*
|
|
15
|
+
* @param existingKey - The key from the translation file to check
|
|
16
|
+
* @param keysAcceptingContext - Set of base keys that were used with context in source code
|
|
17
|
+
* @param pluralSeparator - The separator used for plural forms (default: '_')
|
|
18
|
+
* @param contextSeparator - The separator used for context variants (default: '_')
|
|
19
|
+
* @returns true if the existing key is a context variant of a key accepting context
|
|
20
|
+
*/
|
|
21
|
+
export declare function isContextVariantOfAcceptingKey(existingKey: string, keysAcceptingContext: ReadonlySet<string>, pluralSeparator: string, contextSeparator: string): boolean;
|
|
22
|
+
//# sourceMappingURL=context-variants.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context-variants.d.ts","sourceRoot":"","sources":["../../src/utils/context-variants.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH;;;;;;;;;;;GAWG;AACH,wBAAgB,8BAA8B,CAC5C,WAAW,EAAE,MAAM,EACnB,oBAAoB,EAAE,WAAW,CAAC,MAAM,CAAC,EACzC,eAAe,EAAE,MAAM,EACvB,gBAAgB,EAAE,MAAM,GACvB,OAAO,CAsCT"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared parser for i18next-style nested translation references of the form
|
|
3
|
+
* `$t(key, { options })`.
|
|
4
|
+
*
|
|
5
|
+
* Used in two places:
|
|
6
|
+
* 1. The extractor's AST pass scans source-code keys and default values for
|
|
7
|
+
* nested references and registers the referenced keys (so they show up in
|
|
8
|
+
* output translation files).
|
|
9
|
+
* 2. The translation-manager uses it during `removeUnusedKeys` cleanup so
|
|
10
|
+
* keys that are only referenced from inside a translation value (and thus
|
|
11
|
+
* invisible to the AST pass) are preserved instead of being deleted.
|
|
12
|
+
*/
|
|
13
|
+
export interface NestingConfig {
|
|
14
|
+
nestingPrefix?: string;
|
|
15
|
+
nestingSuffix?: string;
|
|
16
|
+
nestingOptionsSeparator?: string;
|
|
17
|
+
nsSeparator?: string | false | null;
|
|
18
|
+
defaultNS?: string | false;
|
|
19
|
+
}
|
|
20
|
+
export interface NestedReference {
|
|
21
|
+
/** The referenced key (namespace-stripped when a namespace was resolved). */
|
|
22
|
+
key: string;
|
|
23
|
+
/** Resolved namespace for the reference (or defaultNS / undefined). */
|
|
24
|
+
ns: string | false | undefined;
|
|
25
|
+
/** True when the nested options object contains a `count` property. */
|
|
26
|
+
hasCount: boolean;
|
|
27
|
+
/** Static context string, if the nested options contain `context: 'foo'`. */
|
|
28
|
+
context?: string;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Scans a string for `$t(...)` references and returns metadata about each one.
|
|
32
|
+
* The implementation mirrors the behaviour of i18next's Interpolator so that
|
|
33
|
+
* the extractor and translation manager agree on what counts as a reference.
|
|
34
|
+
*/
|
|
35
|
+
export declare function parseNestedReferences(text: string, config: NestingConfig): NestedReference[];
|
|
36
|
+
//# sourceMappingURL=nesting.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nesting.d.ts","sourceRoot":"","sources":["../../src/utils/nesting.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAOH,MAAM,WAAW,aAAa;IAC5B,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,uBAAuB,CAAC,EAAE,MAAM,CAAA;IAChC,WAAW,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,IAAI,CAAA;IACnC,SAAS,CAAC,EAAE,MAAM,GAAG,KAAK,CAAA;CAC3B;AAED,MAAM,WAAW,eAAe;IAC9B,6EAA6E;IAC7E,GAAG,EAAE,MAAM,CAAA;IACX,uEAAuE;IACvE,EAAE,EAAE,MAAM,GAAG,KAAK,GAAG,SAAS,CAAA;IAC9B,uEAAuE;IACvE,QAAQ,EAAE,OAAO,CAAA;IACjB,6EAA6E;IAC7E,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,GAAG,eAAe,EAAE,CAoF7F"}
|