ai-localize-reporting 2.0.3 → 2.0.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/CHANGELOG.md +23 -0
- package/dist/index.js +11 -7
- package/dist/index.mjs +11 -7
- package/package.json +23 -3
- package/preview/fix-exports.js +0 -147
- package/preview/fix-preview-exports.js +0 -166
- package/preview/generate-preview.ts +0 -105
- package/preview/make-preview.cjs +0 -564
- package/preview/make-preview.mjs +0 -398
- package/preview/report-preview.html +0 -831
- package/src/cli-reporter.ts +0 -601
- package/src/html-reporter.ts +0 -1690
- package/src/index.ts +0 -3
- package/src/report-builder.ts +0 -35
- package/tsconfig.json +0 -11
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,28 @@
|
|
|
1
1
|
# ai-localize-reporting
|
|
2
2
|
|
|
3
|
+
## 2.0.5
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- **Bug fix — `report` crashed with `RangeError: Invalid count value`**: The CLI reporter
|
|
8
|
+
computed translation coverage as `(totalUniqueKeys - missingKeys.length) / totalUniqueKeys * 100`.
|
|
9
|
+
Because `missingKeys` are recorded per-language (e.g. 5 unique keys × 2 target languages = 10
|
|
10
|
+
entries), coverage went negative when all keys were untranslated, causing `String.prototype.repeat()`
|
|
11
|
+
to throw. Three fixes applied to `printCliSummary()`:
|
|
12
|
+
- Coverage now counts **distinct** missing key names (`new Set(missingKeys.map(mk => mk.key)).size`)
|
|
13
|
+
instead of raw array length, giving the correct per-key missing count.
|
|
14
|
+
- `coveragePct` is clamped to `[0, 100]` with `Math.max(0, Math.min(100, …))`.
|
|
15
|
+
- `progressBar()`, `miniBar()`, and the `renderTable()` center-align branch all guard
|
|
16
|
+
`filled`, `empty`, and padding values with `Math.max(0, …)` to prevent negative repeats.
|
|
17
|
+
(`packages/reporting/src/cli-reporter.ts`)
|
|
18
|
+
|
|
19
|
+
## 2.0.4
|
|
20
|
+
|
|
21
|
+
### Patch Changes
|
|
22
|
+
|
|
23
|
+
- Updated dependencies
|
|
24
|
+
- ai-localize-shared@2.0.4
|
|
25
|
+
|
|
3
26
|
## 2.0.3
|
|
4
27
|
|
|
5
28
|
### Patch Changes
|
package/dist/index.js
CHANGED
|
@@ -1697,17 +1697,18 @@ function badge2(text, ok) {
|
|
|
1697
1697
|
return ok ? c("bgGreen", c("black", ` ${text} `)) : c("bgRed", c("white", ` ${text} `));
|
|
1698
1698
|
}
|
|
1699
1699
|
function miniBar(value, max, width = 20, color = "cyan") {
|
|
1700
|
-
const filled = max > 0 ? Math.round(value / max * width) : 0;
|
|
1700
|
+
const filled = max > 0 ? Math.max(0, Math.min(width, Math.round(value / max * width))) : 0;
|
|
1701
1701
|
const empty = width - filled;
|
|
1702
1702
|
const bar = c(color, "\u2588".repeat(filled)) + dim("\u2591".repeat(empty));
|
|
1703
1703
|
return "[" + bar + "]";
|
|
1704
1704
|
}
|
|
1705
1705
|
function progressBar(pct, width = 24) {
|
|
1706
|
-
const
|
|
1706
|
+
const safePct = Math.max(0, Math.min(100, pct));
|
|
1707
|
+
const filled = Math.max(0, Math.min(width, Math.round(safePct / 100 * width)));
|
|
1707
1708
|
const empty = width - filled;
|
|
1708
1709
|
let color = "brightGreen";
|
|
1709
|
-
if (
|
|
1710
|
-
else if (
|
|
1710
|
+
if (safePct < 50) color = "brightRed";
|
|
1711
|
+
else if (safePct < 80) color = "brightYellow";
|
|
1711
1712
|
const bar = c(color, "\u2588".repeat(filled)) + dim("\u2591".repeat(empty));
|
|
1712
1713
|
return "[" + bar + "]";
|
|
1713
1714
|
}
|
|
@@ -1738,8 +1739,10 @@ function renderTable(columns, rows) {
|
|
|
1738
1739
|
if (isHeader) s = bold(strip(s));
|
|
1739
1740
|
if (col?.align === "right") s = padStart(s, w);
|
|
1740
1741
|
else if (col?.align === "center") {
|
|
1741
|
-
const
|
|
1742
|
-
|
|
1742
|
+
const visibleLen = visLen(s);
|
|
1743
|
+
const p = Math.max(0, Math.floor((w - visibleLen) / 2));
|
|
1744
|
+
const q = Math.max(0, w - visibleLen - p);
|
|
1745
|
+
s = " ".repeat(p) + s + " ".repeat(q);
|
|
1743
1746
|
} else {
|
|
1744
1747
|
s = padEnd(s, w);
|
|
1745
1748
|
}
|
|
@@ -1760,7 +1763,8 @@ function renderTable(columns, rows) {
|
|
|
1760
1763
|
function computeCliInsights(report) {
|
|
1761
1764
|
const { details } = report;
|
|
1762
1765
|
const totalUniqueKeys = new Set(details.detectedTexts.map((d) => d.suggestedKey)).size;
|
|
1763
|
-
const
|
|
1766
|
+
const missingUniqueKeys = new Set(details.missingKeys.map((mk) => mk.key)).size;
|
|
1767
|
+
const coveragePct = totalUniqueKeys > 0 ? Math.max(0, Math.min(100, Math.round((totalUniqueKeys - missingUniqueKeys) / totalUniqueKeys * 100))) : 100;
|
|
1764
1768
|
const textMap = /* @__PURE__ */ new Map();
|
|
1765
1769
|
for (const dt of details.detectedTexts) {
|
|
1766
1770
|
const t = dt.text.trim();
|
package/dist/index.mjs
CHANGED
|
@@ -1659,17 +1659,18 @@ function badge2(text, ok) {
|
|
|
1659
1659
|
return ok ? c("bgGreen", c("black", ` ${text} `)) : c("bgRed", c("white", ` ${text} `));
|
|
1660
1660
|
}
|
|
1661
1661
|
function miniBar(value, max, width = 20, color = "cyan") {
|
|
1662
|
-
const filled = max > 0 ? Math.round(value / max * width) : 0;
|
|
1662
|
+
const filled = max > 0 ? Math.max(0, Math.min(width, Math.round(value / max * width))) : 0;
|
|
1663
1663
|
const empty = width - filled;
|
|
1664
1664
|
const bar = c(color, "\u2588".repeat(filled)) + dim("\u2591".repeat(empty));
|
|
1665
1665
|
return "[" + bar + "]";
|
|
1666
1666
|
}
|
|
1667
1667
|
function progressBar(pct, width = 24) {
|
|
1668
|
-
const
|
|
1668
|
+
const safePct = Math.max(0, Math.min(100, pct));
|
|
1669
|
+
const filled = Math.max(0, Math.min(width, Math.round(safePct / 100 * width)));
|
|
1669
1670
|
const empty = width - filled;
|
|
1670
1671
|
let color = "brightGreen";
|
|
1671
|
-
if (
|
|
1672
|
-
else if (
|
|
1672
|
+
if (safePct < 50) color = "brightRed";
|
|
1673
|
+
else if (safePct < 80) color = "brightYellow";
|
|
1673
1674
|
const bar = c(color, "\u2588".repeat(filled)) + dim("\u2591".repeat(empty));
|
|
1674
1675
|
return "[" + bar + "]";
|
|
1675
1676
|
}
|
|
@@ -1700,8 +1701,10 @@ function renderTable(columns, rows) {
|
|
|
1700
1701
|
if (isHeader) s = bold(strip(s));
|
|
1701
1702
|
if (col?.align === "right") s = padStart(s, w);
|
|
1702
1703
|
else if (col?.align === "center") {
|
|
1703
|
-
const
|
|
1704
|
-
|
|
1704
|
+
const visibleLen = visLen(s);
|
|
1705
|
+
const p = Math.max(0, Math.floor((w - visibleLen) / 2));
|
|
1706
|
+
const q = Math.max(0, w - visibleLen - p);
|
|
1707
|
+
s = " ".repeat(p) + s + " ".repeat(q);
|
|
1705
1708
|
} else {
|
|
1706
1709
|
s = padEnd(s, w);
|
|
1707
1710
|
}
|
|
@@ -1722,7 +1725,8 @@ function renderTable(columns, rows) {
|
|
|
1722
1725
|
function computeCliInsights(report) {
|
|
1723
1726
|
const { details } = report;
|
|
1724
1727
|
const totalUniqueKeys = new Set(details.detectedTexts.map((d) => d.suggestedKey)).size;
|
|
1725
|
-
const
|
|
1728
|
+
const missingUniqueKeys = new Set(details.missingKeys.map((mk) => mk.key)).size;
|
|
1729
|
+
const coveragePct = totalUniqueKeys > 0 ? Math.max(0, Math.min(100, Math.round((totalUniqueKeys - missingUniqueKeys) / totalUniqueKeys * 100))) : 100;
|
|
1726
1730
|
const textMap = /* @__PURE__ */ new Map();
|
|
1727
1731
|
for (const dt of details.detectedTexts) {
|
|
1728
1732
|
const t = dt.text.trim();
|
package/package.json
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ai-localize-reporting",
|
|
3
|
-
"version": "2.0.
|
|
4
|
-
"description": "Localization scan reporting: JSON, HTML
|
|
3
|
+
"version": "2.0.5",
|
|
4
|
+
"description": "Localization scan reporting: JSON, HTML analytics dashboard and CLI summary",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
7
7
|
"types": "./dist/index.d.ts",
|
|
8
|
+
"files": [
|
|
9
|
+
"dist",
|
|
10
|
+
"README.md",
|
|
11
|
+
"CHANGELOG.md"
|
|
12
|
+
],
|
|
8
13
|
"exports": {
|
|
9
14
|
".": {
|
|
10
15
|
"types": "./dist/index.d.ts",
|
|
@@ -12,8 +17,23 @@
|
|
|
12
17
|
"require": "./dist/index.js"
|
|
13
18
|
}
|
|
14
19
|
},
|
|
20
|
+
"keywords": [
|
|
21
|
+
"i18n",
|
|
22
|
+
"localization",
|
|
23
|
+
"l10n",
|
|
24
|
+
"internationalization",
|
|
25
|
+
"ai-localize",
|
|
26
|
+
"reporting",
|
|
27
|
+
"html-report",
|
|
28
|
+
"cli",
|
|
29
|
+
"dashboard",
|
|
30
|
+
"translation-coverage"
|
|
31
|
+
],
|
|
32
|
+
"engines": {
|
|
33
|
+
"node": ">=18.0.0"
|
|
34
|
+
},
|
|
15
35
|
"dependencies": {
|
|
16
|
-
"ai-localize-shared": "2.0.
|
|
36
|
+
"ai-localize-shared": "2.0.5"
|
|
17
37
|
},
|
|
18
38
|
"devDependencies": {
|
|
19
39
|
"tsup": "^8.0.1",
|
package/preview/fix-exports.js
DELETED
|
@@ -1,147 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
const fs = require('fs');
|
|
3
|
-
const file = 'packages/reporting/src/html-reporter.ts';
|
|
4
|
-
let src = fs.readFileSync(file, 'utf8');
|
|
5
|
-
|
|
6
|
-
// ── 1. Expand reportJson to include full details ───────────────────────────
|
|
7
|
-
const rjStart = src.indexOf('const reportJson = JSON.stringify({');
|
|
8
|
-
// The end marker after the closing })
|
|
9
|
-
const endMarker = '}).replace(/<\\/script>/g, "<\\\\/script>");';
|
|
10
|
-
const rjEnd = src.indexOf(endMarker, rjStart);
|
|
11
|
-
|
|
12
|
-
if (rjStart !== -1 && rjEnd !== -1) {
|
|
13
|
-
const newBlock = `const reportJson = JSON.stringify({
|
|
14
|
-
timestamp: report.timestamp,
|
|
15
|
-
framework: report.framework,
|
|
16
|
-
filesScanned: report.filesScanned,
|
|
17
|
-
hardcodedTexts: report.hardcodedTexts,
|
|
18
|
-
localeKeysGenerated: report.localeKeysGenerated,
|
|
19
|
-
missingTranslations: report.missingTranslations,
|
|
20
|
-
unusedKeys: report.unusedKeys,
|
|
21
|
-
coveragePct: insights.coveragePct,
|
|
22
|
-
assets: report.assets,
|
|
23
|
-
details: {
|
|
24
|
-
detectedTexts: report.details.detectedTexts.map((t) => ({
|
|
25
|
-
filePath: t.filePath,
|
|
26
|
-
line: t.line,
|
|
27
|
-
column: t.column,
|
|
28
|
-
text: t.text,
|
|
29
|
-
suggestedKey: t.suggestedKey,
|
|
30
|
-
context: t.context,
|
|
31
|
-
nodeType: t.nodeType,
|
|
32
|
-
alreadyTranslated: t.alreadyTranslated,
|
|
33
|
-
})),
|
|
34
|
-
missingKeys: report.details.missingKeys.map((m) => ({
|
|
35
|
-
key: m.key,
|
|
36
|
-
language: m.language || '',
|
|
37
|
-
message: m.message,
|
|
38
|
-
filePath: m.filePath || '',
|
|
39
|
-
})),
|
|
40
|
-
unusedKeys: report.details.unusedKeysList,
|
|
41
|
-
assets: report.details.assets.map((a) => ({
|
|
42
|
-
localPath: a.localPath,
|
|
43
|
-
s3Key: a.s3Key,
|
|
44
|
-
cloudfrontUrl: a.cloudfrontUrl,
|
|
45
|
-
contentType: a.contentType,
|
|
46
|
-
sizeKb: (a.size / 1024).toFixed(1),
|
|
47
|
-
})),
|
|
48
|
-
},
|
|
49
|
-
}).replace(/<\\/script>/g, "<\\\\/script>");`;
|
|
50
|
-
src = src.slice(0, rjStart) + newBlock + src.slice(rjEnd + endMarker.length);
|
|
51
|
-
console.log('reportJson expanded OK');
|
|
52
|
-
} else {
|
|
53
|
-
console.log('reportJson block not found — rjStart=' + rjStart + ' rjEnd=' + rjEnd);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// ── 2. Replace exportFullJson ──────────────────────────────────────────────
|
|
57
|
-
const oldFullJson = " window.exportFullJson = function() {\n downloadFile('ai-localize-report.json', ";
|
|
58
|
-
const fjStart = src.indexOf(oldFullJson);
|
|
59
|
-
if (fjStart !== -1) {
|
|
60
|
-
const fjEnd = src.indexOf('\n };', fjStart) + 5;
|
|
61
|
-
const newFullJson = ` window.exportFullJson = function() {
|
|
62
|
-
var fullReport = \${reportJson};
|
|
63
|
-
downloadFile('ai-localize-report.json', JSON.stringify(fullReport, null, 2), 'application/json');
|
|
64
|
-
};`;
|
|
65
|
-
src = src.slice(0, fjStart) + newFullJson + src.slice(fjEnd);
|
|
66
|
-
console.log('exportFullJson replaced OK');
|
|
67
|
-
} else {
|
|
68
|
-
console.log('exportFullJson not found (may already be patched)');
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// ── 3. Replace exportFullCsv ───────────────────────────────────────────────
|
|
72
|
-
const csvMarker = ' window.exportFullCsv = function() {';
|
|
73
|
-
const csvStart = src.indexOf(csvMarker);
|
|
74
|
-
if (csvStart !== -1) {
|
|
75
|
-
// Find matching closing }; by brace counting
|
|
76
|
-
let depth = 0, i = csvStart, started = false;
|
|
77
|
-
while (i < src.length) {
|
|
78
|
-
if (src[i] === '{') { depth++; started = true; }
|
|
79
|
-
else if (src[i] === '}') { depth--; }
|
|
80
|
-
if (started && depth === 0) { i++; break; }
|
|
81
|
-
i++;
|
|
82
|
-
}
|
|
83
|
-
if (src[i] === ';') i++;
|
|
84
|
-
|
|
85
|
-
const newFullCsv = ` // ── Export: full report CSV (all sections) ────────────────────────────────
|
|
86
|
-
window.exportFullCsv = function() {
|
|
87
|
-
var r = \${reportJson};
|
|
88
|
-
var q = function(s) { return '"' + String(s == null ? '' : s).replace(/"/g, '""') + '"'; };
|
|
89
|
-
var rows = [];
|
|
90
|
-
|
|
91
|
-
// Summary
|
|
92
|
-
rows.push('=== SUMMARY ===');
|
|
93
|
-
rows.push('"Metric","Value"');
|
|
94
|
-
rows.push(q('Framework') + ',' + q(r.framework));
|
|
95
|
-
rows.push(q('Generated') + ',' + q(r.timestamp));
|
|
96
|
-
rows.push(q('Files Scanned') + ',' + r.filesScanned);
|
|
97
|
-
rows.push(q('Hardcoded Texts') + ',' + r.hardcodedTexts);
|
|
98
|
-
rows.push(q('Keys Generated') + ',' + r.localeKeysGenerated);
|
|
99
|
-
rows.push(q('Missing Translations') + ',' + r.missingTranslations);
|
|
100
|
-
rows.push(q('Unused Keys') + ',' + r.unusedKeys);
|
|
101
|
-
rows.push(q('Coverage %') + ',' + r.coveragePct);
|
|
102
|
-
rows.push(q('Total Assets') + ',' + r.assets.totalAssets);
|
|
103
|
-
rows.push(q('Uploaded Assets') + ',' + r.assets.uploadedAssets);
|
|
104
|
-
rows.push(q('Replaced URLs') + ',' + r.assets.replacedUrls);
|
|
105
|
-
rows.push(q('Legacy CDN URLs') + ',' + r.assets.legacyCdnUrls);
|
|
106
|
-
rows.push('');
|
|
107
|
-
|
|
108
|
-
// Hardcoded Texts
|
|
109
|
-
rows.push('=== HARDCODED TEXTS ===');
|
|
110
|
-
rows.push('"File","Line","Column","Text","Suggested Key","Context","Node Type","Already Translated"');
|
|
111
|
-
(r.details.detectedTexts || []).forEach(function(t) {
|
|
112
|
-
rows.push([q(t.filePath), t.line, t.column, q(t.text), q(t.suggestedKey), q(t.context), q(t.nodeType), t.alreadyTranslated ? 'true' : 'false'].join(','));
|
|
113
|
-
});
|
|
114
|
-
rows.push('');
|
|
115
|
-
|
|
116
|
-
// Missing Translations
|
|
117
|
-
rows.push('=== MISSING TRANSLATIONS ===');
|
|
118
|
-
rows.push('"Key","Language","Locale File","Message"');
|
|
119
|
-
(r.details.missingKeys || []).forEach(function(m) {
|
|
120
|
-
rows.push([q(m.key), q(m.language), q(m.filePath), q(m.message)].join(','));
|
|
121
|
-
});
|
|
122
|
-
rows.push('');
|
|
123
|
-
|
|
124
|
-
// Unused Keys
|
|
125
|
-
rows.push('=== UNUSED KEYS ===');
|
|
126
|
-
rows.push('"Key"');
|
|
127
|
-
(r.details.unusedKeys || []).forEach(function(k) { rows.push(q(k)); });
|
|
128
|
-
rows.push('');
|
|
129
|
-
|
|
130
|
-
// CDN Assets
|
|
131
|
-
rows.push('=== CDN ASSETS ===');
|
|
132
|
-
rows.push('"Local Path","S3 Key","CloudFront URL","Content Type","Size (KB)"');
|
|
133
|
-
(r.details.assets || []).forEach(function(a) {
|
|
134
|
-
rows.push([q(a.localPath), q(a.s3Key), q(a.cloudfrontUrl), q(a.contentType), a.sizeKb].join(','));
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
downloadFile('ai-localize-full-report.csv', rows.join('\\n'), 'text/csv');
|
|
138
|
-
};`;
|
|
139
|
-
|
|
140
|
-
src = src.slice(0, csvStart) + newFullCsv + src.slice(i);
|
|
141
|
-
console.log('exportFullCsv replaced OK');
|
|
142
|
-
} else {
|
|
143
|
-
console.log('exportFullCsv not found');
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
fs.writeFileSync(file, src, 'utf8');
|
|
147
|
-
console.log('All patches applied. File written.');
|
|
@@ -1,166 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
const fs = require('fs');
|
|
3
|
-
|
|
4
|
-
const file = 'packages/reporting/preview/make-preview.cjs';
|
|
5
|
-
let src = fs.readFileSync(file, 'utf8');
|
|
6
|
-
|
|
7
|
-
// Full detail data object to embed in the preview HTML
|
|
8
|
-
const fullData = {
|
|
9
|
-
timestamp: new Date().toISOString(),
|
|
10
|
-
framework: 'react-vite',
|
|
11
|
-
filesScanned: 148,
|
|
12
|
-
hardcodedTexts: 37,
|
|
13
|
-
localeKeysGenerated: 29,
|
|
14
|
-
missingTranslations: 12,
|
|
15
|
-
unusedKeys: 4,
|
|
16
|
-
coveragePct: 68,
|
|
17
|
-
assets: { totalAssets: 23, uploadedAssets: 18, replacedUrls: 15, legacyCdnUrls: 5 },
|
|
18
|
-
details: {
|
|
19
|
-
detectedTexts: [
|
|
20
|
-
{ filePath: '/app/src/components/Button/PrimaryButton.tsx', line: 14, column: 8, text: 'Submit', suggestedKey: 'common.submit', context: 'button', nodeType: 'JSXAttribute', alreadyTranslated: false },
|
|
21
|
-
{ filePath: '/app/src/components/Button/PrimaryButton.tsx', line: 22, column: 12, text: 'Cancel', suggestedKey: 'common.cancel', context: 'button', nodeType: 'JSXAttribute', alreadyTranslated: false },
|
|
22
|
-
{ filePath: '/app/src/pages/Dashboard/Overview.tsx', line: 31, column: 6, text: 'Welcome back!', suggestedKey: 'dashboard.welcomeBack', context: 'heading', nodeType: 'JSXText', alreadyTranslated: false },
|
|
23
|
-
{ filePath: '/app/src/pages/Dashboard/Overview.tsx', line: 45, column: 10, text: 'Total Revenue', suggestedKey: 'dashboard.totalRevenue', context: 'table-header', nodeType: 'JSXText', alreadyTranslated: false },
|
|
24
|
-
{ filePath: '/app/src/pages/Dashboard/Overview.tsx', line: 67, column: 14, text: 'No data available', suggestedKey: 'dashboard.noData', context: 'jsx-text', nodeType: 'JSXText', alreadyTranslated: false },
|
|
25
|
-
{ filePath: '/app/src/components/Modal/ConfirmModal.tsx', line: 8, column: 4, text: 'Are you sure?', suggestedKey: 'modal.confirmTitle', context: 'modal', nodeType: 'JSXText', alreadyTranslated: false },
|
|
26
|
-
{ filePath: '/app/src/components/Modal/ConfirmModal.tsx', line: 19, column: 8, text: 'This action cannot be undone.', suggestedKey: 'modal.confirmBody', context: 'modal', nodeType: 'JSXText', alreadyTranslated: false },
|
|
27
|
-
{ filePath: '/app/src/components/Modal/ConfirmModal.tsx', line: 32, column: 12, text: 'Delete', suggestedKey: 'common.delete', context: 'button', nodeType: 'JSXAttribute', alreadyTranslated: false },
|
|
28
|
-
{ filePath: '/app/src/forms/LoginForm.tsx', line: 12, column: 6, text: 'Email address', suggestedKey: 'auth.emailLabel', context: 'label', nodeType: 'JSXText', alreadyTranslated: false },
|
|
29
|
-
{ filePath: '/app/src/forms/LoginForm.tsx', line: 18, column: 6, text: 'Password', suggestedKey: 'auth.passwordLabel', context: 'label', nodeType: 'JSXText', alreadyTranslated: false },
|
|
30
|
-
{ filePath: '/app/src/forms/LoginForm.tsx', line: 24, column: 10, text: 'Enter your email', suggestedKey: 'auth.emailPlaceholder', context: 'placeholder', nodeType: 'JSXAttribute', alreadyTranslated: false },
|
|
31
|
-
{ filePath: '/app/src/forms/LoginForm.tsx', line: 30, column: 10, text: 'Enter your password', suggestedKey: 'auth.passwordPlaceholder', context: 'placeholder', nodeType: 'JSXAttribute', alreadyTranslated: false },
|
|
32
|
-
{ filePath: '/app/src/forms/LoginForm.tsx', line: 56, column: 8, text: 'Sign in', suggestedKey: 'auth.signIn', context: 'button', nodeType: 'JSXAttribute', alreadyTranslated: false },
|
|
33
|
-
{ filePath: '/app/src/forms/LoginForm.tsx', line: 60, column: 12, text: 'Forgot password?', suggestedKey: 'auth.forgotPassword', context: 'jsx-text', nodeType: 'JSXText', alreadyTranslated: false },
|
|
34
|
-
{ filePath: '/app/src/components/Nav/Sidebar.tsx', line: 22, column: 6, text: 'Dashboard', suggestedKey: 'nav.dashboard', context: 'jsx-text', nodeType: 'JSXText', alreadyTranslated: false },
|
|
35
|
-
{ filePath: '/app/src/components/Nav/Sidebar.tsx', line: 30, column: 6, text: 'Settings', suggestedKey: 'nav.settings', context: 'jsx-text', nodeType: 'JSXText', alreadyTranslated: false },
|
|
36
|
-
{ filePath: '/app/src/components/Nav/Sidebar.tsx', line: 38, column: 6, text: 'Users', suggestedKey: 'nav.users', context: 'jsx-text', nodeType: 'JSXText', alreadyTranslated: false },
|
|
37
|
-
{ filePath: '/app/src/components/Nav/Sidebar.tsx', line: 46, column: 6, text: 'Reports', suggestedKey: 'nav.reports', context: 'jsx-text', nodeType: 'JSXText', alreadyTranslated: false },
|
|
38
|
-
{ filePath: '/app/src/components/Nav/Sidebar.tsx', line: 54, column: 6, text: 'Log out', suggestedKey: 'nav.logout', context: 'jsx-text', nodeType: 'JSXText', alreadyTranslated: false },
|
|
39
|
-
{ filePath: '/app/src/pages/Settings/ProfileSettings.tsx', line: 11, column: 4, text: 'Profile Settings', suggestedKey: 'settings.profileTitle', context: 'heading', nodeType: 'JSXText', alreadyTranslated: false },
|
|
40
|
-
{ filePath: '/app/src/pages/Settings/ProfileSettings.tsx', line: 28, column: 8, text: 'First name', suggestedKey: 'settings.firstName', context: 'label', nodeType: 'JSXText', alreadyTranslated: false },
|
|
41
|
-
{ filePath: '/app/src/pages/Settings/ProfileSettings.tsx', line: 36, column: 8, text: 'Last name', suggestedKey: 'settings.lastName', context: 'label', nodeType: 'JSXText', alreadyTranslated: false },
|
|
42
|
-
{ filePath: '/app/src/pages/Settings/ProfileSettings.tsx', line: 44, column: 8, text: 'Save changes', suggestedKey: 'common.saveChanges', context: 'button', nodeType: 'JSXAttribute', alreadyTranslated: false },
|
|
43
|
-
{ filePath: '/app/src/components/Toast/ToastManager.tsx', line: 9, column: 6, text: 'Changes saved successfully', suggestedKey: 'toast.saveSuccess', context: 'toast', nodeType: 'string-literal', alreadyTranslated: false },
|
|
44
|
-
{ filePath: '/app/src/components/Toast/ToastManager.tsx', line: 15, column: 6, text: 'An error occurred. Please try again.', suggestedKey: 'toast.genericError', context: 'toast', nodeType: 'string-literal', alreadyTranslated: false },
|
|
45
|
-
{ filePath: '/app/src/components/Toast/ToastManager.tsx', line: 21, column: 6, text: 'Network connection lost', suggestedKey: 'toast.networkError', context: 'toast', nodeType: 'string-literal', alreadyTranslated: false },
|
|
46
|
-
{ filePath: '/app/src/pages/Users/UserTable.tsx', line: 17, column: 6, text: 'Name', suggestedKey: 'users.columnName', context: 'table-header', nodeType: 'JSXText', alreadyTranslated: false },
|
|
47
|
-
{ filePath: '/app/src/pages/Users/UserTable.tsx', line: 24, column: 6, text: 'Email', suggestedKey: 'users.columnEmail', context: 'table-header', nodeType: 'JSXText', alreadyTranslated: false },
|
|
48
|
-
{ filePath: '/app/src/pages/Users/UserTable.tsx', line: 31, column: 6, text: 'Role', suggestedKey: 'users.columnRole', context: 'table-header', nodeType: 'JSXText', alreadyTranslated: false },
|
|
49
|
-
{ filePath: '/app/src/pages/Users/UserTable.tsx', line: 38, column: 6, text: 'Status', suggestedKey: 'users.columnStatus', context: 'table-header', nodeType: 'JSXText', alreadyTranslated: false },
|
|
50
|
-
{ filePath: '/app/src/pages/Users/UserTable.tsx', line: 68, column: 10, text: 'No users found', suggestedKey: 'users.emptyState', context: 'jsx-text', nodeType: 'JSXText', alreadyTranslated: false },
|
|
51
|
-
{ filePath: '/app/src/components/Alert/ErrorBanner.tsx', line: 7, column: 4, text: 'Something went wrong', suggestedKey: 'alert.errorTitle', context: 'alert', nodeType: 'JSXText', alreadyTranslated: false },
|
|
52
|
-
{ filePath: '/app/src/components/Tooltip/HelpTooltip.tsx', line: 5, column: 6, text: 'Click for more information', suggestedKey: 'tooltip.helpInfo', context: 'tooltip', nodeType: 'JSXAttribute', alreadyTranslated: false },
|
|
53
|
-
{ filePath: '/app/src/components/DataGrid/DataGrid.tsx', line: 42, column: 8, text: 'Loading data...', suggestedKey: 'grid.loading', context: 'jsx-text', nodeType: 'JSXText', alreadyTranslated: false },
|
|
54
|
-
{ filePath: '/app/src/components/DataGrid/DataGrid.tsx', line: 58, column: 8, text: 'No results match your search', suggestedKey: 'grid.noResults', context: 'jsx-text', nodeType: 'JSXText', alreadyTranslated: false },
|
|
55
|
-
{ filePath: '/app/src/pages/Reports/ReportHeader.tsx', line: 10, column: 6, text: 'Submit', suggestedKey: 'reports.submit', context: 'button', nodeType: 'JSXAttribute', alreadyTranslated: false },
|
|
56
|
-
{ filePath: '/app/src/pages/Reports/ReportHeader.tsx', line: 22, column: 8, text: 'Cancel', suggestedKey: 'reports.cancel', context: 'button', nodeType: 'JSXAttribute', alreadyTranslated: false },
|
|
57
|
-
],
|
|
58
|
-
missingKeys: [
|
|
59
|
-
{ key: 'common.submit', language: 'fr', message: 'Key missing in fr locale', filePath: '/app/locales/fr/common.json' },
|
|
60
|
-
{ key: 'common.cancel', language: 'fr', message: 'Key missing in fr locale', filePath: '/app/locales/fr/common.json' },
|
|
61
|
-
{ key: 'auth.signIn', language: 'fr', message: 'Key missing in fr locale', filePath: '/app/locales/fr/auth.json' },
|
|
62
|
-
{ key: 'dashboard.welcomeBack', language: 'de', message: 'Key missing in de locale', filePath: '/app/locales/de/dashboard.json' },
|
|
63
|
-
{ key: 'dashboard.totalRevenue', language: 'de', message: 'Key missing in de locale', filePath: '/app/locales/de/dashboard.json' },
|
|
64
|
-
{ key: 'modal.confirmTitle', language: 'de', message: 'Key missing in de locale', filePath: '/app/locales/de/modal.json' },
|
|
65
|
-
{ key: 'modal.confirmBody', language: 'de', message: 'Key missing in de locale', filePath: '/app/locales/de/modal.json' },
|
|
66
|
-
{ key: 'nav.logout', language: 'es', message: 'Key missing in es locale', filePath: '/app/locales/es/nav.json' },
|
|
67
|
-
{ key: 'settings.profileTitle', language: 'es', message: 'Key missing in es locale', filePath: '/app/locales/es/settings.json' },
|
|
68
|
-
{ key: 'toast.genericError', language: 'ja', message: 'Key missing in ja locale', filePath: '/app/locales/ja/toast.json' },
|
|
69
|
-
{ key: 'toast.saveSuccess', language: 'ja', message: 'Key missing in ja locale', filePath: '/app/locales/ja/toast.json' },
|
|
70
|
-
{ key: 'users.emptyState', language: 'ja', message: 'Key missing in ja locale', filePath: '/app/locales/ja/users.json' },
|
|
71
|
-
],
|
|
72
|
-
unusedKeys: ['common.oldButton', 'dashboard.legacyWidget', 'auth.ssoLogin', 'settings.betaFeature'],
|
|
73
|
-
assets: [
|
|
74
|
-
{ localPath: '/app/public/images/logo.png', s3Key: 'assets/images/logo.png', cloudfrontUrl: 'https://d1abc123.cloudfront.net/assets/images/logo.png', contentType: 'image/png', sizeKb: '24.0' },
|
|
75
|
-
{ localPath: '/app/public/images/hero-banner.webp', s3Key: 'assets/images/hero-banner.webp', cloudfrontUrl: 'https://d1abc123.cloudfront.net/assets/images/hero-banner.webp', contentType: 'image/webp', sizeKb: '100.0' },
|
|
76
|
-
{ localPath: '/app/public/fonts/Inter-Regular.woff2', s3Key: 'assets/fonts/Inter-Regular.woff2', cloudfrontUrl: 'https://d1abc123.cloudfront.net/assets/fonts/Inter-Regular.woff2', contentType: 'font/woff2', sizeKb: '64.0' },
|
|
77
|
-
{ localPath: '/app/public/fonts/Inter-Bold.woff2', s3Key: 'assets/fonts/Inter-Bold.woff2', cloudfrontUrl: 'https://d1abc123.cloudfront.net/assets/fonts/Inter-Bold.woff2', contentType: 'font/woff2', sizeKb: '67.5' },
|
|
78
|
-
{ localPath: '/app/public/icons/favicon.svg', s3Key: 'assets/icons/favicon.svg', cloudfrontUrl: 'https://d1abc123.cloudfront.net/assets/icons/favicon.svg', contentType: 'image/svg+xml', sizeKb: '4.0' },
|
|
79
|
-
{ localPath: '/app/public/videos/onboarding.mp4', s3Key: 'assets/videos/onboarding.mp4', cloudfrontUrl: 'https://d1abc123.cloudfront.net/assets/videos/onboarding.mp4', contentType: 'video/mp4', sizeKb: '5120.0' },
|
|
80
|
-
],
|
|
81
|
-
},
|
|
82
|
-
};
|
|
83
|
-
|
|
84
|
-
// Safe JSON string for embedding in a JS literal (no </script> injection)
|
|
85
|
-
const dataStr = JSON.stringify(fullData).replace(/<\/script>/g, '<\\/script>');
|
|
86
|
-
|
|
87
|
-
// The new export functions as a JS string — they parse the embedded JSON and use it
|
|
88
|
-
const newExportFJ = [
|
|
89
|
-
"window.exportFullJson=function(){",
|
|
90
|
-
"var d=" + JSON.stringify(dataStr) + ";",
|
|
91
|
-
"downloadFile('ai-localize-report.json',JSON.stringify(JSON.parse(d),null,2),'application/json');",
|
|
92
|
-
"};"
|
|
93
|
-
].join('');
|
|
94
|
-
|
|
95
|
-
const newExportCSV = [
|
|
96
|
-
"window.exportFullCsv=function(){",
|
|
97
|
-
"var d=" + JSON.stringify(dataStr) + ";",
|
|
98
|
-
"var r=JSON.parse(d);",
|
|
99
|
-
"var q=function(s){return '\"'+String(s==null?'':s).replace(/\"/g,'\"\"')+'\"';};",
|
|
100
|
-
"var rows=[];",
|
|
101
|
-
"rows.push('=== SUMMARY ===');",
|
|
102
|
-
"rows.push('\"Metric\",\"Value\"');",
|
|
103
|
-
"rows.push(q('Framework')+','+q(r.framework));",
|
|
104
|
-
"rows.push(q('Generated')+','+q(r.timestamp));",
|
|
105
|
-
"rows.push(q('Files Scanned')+','+r.filesScanned);",
|
|
106
|
-
"rows.push(q('Hardcoded Texts')+','+r.hardcodedTexts);",
|
|
107
|
-
"rows.push(q('Keys Generated')+','+r.localeKeysGenerated);",
|
|
108
|
-
"rows.push(q('Missing Translations')+','+r.missingTranslations);",
|
|
109
|
-
"rows.push(q('Unused Keys')+','+r.unusedKeys);",
|
|
110
|
-
"rows.push(q('Coverage %')+','+r.coveragePct);",
|
|
111
|
-
"rows.push(q('Total Assets')+','+r.assets.totalAssets);",
|
|
112
|
-
"rows.push(q('Uploaded Assets')+','+r.assets.uploadedAssets);",
|
|
113
|
-
"rows.push(q('Replaced URLs')+','+r.assets.replacedUrls);",
|
|
114
|
-
"rows.push(q('Legacy CDN URLs')+','+r.assets.legacyCdnUrls);",
|
|
115
|
-
"rows.push('');",
|
|
116
|
-
"rows.push('=== HARDCODED TEXTS ===');",
|
|
117
|
-
"rows.push('\"File\",\"Line\",\"Column\",\"Text\",\"Suggested Key\",\"Context\",\"Node Type\",\"Already Translated\"');",
|
|
118
|
-
"(r.details.detectedTexts||[]).forEach(function(t){rows.push([q(t.filePath),t.line,t.column,q(t.text),q(t.suggestedKey),q(t.context),q(t.nodeType),t.alreadyTranslated?'true':'false'].join(','));});",
|
|
119
|
-
"rows.push('');",
|
|
120
|
-
"rows.push('=== MISSING TRANSLATIONS ===');",
|
|
121
|
-
"rows.push('\"Key\",\"Language\",\"Locale File\",\"Message\"');",
|
|
122
|
-
"(r.details.missingKeys||[]).forEach(function(m){rows.push([q(m.key),q(m.language),q(m.filePath),q(m.message)].join(','));});",
|
|
123
|
-
"rows.push('');",
|
|
124
|
-
"rows.push('=== UNUSED KEYS ===');",
|
|
125
|
-
"rows.push('\"Key\"');",
|
|
126
|
-
"(r.details.unusedKeys||[]).forEach(function(k){rows.push(q(k));});",
|
|
127
|
-
"rows.push('');",
|
|
128
|
-
"rows.push('=== CDN ASSETS ===');",
|
|
129
|
-
"rows.push('\"Local Path\",\"S3 Key\",\"CloudFront URL\",\"Content Type\",\"Size (KB)\"');",
|
|
130
|
-
"(r.details.assets||[]).forEach(function(a){rows.push([q(a.localPath),q(a.s3Key),q(a.cloudfrontUrl),q(a.contentType),a.sizeKb].join(','));});",
|
|
131
|
-
"downloadFile('ai-localize-full-report.csv',rows.join('\\n'),'text/csv');",
|
|
132
|
-
"};"
|
|
133
|
-
].join('');
|
|
134
|
-
|
|
135
|
-
// Find and replace line 450 (exportFullJson) in the buildInlineJS function
|
|
136
|
-
const lines = src.split('\n');
|
|
137
|
-
let patched = 0;
|
|
138
|
-
const newLines = lines.map(function(line) {
|
|
139
|
-
if (line.includes("window.exportFullJson=function(){downloadFile")) {
|
|
140
|
-
// Find the prefix (spaces + quote) and suffix (\n" +)
|
|
141
|
-
const prefixEnd = line.indexOf('"window.exportFullJson');
|
|
142
|
-
const suffixStart = line.lastIndexOf('\\n" +');
|
|
143
|
-
if (prefixEnd !== -1 && suffixStart !== -1) {
|
|
144
|
-
const prefix = line.slice(0, prefixEnd);
|
|
145
|
-
const suffix = line.slice(suffixStart);
|
|
146
|
-
patched++;
|
|
147
|
-
return prefix + JSON.stringify(newExportFJ).slice(1, -1) + suffix;
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
if (line.includes("window.exportFullCsv=function(){var lines=")) {
|
|
151
|
-
const prefixEnd = line.indexOf('"window.exportFullCsv');
|
|
152
|
-
const suffixStart = line.lastIndexOf('\\n" +');
|
|
153
|
-
if (prefixEnd !== -1 && suffixStart !== -1) {
|
|
154
|
-
const prefix = line.slice(0, prefixEnd);
|
|
155
|
-
const suffix = line.slice(suffixStart);
|
|
156
|
-
patched++;
|
|
157
|
-
return prefix + JSON.stringify(newExportCSV).slice(1, -1) + suffix;
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
return line;
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
console.log('Patched', patched, 'lines');
|
|
164
|
-
src = newLines.join('\n');
|
|
165
|
-
fs.writeFileSync(file, src, 'utf8');
|
|
166
|
-
console.log('make-preview.cjs updated OK');
|
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Generates a preview HTML report using realistic mock data.
|
|
3
|
-
* Run: npx ts-node --esm packages/reporting/preview/generate-preview.ts
|
|
4
|
-
* Or: node -e "require('./packages/reporting/preview/generate-preview.js')()"
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { generateHtmlReport } from '../src/html-reporter.js';
|
|
8
|
-
import type { Report } from 'ai-localize-shared';
|
|
9
|
-
import * as path from 'path';
|
|
10
|
-
import { fileURLToPath } from 'url';
|
|
11
|
-
|
|
12
|
-
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
13
|
-
|
|
14
|
-
const mockReport: Report = {
|
|
15
|
-
timestamp: new Date().toISOString(),
|
|
16
|
-
duration: 2340,
|
|
17
|
-
framework: 'react-vite',
|
|
18
|
-
filesScanned: 148,
|
|
19
|
-
hardcodedTexts: 37,
|
|
20
|
-
localeKeysGenerated: 29,
|
|
21
|
-
unusedKeys: 4,
|
|
22
|
-
missingTranslations: 12,
|
|
23
|
-
languages: ['en', 'fr', 'de', 'es', 'ja'],
|
|
24
|
-
assets: {
|
|
25
|
-
totalAssets: 23,
|
|
26
|
-
uploadedAssets: 18,
|
|
27
|
-
replacedUrls: 15,
|
|
28
|
-
legacyCdnUrls: 5,
|
|
29
|
-
},
|
|
30
|
-
details: {
|
|
31
|
-
detectedTexts: [
|
|
32
|
-
{ filePath: '/app/src/components/Button/PrimaryButton.tsx', line: 14, column: 8, text: 'Submit', suggestedKey: 'common.submit', context: 'button', nodeType: 'JSXAttribute', alreadyTranslated: false },
|
|
33
|
-
{ filePath: '/app/src/components/Button/PrimaryButton.tsx', line: 22, column: 12, text: 'Cancel', suggestedKey: 'common.cancel', context: 'button', nodeType: 'JSXAttribute', alreadyTranslated: false },
|
|
34
|
-
{ filePath: '/app/src/pages/Dashboard/Overview.tsx', line: 31, column: 6, text: 'Welcome back!', suggestedKey: 'dashboard.welcomeBack', context: 'heading', nodeType: 'JSXText', alreadyTranslated: false },
|
|
35
|
-
{ filePath: '/app/src/pages/Dashboard/Overview.tsx', line: 45, column: 10, text: 'Total Revenue', suggestedKey: 'dashboard.totalRevenue', context: 'table-header', nodeType: 'JSXText', alreadyTranslated: false },
|
|
36
|
-
{ filePath: '/app/src/pages/Dashboard/Overview.tsx', line: 67, column: 14, text: 'No data available', suggestedKey: 'dashboard.noData', context: 'jsx-text', nodeType: 'JSXText', alreadyTranslated: false },
|
|
37
|
-
{ filePath: '/app/src/components/Modal/ConfirmModal.tsx', line: 8, column: 4, text: 'Are you sure?', suggestedKey: 'modal.confirmTitle', context: 'modal', nodeType: 'JSXText', alreadyTranslated: false },
|
|
38
|
-
{ filePath: '/app/src/components/Modal/ConfirmModal.tsx', line: 19, column: 8, text: 'This action cannot be undone.', suggestedKey: 'modal.confirmBody', context: 'modal', nodeType: 'JSXText', alreadyTranslated: false },
|
|
39
|
-
{ filePath: '/app/src/components/Modal/ConfirmModal.tsx', line: 32, column: 12, text: 'Delete', suggestedKey: 'common.delete', context: 'button', nodeType: 'JSXAttribute', alreadyTranslated: false },
|
|
40
|
-
{ filePath: '/app/src/forms/LoginForm.tsx', line: 12, column: 6, text: 'Email address', suggestedKey: 'auth.emailLabel', context: 'label', nodeType: 'JSXText', alreadyTranslated: false },
|
|
41
|
-
{ filePath: '/app/src/forms/LoginForm.tsx', line: 18, column: 6, text: 'Password', suggestedKey: 'auth.passwordLabel', context: 'label', nodeType: 'JSXText', alreadyTranslated: false },
|
|
42
|
-
{ filePath: '/app/src/forms/LoginForm.tsx', line: 24, column: 10, text: 'Enter your email', suggestedKey: 'auth.emailPlaceholder', context: 'placeholder', nodeType: 'JSXAttribute', alreadyTranslated: false },
|
|
43
|
-
{ filePath: '/app/src/forms/LoginForm.tsx', line: 30, column: 10, text: 'Enter your password', suggestedKey: 'auth.passwordPlaceholder', context: 'placeholder', nodeType: 'JSXAttribute', alreadyTranslated: false },
|
|
44
|
-
{ filePath: '/app/src/forms/LoginForm.tsx', line: 56, column: 8, text: 'Sign in', suggestedKey: 'auth.signIn', context: 'button', nodeType: 'JSXAttribute', alreadyTranslated: false },
|
|
45
|
-
{ filePath: '/app/src/forms/LoginForm.tsx', line: 60, column: 12, text: 'Forgot password?', suggestedKey: 'auth.forgotPassword', context: 'jsx-text', nodeType: 'JSXText', alreadyTranslated: false },
|
|
46
|
-
{ filePath: '/app/src/components/Nav/Sidebar.tsx', line: 22, column: 6, text: 'Dashboard', suggestedKey: 'nav.dashboard', context: 'jsx-text', nodeType: 'JSXText', alreadyTranslated: false },
|
|
47
|
-
{ filePath: '/app/src/components/Nav/Sidebar.tsx', line: 30, column: 6, text: 'Settings', suggestedKey: 'nav.settings', context: 'jsx-text', nodeType: 'JSXText', alreadyTranslated: false },
|
|
48
|
-
{ filePath: '/app/src/components/Nav/Sidebar.tsx', line: 38, column: 6, text: 'Users', suggestedKey: 'nav.users', context: 'jsx-text', nodeType: 'JSXText', alreadyTranslated: false },
|
|
49
|
-
{ filePath: '/app/src/components/Nav/Sidebar.tsx', line: 46, column: 6, text: 'Reports', suggestedKey: 'nav.reports', context: 'jsx-text', nodeType: 'JSXText', alreadyTranslated: false },
|
|
50
|
-
{ filePath: '/app/src/components/Nav/Sidebar.tsx', line: 54, column: 6, text: 'Log out', suggestedKey: 'nav.logout', context: 'jsx-text', nodeType: 'JSXText', alreadyTranslated: false },
|
|
51
|
-
{ filePath: '/app/src/pages/Settings/ProfileSettings.tsx', line: 11, column: 4, text: 'Profile Settings', suggestedKey: 'settings.profileTitle', context: 'heading', nodeType: 'JSXText', alreadyTranslated: false },
|
|
52
|
-
{ filePath: '/app/src/pages/Settings/ProfileSettings.tsx', line: 28, column: 8, text: 'First name', suggestedKey: 'settings.firstName', context: 'label', nodeType: 'JSXText', alreadyTranslated: false },
|
|
53
|
-
{ filePath: '/app/src/pages/Settings/ProfileSettings.tsx', line: 36, column: 8, text: 'Last name', suggestedKey: 'settings.lastName', context: 'label', nodeType: 'JSXText', alreadyTranslated: false },
|
|
54
|
-
{ filePath: '/app/src/pages/Settings/ProfileSettings.tsx', line: 44, column: 8, text: 'Save changes', suggestedKey: 'common.saveChanges', context: 'button', nodeType: 'JSXAttribute', alreadyTranslated: false },
|
|
55
|
-
{ filePath: '/app/src/components/Toast/ToastManager.tsx', line: 9, column: 6, text: 'Changes saved successfully', suggestedKey: 'toast.saveSuccess', context: 'toast', nodeType: 'string-literal', alreadyTranslated: false },
|
|
56
|
-
{ filePath: '/app/src/components/Toast/ToastManager.tsx', line: 15, column: 6, text: 'An error occurred. Please try again.', suggestedKey: 'toast.genericError', context: 'toast', nodeType: 'string-literal', alreadyTranslated: false },
|
|
57
|
-
{ filePath: '/app/src/components/Toast/ToastManager.tsx', line: 21, column: 6, text: 'Network connection lost', suggestedKey: 'toast.networkError', context: 'toast', nodeType: 'string-literal', alreadyTranslated: false },
|
|
58
|
-
{ filePath: '/app/src/pages/Users/UserTable.tsx', line: 17, column: 6, text: 'Name', suggestedKey: 'users.columnName', context: 'table-header', nodeType: 'JSXText', alreadyTranslated: false },
|
|
59
|
-
{ filePath: '/app/src/pages/Users/UserTable.tsx', line: 24, column: 6, text: 'Email', suggestedKey: 'users.columnEmail', context: 'table-header', nodeType: 'JSXText', alreadyTranslated: false },
|
|
60
|
-
{ filePath: '/app/src/pages/Users/UserTable.tsx', line: 31, column: 6, text: 'Role', suggestedKey: 'users.columnRole', context: 'table-header', nodeType: 'JSXText', alreadyTranslated: false },
|
|
61
|
-
{ filePath: '/app/src/pages/Users/UserTable.tsx', line: 38, column: 6, text: 'Status', suggestedKey: 'users.columnStatus', context: 'table-header', nodeType: 'JSXText', alreadyTranslated: false },
|
|
62
|
-
{ filePath: '/app/src/pages/Users/UserTable.tsx', line: 68, column: 10, text: 'No users found', suggestedKey: 'users.emptyState', context: 'jsx-text', nodeType: 'JSXText', alreadyTranslated: false },
|
|
63
|
-
{ filePath: '/app/src/components/Alert/ErrorBanner.tsx', line: 7, column: 4, text: 'Something went wrong', suggestedKey: 'alert.errorTitle', context: 'alert', nodeType: 'JSXText', alreadyTranslated: false },
|
|
64
|
-
{ filePath: '/app/src/components/Alert/ErrorBanner.tsx', line: 13, column: 8, text: 'Please refresh the page', suggestedKey: 'alert.refreshHint', context: 'jsx-text', nodeType: 'JSXText', alreadyTranslated: false },
|
|
65
|
-
{ filePath: '/app/src/components/Tooltip/HelpTooltip.tsx', line: 5, column: 6, text: 'Click for more information', suggestedKey: 'tooltip.helpInfo', context: 'tooltip', nodeType: 'JSXAttribute', alreadyTranslated: false },
|
|
66
|
-
{ filePath: '/app/src/components/DataGrid/DataGrid.tsx', line: 42, column: 8, text: 'Loading data…', suggestedKey: 'grid.loading', context: 'jsx-text', nodeType: 'JSXText', alreadyTranslated: false },
|
|
67
|
-
{ filePath: '/app/src/components/DataGrid/DataGrid.tsx', line: 58, column: 8, text: 'No results match your search', suggestedKey: 'grid.noResults', context: 'jsx-text', nodeType: 'JSXText', alreadyTranslated: false },
|
|
68
|
-
// duplicate text scenario
|
|
69
|
-
{ filePath: '/app/src/pages/Reports/ReportHeader.tsx', line: 10, column: 6, text: 'Submit', suggestedKey: 'reports.submit', context: 'button', nodeType: 'JSXAttribute', alreadyTranslated: false },
|
|
70
|
-
{ filePath: '/app/src/pages/Reports/ReportHeader.tsx', line: 22, column: 8, text: 'Cancel', suggestedKey: 'reports.cancel', context: 'button', nodeType: 'JSXAttribute', alreadyTranslated: false },
|
|
71
|
-
],
|
|
72
|
-
missingKeys: [
|
|
73
|
-
{ type: 'missing-key', key: 'common.submit', language: 'fr', message: 'Key missing in fr locale', filePath: '/app/locales/fr/common.json' },
|
|
74
|
-
{ type: 'missing-key', key: 'common.cancel', language: 'fr', message: 'Key missing in fr locale', filePath: '/app/locales/fr/common.json' },
|
|
75
|
-
{ type: 'missing-key', key: 'auth.signIn', language: 'fr', message: 'Key missing in fr locale', filePath: '/app/locales/fr/auth.json' },
|
|
76
|
-
{ type: 'missing-key', key: 'dashboard.welcomeBack', language: 'de', message: 'Key missing in de locale', filePath: '/app/locales/de/dashboard.json' },
|
|
77
|
-
{ type: 'missing-key', key: 'dashboard.totalRevenue', language: 'de', message: 'Key missing in de locale', filePath: '/app/locales/de/dashboard.json' },
|
|
78
|
-
{ type: 'missing-key', key: 'modal.confirmTitle', language: 'de', message: 'Key missing in de locale', filePath: '/app/locales/de/modal.json' },
|
|
79
|
-
{ type: 'missing-key', key: 'modal.confirmBody', language: 'de', message: 'Key missing in de locale', filePath: '/app/locales/de/modal.json' },
|
|
80
|
-
{ type: 'missing-key', key: 'nav.logout', language: 'es', message: 'Key missing in es locale', filePath: '/app/locales/es/nav.json' },
|
|
81
|
-
{ type: 'missing-key', key: 'settings.profileTitle', language: 'es', message: 'Key missing in es locale', filePath: '/app/locales/es/settings.json' },
|
|
82
|
-
{ type: 'missing-key', key: 'toast.genericError', language: 'ja', message: 'Key missing in ja locale', filePath: '/app/locales/ja/toast.json' },
|
|
83
|
-
{ type: 'missing-key', key: 'toast.saveSuccess', language: 'ja', message: 'Key missing in ja locale', filePath: '/app/locales/ja/toast.json' },
|
|
84
|
-
{ type: 'missing-key', key: 'users.emptyState', language: 'ja', message: 'Key missing in ja locale', filePath: '/app/locales/ja/users.json' },
|
|
85
|
-
],
|
|
86
|
-
unusedKeysList: [
|
|
87
|
-
'common.oldButton',
|
|
88
|
-
'dashboard.legacyWidget',
|
|
89
|
-
'auth.ssoLogin',
|
|
90
|
-
'settings.betaFeature',
|
|
91
|
-
],
|
|
92
|
-
assets: [
|
|
93
|
-
{ localPath: '/app/public/images/logo.png', s3Key: 'assets/images/logo.png', hash: 'abc123', cloudfrontUrl: 'https://d1abc123.cloudfront.net/assets/images/logo.png', contentType: 'image/png', size: 24576 },
|
|
94
|
-
{ localPath: '/app/public/images/hero-banner.webp', s3Key: 'assets/images/hero-banner.webp', hash: 'def456', cloudfrontUrl: 'https://d1abc123.cloudfront.net/assets/images/hero-banner.webp', contentType: 'image/webp', size: 102400 },
|
|
95
|
-
{ localPath: '/app/public/fonts/Inter-Regular.woff2', s3Key: 'assets/fonts/Inter-Regular.woff2', hash: 'ghi789', cloudfrontUrl: 'https://d1abc123.cloudfront.net/assets/fonts/Inter-Regular.woff2', contentType: 'font/woff2', size: 65536 },
|
|
96
|
-
{ localPath: '/app/public/fonts/Inter-Bold.woff2', s3Key: 'assets/fonts/Inter-Bold.woff2', hash: 'jkl012', cloudfrontUrl: 'https://d1abc123.cloudfront.net/assets/fonts/Inter-Bold.woff2', contentType: 'font/woff2', size: 69120 },
|
|
97
|
-
{ localPath: '/app/public/icons/favicon.svg', s3Key: 'assets/icons/favicon.svg', hash: 'mno345', cloudfrontUrl: 'https://d1abc123.cloudfront.net/assets/icons/favicon.svg', contentType: 'image/svg+xml', size: 4096 },
|
|
98
|
-
{ localPath: '/app/public/videos/onboarding.mp4', s3Key: 'assets/videos/onboarding.mp4', hash: 'pqr678', cloudfrontUrl: 'https://d1abc123.cloudfront.net/assets/videos/onboarding.mp4', contentType: 'video/mp4', size: 5242880 },
|
|
99
|
-
],
|
|
100
|
-
},
|
|
101
|
-
};
|
|
102
|
-
|
|
103
|
-
const outPath = path.join(__dirname, 'report-preview.html');
|
|
104
|
-
generateHtmlReport(mockReport, outPath);
|
|
105
|
-
console.log('Preview written to:', outPath);
|