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,261 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auditor - Core engine for detecting i18n layout issues
|
|
3
|
+
*
|
|
4
|
+
* Uses Playwright (cross-platform: Mac, Windows, Linux) to analyze pages for:
|
|
5
|
+
* - Text overflow (scrollWidth > offsetWidth)
|
|
6
|
+
* - Text truncation (text-overflow: ellipsis detection)
|
|
7
|
+
* - Layout breaks in RTL mode
|
|
8
|
+
*/
|
|
9
|
+
import { chromium } from 'playwright';
|
|
10
|
+
import { DEFAULTS } from '../constants.js';
|
|
11
|
+
import { PseudoLocaleTransform } from '../transforms/pseudo-locale.js';
|
|
12
|
+
import { RTLTransform } from '../transforms/rtl.js';
|
|
13
|
+
/**
|
|
14
|
+
* Core Auditor class for detecting i18n layout issues
|
|
15
|
+
*
|
|
16
|
+
* Uses Playwright for cross-platform browser automation (Mac, Windows, Linux)
|
|
17
|
+
*/
|
|
18
|
+
export class Auditor {
|
|
19
|
+
browser = null;
|
|
20
|
+
context = null;
|
|
21
|
+
options;
|
|
22
|
+
constructor(options = {}) {
|
|
23
|
+
this.options = {
|
|
24
|
+
width: options.width ?? DEFAULTS.VIEWPORT_WIDTH,
|
|
25
|
+
height: options.height ?? DEFAULTS.VIEWPORT_HEIGHT,
|
|
26
|
+
timeout: options.timeout ?? DEFAULTS.TIMEOUT,
|
|
27
|
+
screenshots: options.screenshots ?? false,
|
|
28
|
+
outputDir: options.outputDir ?? './lingo-guardian-reports',
|
|
29
|
+
verbose: options.verbose ?? false,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Initialize the browser instance
|
|
34
|
+
* Playwright handles cross-platform browser binaries automatically
|
|
35
|
+
*/
|
|
36
|
+
async init() {
|
|
37
|
+
if (this.browser)
|
|
38
|
+
return;
|
|
39
|
+
this.log('Launching Playwright browser (Chromium)...');
|
|
40
|
+
// Playwright handles cross-platform automatically
|
|
41
|
+
this.browser = await chromium.launch({
|
|
42
|
+
headless: true,
|
|
43
|
+
// These args work across Mac, Windows, and Linux
|
|
44
|
+
args: [
|
|
45
|
+
'--no-sandbox',
|
|
46
|
+
'--disable-dev-shm-usage',
|
|
47
|
+
],
|
|
48
|
+
});
|
|
49
|
+
this.context = await this.browser.newContext({
|
|
50
|
+
viewport: {
|
|
51
|
+
width: this.options.width,
|
|
52
|
+
height: this.options.height,
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Close the browser instance
|
|
58
|
+
*/
|
|
59
|
+
async close() {
|
|
60
|
+
if (this.context) {
|
|
61
|
+
await this.context.close();
|
|
62
|
+
this.context = null;
|
|
63
|
+
}
|
|
64
|
+
if (this.browser) {
|
|
65
|
+
await this.browser.close();
|
|
66
|
+
this.browser = null;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Audit a URL for i18n layout issues
|
|
71
|
+
*/
|
|
72
|
+
async audit(url, locale = 'en') {
|
|
73
|
+
const startTime = Date.now();
|
|
74
|
+
if (!this.browser || !this.context) {
|
|
75
|
+
await this.init();
|
|
76
|
+
}
|
|
77
|
+
const page = await this.context.newPage();
|
|
78
|
+
try {
|
|
79
|
+
this.log(`Navigating to ${url}...`);
|
|
80
|
+
await page.goto(url, {
|
|
81
|
+
waitUntil: 'networkidle',
|
|
82
|
+
timeout: this.options.timeout,
|
|
83
|
+
});
|
|
84
|
+
// Apply locale transformations (fallback when Lingo.dev not used)
|
|
85
|
+
await this.applyLocaleTransform(page, locale);
|
|
86
|
+
// Wait for any reflow after transformations
|
|
87
|
+
await page.waitForTimeout(500);
|
|
88
|
+
// Detect overflow issues
|
|
89
|
+
this.log(`Scanning for overflow issues (locale: ${locale})...`);
|
|
90
|
+
const issues = await this.detectOverflows(page, locale);
|
|
91
|
+
const duration = Date.now() - startTime;
|
|
92
|
+
return {
|
|
93
|
+
url,
|
|
94
|
+
locale,
|
|
95
|
+
timestamp: new Date().toISOString(),
|
|
96
|
+
issueCount: issues.length,
|
|
97
|
+
issues,
|
|
98
|
+
duration,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
finally {
|
|
102
|
+
await page.close();
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Apply locale-specific transformations to the page
|
|
107
|
+
* These are fallback transforms when Lingo.dev integration is not used
|
|
108
|
+
*/
|
|
109
|
+
async applyLocaleTransform(page, locale) {
|
|
110
|
+
switch (locale.toLowerCase()) {
|
|
111
|
+
case 'pseudo':
|
|
112
|
+
this.log('Applying pseudo-locale transformation...');
|
|
113
|
+
await PseudoLocaleTransform.applyPlaywright(page);
|
|
114
|
+
break;
|
|
115
|
+
case 'ar':
|
|
116
|
+
case 'he':
|
|
117
|
+
case 'fa':
|
|
118
|
+
case 'ur':
|
|
119
|
+
this.log(`Applying RTL transformation for ${locale}...`);
|
|
120
|
+
await RTLTransform.applyPlaywright(page, locale);
|
|
121
|
+
break;
|
|
122
|
+
case 'de':
|
|
123
|
+
// German has ~30% longer text - simulate expansion
|
|
124
|
+
this.log('Applying German expansion simulation...');
|
|
125
|
+
await PseudoLocaleTransform.applyPlaywright(page, 0.3);
|
|
126
|
+
break;
|
|
127
|
+
case 'ja':
|
|
128
|
+
case 'zh':
|
|
129
|
+
case 'ko':
|
|
130
|
+
// CJK languages - check for font loading and character rendering
|
|
131
|
+
this.log(`Checking CJK rendering for ${locale}...`);
|
|
132
|
+
break;
|
|
133
|
+
default:
|
|
134
|
+
// English or other - no transformation
|
|
135
|
+
this.log(`No transformation for locale: ${locale}`);
|
|
136
|
+
break;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Detect elements with overflow issues
|
|
141
|
+
* Core detection logic: element.scrollWidth > element.offsetWidth
|
|
142
|
+
*/
|
|
143
|
+
async detectOverflows(page, locale) {
|
|
144
|
+
const excludedSelectorsList = [...DEFAULTS.EXCLUDED_SELECTORS];
|
|
145
|
+
const issues = await page.evaluate((excludedSelectors) => {
|
|
146
|
+
const results = [];
|
|
147
|
+
// Get all text-containing elements
|
|
148
|
+
const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_ELEMENT, {
|
|
149
|
+
acceptNode: (node) => {
|
|
150
|
+
const el = node;
|
|
151
|
+
const tagName = el.tagName.toLowerCase();
|
|
152
|
+
if (excludedSelectors.includes(tagName)) {
|
|
153
|
+
return NodeFilter.FILTER_REJECT;
|
|
154
|
+
}
|
|
155
|
+
return NodeFilter.FILTER_ACCEPT;
|
|
156
|
+
},
|
|
157
|
+
});
|
|
158
|
+
const elements = [];
|
|
159
|
+
let currentNode;
|
|
160
|
+
while ((currentNode = walker.nextNode())) {
|
|
161
|
+
elements.push(currentNode);
|
|
162
|
+
}
|
|
163
|
+
for (const el of elements) {
|
|
164
|
+
const htmlEl = el;
|
|
165
|
+
const rect = htmlEl.getBoundingClientRect();
|
|
166
|
+
if (rect.width === 0 || rect.height === 0)
|
|
167
|
+
continue;
|
|
168
|
+
const scrollWidth = htmlEl.scrollWidth;
|
|
169
|
+
const offsetWidth = htmlEl.offsetWidth;
|
|
170
|
+
const scrollHeight = htmlEl.scrollHeight;
|
|
171
|
+
const offsetHeight = htmlEl.offsetHeight;
|
|
172
|
+
// The "Red Glow" Detection - core logic
|
|
173
|
+
const hasHorizontalOverflow = scrollWidth > offsetWidth + 1;
|
|
174
|
+
const hasVerticalOverflow = scrollHeight > offsetHeight + 1;
|
|
175
|
+
if (hasHorizontalOverflow || hasVerticalOverflow) {
|
|
176
|
+
const selector = generateSelector(el);
|
|
177
|
+
const textContent = htmlEl.textContent?.trim().slice(0, 50) || '';
|
|
178
|
+
let overflowDirection;
|
|
179
|
+
if (hasHorizontalOverflow && hasVerticalOverflow) {
|
|
180
|
+
overflowDirection = 'both';
|
|
181
|
+
}
|
|
182
|
+
else if (hasHorizontalOverflow) {
|
|
183
|
+
overflowDirection = 'horizontal';
|
|
184
|
+
}
|
|
185
|
+
else {
|
|
186
|
+
overflowDirection = 'vertical';
|
|
187
|
+
}
|
|
188
|
+
const overflowAmount = Math.max(scrollWidth - offsetWidth, scrollHeight - offsetHeight);
|
|
189
|
+
let severity;
|
|
190
|
+
if (overflowAmount > 50) {
|
|
191
|
+
severity = 'error';
|
|
192
|
+
}
|
|
193
|
+
else if (overflowAmount > 20) {
|
|
194
|
+
severity = 'warning';
|
|
195
|
+
}
|
|
196
|
+
else {
|
|
197
|
+
severity = 'info';
|
|
198
|
+
}
|
|
199
|
+
results.push({
|
|
200
|
+
selector,
|
|
201
|
+
tagName: el.tagName.toLowerCase(),
|
|
202
|
+
textContent,
|
|
203
|
+
offsetWidth,
|
|
204
|
+
scrollWidth,
|
|
205
|
+
offsetHeight,
|
|
206
|
+
scrollHeight,
|
|
207
|
+
overflowDirection,
|
|
208
|
+
severity,
|
|
209
|
+
boundingRect: {
|
|
210
|
+
x: rect.x,
|
|
211
|
+
y: rect.y,
|
|
212
|
+
width: rect.width,
|
|
213
|
+
height: rect.height,
|
|
214
|
+
},
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
function generateSelector(el) {
|
|
219
|
+
if (el.id) {
|
|
220
|
+
return `#${el.id}`;
|
|
221
|
+
}
|
|
222
|
+
const path = [];
|
|
223
|
+
let current = el;
|
|
224
|
+
while (current && current !== document.body) {
|
|
225
|
+
let selector = current.tagName.toLowerCase();
|
|
226
|
+
if (current.className && typeof current.className === 'string') {
|
|
227
|
+
const classes = current.className.trim().split(/\s+/).slice(0, 2);
|
|
228
|
+
if (classes.length > 0 && classes[0]) {
|
|
229
|
+
selector += '.' + classes.join('.');
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
const parent = current.parentElement;
|
|
233
|
+
if (parent) {
|
|
234
|
+
const siblings = Array.from(parent.children).filter((c) => c.tagName === current.tagName);
|
|
235
|
+
if (siblings.length > 1) {
|
|
236
|
+
const index = siblings.indexOf(current) + 1;
|
|
237
|
+
selector += `:nth-child(${index})`;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
path.unshift(selector);
|
|
241
|
+
current = current.parentElement;
|
|
242
|
+
}
|
|
243
|
+
return path.join(' > ');
|
|
244
|
+
}
|
|
245
|
+
return results;
|
|
246
|
+
}, excludedSelectorsList);
|
|
247
|
+
return issues.map((issue) => ({
|
|
248
|
+
...issue,
|
|
249
|
+
locale,
|
|
250
|
+
}));
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Log message if verbose mode is enabled
|
|
254
|
+
*/
|
|
255
|
+
log(message) {
|
|
256
|
+
if (this.options.verbose) {
|
|
257
|
+
console.log(` [Auditor] ${message}`);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
//# sourceMappingURL=auditor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auditor.js","sourceRoot":"","sources":["../../src/core/auditor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lingo.dev Integration Module
|
|
3
|
+
*
|
|
4
|
+
* This module integrates with the official Lingo.dev CLI and SDK.
|
|
5
|
+
* It runs `npx lingo.dev run` to generate translations using their engine.
|
|
6
|
+
*
|
|
7
|
+
* This is the KEY integration that makes Lingo-Guardian work with Lingo.dev
|
|
8
|
+
* rather than being a standalone tool.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Lingo.dev configuration file structure
|
|
12
|
+
*/
|
|
13
|
+
export interface LingoConfig {
|
|
14
|
+
$schema?: string;
|
|
15
|
+
version: string;
|
|
16
|
+
locale: {
|
|
17
|
+
source: string;
|
|
18
|
+
targets: string[];
|
|
19
|
+
};
|
|
20
|
+
buckets: Record<string, {
|
|
21
|
+
include: string[];
|
|
22
|
+
exclude?: string[];
|
|
23
|
+
}>;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Result of running Lingo.dev CLI
|
|
27
|
+
*/
|
|
28
|
+
export interface LingoRunResult {
|
|
29
|
+
success: boolean;
|
|
30
|
+
output: string;
|
|
31
|
+
error?: string;
|
|
32
|
+
localesGenerated: string[];
|
|
33
|
+
filesModified: string[];
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Lingo.dev Integration - Orchestrates the Lingo.dev CLI
|
|
37
|
+
*
|
|
38
|
+
* Usage:
|
|
39
|
+
* const lingo = new LingoIntegration('/path/to/project');
|
|
40
|
+
* await lingo.detectConfig();
|
|
41
|
+
* await lingo.runTranslation({ locale: 'pseudo' });
|
|
42
|
+
*/
|
|
43
|
+
export declare class LingoIntegration {
|
|
44
|
+
private projectPath;
|
|
45
|
+
private configPath;
|
|
46
|
+
private config;
|
|
47
|
+
private verbose;
|
|
48
|
+
constructor(projectPath: string, verbose?: boolean);
|
|
49
|
+
/**
|
|
50
|
+
* Detect Lingo.dev configuration in the project
|
|
51
|
+
* Looks for: i18n.json, lingo.config.js, lingo.config.json
|
|
52
|
+
*/
|
|
53
|
+
detectConfig(): Promise<boolean>;
|
|
54
|
+
/**
|
|
55
|
+
* Get the detected configuration
|
|
56
|
+
*/
|
|
57
|
+
getConfig(): LingoConfig | null;
|
|
58
|
+
/**
|
|
59
|
+
* Get available target locales from config
|
|
60
|
+
*/
|
|
61
|
+
getTargetLocales(): string[];
|
|
62
|
+
/**
|
|
63
|
+
* Get source locale from config
|
|
64
|
+
*/
|
|
65
|
+
getSourceLocale(): string;
|
|
66
|
+
/**
|
|
67
|
+
* Initialize a new Lingo.dev project if config doesn't exist
|
|
68
|
+
*/
|
|
69
|
+
initProject(): Promise<boolean>;
|
|
70
|
+
/**
|
|
71
|
+
* Run Lingo.dev CLI translation
|
|
72
|
+
*
|
|
73
|
+
* This is the KEY integration - we run their CLI to generate translations
|
|
74
|
+
*
|
|
75
|
+
* @param options.locale - Optional specific locale to target
|
|
76
|
+
* @param options.force - Force retranslation even if cached
|
|
77
|
+
*/
|
|
78
|
+
runTranslation(options?: {
|
|
79
|
+
locale?: string;
|
|
80
|
+
force?: boolean;
|
|
81
|
+
}): Promise<LingoRunResult>;
|
|
82
|
+
/**
|
|
83
|
+
* Add pseudo-locale to the Lingo.dev config if not present
|
|
84
|
+
* This enables testing with expanded text
|
|
85
|
+
*/
|
|
86
|
+
enablePseudoLocale(): Promise<boolean>;
|
|
87
|
+
/**
|
|
88
|
+
* Check if Lingo.dev CLI is available
|
|
89
|
+
*/
|
|
90
|
+
isLingoCliAvailable(): Promise<boolean>;
|
|
91
|
+
/**
|
|
92
|
+
* Get the locale query parameter for SDK integration
|
|
93
|
+
* The Lingo.dev SDK can switch locales via URL param
|
|
94
|
+
*/
|
|
95
|
+
getLocaleQueryParam(locale: string): string;
|
|
96
|
+
/**
|
|
97
|
+
* Parse generated locales from CLI output
|
|
98
|
+
*/
|
|
99
|
+
private parseLocalesFromOutput;
|
|
100
|
+
/**
|
|
101
|
+
* Parse modified files from CLI output
|
|
102
|
+
*/
|
|
103
|
+
private parseFilesFromOutput;
|
|
104
|
+
/**
|
|
105
|
+
* Log message if verbose mode is enabled
|
|
106
|
+
*/
|
|
107
|
+
private log;
|
|
108
|
+
}
|
|
109
|
+
//# sourceMappingURL=lingo-integration.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lingo-integration.d.ts","sourceRoot":"","sources":["../../src/core/lingo-integration.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AASH;;GAEG;AACH,MAAM,WAAW,WAAW;IACxB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE;QACJ,MAAM,EAAE,MAAM,CAAC;QACf,OAAO,EAAE,MAAM,EAAE,CAAC;KACrB,CAAC;IACF,OAAO,EAAE,MAAM,CACX,MAAM,EACN;QACI,OAAO,EAAE,MAAM,EAAE,CAAC;QAClB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;KACtB,CACJ,CAAC;CACL;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,aAAa,EAAE,MAAM,EAAE,CAAC;CAC3B;AAED;;;;;;;GAOG;AACH,qBAAa,gBAAgB;IACzB,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,UAAU,CAAuB;IACzC,OAAO,CAAC,MAAM,CAA4B;IAC1C,OAAO,CAAC,OAAO,CAAU;gBAEb,WAAW,EAAE,MAAM,EAAE,OAAO,UAAQ;IAKhD;;;OAGG;IACG,YAAY,IAAI,OAAO,CAAC,OAAO,CAAC;IA0BtC;;OAEG;IACH,SAAS,IAAI,WAAW,GAAG,IAAI;IAI/B;;OAEG;IACH,gBAAgB,IAAI,MAAM,EAAE;IAK5B;;OAEG;IACH,eAAe,IAAI,MAAM;IAIzB;;OAEG;IACG,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;IAqBrC;;;;;;;OAOG;IACG,cAAc,CAAC,OAAO,GAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAO,GAAG,OAAO,CAAC,cAAc,CAAC;IA6DjG;;;OAGG;IACG,kBAAkB,IAAI,OAAO,CAAC,OAAO,CAAC;IA0B5C;;OAEG;IACG,mBAAmB,IAAI,OAAO,CAAC,OAAO,CAAC;IAS7C;;;OAGG;IACH,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM;IAI3C;;OAEG;IACH,OAAO,CAAC,sBAAsB;IAY9B;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAY5B;;OAEG;IACH,OAAO,CAAC,GAAG;CAKd"}
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lingo.dev Integration Module
|
|
3
|
+
*
|
|
4
|
+
* This module integrates with the official Lingo.dev CLI and SDK.
|
|
5
|
+
* It runs `npx lingo.dev run` to generate translations using their engine.
|
|
6
|
+
*
|
|
7
|
+
* This is the KEY integration that makes Lingo-Guardian work with Lingo.dev
|
|
8
|
+
* rather than being a standalone tool.
|
|
9
|
+
*/
|
|
10
|
+
import { spawn, exec } from 'child_process';
|
|
11
|
+
import { promisify } from 'util';
|
|
12
|
+
import fs from 'fs/promises';
|
|
13
|
+
import path from 'path';
|
|
14
|
+
const execAsync = promisify(exec);
|
|
15
|
+
/**
|
|
16
|
+
* Lingo.dev Integration - Orchestrates the Lingo.dev CLI
|
|
17
|
+
*
|
|
18
|
+
* Usage:
|
|
19
|
+
* const lingo = new LingoIntegration('/path/to/project');
|
|
20
|
+
* await lingo.detectConfig();
|
|
21
|
+
* await lingo.runTranslation({ locale: 'pseudo' });
|
|
22
|
+
*/
|
|
23
|
+
export class LingoIntegration {
|
|
24
|
+
projectPath;
|
|
25
|
+
configPath = null;
|
|
26
|
+
config = null;
|
|
27
|
+
verbose;
|
|
28
|
+
constructor(projectPath, verbose = false) {
|
|
29
|
+
this.projectPath = projectPath;
|
|
30
|
+
this.verbose = verbose;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Detect Lingo.dev configuration in the project
|
|
34
|
+
* Looks for: i18n.json, lingo.config.js, lingo.config.json
|
|
35
|
+
*/
|
|
36
|
+
async detectConfig() {
|
|
37
|
+
const configFiles = ['i18n.json', 'lingo.config.json', 'lingo.config.js'];
|
|
38
|
+
for (const configFile of configFiles) {
|
|
39
|
+
const fullPath = path.join(this.projectPath, configFile);
|
|
40
|
+
try {
|
|
41
|
+
await fs.access(fullPath);
|
|
42
|
+
this.configPath = fullPath;
|
|
43
|
+
this.log(`Found Lingo.dev config: ${configFile}`);
|
|
44
|
+
// Read the config
|
|
45
|
+
if (configFile.endsWith('.json')) {
|
|
46
|
+
const content = await fs.readFile(fullPath, 'utf-8');
|
|
47
|
+
this.config = JSON.parse(content);
|
|
48
|
+
}
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
// File doesn't exist, try next
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
this.log('No Lingo.dev config found');
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Get the detected configuration
|
|
60
|
+
*/
|
|
61
|
+
getConfig() {
|
|
62
|
+
return this.config;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Get available target locales from config
|
|
66
|
+
*/
|
|
67
|
+
getTargetLocales() {
|
|
68
|
+
if (!this.config)
|
|
69
|
+
return [];
|
|
70
|
+
return this.config.locale.targets;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Get source locale from config
|
|
74
|
+
*/
|
|
75
|
+
getSourceLocale() {
|
|
76
|
+
return this.config?.locale.source || 'en';
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Initialize a new Lingo.dev project if config doesn't exist
|
|
80
|
+
*/
|
|
81
|
+
async initProject() {
|
|
82
|
+
this.log('Initializing Lingo.dev project...');
|
|
83
|
+
try {
|
|
84
|
+
const { stdout, stderr } = await execAsync('npx lingo.dev@latest init --yes', {
|
|
85
|
+
cwd: this.projectPath,
|
|
86
|
+
timeout: 60000,
|
|
87
|
+
});
|
|
88
|
+
this.log(stdout);
|
|
89
|
+
if (stderr)
|
|
90
|
+
this.log(`Warning: ${stderr}`);
|
|
91
|
+
// Re-detect config after init
|
|
92
|
+
return this.detectConfig();
|
|
93
|
+
}
|
|
94
|
+
catch (error) {
|
|
95
|
+
const err = error;
|
|
96
|
+
this.log(`Failed to init: ${err.message}`);
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Run Lingo.dev CLI translation
|
|
102
|
+
*
|
|
103
|
+
* This is the KEY integration - we run their CLI to generate translations
|
|
104
|
+
*
|
|
105
|
+
* @param options.locale - Optional specific locale to target
|
|
106
|
+
* @param options.force - Force retranslation even if cached
|
|
107
|
+
*/
|
|
108
|
+
async runTranslation(options = {}) {
|
|
109
|
+
const args = ['lingo.dev@latest', 'run'];
|
|
110
|
+
if (options.locale) {
|
|
111
|
+
args.push('--locale', options.locale);
|
|
112
|
+
}
|
|
113
|
+
if (options.force) {
|
|
114
|
+
args.push('--force');
|
|
115
|
+
}
|
|
116
|
+
this.log(`Running: npx ${args.join(' ')}`);
|
|
117
|
+
return new Promise((resolve) => {
|
|
118
|
+
const proc = spawn('npx', args, {
|
|
119
|
+
cwd: this.projectPath,
|
|
120
|
+
shell: true,
|
|
121
|
+
env: { ...process.env },
|
|
122
|
+
});
|
|
123
|
+
let output = '';
|
|
124
|
+
let errorOutput = '';
|
|
125
|
+
proc.stdout.on('data', (data) => {
|
|
126
|
+
const text = data.toString();
|
|
127
|
+
output += text;
|
|
128
|
+
if (this.verbose)
|
|
129
|
+
console.log(text);
|
|
130
|
+
});
|
|
131
|
+
proc.stderr.on('data', (data) => {
|
|
132
|
+
const text = data.toString();
|
|
133
|
+
errorOutput += text;
|
|
134
|
+
if (this.verbose)
|
|
135
|
+
console.error(text);
|
|
136
|
+
});
|
|
137
|
+
proc.on('close', (code) => {
|
|
138
|
+
const success = code === 0;
|
|
139
|
+
const localesGenerated = this.parseLocalesFromOutput(output);
|
|
140
|
+
const filesModified = this.parseFilesFromOutput(output);
|
|
141
|
+
resolve({
|
|
142
|
+
success,
|
|
143
|
+
output,
|
|
144
|
+
error: errorOutput || undefined,
|
|
145
|
+
localesGenerated,
|
|
146
|
+
filesModified,
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
proc.on('error', (error) => {
|
|
150
|
+
resolve({
|
|
151
|
+
success: false,
|
|
152
|
+
output,
|
|
153
|
+
error: error.message,
|
|
154
|
+
localesGenerated: [],
|
|
155
|
+
filesModified: [],
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Add pseudo-locale to the Lingo.dev config if not present
|
|
162
|
+
* This enables testing with expanded text
|
|
163
|
+
*/
|
|
164
|
+
async enablePseudoLocale() {
|
|
165
|
+
if (!this.config || !this.configPath) {
|
|
166
|
+
this.log('No config to modify');
|
|
167
|
+
return false;
|
|
168
|
+
}
|
|
169
|
+
// Check if pseudo is already a target
|
|
170
|
+
if (this.config.locale.targets.includes('pseudo')) {
|
|
171
|
+
this.log('Pseudo-locale already enabled');
|
|
172
|
+
return true;
|
|
173
|
+
}
|
|
174
|
+
// Add pseudo to targets
|
|
175
|
+
this.config.locale.targets.push('pseudo');
|
|
176
|
+
try {
|
|
177
|
+
await fs.writeFile(this.configPath, JSON.stringify(this.config, null, 2), 'utf-8');
|
|
178
|
+
this.log('Added pseudo-locale to config');
|
|
179
|
+
return true;
|
|
180
|
+
}
|
|
181
|
+
catch (error) {
|
|
182
|
+
const err = error;
|
|
183
|
+
this.log(`Failed to update config: ${err.message}`);
|
|
184
|
+
return false;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Check if Lingo.dev CLI is available
|
|
189
|
+
*/
|
|
190
|
+
async isLingoCliAvailable() {
|
|
191
|
+
try {
|
|
192
|
+
await execAsync('npx lingo.dev@latest --version', { timeout: 10000 });
|
|
193
|
+
return true;
|
|
194
|
+
}
|
|
195
|
+
catch {
|
|
196
|
+
return false;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Get the locale query parameter for SDK integration
|
|
201
|
+
* The Lingo.dev SDK can switch locales via URL param
|
|
202
|
+
*/
|
|
203
|
+
getLocaleQueryParam(locale) {
|
|
204
|
+
return `?lang=${locale}`;
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Parse generated locales from CLI output
|
|
208
|
+
*/
|
|
209
|
+
parseLocalesFromOutput(output) {
|
|
210
|
+
const locales = [];
|
|
211
|
+
const matches = output.match(/Translating to (\w+)/g);
|
|
212
|
+
if (matches) {
|
|
213
|
+
for (const match of matches) {
|
|
214
|
+
const locale = match.replace('Translating to ', '');
|
|
215
|
+
locales.push(locale);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
return locales;
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Parse modified files from CLI output
|
|
222
|
+
*/
|
|
223
|
+
parseFilesFromOutput(output) {
|
|
224
|
+
const files = [];
|
|
225
|
+
const matches = output.match(/Writing to ([^\s]+)/g);
|
|
226
|
+
if (matches) {
|
|
227
|
+
for (const match of matches) {
|
|
228
|
+
const file = match.replace('Writing to ', '');
|
|
229
|
+
files.push(file);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
return files;
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Log message if verbose mode is enabled
|
|
236
|
+
*/
|
|
237
|
+
log(message) {
|
|
238
|
+
if (this.verbose) {
|
|
239
|
+
console.log(` [Lingo.dev] ${message}`);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
//# sourceMappingURL=lingo-integration.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lingo-integration.js","sourceRoot":"","sources":["../../src/core/lingo-integration.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lingo-Guardian CLI - Public API
|
|
3
|
+
* Export main functionality for programmatic usage
|
|
4
|
+
*/
|
|
5
|
+
export { Auditor } from './core/auditor.js';
|
|
6
|
+
export { LingoIntegration } from './core/lingo-integration.js';
|
|
7
|
+
export { PseudoLocaleTransform } from './transforms/pseudo-locale.js';
|
|
8
|
+
export { RTLTransform } from './transforms/rtl.js';
|
|
9
|
+
export { Reporter } from './reporters/reporter.js';
|
|
10
|
+
export type { OverflowIssue, AuditResult, LintOptions, Severity, } from './constants.js';
|
|
11
|
+
export { VERSION, DEFAULTS } from './constants.js';
|
|
12
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAC5C,OAAO,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAC/D,OAAO,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AACtE,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AAEnD,YAAY,EACR,aAAa,EACb,WAAW,EACX,WAAW,EACX,QAAQ,GACX,MAAM,gBAAgB,CAAC;AAExB,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lingo-Guardian CLI - Public API
|
|
3
|
+
* Export main functionality for programmatic usage
|
|
4
|
+
*/
|
|
5
|
+
export { Auditor } from './core/auditor.js';
|
|
6
|
+
export { LingoIntegration } from './core/lingo-integration.js';
|
|
7
|
+
export { PseudoLocaleTransform } from './transforms/pseudo-locale.js';
|
|
8
|
+
export { RTLTransform } from './transforms/rtl.js';
|
|
9
|
+
export { Reporter } from './reporters/reporter.js';
|
|
10
|
+
export { VERSION, DEFAULTS } from './constants.js';
|
|
11
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAC5C,OAAO,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAC/D,OAAO,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AACtE,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AASnD,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC"}
|