@better-i18n/cli 0.1.3 → 0.1.5
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 +39 -54
- package/dist/analyzer/index.d.ts +8 -2
- package/dist/analyzer/index.d.ts.map +1 -1
- package/dist/analyzer/index.js +133 -2
- package/dist/analyzer/index.js.map +1 -1
- package/dist/analyzer/rules/translation-function.d.ts.map +1 -1
- package/dist/analyzer/rules/translation-function.js +36 -21
- package/dist/analyzer/rules/translation-function.js.map +1 -1
- package/dist/analyzer/types.d.ts +40 -0
- package/dist/analyzer/types.d.ts.map +1 -1
- package/dist/commands/scan.d.ts.map +1 -1
- package/dist/commands/scan.js +18 -1
- package/dist/commands/scan.js.map +1 -1
- package/dist/commands/sync.d.ts +12 -0
- package/dist/commands/sync.d.ts.map +1 -0
- package/dist/commands/sync.js +574 -0
- package/dist/commands/sync.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +8 -10
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/commands/extract-keys.d.ts +0 -13
- package/dist/commands/extract-keys.d.ts.map +0 -1
- package/dist/commands/extract-keys.js +0 -347
- package/dist/commands/extract-keys.js.map +0 -1
package/README.md
CHANGED
|
@@ -101,25 +101,34 @@ better-i18n scan --staged # Only scan git staged files
|
|
|
101
101
|
better-i18n scan --verbose # Show detailed output
|
|
102
102
|
```
|
|
103
103
|
|
|
104
|
-
### `better-i18n
|
|
104
|
+
### `better-i18n sync`
|
|
105
105
|
|
|
106
|
-
|
|
106
|
+
Compare local translation keys (t() calls) with your Better i18n cloud project.
|
|
107
107
|
|
|
108
108
|
```bash
|
|
109
|
-
#
|
|
110
|
-
better-i18n
|
|
109
|
+
# Basic usage (grouped tree output)
|
|
110
|
+
better-i18n sync
|
|
111
111
|
|
|
112
|
-
#
|
|
113
|
-
better-i18n
|
|
112
|
+
# Minimal metrics only
|
|
113
|
+
better-i18n sync --summary
|
|
114
114
|
|
|
115
|
-
#
|
|
116
|
-
better-i18n
|
|
115
|
+
# Deep audit log & scope trace
|
|
116
|
+
better-i18n sync --verbose
|
|
117
117
|
|
|
118
|
-
# JSON output
|
|
119
|
-
better-i18n
|
|
118
|
+
# JSON output for CI automation
|
|
119
|
+
better-i18n sync --format json
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Hook-based Namespace Detection
|
|
123
|
+
|
|
124
|
+
The CLI automatically detects namespaces from the `useTranslation` hook.
|
|
120
125
|
|
|
121
|
-
|
|
122
|
-
|
|
126
|
+
```tsx
|
|
127
|
+
// This will be extracted as 'auth.login' and 'auth.register'
|
|
128
|
+
const { t } = useTranslation('auth');
|
|
129
|
+
|
|
130
|
+
t('login');
|
|
131
|
+
t('register');
|
|
123
132
|
```
|
|
124
133
|
|
|
125
134
|
**Output format (JSON):**
|
|
@@ -139,53 +148,29 @@ better-i18n extract-keys --verbose
|
|
|
139
148
|
}
|
|
140
149
|
```
|
|
141
150
|
|
|
142
|
-
**With
|
|
143
|
-
|
|
144
|
-
```json
|
|
145
|
-
{
|
|
146
|
-
"comparison": {
|
|
147
|
-
"localKeys": { ... },
|
|
148
|
-
"remoteKeys": {
|
|
149
|
-
"namespaces": { ... },
|
|
150
|
-
"totalCount": 150
|
|
151
|
-
},
|
|
152
|
-
"missingKeys": {
|
|
153
|
-
"hero": ["hero.cta", "hero.benefits"]
|
|
154
|
-
},
|
|
155
|
-
"unusedKeys": {
|
|
156
|
-
"old": ["old.section"]
|
|
157
|
-
},
|
|
158
|
-
"coverage": {
|
|
159
|
-
"local": 85,
|
|
160
|
-
"remote": 96
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
```
|
|
165
|
-
|
|
166
|
-
**Human-readable output with `--compare`:**
|
|
167
|
-
|
|
168
|
-
```
|
|
169
|
-
✓ Project: better-i18n/landing
|
|
170
|
-
✓ Found 57 files
|
|
171
|
-
✓ Fetched 12 namespaces from CDN
|
|
151
|
+
**With `sync` output (default):**
|
|
172
152
|
|
|
153
|
+
```text
|
|
173
154
|
📊 Translation Keys Comparison
|
|
155
|
+
Source locale: en
|
|
174
156
|
|
|
175
157
|
Coverage:
|
|
176
|
-
Local → Remote:
|
|
177
|
-
Remote Used:
|
|
158
|
+
Local → Remote: 59%
|
|
159
|
+
Remote Used: 63%
|
|
160
|
+
|
|
161
|
+
⊕ Missing in Remote (473 keys)
|
|
162
|
+
pages (300)
|
|
163
|
+
affordableEnglishLearning (meta.title, meta.description, ...+12)
|
|
164
|
+
bestApps (hero.badge, title_prefix, title_accent)
|
|
178
165
|
|
|
179
|
-
|
|
180
|
-
hero
|
|
181
|
-
• hero.cta
|
|
182
|
-
• hero.benefits
|
|
166
|
+
hero (5)
|
|
167
|
+
hero (ariaLabel, imageAlt, ...)
|
|
183
168
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
169
|
+
⊖ Unused in Code (386 keys)
|
|
170
|
+
features (25)
|
|
171
|
+
practiceSpeaking (title, subtitle, icon)
|
|
187
172
|
|
|
188
|
-
Scanned
|
|
173
|
+
Scanned 246 files in 0.85s
|
|
189
174
|
✓ Comparison complete
|
|
190
175
|
```
|
|
191
176
|
|
|
@@ -478,10 +463,10 @@ This CLI is one component of the **Better i18n translation management platform**
|
|
|
478
463
|
Developer Workflow:
|
|
479
464
|
├─ Write code with hardcoded strings
|
|
480
465
|
├─ Run: better-i18n scan → Detect hardcoded strings ⚠️
|
|
481
|
-
├─ Run: better-i18n
|
|
466
|
+
├─ Run: better-i18n sync → Compare local vs cloud keys
|
|
482
467
|
├─ Review in Better i18n Dashboard
|
|
483
468
|
├─ GitHub Hook: better-i18n scan --staged → Pre-commit check
|
|
484
|
-
├─ CI/CD: better-i18n
|
|
469
|
+
├─ CI/CD: better-i18n sync --format json → Audit translations in pipeline
|
|
485
470
|
└─ Dashboard: Manage translations, sync with GitHub
|
|
486
471
|
```
|
|
487
472
|
|
package/dist/analyzer/index.d.ts
CHANGED
|
@@ -7,9 +7,15 @@ import type { Issue, LintConfig } from "./types.js";
|
|
|
7
7
|
/**
|
|
8
8
|
* Analyze a single file for hardcoded strings
|
|
9
9
|
*/
|
|
10
|
-
export declare function analyzeFile(filePath: string, config?: LintConfig): Promise<
|
|
10
|
+
export declare function analyzeFile(filePath: string, config?: LintConfig): Promise<{
|
|
11
|
+
issues: Issue[];
|
|
12
|
+
stats: any;
|
|
13
|
+
}>;
|
|
11
14
|
/**
|
|
12
15
|
* Analyze source text (useful for testing)
|
|
13
16
|
*/
|
|
14
|
-
export declare function analyzeSourceText(sourceText: string, filePath: string, config?: LintConfig):
|
|
17
|
+
export declare function analyzeSourceText(sourceText: string, filePath: string, config?: LintConfig): {
|
|
18
|
+
issues: Issue[];
|
|
19
|
+
stats: any;
|
|
20
|
+
};
|
|
15
21
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/analyzer/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAUH,OAAO,KAAK,EAAE,KAAK,EAAE,UAAU,EAAe,MAAM,YAAY,CAAC;AAEjE;;GAEG;AACH,wBAAsB,WAAW,CAC/B,QAAQ,EAAE,MAAM,EAChB,MAAM,CAAC,EAAE,UAAU,GAClB,OAAO,CAAC,KAAK,EAAE,CAAC,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/analyzer/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAUH,OAAO,KAAK,EAAE,KAAK,EAAE,UAAU,EAAe,MAAM,YAAY,CAAC;AAEjE;;GAEG;AACH,wBAAsB,WAAW,CAC/B,QAAQ,EAAE,MAAM,EAChB,MAAM,CAAC,EAAE,UAAU,GAClB,OAAO,CAAC;IAAE,MAAM,EAAE,KAAK,EAAE,CAAC;IAAC,KAAK,EAAE,GAAG,CAAA;CAAE,CAAC,CAG1C;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAC/B,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,EAChB,MAAM,CAAC,EAAE,UAAU,GAClB;IAAE,MAAM,EAAE,KAAK,EAAE,CAAC;IAAC,KAAK,EAAE,GAAG,CAAA;CAAE,CA8LjC"}
|
package/dist/analyzer/index.js
CHANGED
|
@@ -19,13 +19,132 @@ export async function analyzeFile(filePath, config) {
|
|
|
19
19
|
export function analyzeSourceText(sourceText, filePath, config) {
|
|
20
20
|
const sourceFile = ts.createSourceFile(filePath, sourceText, ts.ScriptTarget.Latest, true, getScriptKind(filePath));
|
|
21
21
|
const issues = [];
|
|
22
|
-
const
|
|
22
|
+
const stats = {
|
|
23
|
+
dynamicKeys: 0,
|
|
24
|
+
dynamicNamespaces: 0,
|
|
25
|
+
unboundTranslators: 0,
|
|
26
|
+
rootScopedTranslators: 0,
|
|
27
|
+
};
|
|
28
|
+
// Lexical scope stack: each element is a map of identifier -> NamespaceBinding
|
|
29
|
+
// NamespaceBinding type is imported from types.js
|
|
30
|
+
const scopeStack = [{}];
|
|
31
|
+
function pushScope() {
|
|
32
|
+
scopeStack.push({});
|
|
33
|
+
}
|
|
34
|
+
function popScope() {
|
|
35
|
+
scopeStack.pop();
|
|
36
|
+
}
|
|
37
|
+
function getCurrentBindings() {
|
|
38
|
+
const combined = {};
|
|
39
|
+
for (const scope of scopeStack) {
|
|
40
|
+
Object.assign(combined, scope);
|
|
41
|
+
}
|
|
42
|
+
return combined;
|
|
43
|
+
}
|
|
44
|
+
function registerBinding(name, binding) {
|
|
45
|
+
scopeStack[scopeStack.length - 1][name] = binding;
|
|
46
|
+
}
|
|
47
|
+
function detectBinding(initializer) {
|
|
48
|
+
// Unwrap await
|
|
49
|
+
const call = ts.isAwaitExpression(initializer)
|
|
50
|
+
? initializer.expression
|
|
51
|
+
: initializer;
|
|
52
|
+
if (!ts.isCallExpression(call))
|
|
53
|
+
return null;
|
|
54
|
+
const funcName = getIdentifierName(call.expression);
|
|
55
|
+
if (funcName === "useTranslations" || funcName === "useTranslation") {
|
|
56
|
+
if (call.arguments.length === 0) {
|
|
57
|
+
stats.rootScopedTranslators++;
|
|
58
|
+
return { type: "root-scoped" };
|
|
59
|
+
}
|
|
60
|
+
const arg = call.arguments[0];
|
|
61
|
+
if (ts.isStringLiteral(arg)) {
|
|
62
|
+
return { type: "bound-scoped", namespace: arg.text };
|
|
63
|
+
}
|
|
64
|
+
stats.dynamicNamespaces++;
|
|
65
|
+
return { type: "unknown-scoped", dynamic: true };
|
|
66
|
+
}
|
|
67
|
+
if (funcName === "getTranslations") {
|
|
68
|
+
if (call.arguments.length === 0) {
|
|
69
|
+
stats.rootScopedTranslators++;
|
|
70
|
+
return { type: "root-scoped" };
|
|
71
|
+
}
|
|
72
|
+
const arg = call.arguments[0];
|
|
73
|
+
if (ts.isObjectLiteralExpression(arg)) {
|
|
74
|
+
const nsProp = arg.properties.find((p) => (ts.isPropertyAssignment(p) ||
|
|
75
|
+
ts.isShorthandPropertyAssignment(p)) &&
|
|
76
|
+
ts.isIdentifier(p.name) &&
|
|
77
|
+
p.name.text === "namespace");
|
|
78
|
+
if (!nsProp) {
|
|
79
|
+
stats.rootScopedTranslators++;
|
|
80
|
+
return { type: "root-scoped" };
|
|
81
|
+
}
|
|
82
|
+
if (ts.isPropertyAssignment(nsProp)) {
|
|
83
|
+
if (ts.isStringLiteral(nsProp.initializer)) {
|
|
84
|
+
return {
|
|
85
|
+
type: "bound-scoped",
|
|
86
|
+
namespace: nsProp.initializer.text,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
stats.dynamicNamespaces++;
|
|
90
|
+
return { type: "unknown-scoped", dynamic: true };
|
|
91
|
+
}
|
|
92
|
+
// Shorthand implies identifier, which is dynamic in this context
|
|
93
|
+
stats.dynamicNamespaces++;
|
|
94
|
+
return { type: "unknown-scoped", dynamic: true };
|
|
95
|
+
}
|
|
96
|
+
stats.dynamicNamespaces++;
|
|
97
|
+
return { type: "unknown-scoped", dynamic: true };
|
|
98
|
+
}
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
23
101
|
// Check if rules are enabled
|
|
24
102
|
const rules = config?.rules || {};
|
|
25
103
|
const jsxTextEnabled = rules["jsx-text"] !== "off";
|
|
26
104
|
const jsxAttrEnabled = rules["jsx-attribute"] !== "off";
|
|
27
105
|
const ternaryEnabled = rules["ternary-locale"] !== "off";
|
|
28
106
|
function visit(node) {
|
|
107
|
+
let pushed = false;
|
|
108
|
+
// Scoping nodes: functions, blocks
|
|
109
|
+
if (ts.isFunctionLike(node) || ts.isBlock(node)) {
|
|
110
|
+
pushScope();
|
|
111
|
+
pushed = true;
|
|
112
|
+
}
|
|
113
|
+
// Detect bindings in VariableDeclarations
|
|
114
|
+
if (ts.isVariableDeclaration(node) && node.initializer) {
|
|
115
|
+
const binding = detectBinding(node.initializer);
|
|
116
|
+
if (binding) {
|
|
117
|
+
if (ts.isIdentifier(node.name)) {
|
|
118
|
+
registerBinding(node.name.text, binding);
|
|
119
|
+
}
|
|
120
|
+
else if (ts.isObjectBindingPattern(node.name)) {
|
|
121
|
+
// const { t } = useTranslations()
|
|
122
|
+
for (const element of node.name.elements) {
|
|
123
|
+
const id = ts.isIdentifier(element.name) ? element.name.text : null;
|
|
124
|
+
const prop = element.propertyName && ts.isIdentifier(element.propertyName)
|
|
125
|
+
? element.propertyName.text
|
|
126
|
+
: id;
|
|
127
|
+
if (id && prop === "t") {
|
|
128
|
+
registerBinding(id, binding);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
// Detect bindings in Assignments (t = useTranslations())
|
|
135
|
+
if (ts.isBinaryExpression(node) &&
|
|
136
|
+
node.operatorToken.kind === ts.SyntaxKind.EqualsToken) {
|
|
137
|
+
const binding = detectBinding(node.right);
|
|
138
|
+
if (binding && ts.isIdentifier(node.left)) {
|
|
139
|
+
registerBinding(node.left.text, binding);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
const ctx = {
|
|
143
|
+
filePath,
|
|
144
|
+
sourceFile,
|
|
145
|
+
namespaceMap: getCurrentBindings(),
|
|
146
|
+
stats,
|
|
147
|
+
};
|
|
29
148
|
// JSX Text
|
|
30
149
|
if (jsxTextEnabled && ts.isJsxText(node)) {
|
|
31
150
|
const issue = checkJsxText(node, ctx);
|
|
@@ -51,9 +170,21 @@ export function analyzeSourceText(sourceText, filePath, config) {
|
|
|
51
170
|
issues.push(issue);
|
|
52
171
|
}
|
|
53
172
|
ts.forEachChild(node, visit);
|
|
173
|
+
if (pushed)
|
|
174
|
+
popScope();
|
|
54
175
|
}
|
|
55
176
|
visit(sourceFile);
|
|
56
|
-
return issues;
|
|
177
|
+
return { issues, stats };
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Helper to get identifier name
|
|
181
|
+
*/
|
|
182
|
+
function getIdentifierName(expr) {
|
|
183
|
+
if (ts.isIdentifier(expr))
|
|
184
|
+
return expr.text;
|
|
185
|
+
if (ts.isPropertyAccessExpression(expr))
|
|
186
|
+
return expr.name.text;
|
|
187
|
+
return null;
|
|
57
188
|
}
|
|
58
189
|
/**
|
|
59
190
|
* Get TypeScript script kind based on file extension
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/analyzer/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,MAAM,YAAY,CAAC;AAC5B,OAAO,EACL,iBAAiB,EACjB,YAAY,EACZ,kBAAkB,EAClB,wBAAwB,GACzB,MAAM,kBAAkB,CAAC;AAG1B;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,QAAgB,EAChB,MAAmB;IAEnB,MAAM,UAAU,GAAG,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IACnD,OAAO,iBAAiB,CAAC,UAAU,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;AACzD,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,iBAAiB,CAC/B,UAAkB,EAClB,QAAgB,EAChB,MAAmB;IAEnB,MAAM,UAAU,GAAG,EAAE,CAAC,gBAAgB,CACpC,QAAQ,EACR,UAAU,EACV,EAAE,CAAC,YAAY,CAAC,MAAM,EACtB,IAAI,EACJ,aAAa,CAAC,QAAQ,CAAC,CACxB,CAAC;IAEF,MAAM,MAAM,GAAY,EAAE,CAAC;IAC3B,MAAM,GAAG,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/analyzer/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"translation-function.d.ts","sourceRoot":"","sources":["../../../src/analyzer/rules/translation-function.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,MAAM,YAAY,CAAC;AAC5B,OAAO,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAOtD;;GAEG;AACH,wBAAgB,wBAAwB,CACtC,IAAI,EAAE,EAAE,CAAC,cAAc,EACvB,GAAG,EAAE,WAAW,GACf,KAAK,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"translation-function.d.ts","sourceRoot":"","sources":["../../../src/analyzer/rules/translation-function.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,MAAM,YAAY,CAAC;AAC5B,OAAO,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAOtD;;GAEG;AACH,wBAAgB,wBAAwB,CACtC,IAAI,EAAE,EAAE,CAAC,cAAc,EACvB,GAAG,EAAE,WAAW,GACf,KAAK,GAAG,IAAI,CAmDd"}
|
|
@@ -12,20 +12,33 @@ const TRANSLATION_FUNCTIONS = new Set(["t", "useTranslation"]);
|
|
|
12
12
|
* Check for translation function calls
|
|
13
13
|
*/
|
|
14
14
|
export function checkTranslationFunction(node, ctx) {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
const bindingId = getBindingIdentifier(node);
|
|
16
|
+
if (!bindingId)
|
|
17
|
+
return null;
|
|
18
|
+
// Check if this identifier is bound to a translation namespace
|
|
19
|
+
const binding = ctx.namespaceMap?.[bindingId];
|
|
20
|
+
// If not bound and not a known global/default translation function, skip
|
|
21
|
+
if (!binding && !TRANSLATION_FUNCTIONS.has(bindingId)) {
|
|
22
|
+
if (ctx.stats)
|
|
23
|
+
ctx.stats.unboundTranslators++;
|
|
18
24
|
return null;
|
|
19
25
|
}
|
|
20
|
-
// Get the first argument (
|
|
26
|
+
// Get the first argument (translation key)
|
|
21
27
|
const args = node.arguments;
|
|
22
|
-
if (args.length === 0)
|
|
28
|
+
if (args.length === 0)
|
|
23
29
|
return null;
|
|
24
|
-
}
|
|
25
30
|
const firstArg = args[0];
|
|
26
31
|
// Handle string literal: t("auth.login")
|
|
27
32
|
if (ts.isStringLiteral(firstArg)) {
|
|
28
33
|
const pos = ctx.sourceFile.getLineAndCharacterOfPosition(firstArg.getStart());
|
|
34
|
+
let key = firstArg.text;
|
|
35
|
+
// Apply namespace prefix if bound to a specific namespace
|
|
36
|
+
if (binding?.type === "bound-scoped" && binding.namespace) {
|
|
37
|
+
const prefix = binding.namespace;
|
|
38
|
+
if (!key.startsWith(`${prefix}.`)) {
|
|
39
|
+
key = `${prefix}.${key}`;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
29
42
|
return {
|
|
30
43
|
file: ctx.filePath,
|
|
31
44
|
line: pos.line + 1,
|
|
@@ -33,33 +46,35 @@ export function checkTranslationFunction(node, ctx) {
|
|
|
33
46
|
text: firstArg.text,
|
|
34
47
|
type: "string-variable",
|
|
35
48
|
severity: "info",
|
|
36
|
-
message: `Translation key: "${
|
|
37
|
-
key
|
|
49
|
+
message: `Translation key: "${key}"`,
|
|
50
|
+
key,
|
|
51
|
+
bindingType: binding?.type || "unbound",
|
|
52
|
+
namespace: binding?.namespace,
|
|
38
53
|
};
|
|
39
54
|
}
|
|
40
|
-
// Handle template literal
|
|
41
|
-
if (
|
|
42
|
-
|
|
43
|
-
return null;
|
|
44
|
-
}
|
|
45
|
-
// Handle identifier: t(keyVar) - can't extract statically
|
|
55
|
+
// Handle template literal or dynamic keys
|
|
56
|
+
if (ctx.stats)
|
|
57
|
+
ctx.stats.dynamicKeys++;
|
|
46
58
|
return null;
|
|
47
59
|
}
|
|
48
60
|
/**
|
|
49
|
-
* Extract
|
|
61
|
+
* Extract the binding identifier from a call expression.
|
|
62
|
+
* Handles: t(), t.raw(), t.rich(), t.has()
|
|
50
63
|
*/
|
|
51
|
-
function
|
|
64
|
+
function getBindingIdentifier(node) {
|
|
52
65
|
const expr = node.expression;
|
|
53
66
|
// Simple identifier: t()
|
|
54
67
|
if (ts.isIdentifier(expr)) {
|
|
55
68
|
return expr.text;
|
|
56
69
|
}
|
|
57
|
-
// Property access:
|
|
58
|
-
if (ts.isPropertyAccessExpression(expr)
|
|
59
|
-
|
|
70
|
+
// Property access: t.raw(), t.rich(), t.has()
|
|
71
|
+
if (ts.isPropertyAccessExpression(expr) &&
|
|
72
|
+
ts.isIdentifier(expr.expression)) {
|
|
73
|
+
const propName = expr.name.text;
|
|
74
|
+
if (["raw", "rich", "has"].includes(propName)) {
|
|
75
|
+
return expr.expression.text;
|
|
76
|
+
}
|
|
60
77
|
}
|
|
61
|
-
// Hook destructuring: const { t } = useTranslation(); t()
|
|
62
|
-
// Already handled by Identifier case above
|
|
63
78
|
return null;
|
|
64
79
|
}
|
|
65
80
|
//# sourceMappingURL=translation-function.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"translation-function.js","sourceRoot":"","sources":["../../../src/analyzer/rules/translation-function.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,MAAM,YAAY,CAAC;AAG5B;;GAEG;AACH,MAAM,qBAAqB,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,EAAE,gBAAgB,CAAC,CAAC,CAAC;AAE/D;;GAEG;AACH,MAAM,UAAU,wBAAwB,CACtC,IAAuB,EACvB,GAAgB;IAEhB,
|
|
1
|
+
{"version":3,"file":"translation-function.js","sourceRoot":"","sources":["../../../src/analyzer/rules/translation-function.ts"],"names":[],"mappings":"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"}
|
package/dist/analyzer/types.d.ts
CHANGED
|
@@ -11,10 +11,26 @@ export interface Issue {
|
|
|
11
11
|
message: string;
|
|
12
12
|
suggestedKey?: string;
|
|
13
13
|
key?: string;
|
|
14
|
+
bindingType?: NamespaceBindingType;
|
|
15
|
+
namespace?: string;
|
|
16
|
+
}
|
|
17
|
+
export type NamespaceBindingType = "bound-scoped" | "root-scoped" | "unknown-scoped" | "unbound";
|
|
18
|
+
export interface NamespaceBinding {
|
|
19
|
+
type: NamespaceBindingType;
|
|
20
|
+
namespace?: string;
|
|
21
|
+
dynamic?: boolean;
|
|
22
|
+
}
|
|
23
|
+
export interface ScanStats {
|
|
24
|
+
dynamicKeys: number;
|
|
25
|
+
dynamicNamespaces: number;
|
|
26
|
+
unboundTranslators: number;
|
|
27
|
+
rootScopedTranslators: number;
|
|
14
28
|
}
|
|
15
29
|
export interface RuleContext {
|
|
16
30
|
filePath: string;
|
|
17
31
|
sourceFile: import("typescript").SourceFile;
|
|
32
|
+
namespaceMap?: Record<string, NamespaceBinding>;
|
|
33
|
+
stats?: ScanStats;
|
|
18
34
|
}
|
|
19
35
|
export interface ScanOptions {
|
|
20
36
|
dir?: string;
|
|
@@ -57,4 +73,28 @@ export interface ScanResult {
|
|
|
57
73
|
};
|
|
58
74
|
};
|
|
59
75
|
}
|
|
76
|
+
/**
|
|
77
|
+
* CDN Manifest structure from Better i18n
|
|
78
|
+
*/
|
|
79
|
+
export interface CdnManifest {
|
|
80
|
+
projectSlug: string;
|
|
81
|
+
sourceLanguage: string;
|
|
82
|
+
languages: CdnLanguage[];
|
|
83
|
+
files: Record<string, {
|
|
84
|
+
url: string;
|
|
85
|
+
size: number;
|
|
86
|
+
lastModified: string;
|
|
87
|
+
}>;
|
|
88
|
+
updatedAt: string;
|
|
89
|
+
}
|
|
90
|
+
export interface CdnLanguage {
|
|
91
|
+
code: string;
|
|
92
|
+
name: string;
|
|
93
|
+
nativeName: string;
|
|
94
|
+
isSource: boolean;
|
|
95
|
+
lastUpdated: string;
|
|
96
|
+
keyCount: number;
|
|
97
|
+
flagUrl: string;
|
|
98
|
+
countryCode: string;
|
|
99
|
+
}
|
|
60
100
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/analyzer/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,WAAW,KAAK;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,UAAU,GAAG,eAAe,GAAG,gBAAgB,GAAG,iBAAiB,CAAC;IAC1E,QAAQ,EAAE,OAAO,GAAG,SAAS,GAAG,MAAM,CAAC;IACvC,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,GAAG,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/analyzer/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,WAAW,KAAK;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,UAAU,GAAG,eAAe,GAAG,gBAAgB,GAAG,iBAAiB,CAAC;IAC1E,QAAQ,EAAE,OAAO,GAAG,SAAS,GAAG,MAAM,CAAC;IACvC,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,oBAAoB,CAAC;IACnC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,MAAM,oBAAoB,GAC5B,cAAc,GACd,aAAa,GACb,gBAAgB,GAChB,SAAS,CAAC;AAEd,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,oBAAoB,CAAC;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,SAAS;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,qBAAqB,EAAE,MAAM,CAAC;CAC/B;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,OAAO,YAAY,EAAE,UAAU,CAAC;IAC5C,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;IAChD,KAAK,CAAC,EAAE,SAAS,CAAC;CACnB;AAED,MAAM,WAAW,WAAW;IAC1B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,QAAQ,GAAG,MAAM,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,EAAE,CAAC,EAAE,OAAO,CAAC;IACb,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,cAAc;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,UAAU,CAAC;CACnB;AAED,MAAM,WAAW,UAAU;IACzB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,GAAG,SAAS,GAAG,KAAK,CAAC,CAAC;IACpD,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,oBAAoB,CAAC,EAAE,MAAM,EAAE,CAAC;CACjC;AAED,MAAM,WAAW,UAAU;IACzB,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,KAAK,EAAE,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,GAAG,CAAC;IAChB,UAAU,CAAC,EAAE;QACX,SAAS,EAAE,GAAG,CAAC;QACf,UAAU,EAAE,GAAG,CAAC;QAChB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;QACtC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;QACrC,QAAQ,EAAE;YACR,KAAK,EAAE,MAAM,CAAC;YACd,MAAM,EAAE,MAAM,CAAC;SAChB,CAAC;KACH,CAAC;CACH;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,WAAW,EAAE,CAAC;IACzB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC3E,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,OAAO,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;CACrB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scan.d.ts","sourceRoot":"","sources":["../../src/commands/scan.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH,OAAO,KAAK,EAAS,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAM/D,wBAAsB,WAAW,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"scan.d.ts","sourceRoot":"","sources":["../../src/commands/scan.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH,OAAO,KAAK,EAAS,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAM/D,wBAAsB,WAAW,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAgGrE"}
|
package/dist/commands/scan.js
CHANGED
|
@@ -35,10 +35,20 @@ export async function scanCommand(options) {
|
|
|
35
35
|
// Step 3: Analyze files
|
|
36
36
|
spinner.start("Scanning for hardcoded strings...");
|
|
37
37
|
const allIssues = [];
|
|
38
|
+
const aggregatedStats = {
|
|
39
|
+
dynamicKeys: 0,
|
|
40
|
+
dynamicNamespaces: 0,
|
|
41
|
+
unboundTranslators: 0,
|
|
42
|
+
rootScopedTranslators: 0,
|
|
43
|
+
};
|
|
38
44
|
for (const file of files) {
|
|
39
45
|
try {
|
|
40
|
-
const issues = await analyzeFile(file, context?.lint);
|
|
46
|
+
const { issues, stats } = await analyzeFile(file, context?.lint);
|
|
41
47
|
allIssues.push(...issues);
|
|
48
|
+
aggregatedStats.dynamicKeys += stats.dynamicKeys || 0;
|
|
49
|
+
aggregatedStats.dynamicNamespaces += stats.dynamicNamespaces || 0;
|
|
50
|
+
aggregatedStats.unboundTranslators += stats.unboundTranslators || 0;
|
|
51
|
+
aggregatedStats.rootScopedTranslators += stats.rootScopedTranslators || 0;
|
|
42
52
|
}
|
|
43
53
|
catch (error) {
|
|
44
54
|
if (options.verbose) {
|
|
@@ -67,6 +77,13 @@ export async function scanCommand(options) {
|
|
|
67
77
|
console.log(green(bold("✓ No hardcoded strings found")));
|
|
68
78
|
}
|
|
69
79
|
console.log(dim(`Scanned ${files.length} files in ${(duration / 1000).toFixed(2)}s`));
|
|
80
|
+
if (options.verbose) {
|
|
81
|
+
console.log(dim("\n🔍 Scan Details:"));
|
|
82
|
+
console.log(` - Root-scoped translators: ${aggregatedStats.rootScopedTranslators}`);
|
|
83
|
+
console.log(` - Unbound translators: ${aggregatedStats.unboundTranslators}`);
|
|
84
|
+
console.log(` - Dynamic namespaces skipped: ${aggregatedStats.dynamicNamespaces}`);
|
|
85
|
+
console.log(` - Dynamic keys skipped: ${aggregatedStats.dynamicKeys}`);
|
|
86
|
+
}
|
|
70
87
|
}
|
|
71
88
|
// Exit with error code for CI
|
|
72
89
|
if (options.ci && allIssues.length > 0) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scan.js","sourceRoot":"","sources":["../../src/commands/scan.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,GAAG,MAAM,KAAK,CAAC;AACtB,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAC7D,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAEnD,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACjE,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAClD,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAEtD,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,OAAoB;IACpD,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAC7B,MAAM,OAAO,GAAG,GAAG,CAAC,EAAE,IAAI,EAAE,sBAAsB,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC;IAE7E,iCAAiC;IACjC,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;IAC7C,MAAM,OAAO,GAAG,MAAM,oBAAoB,CAAC,OAAO,CAAC,CAAC;IAEpD,IAAI,OAAO,EAAE,CAAC;QACZ,OAAO,CAAC,OAAO,CACb,YAAY,IAAI,CAAC,OAAO,CAAC,WAAW,GAAG,GAAG,GAAG,OAAO,CAAC,WAAW,CAAC,EAAE,CACpE,CAAC;IACJ,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,IAAI,CAAC,yCAAyC,CAAC,CAAC;IAC1D,CAAC;IAED,wBAAwB;IACxB,OAAO,CAAC,KAAK,CAAC,qBAAqB,CAAC,CAAC;IACrC,MAAM,KAAK,GAAG,MAAM,YAAY,CAAC;QAC/B,OAAO;QACP,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO;QAC/B,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO;KAChC,CAAC,CAAC;IAEH,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,OAAO,CAAC,IAAI,CAAC,6BAA6B,CAAC,CAAC;QAC5C,OAAO;IACT,CAAC;IAED,OAAO,CAAC,OAAO,CAAC,SAAS,KAAK,CAAC,MAAM,QAAQ,CAAC,CAAC;IAE/C,wBAAwB;IACxB,OAAO,CAAC,KAAK,CAAC,mCAAmC,CAAC,CAAC;IACnD,MAAM,SAAS,GAAY,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"scan.js","sourceRoot":"","sources":["../../src/commands/scan.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sync command
|
|
3
|
+
*
|
|
4
|
+
* Compares local t() calls with Better i18n cloud translations
|
|
5
|
+
*/
|
|
6
|
+
import type { ScanOptions } from "../analyzer/types.js";
|
|
7
|
+
export interface SyncOptions extends ScanOptions {
|
|
8
|
+
format: "eslint" | "json";
|
|
9
|
+
summary?: boolean;
|
|
10
|
+
}
|
|
11
|
+
export declare function syncCommand(options: SyncOptions): Promise<void>;
|
|
12
|
+
//# sourceMappingURL=sync.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../../src/commands/sync.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,OAAO,KAAK,EAAsB,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAI5E,MAAM,WAAW,WAAY,SAAQ,WAAW;IAC9C,MAAM,EAAE,QAAQ,GAAG,MAAM,CAAC;IAC1B,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,wBAAsB,WAAW,CAAC,OAAO,EAAE,WAAW,iBAuPrD"}
|