lingo-guardian 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +45 -0
- package/dist/bin/cli.d.ts +10 -0
- package/dist/bin/cli.d.ts.map +1 -0
- package/dist/bin/cli.js +36 -0
- package/dist/bin/cli.js.map +1 -0
- package/dist/commands/lint.d.ts +19 -0
- package/dist/commands/lint.d.ts.map +1 -0
- package/dist/commands/lint.js +166 -0
- package/dist/commands/lint.js.map +1 -0
- package/dist/constants.d.ts +110 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +41 -0
- package/dist/constants.js.map +1 -0
- package/dist/core/auditor.d.ts +62 -0
- package/dist/core/auditor.d.ts.map +1 -0
- package/dist/core/auditor.js +261 -0
- package/dist/core/auditor.js.map +1 -0
- package/dist/core/lingo-integration.d.ts +109 -0
- package/dist/core/lingo-integration.d.ts.map +1 -0
- package/dist/core/lingo-integration.js +243 -0
- package/dist/core/lingo-integration.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/reporters/reporter.d.ts +35 -0
- package/dist/reporters/reporter.d.ts.map +1 -0
- package/dist/reporters/reporter.js +322 -0
- package/dist/reporters/reporter.js.map +1 -0
- package/dist/transforms/pseudo-locale.d.ts +22 -0
- package/dist/transforms/pseudo-locale.d.ts.map +1 -0
- package/dist/transforms/pseudo-locale.js +181 -0
- package/dist/transforms/pseudo-locale.js.map +1 -0
- package/dist/transforms/rtl.d.ts +26 -0
- package/dist/transforms/rtl.d.ts.map +1 -0
- package/dist/transforms/rtl.js +120 -0
- package/dist/transforms/rtl.js.map +1 -0
- package/package.json +70 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reporter - Formats and outputs audit results
|
|
3
|
+
*
|
|
4
|
+
* Supports multiple output formats:
|
|
5
|
+
* - Table (default): Pretty terminal output
|
|
6
|
+
* - JSON: Machine-readable format
|
|
7
|
+
* - HTML: Visual report with styling
|
|
8
|
+
*/
|
|
9
|
+
import type { AuditResult } from '../constants.js';
|
|
10
|
+
export type OutputFormat = 'table' | 'json' | 'html';
|
|
11
|
+
export declare class Reporter {
|
|
12
|
+
private format;
|
|
13
|
+
constructor(format?: OutputFormat);
|
|
14
|
+
/**
|
|
15
|
+
* Generate report from audit results
|
|
16
|
+
*/
|
|
17
|
+
report(results: AuditResult[]): Promise<string>;
|
|
18
|
+
/**
|
|
19
|
+
* Print report to console
|
|
20
|
+
*/
|
|
21
|
+
print(results: AuditResult[]): void;
|
|
22
|
+
/**
|
|
23
|
+
* Save report to file
|
|
24
|
+
*/
|
|
25
|
+
save(results: AuditResult[], outputPath: string): Promise<void>;
|
|
26
|
+
private formatTable;
|
|
27
|
+
private formatJSON;
|
|
28
|
+
private formatHTML;
|
|
29
|
+
private formatSeverity;
|
|
30
|
+
private formatOverflow;
|
|
31
|
+
private colorize;
|
|
32
|
+
private truncate;
|
|
33
|
+
private escapeHtml;
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=reporter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reporter.d.ts","sourceRoot":"","sources":["../../src/reporters/reporter.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,KAAK,EAAE,WAAW,EAA2B,MAAM,iBAAiB,CAAC;AAI5E,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,CAAC;AAErD,qBAAa,QAAQ;IACjB,OAAO,CAAC,MAAM,CAAe;gBAEjB,MAAM,GAAE,YAAsB;IAI1C;;OAEG;IACG,MAAM,CAAC,OAAO,EAAE,WAAW,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC;IAYrD;;OAEG;IACH,KAAK,CAAC,OAAO,EAAE,WAAW,EAAE,GAAG,IAAI;IAsFnC;;OAEG;IACG,IAAI,CAAC,OAAO,EAAE,WAAW,EAAE,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAYrE,OAAO,CAAC,WAAW;IAsBnB,OAAO,CAAC,UAAU;IAIlB,OAAO,CAAC,UAAU;IAoJlB,OAAO,CAAC,cAAc;IAWtB,OAAO,CAAC,cAAc;IAatB,OAAO,CAAC,QAAQ;IAMhB,OAAO,CAAC,QAAQ;IAKhB,OAAO,CAAC,UAAU;CAOrB"}
|
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reporter - Formats and outputs audit results
|
|
3
|
+
*
|
|
4
|
+
* Supports multiple output formats:
|
|
5
|
+
* - Table (default): Pretty terminal output
|
|
6
|
+
* - JSON: Machine-readable format
|
|
7
|
+
* - HTML: Visual report with styling
|
|
8
|
+
*/
|
|
9
|
+
import chalk from 'chalk';
|
|
10
|
+
import { table } from 'table';
|
|
11
|
+
import fs from 'fs/promises';
|
|
12
|
+
import path from 'path';
|
|
13
|
+
export class Reporter {
|
|
14
|
+
format;
|
|
15
|
+
constructor(format = 'table') {
|
|
16
|
+
this.format = format;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Generate report from audit results
|
|
20
|
+
*/
|
|
21
|
+
async report(results) {
|
|
22
|
+
switch (this.format) {
|
|
23
|
+
case 'json':
|
|
24
|
+
return this.formatJSON(results);
|
|
25
|
+
case 'html':
|
|
26
|
+
return this.formatHTML(results);
|
|
27
|
+
case 'table':
|
|
28
|
+
default:
|
|
29
|
+
return this.formatTable(results);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Print report to console
|
|
34
|
+
*/
|
|
35
|
+
print(results) {
|
|
36
|
+
const totalIssues = results.reduce((sum, r) => sum + r.issueCount, 0);
|
|
37
|
+
const errors = results.flatMap((r) => r.issues).filter((i) => i.severity === 'error').length;
|
|
38
|
+
const warnings = results.flatMap((r) => r.issues).filter((i) => i.severity === 'warning').length;
|
|
39
|
+
console.log('\n' + chalk.bold('═══ LINGO-GUARDIAN AUDIT RESULTS ═══') + '\n');
|
|
40
|
+
// Summary
|
|
41
|
+
console.log(chalk.bold('📊 Summary'));
|
|
42
|
+
console.log(` URL: ${chalk.cyan(results[0]?.url || 'N/A')}`);
|
|
43
|
+
console.log(` Locales tested: ${chalk.cyan(results.map((r) => r.locale).join(', '))}`);
|
|
44
|
+
console.log(` Total issues: ${this.colorize(totalIssues, 'count')}`);
|
|
45
|
+
console.log(` Errors: ${chalk.red(errors)}`);
|
|
46
|
+
console.log(` Warnings: ${chalk.yellow(warnings)}`);
|
|
47
|
+
console.log();
|
|
48
|
+
// Issues by locale
|
|
49
|
+
for (const result of results) {
|
|
50
|
+
if (result.issues.length === 0) {
|
|
51
|
+
console.log(chalk.green(`✓ ${result.locale.toUpperCase()}: No issues found`));
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
console.log(chalk.bold(`\n🔍 ${result.locale.toUpperCase()} (${result.issues.length} issues)`));
|
|
55
|
+
const tableData = [
|
|
56
|
+
[
|
|
57
|
+
chalk.bold('Severity'),
|
|
58
|
+
chalk.bold('Element'),
|
|
59
|
+
chalk.bold('Overflow'),
|
|
60
|
+
chalk.bold('Text'),
|
|
61
|
+
],
|
|
62
|
+
...result.issues.map((issue) => [
|
|
63
|
+
this.formatSeverity(issue.severity),
|
|
64
|
+
this.truncate(issue.selector, 40),
|
|
65
|
+
this.formatOverflow(issue),
|
|
66
|
+
this.truncate(issue.textContent, 25),
|
|
67
|
+
]),
|
|
68
|
+
];
|
|
69
|
+
console.log(table(tableData, {
|
|
70
|
+
border: {
|
|
71
|
+
topBody: '─',
|
|
72
|
+
topJoin: '┬',
|
|
73
|
+
topLeft: '┌',
|
|
74
|
+
topRight: '┐',
|
|
75
|
+
bottomBody: '─',
|
|
76
|
+
bottomJoin: '┴',
|
|
77
|
+
bottomLeft: '└',
|
|
78
|
+
bottomRight: '┘',
|
|
79
|
+
bodyLeft: '│',
|
|
80
|
+
bodyRight: '│',
|
|
81
|
+
bodyJoin: '│',
|
|
82
|
+
joinBody: '─',
|
|
83
|
+
joinLeft: '├',
|
|
84
|
+
joinRight: '┤',
|
|
85
|
+
joinJoin: '┼',
|
|
86
|
+
},
|
|
87
|
+
}));
|
|
88
|
+
}
|
|
89
|
+
// Overall status
|
|
90
|
+
console.log();
|
|
91
|
+
if (errors > 0) {
|
|
92
|
+
console.log(chalk.bgRed.white.bold(' FAIL ') +
|
|
93
|
+
` Found ${errors} error(s) that will likely cause layout breaks.`);
|
|
94
|
+
}
|
|
95
|
+
else if (warnings > 0) {
|
|
96
|
+
console.log(chalk.bgYellow.black.bold(' WARN ') +
|
|
97
|
+
` Found ${warnings} warning(s) that should be reviewed.`);
|
|
98
|
+
}
|
|
99
|
+
else if (totalIssues > 0) {
|
|
100
|
+
console.log(chalk.bgBlue.white.bold(' INFO ') +
|
|
101
|
+
` Found ${totalIssues} minor issue(s).`);
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
console.log(chalk.bgGreen.white.bold(' PASS ') + ' No i18n layout issues detected! 🎉');
|
|
105
|
+
}
|
|
106
|
+
console.log();
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Save report to file
|
|
110
|
+
*/
|
|
111
|
+
async save(results, outputPath) {
|
|
112
|
+
const content = await this.report(results);
|
|
113
|
+
const ext = this.format === 'json' ? 'json' : this.format === 'html' ? 'html' : 'txt';
|
|
114
|
+
const filename = `lingo-guardian-report-${Date.now()}.${ext}`;
|
|
115
|
+
const fullPath = path.join(outputPath, filename);
|
|
116
|
+
await fs.mkdir(outputPath, { recursive: true });
|
|
117
|
+
await fs.writeFile(fullPath, content, 'utf-8');
|
|
118
|
+
console.log(chalk.green(`\n📄 Report saved to: ${fullPath}`));
|
|
119
|
+
}
|
|
120
|
+
formatTable(results) {
|
|
121
|
+
// Return plain text version of the table
|
|
122
|
+
const lines = [];
|
|
123
|
+
for (const result of results) {
|
|
124
|
+
lines.push(`\n=== ${result.locale.toUpperCase()} ===`);
|
|
125
|
+
lines.push(`URL: ${result.url}`);
|
|
126
|
+
lines.push(`Issues: ${result.issueCount}`);
|
|
127
|
+
lines.push('');
|
|
128
|
+
for (const issue of result.issues) {
|
|
129
|
+
lines.push(`[${issue.severity.toUpperCase()}] ${issue.selector}` +
|
|
130
|
+
` (${issue.scrollWidth}x${issue.scrollHeight} > ${issue.offsetWidth}x${issue.offsetHeight})` +
|
|
131
|
+
` "${issue.textContent}"`);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return lines.join('\n');
|
|
135
|
+
}
|
|
136
|
+
formatJSON(results) {
|
|
137
|
+
return JSON.stringify(results, null, 2);
|
|
138
|
+
}
|
|
139
|
+
formatHTML(results) {
|
|
140
|
+
const totalIssues = results.reduce((sum, r) => sum + r.issueCount, 0);
|
|
141
|
+
const errors = results.flatMap((r) => r.issues).filter((i) => i.severity === 'error').length;
|
|
142
|
+
return `<!DOCTYPE html>
|
|
143
|
+
<html lang="en">
|
|
144
|
+
<head>
|
|
145
|
+
<meta charset="UTF-8">
|
|
146
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
147
|
+
<title>Lingo-Guardian Audit Report</title>
|
|
148
|
+
<style>
|
|
149
|
+
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
150
|
+
body {
|
|
151
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
152
|
+
background: #0f172a;
|
|
153
|
+
color: #e2e8f0;
|
|
154
|
+
padding: 2rem;
|
|
155
|
+
min-height: 100vh;
|
|
156
|
+
}
|
|
157
|
+
.container { max-width: 1200px; margin: 0 auto; }
|
|
158
|
+
h1 {
|
|
159
|
+
font-size: 2rem;
|
|
160
|
+
background: linear-gradient(135deg, #8B5CF6, #06B6D4);
|
|
161
|
+
-webkit-background-clip: text;
|
|
162
|
+
-webkit-text-fill-color: transparent;
|
|
163
|
+
margin-bottom: 1rem;
|
|
164
|
+
}
|
|
165
|
+
.summary {
|
|
166
|
+
display: grid;
|
|
167
|
+
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
|
168
|
+
gap: 1rem;
|
|
169
|
+
margin: 2rem 0;
|
|
170
|
+
}
|
|
171
|
+
.stat {
|
|
172
|
+
background: #1e293b;
|
|
173
|
+
border-radius: 12px;
|
|
174
|
+
padding: 1.5rem;
|
|
175
|
+
text-align: center;
|
|
176
|
+
}
|
|
177
|
+
.stat-value { font-size: 2.5rem; font-weight: bold; }
|
|
178
|
+
.stat-label { color: #94a3b8; margin-top: 0.5rem; }
|
|
179
|
+
.stat.error .stat-value { color: #f87171; }
|
|
180
|
+
.stat.warning .stat-value { color: #fbbf24; }
|
|
181
|
+
.stat.success .stat-value { color: #4ade80; }
|
|
182
|
+
.locale-section { margin: 2rem 0; }
|
|
183
|
+
.locale-header {
|
|
184
|
+
background: #1e293b;
|
|
185
|
+
padding: 1rem 1.5rem;
|
|
186
|
+
border-radius: 12px 12px 0 0;
|
|
187
|
+
font-weight: 600;
|
|
188
|
+
}
|
|
189
|
+
table { width: 100%; border-collapse: collapse; }
|
|
190
|
+
th, td { padding: 1rem; text-align: left; border-bottom: 1px solid #334155; }
|
|
191
|
+
th { background: #1e293b; color: #94a3b8; font-weight: 500; }
|
|
192
|
+
td { background: #0f172a; }
|
|
193
|
+
.severity {
|
|
194
|
+
display: inline-block;
|
|
195
|
+
padding: 0.25rem 0.75rem;
|
|
196
|
+
border-radius: 99px;
|
|
197
|
+
font-size: 0.75rem;
|
|
198
|
+
font-weight: 600;
|
|
199
|
+
text-transform: uppercase;
|
|
200
|
+
}
|
|
201
|
+
.severity.error { background: #7f1d1d; color: #fca5a5; }
|
|
202
|
+
.severity.warning { background: #78350f; color: #fcd34d; }
|
|
203
|
+
.severity.info { background: #1e3a5f; color: #7dd3fc; }
|
|
204
|
+
.selector { font-family: monospace; font-size: 0.85rem; color: #06B6D4; }
|
|
205
|
+
.overflow { color: #f472b6; font-family: monospace; }
|
|
206
|
+
.text { color: #94a3b8; max-width: 200px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
207
|
+
.pass-badge {
|
|
208
|
+
background: linear-gradient(135deg, #4ade80, #22d3ee);
|
|
209
|
+
color: #0f172a;
|
|
210
|
+
padding: 1rem 2rem;
|
|
211
|
+
border-radius: 12px;
|
|
212
|
+
font-size: 1.25rem;
|
|
213
|
+
font-weight: 600;
|
|
214
|
+
text-align: center;
|
|
215
|
+
margin: 2rem 0;
|
|
216
|
+
}
|
|
217
|
+
</style>
|
|
218
|
+
</head>
|
|
219
|
+
<body>
|
|
220
|
+
<div class="container">
|
|
221
|
+
<h1>🛡️ Lingo-Guardian Audit Report</h1>
|
|
222
|
+
<p>Generated: ${new Date().toLocaleString()}</p>
|
|
223
|
+
|
|
224
|
+
<div class="summary">
|
|
225
|
+
<div class="stat ${totalIssues === 0 ? 'success' : errors > 0 ? 'error' : 'warning'}">
|
|
226
|
+
<div class="stat-value">${totalIssues}</div>
|
|
227
|
+
<div class="stat-label">Total Issues</div>
|
|
228
|
+
</div>
|
|
229
|
+
<div class="stat">
|
|
230
|
+
<div class="stat-value">${results.length}</div>
|
|
231
|
+
<div class="stat-label">Locales Tested</div>
|
|
232
|
+
</div>
|
|
233
|
+
<div class="stat error">
|
|
234
|
+
<div class="stat-value">${errors}</div>
|
|
235
|
+
<div class="stat-label">Errors</div>
|
|
236
|
+
</div>
|
|
237
|
+
</div>
|
|
238
|
+
|
|
239
|
+
${totalIssues === 0 ? '<div class="pass-badge">✓ No i18n layout issues detected! 🎉</div>' : ''}
|
|
240
|
+
|
|
241
|
+
${results
|
|
242
|
+
.map((result) => `
|
|
243
|
+
<div class="locale-section">
|
|
244
|
+
<div class="locale-header">
|
|
245
|
+
${result.locale.toUpperCase()} - ${result.issues.length} issues
|
|
246
|
+
</div>
|
|
247
|
+
${result.issues.length > 0
|
|
248
|
+
? `
|
|
249
|
+
<table>
|
|
250
|
+
<thead>
|
|
251
|
+
<tr>
|
|
252
|
+
<th>Severity</th>
|
|
253
|
+
<th>Element</th>
|
|
254
|
+
<th>Overflow</th>
|
|
255
|
+
<th>Text</th>
|
|
256
|
+
</tr>
|
|
257
|
+
</thead>
|
|
258
|
+
<tbody>
|
|
259
|
+
${result.issues
|
|
260
|
+
.map((issue) => `
|
|
261
|
+
<tr>
|
|
262
|
+
<td><span class="severity ${issue.severity}">${issue.severity}</span></td>
|
|
263
|
+
<td class="selector">${this.escapeHtml(issue.selector)}</td>
|
|
264
|
+
<td class="overflow">${issue.overflowDirection}: ${issue.scrollWidth - issue.offsetWidth}px</td>
|
|
265
|
+
<td class="text">${this.escapeHtml(issue.textContent)}</td>
|
|
266
|
+
</tr>
|
|
267
|
+
`)
|
|
268
|
+
.join('')}
|
|
269
|
+
</tbody>
|
|
270
|
+
</table>
|
|
271
|
+
`
|
|
272
|
+
: '<p style="padding: 1rem; background: #1e293b; border-radius: 0 0 12px 12px;">✓ No issues</p>'}
|
|
273
|
+
</div>
|
|
274
|
+
`)
|
|
275
|
+
.join('')}
|
|
276
|
+
</div>
|
|
277
|
+
</body>
|
|
278
|
+
</html>`;
|
|
279
|
+
}
|
|
280
|
+
formatSeverity(severity) {
|
|
281
|
+
switch (severity) {
|
|
282
|
+
case 'error':
|
|
283
|
+
return chalk.red('● ERROR');
|
|
284
|
+
case 'warning':
|
|
285
|
+
return chalk.yellow('● WARN');
|
|
286
|
+
case 'info':
|
|
287
|
+
return chalk.blue('● INFO');
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
formatOverflow(issue) {
|
|
291
|
+
const direction = issue.overflowDirection === 'horizontal' ? '↔' : issue.overflowDirection === 'vertical' ? '↕' : '↔↕';
|
|
292
|
+
const widthDiff = issue.scrollWidth - issue.offsetWidth;
|
|
293
|
+
const heightDiff = issue.scrollHeight - issue.offsetHeight;
|
|
294
|
+
if (issue.overflowDirection === 'horizontal') {
|
|
295
|
+
return chalk.magenta(`${direction} +${widthDiff}px`);
|
|
296
|
+
}
|
|
297
|
+
else if (issue.overflowDirection === 'vertical') {
|
|
298
|
+
return chalk.magenta(`${direction} +${heightDiff}px`);
|
|
299
|
+
}
|
|
300
|
+
return chalk.magenta(`${direction} +${widthDiff}x${heightDiff}px`);
|
|
301
|
+
}
|
|
302
|
+
colorize(value, type) {
|
|
303
|
+
if (value === 0)
|
|
304
|
+
return chalk.green(value.toString());
|
|
305
|
+
if (value <= 3)
|
|
306
|
+
return chalk.yellow(value.toString());
|
|
307
|
+
return chalk.red(value.toString());
|
|
308
|
+
}
|
|
309
|
+
truncate(str, maxLength) {
|
|
310
|
+
if (str.length <= maxLength)
|
|
311
|
+
return str;
|
|
312
|
+
return str.slice(0, maxLength - 3) + '...';
|
|
313
|
+
}
|
|
314
|
+
escapeHtml(str) {
|
|
315
|
+
return str
|
|
316
|
+
.replace(/&/g, '&')
|
|
317
|
+
.replace(/</g, '<')
|
|
318
|
+
.replace(/>/g, '>')
|
|
319
|
+
.replace(/"/g, '"');
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
//# sourceMappingURL=reporter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reporter.js","sourceRoot":"","sources":["../../src/reporters/reporter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pseudo-Locale Transformation
|
|
3
|
+
*
|
|
4
|
+
* Expands text content to simulate translations that are longer than English.
|
|
5
|
+
* German text is ~30% longer, some languages can be 40%+ longer.
|
|
6
|
+
*
|
|
7
|
+
* NOTE: This is a fallback when Lingo.dev integration is not used.
|
|
8
|
+
* The preferred approach is using Lingo.dev's built-in pseudo-locale.
|
|
9
|
+
*/
|
|
10
|
+
import type { Page as PlaywrightPage } from 'playwright';
|
|
11
|
+
export declare class PseudoLocaleTransform {
|
|
12
|
+
/**
|
|
13
|
+
* Apply pseudo-locale transformation using Playwright
|
|
14
|
+
* Cross-platform compatible (Mac, Windows, Linux)
|
|
15
|
+
*/
|
|
16
|
+
static applyPlaywright(page: PlaywrightPage, expansionFactor?: number): Promise<void>;
|
|
17
|
+
/**
|
|
18
|
+
* Transform a single string to pseudo-locale (for testing)
|
|
19
|
+
*/
|
|
20
|
+
static transform(text: string, expansionFactor?: number): string;
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=pseudo-locale.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pseudo-locale.d.ts","sourceRoot":"","sources":["../../src/transforms/pseudo-locale.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,IAAI,IAAI,cAAc,EAAE,MAAM,YAAY,CAAC;AAmEzD,qBAAa,qBAAqB;IAC9B;;;OAGG;WACU,eAAe,CACxB,IAAI,EAAE,cAAc,EACpB,eAAe,GAAE,MAAyC,GAC3D,OAAO,CAAC,IAAI,CAAC;IAoGhB;;OAEG;IACH,MAAM,CAAC,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,eAAe,GAAE,MAAa,GAAG,MAAM;CAmBzE"}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pseudo-Locale Transformation
|
|
3
|
+
*
|
|
4
|
+
* Expands text content to simulate translations that are longer than English.
|
|
5
|
+
* German text is ~30% longer, some languages can be 40%+ longer.
|
|
6
|
+
*
|
|
7
|
+
* NOTE: This is a fallback when Lingo.dev integration is not used.
|
|
8
|
+
* The preferred approach is using Lingo.dev's built-in pseudo-locale.
|
|
9
|
+
*/
|
|
10
|
+
import { DEFAULTS } from '../constants.js';
|
|
11
|
+
/**
|
|
12
|
+
* Character mappings for pseudo-localization
|
|
13
|
+
* Maps ASCII to accented equivalents for visual distinction
|
|
14
|
+
*/
|
|
15
|
+
const CHAR_MAP = {
|
|
16
|
+
a: 'à',
|
|
17
|
+
b: 'ƀ',
|
|
18
|
+
c: 'ç',
|
|
19
|
+
d: 'ð',
|
|
20
|
+
e: 'è',
|
|
21
|
+
f: 'ƒ',
|
|
22
|
+
g: 'ĝ',
|
|
23
|
+
h: 'ĥ',
|
|
24
|
+
i: 'ï',
|
|
25
|
+
j: 'ĵ',
|
|
26
|
+
k: 'ķ',
|
|
27
|
+
l: 'ĺ',
|
|
28
|
+
m: 'ɱ',
|
|
29
|
+
n: 'ñ',
|
|
30
|
+
o: 'ö',
|
|
31
|
+
p: 'þ',
|
|
32
|
+
q: 'ǫ',
|
|
33
|
+
r: 'ŕ',
|
|
34
|
+
s: 'š',
|
|
35
|
+
t: 'ţ',
|
|
36
|
+
u: 'ü',
|
|
37
|
+
v: 'ṿ',
|
|
38
|
+
w: 'ŵ',
|
|
39
|
+
x: 'ẋ',
|
|
40
|
+
y: 'ÿ',
|
|
41
|
+
z: 'ž',
|
|
42
|
+
A: 'À',
|
|
43
|
+
B: 'Ɓ',
|
|
44
|
+
C: 'Ç',
|
|
45
|
+
D: 'Ð',
|
|
46
|
+
E: 'È',
|
|
47
|
+
F: 'Ƒ',
|
|
48
|
+
G: 'Ĝ',
|
|
49
|
+
H: 'Ĥ',
|
|
50
|
+
I: 'Ï',
|
|
51
|
+
J: 'Ĵ',
|
|
52
|
+
K: 'Ķ',
|
|
53
|
+
L: 'Ĺ',
|
|
54
|
+
M: 'Ṃ',
|
|
55
|
+
N: 'Ñ',
|
|
56
|
+
O: 'Ö',
|
|
57
|
+
P: 'Þ',
|
|
58
|
+
Q: 'Ǫ',
|
|
59
|
+
R: 'Ŕ',
|
|
60
|
+
S: 'Š',
|
|
61
|
+
T: 'Ţ',
|
|
62
|
+
U: 'Ü',
|
|
63
|
+
V: 'Ṿ',
|
|
64
|
+
W: 'Ŵ',
|
|
65
|
+
X: 'Ẋ',
|
|
66
|
+
Y: 'Ÿ',
|
|
67
|
+
Z: 'Ž',
|
|
68
|
+
};
|
|
69
|
+
/**
|
|
70
|
+
* Padding characters added to expand text length
|
|
71
|
+
*/
|
|
72
|
+
const PADDING_CHARS = ['ẍ', 'ỳ', 'ẑ', 'ẅ'];
|
|
73
|
+
export class PseudoLocaleTransform {
|
|
74
|
+
/**
|
|
75
|
+
* Apply pseudo-locale transformation using Playwright
|
|
76
|
+
* Cross-platform compatible (Mac, Windows, Linux)
|
|
77
|
+
*/
|
|
78
|
+
static async applyPlaywright(page, expansionFactor = DEFAULTS.PSEUDO_EXPANSION_FACTOR) {
|
|
79
|
+
await page.evaluate(({ charMap, paddingChars, factor }) => {
|
|
80
|
+
function pseudoLocalize(text) {
|
|
81
|
+
if (!text || text.trim().length === 0)
|
|
82
|
+
return text;
|
|
83
|
+
if (text.includes('://') ||
|
|
84
|
+
text.includes('{') ||
|
|
85
|
+
text.includes('<') ||
|
|
86
|
+
/^[\d\s.,!?]+$/.test(text)) {
|
|
87
|
+
return text;
|
|
88
|
+
}
|
|
89
|
+
let result = '';
|
|
90
|
+
for (const char of text) {
|
|
91
|
+
result += charMap[char] || char;
|
|
92
|
+
}
|
|
93
|
+
const paddingLength = Math.ceil(text.length * factor);
|
|
94
|
+
if (paddingLength > 0) {
|
|
95
|
+
let padding = '';
|
|
96
|
+
for (let i = 0; i < paddingLength; i++) {
|
|
97
|
+
padding += paddingChars[i % paddingChars.length];
|
|
98
|
+
}
|
|
99
|
+
result = `[${result}${padding}]`;
|
|
100
|
+
}
|
|
101
|
+
return result;
|
|
102
|
+
}
|
|
103
|
+
function processTextNodes(node) {
|
|
104
|
+
if (node.nodeType === Node.TEXT_NODE) {
|
|
105
|
+
const text = node.textContent;
|
|
106
|
+
if (text && text.trim().length > 0) {
|
|
107
|
+
node.textContent = pseudoLocalize(text);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
else if (node.nodeType === Node.ELEMENT_NODE) {
|
|
111
|
+
const element = node;
|
|
112
|
+
const tagName = element.tagName.toLowerCase();
|
|
113
|
+
if (tagName === 'script' ||
|
|
114
|
+
tagName === 'style' ||
|
|
115
|
+
tagName === 'noscript' ||
|
|
116
|
+
tagName === 'svg' ||
|
|
117
|
+
tagName === 'textarea' ||
|
|
118
|
+
tagName === 'input') {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
for (const child of Array.from(node.childNodes)) {
|
|
122
|
+
processTextNodes(child);
|
|
123
|
+
}
|
|
124
|
+
if (element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement) {
|
|
125
|
+
if (element.placeholder) {
|
|
126
|
+
element.placeholder = pseudoLocalize(element.placeholder);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
const title = element.getAttribute('title');
|
|
130
|
+
if (title) {
|
|
131
|
+
element.setAttribute('title', pseudoLocalize(title));
|
|
132
|
+
}
|
|
133
|
+
const ariaLabel = element.getAttribute('aria-label');
|
|
134
|
+
if (ariaLabel) {
|
|
135
|
+
element.setAttribute('aria-label', pseudoLocalize(ariaLabel));
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
processTextNodes(document.body);
|
|
140
|
+
// Add visual indicator
|
|
141
|
+
const indicator = document.createElement('div');
|
|
142
|
+
indicator.id = 'lingo-guardian-pseudo-indicator';
|
|
143
|
+
indicator.style.cssText = `
|
|
144
|
+
position: fixed;
|
|
145
|
+
top: 4px;
|
|
146
|
+
right: 4px;
|
|
147
|
+
background: #8B5CF6;
|
|
148
|
+
color: white;
|
|
149
|
+
padding: 4px 8px;
|
|
150
|
+
border-radius: 4px;
|
|
151
|
+
font-size: 10px;
|
|
152
|
+
font-family: monospace;
|
|
153
|
+
z-index: 99999;
|
|
154
|
+
opacity: 0.9;
|
|
155
|
+
`;
|
|
156
|
+
indicator.textContent = '🔤 PSEUDO';
|
|
157
|
+
document.body.appendChild(indicator);
|
|
158
|
+
}, { charMap: CHAR_MAP, paddingChars: PADDING_CHARS, factor: expansionFactor });
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Transform a single string to pseudo-locale (for testing)
|
|
162
|
+
*/
|
|
163
|
+
static transform(text, expansionFactor = 0.35) {
|
|
164
|
+
if (!text || text.trim().length === 0)
|
|
165
|
+
return text;
|
|
166
|
+
let result = '';
|
|
167
|
+
for (const char of text) {
|
|
168
|
+
result += CHAR_MAP[char] || char;
|
|
169
|
+
}
|
|
170
|
+
const paddingLength = Math.ceil(text.length * expansionFactor);
|
|
171
|
+
if (paddingLength > 0) {
|
|
172
|
+
let padding = '';
|
|
173
|
+
for (let i = 0; i < paddingLength; i++) {
|
|
174
|
+
padding += PADDING_CHARS[i % PADDING_CHARS.length];
|
|
175
|
+
}
|
|
176
|
+
result = `[${result}${padding}]`;
|
|
177
|
+
}
|
|
178
|
+
return result;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
//# sourceMappingURL=pseudo-locale.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pseudo-locale.js","sourceRoot":"","sources":["../../src/transforms/pseudo-locale.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RTL (Right-to-Left) Transformation
|
|
3
|
+
*
|
|
4
|
+
* Applies RTL layout transformation for testing Arabic, Hebrew,
|
|
5
|
+
* Farsi, and other RTL languages.
|
|
6
|
+
*
|
|
7
|
+
* Detects layout breaks from improper CSS that doesn't support
|
|
8
|
+
* bidirectional text properly.
|
|
9
|
+
*/
|
|
10
|
+
import type { Page as PlaywrightPage } from 'playwright';
|
|
11
|
+
export declare class RTLTransform {
|
|
12
|
+
/**
|
|
13
|
+
* Check if a locale is RTL
|
|
14
|
+
*/
|
|
15
|
+
static isRTL(locale: string): boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Apply RTL transformation using Playwright
|
|
18
|
+
* Cross-platform compatible (Mac, Windows, Linux)
|
|
19
|
+
*/
|
|
20
|
+
static applyPlaywright(page: PlaywrightPage, locale?: string): Promise<void>;
|
|
21
|
+
/**
|
|
22
|
+
* Get sample RTL text for testing
|
|
23
|
+
*/
|
|
24
|
+
static getSampleText(locale?: string): string;
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=rtl.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rtl.d.ts","sourceRoot":"","sources":["../../src/transforms/rtl.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,IAAI,IAAI,cAAc,EAAE,MAAM,YAAY,CAAC;AAOzD,qBAAa,YAAY;IACrB;;OAEG;IACH,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO;IAIrC;;;OAGG;WACU,eAAe,CAAC,IAAI,EAAE,cAAc,EAAE,MAAM,GAAE,MAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IAwFxF;;OAEG;IACH,MAAM,CAAC,aAAa,CAAC,MAAM,GAAE,MAAa,GAAG,MAAM;CAStD"}
|