ai-localize-reporting 2.0.3 → 2.0.4

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/package.json CHANGED
@@ -1,10 +1,15 @@
1
1
  {
2
2
  "name": "ai-localize-reporting",
3
- "version": "2.0.3",
4
- "description": "Localization scan reporting: JSON, HTML, CLI summary",
3
+ "version": "2.0.4",
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.3"
36
+ "ai-localize-shared": "2.0.4"
17
37
  },
18
38
  "devDependencies": {
19
39
  "tsup": "^8.0.1",
@@ -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);