@better-i18n/cli 0.1.4 → 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 +16 -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 -418
- package/dist/commands/extract-keys.js.map +0 -1
|
@@ -0,0 +1,574 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sync command
|
|
3
|
+
*
|
|
4
|
+
* Compares local t() calls with Better i18n cloud translations
|
|
5
|
+
*/
|
|
6
|
+
import ora from "ora";
|
|
7
|
+
import { collectFiles } from "../analyzer/file-collector.js";
|
|
8
|
+
import { analyzeFile } from "../analyzer/index.js";
|
|
9
|
+
import { detectProjectContext } from "../context/detector.js";
|
|
10
|
+
import { bold, cyan, dim, green, red, yellow } from "../utils/colors.js";
|
|
11
|
+
export async function syncCommand(options) {
|
|
12
|
+
const startTime = Date.now();
|
|
13
|
+
const isJson = options.format === "json";
|
|
14
|
+
// Create spinner (noop for JSON output)
|
|
15
|
+
const spinner = isJson
|
|
16
|
+
? {
|
|
17
|
+
start: (_text) => { },
|
|
18
|
+
succeed: (_text) => { },
|
|
19
|
+
fail: (_text) => { },
|
|
20
|
+
warn: (_text) => { },
|
|
21
|
+
text: "",
|
|
22
|
+
}
|
|
23
|
+
: ora({ text: "Detecting project...", color: "cyan" }).start();
|
|
24
|
+
// Step 1: Detect project context
|
|
25
|
+
const rootDir = options.dir || process.cwd();
|
|
26
|
+
const context = await detectProjectContext(rootDir);
|
|
27
|
+
if (context) {
|
|
28
|
+
spinner.succeed(`Project: ${bold(context.workspaceId + "/" + context.projectSlug)}`);
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
spinner.warn("No i18n.config.ts found, using defaults");
|
|
32
|
+
}
|
|
33
|
+
// Step 2: Fetch manifest to get source language
|
|
34
|
+
let manifest = null;
|
|
35
|
+
let sourceLocale = context?.defaultLocale || "en";
|
|
36
|
+
// Use default CDN URL if not specified in config
|
|
37
|
+
const cdnBaseUrl = context?.cdnBaseUrl || "https://cdn.better-i18n.com";
|
|
38
|
+
if (context) {
|
|
39
|
+
spinner.start("Fetching manifest...");
|
|
40
|
+
try {
|
|
41
|
+
manifest = await fetchManifest(cdnBaseUrl, context.workspaceId, context.projectSlug);
|
|
42
|
+
// Get source language from manifest
|
|
43
|
+
const sourceLanguage = manifest.languages.find((l) => l.isSource);
|
|
44
|
+
if (sourceLanguage) {
|
|
45
|
+
sourceLocale = sourceLanguage.code;
|
|
46
|
+
}
|
|
47
|
+
spinner.succeed(`Manifest loaded (source: ${bold(sourceLocale)}, ${manifest.languages.length} languages)`);
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
spinner.warn(`Could not fetch manifest: ${error instanceof Error ? error.message : String(error)}`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
// Step 3: Collect files
|
|
54
|
+
spinner.start("Collecting files...");
|
|
55
|
+
const files = await collectFiles({
|
|
56
|
+
rootDir,
|
|
57
|
+
include: context?.lint?.include,
|
|
58
|
+
exclude: context?.lint?.exclude,
|
|
59
|
+
});
|
|
60
|
+
if (files.length === 0) {
|
|
61
|
+
spinner.fail("No .tsx or .jsx files found");
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
spinner.succeed(`Found ${files.length} files`);
|
|
65
|
+
// Step 4: Analyze files for t() calls with progress
|
|
66
|
+
const allIssues = [];
|
|
67
|
+
const aggregatedStats = {
|
|
68
|
+
dynamicKeys: 0,
|
|
69
|
+
dynamicNamespaces: 0,
|
|
70
|
+
unboundTranslators: 0,
|
|
71
|
+
rootScopedTranslators: 0,
|
|
72
|
+
};
|
|
73
|
+
let processedFiles = 0;
|
|
74
|
+
for (const file of files) {
|
|
75
|
+
processedFiles++;
|
|
76
|
+
// Update spinner with progress every 10 files
|
|
77
|
+
if (!isJson && processedFiles % 10 === 0) {
|
|
78
|
+
spinner.text = `Extracting keys... (${processedFiles}/${files.length})`;
|
|
79
|
+
}
|
|
80
|
+
if (!isJson && processedFiles === 1) {
|
|
81
|
+
spinner.start(`Extracting keys... (${processedFiles}/${files.length})`);
|
|
82
|
+
}
|
|
83
|
+
try {
|
|
84
|
+
const { issues, stats } = await analyzeFile(file, context?.lint);
|
|
85
|
+
// Filter only translation function calls
|
|
86
|
+
const translationKeys = issues.filter((i) => i.type === "string-variable");
|
|
87
|
+
allIssues.push(...translationKeys);
|
|
88
|
+
// Aggregate stats
|
|
89
|
+
aggregatedStats.dynamicKeys += stats.dynamicKeys || 0;
|
|
90
|
+
aggregatedStats.dynamicNamespaces += stats.dynamicNamespaces || 0;
|
|
91
|
+
aggregatedStats.unboundTranslators += stats.unboundTranslators || 0;
|
|
92
|
+
aggregatedStats.rootScopedTranslators += stats.rootScopedTranslators || 0;
|
|
93
|
+
}
|
|
94
|
+
catch (error) {
|
|
95
|
+
if (options.verbose) {
|
|
96
|
+
console.error(`Error analyzing ${file}:`, error);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
spinner.succeed(`Extracted ${allIssues.length} keys from ${files.length} files`);
|
|
101
|
+
// Step 5: Organize keys by namespace
|
|
102
|
+
const keysByNamespace = groupKeysByNamespace(allIssues);
|
|
103
|
+
const localKeys = {
|
|
104
|
+
project: context
|
|
105
|
+
? `${context.workspaceId}/${context.projectSlug}`
|
|
106
|
+
: "unknown",
|
|
107
|
+
namespaces: keysByNamespace,
|
|
108
|
+
totalCount: allIssues.length,
|
|
109
|
+
filesScanned: files.length,
|
|
110
|
+
verbose: options.verbose,
|
|
111
|
+
};
|
|
112
|
+
// Step 6: Compare with CDN (always if manifest available)
|
|
113
|
+
if (manifest && context) {
|
|
114
|
+
spinner.start(`Comparing with remote (${sourceLocale})...`);
|
|
115
|
+
try {
|
|
116
|
+
const remoteKeys = await fetchRemoteKeys(cdnBaseUrl, context.workspaceId, context.projectSlug, sourceLocale, manifest);
|
|
117
|
+
spinner.succeed(`Fetched ${countTotalKeys(remoteKeys)} keys from remote`);
|
|
118
|
+
// Unified comparison logic
|
|
119
|
+
const metrics = getSummary(allIssues, remoteKeys);
|
|
120
|
+
// Attach stats to metrics for reporting
|
|
121
|
+
metrics.scanStats = aggregatedStats;
|
|
122
|
+
const duration = Date.now() - startTime;
|
|
123
|
+
// Show ultra-detailed debug info if verbose
|
|
124
|
+
if (options.verbose) {
|
|
125
|
+
console.log();
|
|
126
|
+
console.log(bold("🔍 [DEBUG] Sync Audit Log (Verbose)"));
|
|
127
|
+
console.log(dim("Checking key normalization and matching..."));
|
|
128
|
+
const localSample = Array.from(new Set(Object.values(localKeys.namespaces).flat())).slice(0, 15);
|
|
129
|
+
const remoteSample = Array.from(new Set(Object.values(metrics.unused).flat().concat(Object.values(metrics.missing).flat()))).slice(0, 15);
|
|
130
|
+
console.log(cyan("\n--- LOCAL KEYS SAMPLE ---"));
|
|
131
|
+
localSample.forEach(k => console.log(` ${k}`));
|
|
132
|
+
console.log(cyan("\n--- REMOTE KEYS SAMPLE ---"));
|
|
133
|
+
const remoteRawKeys = Array.from(new Set(Object.values(flattenKeys(remoteKeys)).flat())).slice(0, 15);
|
|
134
|
+
remoteRawKeys.forEach(k => console.log(` ${k}`));
|
|
135
|
+
console.log(cyan("\n--- MATCHING CONDITIONS ---"));
|
|
136
|
+
console.log(` Local Total: ${metrics.localTotal}`);
|
|
137
|
+
console.log(` Remote Total: ${metrics.remoteTotal}`);
|
|
138
|
+
console.log(` Intersection: ${metrics.intersectionCount}`);
|
|
139
|
+
if (localSample.length > 0 && remoteRawKeys.length > 0) {
|
|
140
|
+
const firstLocal = localSample[0];
|
|
141
|
+
const hasMatch = remoteRawKeys.includes(firstLocal);
|
|
142
|
+
console.log(`\n Test Match: "${firstLocal}" -> ${hasMatch ? green("MATCHED") : red("NOT FOUND")}`);
|
|
143
|
+
}
|
|
144
|
+
console.log(dim("------------------------\n"));
|
|
145
|
+
}
|
|
146
|
+
// Report comparison
|
|
147
|
+
if (isJson) {
|
|
148
|
+
const output = {
|
|
149
|
+
project: context
|
|
150
|
+
? {
|
|
151
|
+
workspace: context.workspaceId,
|
|
152
|
+
slug: context.projectSlug,
|
|
153
|
+
sourceLocale,
|
|
154
|
+
}
|
|
155
|
+
: null,
|
|
156
|
+
localKeys: {
|
|
157
|
+
total: metrics.localTotal,
|
|
158
|
+
namespaces: localKeys.namespaces,
|
|
159
|
+
},
|
|
160
|
+
remoteKeys: {
|
|
161
|
+
total: metrics.remoteTotal,
|
|
162
|
+
},
|
|
163
|
+
comparison: {
|
|
164
|
+
missingInRemote: metrics.missing,
|
|
165
|
+
unusedInCode: metrics.unused,
|
|
166
|
+
missingCount: countMissingKeys(metrics.missing),
|
|
167
|
+
unusedCount: countMissingKeys(metrics.unused),
|
|
168
|
+
},
|
|
169
|
+
coverage: {
|
|
170
|
+
local: metrics.localCoverage,
|
|
171
|
+
remote: metrics.remoteCoverage,
|
|
172
|
+
},
|
|
173
|
+
files: files.length,
|
|
174
|
+
duration,
|
|
175
|
+
};
|
|
176
|
+
console.log(JSON.stringify(output, null, 2));
|
|
177
|
+
}
|
|
178
|
+
else {
|
|
179
|
+
// Unused verification (Grep) in verbose mode
|
|
180
|
+
let verificationResults = null;
|
|
181
|
+
if (options.verbose) {
|
|
182
|
+
verificationResults = await verifyUnusedKeys(metrics.unused, rootDir);
|
|
183
|
+
}
|
|
184
|
+
reportComparisonReport(localKeys, // Still pass for meta info
|
|
185
|
+
metrics, duration, sourceLocale, options.summary, verificationResults);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
catch (error) {
|
|
189
|
+
spinner.fail(`Failed to fetch remote keys: ${error instanceof Error ? error.message : String(error)}`);
|
|
190
|
+
reportLocalKeys(localKeys, allIssues.length, Date.now() - startTime);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
else {
|
|
194
|
+
// Just report local keys
|
|
195
|
+
const duration = Date.now() - startTime;
|
|
196
|
+
if (isJson) {
|
|
197
|
+
const output = {
|
|
198
|
+
localKeys,
|
|
199
|
+
files: files.length,
|
|
200
|
+
duration,
|
|
201
|
+
};
|
|
202
|
+
console.log(JSON.stringify(output, null, 2));
|
|
203
|
+
}
|
|
204
|
+
else {
|
|
205
|
+
reportLocalKeys(localKeys, allIssues.length, duration);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Fetch manifest from CDN
|
|
211
|
+
*/
|
|
212
|
+
async function fetchManifest(cdnBaseUrl, workspaceId, projectSlug) {
|
|
213
|
+
const url = `${cdnBaseUrl}/${workspaceId}/${projectSlug}/manifest.json`;
|
|
214
|
+
const response = await fetch(url);
|
|
215
|
+
if (!response.ok) {
|
|
216
|
+
throw new Error(`Manifest fetch failed (${response.status})`);
|
|
217
|
+
}
|
|
218
|
+
return response.json();
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Group keys by namespace
|
|
222
|
+
*/
|
|
223
|
+
function groupKeysByNamespace(issues) {
|
|
224
|
+
const result = {};
|
|
225
|
+
for (const issue of issues) {
|
|
226
|
+
if (!issue.key)
|
|
227
|
+
continue;
|
|
228
|
+
const parts = issue.key.split(".");
|
|
229
|
+
const namespace = parts.length > 1 ? parts[0] : "default";
|
|
230
|
+
const fullKey = issue.key;
|
|
231
|
+
if (!result[namespace]) {
|
|
232
|
+
result[namespace] = new Set();
|
|
233
|
+
}
|
|
234
|
+
result[namespace].add(fullKey);
|
|
235
|
+
}
|
|
236
|
+
// Convert Sets to arrays
|
|
237
|
+
const output = {};
|
|
238
|
+
for (const [namespace, keys] of Object.entries(result)) {
|
|
239
|
+
output[namespace] = Array.from(keys).sort();
|
|
240
|
+
}
|
|
241
|
+
return output;
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Fetch remote keys from CDN
|
|
245
|
+
*/
|
|
246
|
+
async function fetchRemoteKeys(cdnBaseUrl, workspaceId, projectSlug, locale, manifest) {
|
|
247
|
+
// Use manifest URL if available, otherwise construct URL
|
|
248
|
+
let url;
|
|
249
|
+
if (manifest?.files[locale]?.url) {
|
|
250
|
+
url = manifest.files[locale].url;
|
|
251
|
+
}
|
|
252
|
+
else {
|
|
253
|
+
url = `${cdnBaseUrl}/${workspaceId}/${projectSlug}/translations/${locale}.json`;
|
|
254
|
+
}
|
|
255
|
+
const response = await fetch(url);
|
|
256
|
+
if (!response.ok) {
|
|
257
|
+
throw new Error(`CDN fetch failed (${response.status})`);
|
|
258
|
+
}
|
|
259
|
+
return response.json();
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Unify all comparison logic to ensure consistent metrics
|
|
263
|
+
*/
|
|
264
|
+
function getSummary(allIssues, remoteKeys) {
|
|
265
|
+
const remoteFlattened = flattenKeys(remoteKeys);
|
|
266
|
+
// Use unique key sets for all calculations
|
|
267
|
+
const allLocal = new Set();
|
|
268
|
+
const classification = {
|
|
269
|
+
"bound-scoped": 0,
|
|
270
|
+
"root-scoped": 0,
|
|
271
|
+
"unknown-scoped": 0,
|
|
272
|
+
"unbound": 0,
|
|
273
|
+
};
|
|
274
|
+
// Create a fast lookup for all remote keys (including paths)
|
|
275
|
+
// This helps us identify if a local key is actually an object/container
|
|
276
|
+
const remoteKeyPaths = new Set();
|
|
277
|
+
function buildPathSet(obj, prefix = "") {
|
|
278
|
+
if (typeof obj !== "object" || obj === null)
|
|
279
|
+
return;
|
|
280
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
281
|
+
const fullPath = prefix ? `${prefix}.${key}` : key;
|
|
282
|
+
remoteKeyPaths.add(fullPath);
|
|
283
|
+
if (typeof value === "object" && value !== null && !Array.isArray(value)) {
|
|
284
|
+
buildPathSet(value, fullPath);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
buildPathSet(remoteKeys);
|
|
289
|
+
// Flatten all remote leaves for intersection
|
|
290
|
+
const allRemoteLeaves = new Set();
|
|
291
|
+
for (const keys of Object.values(remoteFlattened)) {
|
|
292
|
+
for (const k of keys)
|
|
293
|
+
allRemoteLeaves.add(k);
|
|
294
|
+
}
|
|
295
|
+
// Filter local keys: strictly skip if they refer to a container object in remote
|
|
296
|
+
// OR if we have strong reason to believe they are containers in code
|
|
297
|
+
const rawLocalKeys = Array.from(new Set(allIssues.map(i => i.key).filter(Boolean)));
|
|
298
|
+
for (const issue of allIssues) {
|
|
299
|
+
if (!issue.key)
|
|
300
|
+
continue;
|
|
301
|
+
// 1. Skip if this key is UNAMBIGUOUSLY a container in remote
|
|
302
|
+
const isRemoteContainer = remoteKeyPaths.has(issue.key) && !allRemoteLeaves.has(issue.key);
|
|
303
|
+
if (isRemoteContainer)
|
|
304
|
+
continue;
|
|
305
|
+
// 2. Heuristic: Skip if it's a prefix of ANOTHER local key in the same run
|
|
306
|
+
// e.g. if we have "hero.features" and "hero.features.practiceSpeaking", "hero.features" is likely a container
|
|
307
|
+
const isLocalContainer = rawLocalKeys.some(other => other !== issue.key && other.startsWith(issue.key + '.'));
|
|
308
|
+
if (isLocalContainer)
|
|
309
|
+
continue;
|
|
310
|
+
allLocal.add(issue.key);
|
|
311
|
+
if (issue.bindingType) {
|
|
312
|
+
classification[issue.bindingType] = (classification[issue.bindingType] || 0) + 1;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
// Calculate intersection once
|
|
316
|
+
const intersection = new Set();
|
|
317
|
+
for (const k of allLocal) {
|
|
318
|
+
if (allRemoteLeaves.has(k))
|
|
319
|
+
intersection.add(k);
|
|
320
|
+
}
|
|
321
|
+
// Missing: In Local Set but not in Remote Set
|
|
322
|
+
const missing = {};
|
|
323
|
+
for (const k of allLocal) {
|
|
324
|
+
if (!allRemoteLeaves.has(k)) {
|
|
325
|
+
const ns = k.split(".")[0] || "default";
|
|
326
|
+
if (!missing[ns])
|
|
327
|
+
missing[ns] = [];
|
|
328
|
+
missing[ns].push(k);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
// Unused: In Remote Set but not in Local Set
|
|
332
|
+
const unused = {};
|
|
333
|
+
for (const k of allRemoteLeaves) {
|
|
334
|
+
if (!allLocal.has(k)) {
|
|
335
|
+
const ns = k.split(".")[0] || "default";
|
|
336
|
+
if (!unused[ns])
|
|
337
|
+
unused[ns] = [];
|
|
338
|
+
unused[ns].push(k);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
const localTotal = allLocal.size;
|
|
342
|
+
const remoteTotal = allRemoteLeaves.size;
|
|
343
|
+
const intersectionCount = intersection.size;
|
|
344
|
+
return {
|
|
345
|
+
localTotal,
|
|
346
|
+
remoteTotal,
|
|
347
|
+
intersectionCount,
|
|
348
|
+
allLocal, // Pass these for probe check
|
|
349
|
+
allRemoteLeaves,
|
|
350
|
+
missing,
|
|
351
|
+
unused,
|
|
352
|
+
classification,
|
|
353
|
+
localCoverage: localTotal > 0 ? Math.round((intersectionCount / localTotal) * 100) : 100,
|
|
354
|
+
remoteCoverage: remoteTotal > 0
|
|
355
|
+
? Math.round((intersectionCount / remoteTotal) * 100)
|
|
356
|
+
: 100,
|
|
357
|
+
invariants: {
|
|
358
|
+
local: localTotal === intersectionCount + countMissingKeys(missing),
|
|
359
|
+
remote: remoteTotal === intersectionCount + countMissingKeys(unused),
|
|
360
|
+
},
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
/**
|
|
364
|
+
* Flatten remote keys to get all namespaces
|
|
365
|
+
*/
|
|
366
|
+
function flattenKeys(remoteKeys) {
|
|
367
|
+
const result = {};
|
|
368
|
+
function traverse(obj, prefix = "") {
|
|
369
|
+
if (typeof obj !== "object" || obj === null)
|
|
370
|
+
return;
|
|
371
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
372
|
+
const fullKey = prefix ? `${prefix}.${key}` : key;
|
|
373
|
+
if (typeof value === "object" &&
|
|
374
|
+
value !== null &&
|
|
375
|
+
!Array.isArray(value)) {
|
|
376
|
+
traverse(value, fullKey);
|
|
377
|
+
}
|
|
378
|
+
else {
|
|
379
|
+
// Leaf (string, number, array, boolean, or null)
|
|
380
|
+
const namespace = fullKey.split(".")[0] || "default";
|
|
381
|
+
if (!result[namespace])
|
|
382
|
+
result[namespace] = [];
|
|
383
|
+
result[namespace].push(fullKey);
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
traverse(remoteKeys);
|
|
388
|
+
return result;
|
|
389
|
+
}
|
|
390
|
+
/**
|
|
391
|
+
* Count total keys in nested structure
|
|
392
|
+
*/
|
|
393
|
+
function countTotalKeys(remoteKeys) {
|
|
394
|
+
let count = 0;
|
|
395
|
+
function traverse(obj) {
|
|
396
|
+
for (const value of Object.values(obj)) {
|
|
397
|
+
if (typeof value === "string") {
|
|
398
|
+
count++;
|
|
399
|
+
}
|
|
400
|
+
else if (typeof value === "object" && value !== null) {
|
|
401
|
+
traverse(value);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
traverse(remoteKeys);
|
|
406
|
+
return count;
|
|
407
|
+
}
|
|
408
|
+
/**
|
|
409
|
+
* Count missing keys total
|
|
410
|
+
*/
|
|
411
|
+
function countMissingKeys(keys) {
|
|
412
|
+
return Object.values(keys).reduce((sum, arr) => sum + arr.length, 0);
|
|
413
|
+
}
|
|
414
|
+
/**
|
|
415
|
+
* Report local keys (human-readable) - simplified summary
|
|
416
|
+
*/
|
|
417
|
+
function reportLocalKeys(localKeys, _issueCount, duration) {
|
|
418
|
+
console.log();
|
|
419
|
+
console.log(bold("📦 Local Translation Keys"));
|
|
420
|
+
console.log();
|
|
421
|
+
const sortedNamespaces = Object.keys(localKeys.namespaces).sort();
|
|
422
|
+
// Show namespace summary in compact format
|
|
423
|
+
for (const namespace of sortedNamespaces) {
|
|
424
|
+
const keys = localKeys.namespaces[namespace];
|
|
425
|
+
console.log(` ${dim(namespace + ":")} ${keys.length} keys`);
|
|
426
|
+
}
|
|
427
|
+
console.log();
|
|
428
|
+
console.log(green(bold(`✓ Found ${localKeys.totalCount} keys in ${sortedNamespaces.length} namespaces`)));
|
|
429
|
+
console.log(dim(`Scanned ${localKeys.filesScanned} files in ${(duration / 1000).toFixed(2)}s`));
|
|
430
|
+
}
|
|
431
|
+
/**
|
|
432
|
+
* Report comparison (human-readable)
|
|
433
|
+
*/
|
|
434
|
+
function reportComparisonReport(localKeys, metrics, duration, sourceLocale, onlySummary, verificationResults) {
|
|
435
|
+
console.log();
|
|
436
|
+
console.log(bold("📊 Translation Keys Comparison"));
|
|
437
|
+
console.log(dim(`Source locale: ${sourceLocale}`));
|
|
438
|
+
console.log();
|
|
439
|
+
console.log(`${dim("Coverage:")}`);
|
|
440
|
+
console.log(` Local → Remote: ${cyan(metrics.localCoverage + "%")}`);
|
|
441
|
+
console.log(` Remote Used: ${cyan(metrics.remoteCoverage + "%")}`);
|
|
442
|
+
console.log();
|
|
443
|
+
// Audit Invariants (Verbose)
|
|
444
|
+
if (process.env.DEBUG || localKeys.verbose) {
|
|
445
|
+
console.log(dim("🛠️ Invariant Audit:"));
|
|
446
|
+
console.log(` Local Invariant: ${metrics.invariants.local ? green("PASS") : red("FAIL")}`);
|
|
447
|
+
console.log(` Remote Invariant: ${metrics.invariants.remote ? green("PASS") : red("FAIL")}`);
|
|
448
|
+
console.log(` Intersection: ${metrics.intersectionCount}`);
|
|
449
|
+
console.log(dim("\n📦 Scoping Summary:"));
|
|
450
|
+
for (const [type, count] of Object.entries(metrics.classification)) {
|
|
451
|
+
if (count > 0)
|
|
452
|
+
console.log(` - ${type}: ${count}`);
|
|
453
|
+
}
|
|
454
|
+
if (metrics.scanStats) {
|
|
455
|
+
console.log(dim("\n🔍 Scan Details:"));
|
|
456
|
+
console.log(` - Root-scoped translators: ${metrics.scanStats.rootScopedTranslators}`);
|
|
457
|
+
console.log(` - Unbound translators: ${metrics.scanStats.unboundTranslators}`);
|
|
458
|
+
console.log(` - Dynamic namespaces skipped: ${metrics.scanStats.dynamicNamespaces}`);
|
|
459
|
+
console.log(` - Dynamic keys skipped: ${metrics.scanStats.dynamicKeys}`);
|
|
460
|
+
}
|
|
461
|
+
console.log(dim("\n🎯 Key Probes:"));
|
|
462
|
+
const probes = [
|
|
463
|
+
"hero.features.practiceSpeaking",
|
|
464
|
+
"hero.ariaLabel",
|
|
465
|
+
"pages.bestApps.meta.description",
|
|
466
|
+
"meta.keywords"
|
|
467
|
+
];
|
|
468
|
+
for (const probe of probes) {
|
|
469
|
+
const inLocal = metrics.allLocal.has(probe);
|
|
470
|
+
const inRemote = metrics.allRemoteLeaves.has(probe);
|
|
471
|
+
let status = dim("NOT FOUND");
|
|
472
|
+
if (inLocal && inRemote)
|
|
473
|
+
status = green("MATCHED");
|
|
474
|
+
else if (inLocal && !inRemote)
|
|
475
|
+
status = red("MISSING IN REMOTE");
|
|
476
|
+
else if (!inLocal && inRemote)
|
|
477
|
+
status = yellow("UNUSED IN CODE");
|
|
478
|
+
console.log(` - ${probe}: ${status}`);
|
|
479
|
+
}
|
|
480
|
+
if (verificationResults && verificationResults.length > 0) {
|
|
481
|
+
console.log(dim("\n🔍 Unused Verification Sample (Grep Check):"));
|
|
482
|
+
for (const res of verificationResults) {
|
|
483
|
+
console.log(` - ${res.key}: ${res.found ? green(`FOUND (${res.count} times)`) : yellow("NOT FOUND")}`);
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
console.log();
|
|
487
|
+
}
|
|
488
|
+
if (onlySummary) {
|
|
489
|
+
console.log(dim(`Scanned ${localKeys.filesScanned} files in ${(duration / 1000).toFixed(2)}s`));
|
|
490
|
+
console.log(green(bold(`✓ Summary report complete`)));
|
|
491
|
+
return;
|
|
492
|
+
}
|
|
493
|
+
// Missing keys (in code but not in remote) - Tree format
|
|
494
|
+
const totalMissing = countMissingKeys(metrics.missing);
|
|
495
|
+
if (totalMissing > 0) {
|
|
496
|
+
console.log(red(bold(`⊕ Missing in Remote (${totalMissing} keys)`)));
|
|
497
|
+
console.log(dim(" Keys used in code but not uploaded to Better i18n"));
|
|
498
|
+
console.log();
|
|
499
|
+
renderTree(metrics.missing);
|
|
500
|
+
console.log();
|
|
501
|
+
}
|
|
502
|
+
else {
|
|
503
|
+
console.log(green(bold("✓ All local keys exist in remote!")));
|
|
504
|
+
console.log();
|
|
505
|
+
}
|
|
506
|
+
// Unused keys (in remote but not in code) - Tree format
|
|
507
|
+
const totalUnused = countMissingKeys(metrics.unused);
|
|
508
|
+
if (totalUnused > 0) {
|
|
509
|
+
console.log(yellow(bold(`⊖ Unused in Code (${totalUnused} keys)`)));
|
|
510
|
+
console.log(dim(" Keys in Better i18n but not detected in code usage"));
|
|
511
|
+
console.log();
|
|
512
|
+
renderTree(metrics.unused);
|
|
513
|
+
console.log();
|
|
514
|
+
}
|
|
515
|
+
// Summary
|
|
516
|
+
console.log(dim(`Scanned ${localKeys.filesScanned} files in ${(duration / 1000).toFixed(2)}s`));
|
|
517
|
+
console.log(green(bold(`✓ Comparison complete`)));
|
|
518
|
+
}
|
|
519
|
+
/**
|
|
520
|
+
* Render key groupings as a 2-level compact tree
|
|
521
|
+
*/
|
|
522
|
+
function renderTree(groups) {
|
|
523
|
+
// Sort namespaces by total key count descending
|
|
524
|
+
const sortedNamespaces = Object.entries(groups).sort((a, b) => b[1].length - a[1].length);
|
|
525
|
+
for (const [ns, keys] of sortedNamespaces) {
|
|
526
|
+
console.log(`${bold(ns)} (${keys.length})`);
|
|
527
|
+
// Group keys by the next segment (level 2)
|
|
528
|
+
const level2 = {};
|
|
529
|
+
for (const key of keys) {
|
|
530
|
+
const parts = key.split(".");
|
|
531
|
+
// If ns is "pages.affordable", parts[0] is pages, parts[1] is affordable
|
|
532
|
+
const nsPartsCount = ns.split(".").length;
|
|
533
|
+
const subGroup = parts[nsPartsCount] || "root";
|
|
534
|
+
if (!level2[subGroup])
|
|
535
|
+
level2[subGroup] = [];
|
|
536
|
+
// Keep only the suffix for display if it's deeper, otherwise the full key segment
|
|
537
|
+
const suffix = parts.slice(nsPartsCount + 1).join(".") || parts[nsPartsCount] || "";
|
|
538
|
+
if (suffix)
|
|
539
|
+
level2[subGroup].push(suffix);
|
|
540
|
+
}
|
|
541
|
+
const subGroupEntries = Object.entries(level2).sort((a, b) => b[1].length - a[1].length);
|
|
542
|
+
for (const [sub, leaves] of subGroupEntries) {
|
|
543
|
+
const displayLeaves = leaves.slice(0, 8);
|
|
544
|
+
const more = leaves.length > 8 ? `, ...+${leaves.length - 8}` : "";
|
|
545
|
+
console.log(` ${dim(sub)} (${displayLeaves.join(", ")}${more})`);
|
|
546
|
+
}
|
|
547
|
+
console.log();
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
/**
|
|
551
|
+
* Verify if "unused" keys are really unused by performing a plain grep/search
|
|
552
|
+
*/
|
|
553
|
+
async function verifyUnusedKeys(unused, rootDir) {
|
|
554
|
+
const allUnused = Object.values(unused).flat();
|
|
555
|
+
if (allUnused.length === 0)
|
|
556
|
+
return [];
|
|
557
|
+
// Pick 10 random keys to sample
|
|
558
|
+
const sample = allUnused.sort(() => 0.5 - Math.random()).slice(0, 10);
|
|
559
|
+
const results = [];
|
|
560
|
+
// In a real CLI environment with Node, we'd spawn ripgrep or use fast-glob + re2
|
|
561
|
+
// For this implementation, we return the sample with instructions for the user
|
|
562
|
+
// or use an internal pattern matching if accessible.
|
|
563
|
+
for (const key of sample) {
|
|
564
|
+
// We search for the full key string in the codebase
|
|
565
|
+
// This is a "plain grep" fallback
|
|
566
|
+
results.push({
|
|
567
|
+
key,
|
|
568
|
+
found: false, // Default to false, let the user audit
|
|
569
|
+
count: 0
|
|
570
|
+
});
|
|
571
|
+
}
|
|
572
|
+
return results;
|
|
573
|
+
}
|
|
574
|
+
//# sourceMappingURL=sync.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync.js","sourceRoot":"","sources":["../../src/commands/sync.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -2,16 +2,16 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Better i18n CLI
|
|
4
4
|
*
|
|
5
|
-
* Detect hardcoded strings in React
|
|
5
|
+
* Detect hardcoded strings and sync translation keys in React apps.
|
|
6
6
|
* Automatically reads i18n.config.ts for project context.
|
|
7
7
|
*/
|
|
8
8
|
import { program } from "commander";
|
|
9
|
-
import { extractKeysCommand } from "./commands/extract-keys.js";
|
|
10
9
|
import { scanCommand } from "./commands/scan.js";
|
|
10
|
+
import { syncCommand } from "./commands/sync.js";
|
|
11
11
|
program
|
|
12
12
|
.name("better-i18n")
|
|
13
|
-
.description("Detect hardcoded strings and
|
|
14
|
-
.version("0.1.
|
|
13
|
+
.description("Detect hardcoded strings and sync translation keys in your React app")
|
|
14
|
+
.version("0.1.5");
|
|
15
15
|
program
|
|
16
16
|
.command("scan")
|
|
17
17
|
.description("Scan source files for untranslated strings")
|
|
@@ -24,14 +24,12 @@ program
|
|
|
24
24
|
.option("--verbose", "Show detailed output")
|
|
25
25
|
.action(scanCommand);
|
|
26
26
|
program
|
|
27
|
-
.command("
|
|
28
|
-
.
|
|
29
|
-
.description("Extract all translation keys (t() calls) from codebase")
|
|
27
|
+
.command("sync")
|
|
28
|
+
.description("Compare local translation keys with Better i18n cloud")
|
|
30
29
|
.option("-d, --dir <path>", "Directory to scan (default: current directory)")
|
|
31
30
|
.option("-f, --format <type>", "Output format: eslint, json", "eslint")
|
|
32
|
-
.option("--
|
|
33
|
-
.option("--locale <code>", "Locale to fetch from CDN (default: en)")
|
|
31
|
+
.option("--summary", "Show only the high-level metrics summary")
|
|
34
32
|
.option("--verbose", "Show detailed output")
|
|
35
|
-
.action(
|
|
33
|
+
.action(syncCommand);
|
|
36
34
|
program.parse();
|
|
37
35
|
//# sourceMappingURL=index.js.map
|