@questlang/mcp-server 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/LICENSE +21 -0
- package/README.md +88 -0
- package/SKILL.md +50 -0
- package/dist/client.d.ts +31 -0
- package/dist/client.js +120 -0
- package/dist/client.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +105 -0
- package/dist/index.js.map +1 -0
- package/dist/init.d.ts +1 -0
- package/dist/init.js +84 -0
- package/dist/init.js.map +1 -0
- package/dist/parsers/android-xml.d.ts +2 -0
- package/dist/parsers/android-xml.js +41 -0
- package/dist/parsers/android-xml.js.map +1 -0
- package/dist/parsers/csv.d.ts +2 -0
- package/dist/parsers/csv.js +55 -0
- package/dist/parsers/csv.js.map +1 -0
- package/dist/parsers/index.d.ts +5 -0
- package/dist/parsers/index.js +55 -0
- package/dist/parsers/index.js.map +1 -0
- package/dist/parsers/ios-strings.d.ts +2 -0
- package/dist/parsers/ios-strings.js +42 -0
- package/dist/parsers/ios-strings.js.map +1 -0
- package/dist/parsers/json.d.ts +2 -0
- package/dist/parsers/json.js +41 -0
- package/dist/parsers/json.js.map +1 -0
- package/dist/parsers/po.d.ts +2 -0
- package/dist/parsers/po.js +77 -0
- package/dist/parsers/po.js.map +1 -0
- package/dist/parsers/xliff.d.ts +2 -0
- package/dist/parsers/xliff.js +48 -0
- package/dist/parsers/xliff.js.map +1 -0
- package/dist/tools/balance.d.ts +4 -0
- package/dist/tools/balance.js +4 -0
- package/dist/tools/balance.js.map +1 -0
- package/dist/tools/estimate.d.ts +11 -0
- package/dist/tools/estimate.js +43 -0
- package/dist/tools/estimate.js.map +1 -0
- package/dist/tools/glossary.d.ts +7 -0
- package/dist/tools/glossary.js +28 -0
- package/dist/tools/glossary.js.map +1 -0
- package/dist/tools/orders.d.ts +10 -0
- package/dist/tools/orders.js +13 -0
- package/dist/tools/orders.js.map +1 -0
- package/dist/tools/review.d.ts +3 -0
- package/dist/tools/review.js +52 -0
- package/dist/tools/review.js.map +1 -0
- package/dist/tools/scan.d.ts +12 -0
- package/dist/tools/scan.js +65 -0
- package/dist/tools/scan.js.map +1 -0
- package/dist/tools/status.d.ts +12 -0
- package/dist/tools/status.js +41 -0
- package/dist/tools/status.js.map +1 -0
- package/dist/tools/translate.d.ts +8 -0
- package/dist/tools/translate.js +21 -0
- package/dist/tools/translate.js.map +1 -0
- package/dist/types.d.ts +38 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +52 -0
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { ParseResult, ParsedEntry } from '../types.js';
|
|
2
|
+
export declare const SUPPORTED_EXTENSIONS: string[];
|
|
3
|
+
export declare function getFormat(filename: string): string | null;
|
|
4
|
+
export declare function parseFile(content: string, filename: string): ParseResult;
|
|
5
|
+
export declare function toCsvContent(entries: ParsedEntry[]): string;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { parseJson } from './json.js';
|
|
2
|
+
import { parseCsv } from './csv.js';
|
|
3
|
+
import { parseAndroidXml } from './android-xml.js';
|
|
4
|
+
import { parseIosStrings } from './ios-strings.js';
|
|
5
|
+
import { parsePo } from './po.js';
|
|
6
|
+
import { parseXliff } from './xliff.js';
|
|
7
|
+
const EXTENSION_MAP = {
|
|
8
|
+
'.json': 'json',
|
|
9
|
+
'.arb': 'json',
|
|
10
|
+
'.csv': 'csv',
|
|
11
|
+
'.xml': 'android-xml',
|
|
12
|
+
'.strings': 'ios-strings',
|
|
13
|
+
'.po': 'po',
|
|
14
|
+
'.pot': 'po',
|
|
15
|
+
'.xliff': 'xliff',
|
|
16
|
+
'.xlf': 'xliff',
|
|
17
|
+
};
|
|
18
|
+
export const SUPPORTED_EXTENSIONS = Object.keys(EXTENSION_MAP);
|
|
19
|
+
export function getFormat(filename) {
|
|
20
|
+
const ext = filename.slice(filename.lastIndexOf('.')).toLowerCase();
|
|
21
|
+
return EXTENSION_MAP[ext] ?? null;
|
|
22
|
+
}
|
|
23
|
+
export function parseFile(content, filename) {
|
|
24
|
+
const format = getFormat(filename);
|
|
25
|
+
if (!format) {
|
|
26
|
+
throw new Error(`Unsupported file format: ${filename}`);
|
|
27
|
+
}
|
|
28
|
+
switch (format) {
|
|
29
|
+
case 'json':
|
|
30
|
+
return parseJson(content, filename);
|
|
31
|
+
case 'csv':
|
|
32
|
+
return parseCsv(content);
|
|
33
|
+
case 'android-xml':
|
|
34
|
+
return parseAndroidXml(content);
|
|
35
|
+
case 'ios-strings':
|
|
36
|
+
return parseIosStrings(content);
|
|
37
|
+
case 'po':
|
|
38
|
+
return parsePo(content);
|
|
39
|
+
case 'xliff':
|
|
40
|
+
return parseXliff(content);
|
|
41
|
+
default:
|
|
42
|
+
throw new Error(`No parser available for format: ${format}`);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
function escapeCsvField(field) {
|
|
46
|
+
if (field.includes(',') || field.includes('"') || field.includes('\n') || field.includes('\r')) {
|
|
47
|
+
return '"' + field.replace(/"/g, '""') + '"';
|
|
48
|
+
}
|
|
49
|
+
return field;
|
|
50
|
+
}
|
|
51
|
+
export function toCsvContent(entries) {
|
|
52
|
+
const lines = entries.map(entry => `${escapeCsvField(entry.key)},${escapeCsvField(entry.value)}`);
|
|
53
|
+
return lines.join('\n') + '\n';
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/parsers/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AACpC,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAExC,MAAM,aAAa,GAA2B;IAC5C,OAAO,EAAE,MAAM;IACf,MAAM,EAAE,MAAM;IACd,MAAM,EAAE,KAAK;IACb,MAAM,EAAE,aAAa;IACrB,UAAU,EAAE,aAAa;IACzB,KAAK,EAAE,IAAI;IACX,MAAM,EAAE,IAAI;IACZ,QAAQ,EAAE,OAAO;IACjB,MAAM,EAAE,OAAO;CAChB,CAAC;AAEF,MAAM,CAAC,MAAM,oBAAoB,GAAG,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;AAE/D,MAAM,UAAU,SAAS,CAAC,QAAgB;IACxC,MAAM,GAAG,GAAG,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;IACpE,OAAO,aAAa,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC;AACpC,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,OAAe,EAAE,QAAgB;IACzD,MAAM,MAAM,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;IACnC,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,4BAA4B,QAAQ,EAAE,CAAC,CAAC;IAC1D,CAAC;IAED,QAAQ,MAAM,EAAE,CAAC;QACf,KAAK,MAAM;YACT,OAAO,SAAS,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;QACtC,KAAK,KAAK;YACR,OAAO,QAAQ,CAAC,OAAO,CAAC,CAAC;QAC3B,KAAK,aAAa;YAChB,OAAO,eAAe,CAAC,OAAO,CAAC,CAAC;QAClC,KAAK,aAAa;YAChB,OAAO,eAAe,CAAC,OAAO,CAAC,CAAC;QAClC,KAAK,IAAI;YACP,OAAO,OAAO,CAAC,OAAO,CAAC,CAAC;QAC1B,KAAK,OAAO;YACV,OAAO,UAAU,CAAC,OAAO,CAAC,CAAC;QAC7B;YACE,MAAM,IAAI,KAAK,CAAC,mCAAmC,MAAM,EAAE,CAAC,CAAC;IACjE,CAAC;AACH,CAAC;AAED,SAAS,cAAc,CAAC,KAAa;IACnC,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QAC/F,OAAO,GAAG,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,GAAG,CAAC;IAC/C,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,OAAsB;IACjD,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CACvB,KAAK,CAAC,EAAE,CAAC,GAAG,cAAc,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,cAAc,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CACvE,CAAC;IACF,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;AACjC,CAAC"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
function unescapeStrings(str) {
|
|
2
|
+
return str
|
|
3
|
+
.replace(/\\"/g, '"')
|
|
4
|
+
.replace(/\\\\/g, '\\')
|
|
5
|
+
.replace(/\\n/g, '\n')
|
|
6
|
+
.replace(/\\t/g, '\t');
|
|
7
|
+
}
|
|
8
|
+
export function parseIosStrings(content) {
|
|
9
|
+
const entries = [];
|
|
10
|
+
const lines = content.split('\n');
|
|
11
|
+
let inBlockComment = false;
|
|
12
|
+
for (const line of lines) {
|
|
13
|
+
const trimmed = line.trim();
|
|
14
|
+
// Handle block comments
|
|
15
|
+
if (inBlockComment) {
|
|
16
|
+
if (trimmed.includes('*/')) {
|
|
17
|
+
inBlockComment = false;
|
|
18
|
+
}
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
if (trimmed.startsWith('/*')) {
|
|
22
|
+
if (!trimmed.includes('*/')) {
|
|
23
|
+
inBlockComment = true;
|
|
24
|
+
}
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
// Skip line comments
|
|
28
|
+
if (trimmed.startsWith('//')) {
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
// Match "key" = "value";
|
|
32
|
+
const match = trimmed.match(/^"(.+?)"\s*=\s*"(.*?)"\s*;/);
|
|
33
|
+
if (match) {
|
|
34
|
+
entries.push({
|
|
35
|
+
key: unescapeStrings(match[1]),
|
|
36
|
+
value: unescapeStrings(match[2]),
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return { entries, format: 'ios-strings' };
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=ios-strings.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ios-strings.js","sourceRoot":"","sources":["../../src/parsers/ios-strings.ts"],"names":[],"mappings":"AAEA,SAAS,eAAe,CAAC,GAAW;IAClC,OAAO,GAAG;SACP,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC;SACpB,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC;SACtB,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC;SACrB,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;AAC3B,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,OAAe;IAC7C,MAAM,OAAO,GAAkB,EAAE,CAAC;IAClC,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAClC,IAAI,cAAc,GAAG,KAAK,CAAC;IAE3B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QAE5B,wBAAwB;QACxB,IAAI,cAAc,EAAE,CAAC;YACnB,IAAI,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC3B,cAAc,GAAG,KAAK,CAAC;YACzB,CAAC;YACD,SAAS;QACX,CAAC;QAED,IAAI,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YAC7B,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC5B,cAAc,GAAG,IAAI,CAAC;YACxB,CAAC;YACD,SAAS;QACX,CAAC;QAED,qBAAqB;QACrB,IAAI,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YAC7B,SAAS;QACX,CAAC;QAED,yBAAyB;QACzB,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,4BAA4B,CAAC,CAAC;QAC1D,IAAI,KAAK,EAAE,CAAC;YACV,OAAO,CAAC,IAAI,CAAC;gBACX,GAAG,EAAE,eAAe,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;gBAC9B,KAAK,EAAE,eAAe,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;aACjC,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,CAAC;AAC5C,CAAC"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
function flattenObject(obj, prefix = '') {
|
|
2
|
+
const entries = [];
|
|
3
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
4
|
+
const fullKey = prefix ? `${prefix}.${key}` : key;
|
|
5
|
+
if (typeof value === 'string') {
|
|
6
|
+
entries.push({ key: fullKey, value });
|
|
7
|
+
}
|
|
8
|
+
else if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
|
|
9
|
+
entries.push(...flattenObject(value, fullKey));
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
return entries;
|
|
13
|
+
}
|
|
14
|
+
export function parseJson(content, filename) {
|
|
15
|
+
const data = JSON.parse(content);
|
|
16
|
+
const isArb = filename.endsWith('.arb');
|
|
17
|
+
if (typeof data !== 'object' || data === null || Array.isArray(data)) {
|
|
18
|
+
throw new Error('JSON file must contain an object at the root level');
|
|
19
|
+
}
|
|
20
|
+
let sourceLanguage;
|
|
21
|
+
const filtered = {};
|
|
22
|
+
for (const [key, value] of Object.entries(data)) {
|
|
23
|
+
if (isArb) {
|
|
24
|
+
if (key === '@@locale') {
|
|
25
|
+
sourceLanguage = value;
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
if (key.startsWith('@')) {
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
filtered[key] = value;
|
|
33
|
+
}
|
|
34
|
+
const entries = flattenObject(filtered);
|
|
35
|
+
return {
|
|
36
|
+
entries,
|
|
37
|
+
format: isArb ? 'arb' : 'json',
|
|
38
|
+
sourceLanguage,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=json.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"json.js","sourceRoot":"","sources":["../../src/parsers/json.ts"],"names":[],"mappings":"AAEA,SAAS,aAAa,CAAC,GAA4B,EAAE,MAAM,GAAG,EAAE;IAC9D,MAAM,OAAO,GAAkB,EAAE,CAAC;IAClC,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QAC/C,MAAM,OAAO,GAAG,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI,GAAG,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC;QAClD,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;YAC9B,OAAO,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC;QACxC,CAAC;aAAM,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YAChF,OAAO,CAAC,IAAI,CAAC,GAAG,aAAa,CAAC,KAAgC,EAAE,OAAO,CAAC,CAAC,CAAC;QAC5E,CAAC;IACH,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,OAAe,EAAE,QAAgB;IACzD,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACjC,MAAM,KAAK,GAAG,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IAExC,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;QACrE,MAAM,IAAI,KAAK,CAAC,oDAAoD,CAAC,CAAC;IACxE,CAAC;IAED,IAAI,cAAkC,CAAC;IACvC,MAAM,QAAQ,GAA4B,EAAE,CAAC;IAE7C,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAA+B,CAAC,EAAE,CAAC;QAC3E,IAAI,KAAK,EAAE,CAAC;YACV,IAAI,GAAG,KAAK,UAAU,EAAE,CAAC;gBACvB,cAAc,GAAG,KAAe,CAAC;gBACjC,SAAS;YACX,CAAC;YACD,IAAI,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;gBACxB,SAAS;YACX,CAAC;QACH,CAAC;QACD,QAAQ,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;IACxB,CAAC;IAED,MAAM,OAAO,GAAG,aAAa,CAAC,QAAQ,CAAC,CAAC;IAExC,OAAO;QACL,OAAO;QACP,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM;QAC9B,cAAc;KACf,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
function unescapePo(str) {
|
|
2
|
+
return str
|
|
3
|
+
.replace(/\\"/g, '"')
|
|
4
|
+
.replace(/\\\\/g, '\\')
|
|
5
|
+
.replace(/\\n/g, '\n')
|
|
6
|
+
.replace(/\\t/g, '\t');
|
|
7
|
+
}
|
|
8
|
+
function extractQuoted(line) {
|
|
9
|
+
const match = line.match(/^"(.*)"$/);
|
|
10
|
+
return match ? match[1] : null;
|
|
11
|
+
}
|
|
12
|
+
export function parsePo(content) {
|
|
13
|
+
const entries = [];
|
|
14
|
+
const lines = content.split('\n');
|
|
15
|
+
let msgid = '';
|
|
16
|
+
let msgstr = '';
|
|
17
|
+
let state = 'none';
|
|
18
|
+
function flush() {
|
|
19
|
+
// Skip empty msgid (header entry)
|
|
20
|
+
if (msgid && msgstr) {
|
|
21
|
+
entries.push({
|
|
22
|
+
key: unescapePo(msgid),
|
|
23
|
+
value: unescapePo(msgstr),
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
msgid = '';
|
|
27
|
+
msgstr = '';
|
|
28
|
+
state = 'none';
|
|
29
|
+
}
|
|
30
|
+
for (const line of lines) {
|
|
31
|
+
const trimmed = line.trim();
|
|
32
|
+
// Skip comments
|
|
33
|
+
if (trimmed.startsWith('#')) {
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
// Empty line = end of entry
|
|
37
|
+
if (trimmed === '') {
|
|
38
|
+
flush();
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
// msgid line
|
|
42
|
+
if (trimmed.startsWith('msgid ')) {
|
|
43
|
+
if (state === 'msgstr') {
|
|
44
|
+
flush();
|
|
45
|
+
}
|
|
46
|
+
const quoted = extractQuoted(trimmed.slice(6).trim());
|
|
47
|
+
if (quoted !== null) {
|
|
48
|
+
msgid = quoted;
|
|
49
|
+
}
|
|
50
|
+
state = 'msgid';
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
// msgstr line
|
|
54
|
+
if (trimmed.startsWith('msgstr ')) {
|
|
55
|
+
const quoted = extractQuoted(trimmed.slice(7).trim());
|
|
56
|
+
if (quoted !== null) {
|
|
57
|
+
msgstr = quoted;
|
|
58
|
+
}
|
|
59
|
+
state = 'msgstr';
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
// Continuation line (starts with ")
|
|
63
|
+
const quoted = extractQuoted(trimmed);
|
|
64
|
+
if (quoted !== null) {
|
|
65
|
+
if (state === 'msgid') {
|
|
66
|
+
msgid += quoted;
|
|
67
|
+
}
|
|
68
|
+
else if (state === 'msgstr') {
|
|
69
|
+
msgstr += quoted;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
// Flush last entry
|
|
74
|
+
flush();
|
|
75
|
+
return { entries, format: 'po' };
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=po.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"po.js","sourceRoot":"","sources":["../../src/parsers/po.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
function unescapeXml(str) {
|
|
2
|
+
return str
|
|
3
|
+
.replace(/&/g, '&')
|
|
4
|
+
.replace(/</g, '<')
|
|
5
|
+
.replace(/>/g, '>')
|
|
6
|
+
.replace(/'/g, "'")
|
|
7
|
+
.replace(/"/g, '"');
|
|
8
|
+
}
|
|
9
|
+
export function parseXliff(content) {
|
|
10
|
+
const entries = [];
|
|
11
|
+
// Detect XLIFF version
|
|
12
|
+
const versionMatch = content.match(/<xliff[^>]*version="(\d+\.\d+)"/);
|
|
13
|
+
const version = versionMatch ? versionMatch[1] : '1.2';
|
|
14
|
+
// Extract source language
|
|
15
|
+
let sourceLanguage;
|
|
16
|
+
if (version.startsWith('2')) {
|
|
17
|
+
const srcLangMatch = content.match(/srcLang="([^"]+)"/);
|
|
18
|
+
sourceLanguage = srcLangMatch ? srcLangMatch[1] : undefined;
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
const srcLangMatch = content.match(/source-language="([^"]+)"/);
|
|
22
|
+
sourceLanguage = srcLangMatch ? srcLangMatch[1] : undefined;
|
|
23
|
+
}
|
|
24
|
+
if (version.startsWith('2')) {
|
|
25
|
+
// XLIFF 2.0: <unit id="..."><segment><source>...</source></segment></unit>
|
|
26
|
+
const unitRegex = /<unit\s+id="([^"]+)"[\s\S]*?<source>([\s\S]*?)<\/source>/g;
|
|
27
|
+
let match;
|
|
28
|
+
while ((match = unitRegex.exec(content)) !== null) {
|
|
29
|
+
entries.push({
|
|
30
|
+
key: match[1],
|
|
31
|
+
value: unescapeXml(match[2]),
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
// XLIFF 1.2: <trans-unit id="..."><source>...</source></trans-unit>
|
|
37
|
+
const unitRegex = /<trans-unit\s+id="([^"]+)"[\s\S]*?<source>([\s\S]*?)<\/source>/g;
|
|
38
|
+
let match;
|
|
39
|
+
while ((match = unitRegex.exec(content)) !== null) {
|
|
40
|
+
entries.push({
|
|
41
|
+
key: match[1],
|
|
42
|
+
value: unescapeXml(match[2]),
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return { entries, format: 'xliff', sourceLanguage };
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=xliff.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"xliff.js","sourceRoot":"","sources":["../../src/parsers/xliff.ts"],"names":[],"mappings":"AAEA,SAAS,WAAW,CAAC,GAAW;IAC9B,OAAO,GAAG;SACP,OAAO,CAAC,QAAQ,EAAE,GAAG,CAAC;SACtB,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC;SACrB,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC;SACrB,OAAO,CAAC,SAAS,EAAE,GAAG,CAAC;SACvB,OAAO,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC;AAC7B,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,OAAe;IACxC,MAAM,OAAO,GAAkB,EAAE,CAAC;IAElC,uBAAuB;IACvB,MAAM,YAAY,GAAG,OAAO,CAAC,KAAK,CAAC,iCAAiC,CAAC,CAAC;IACtE,MAAM,OAAO,GAAG,YAAY,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;IAEvD,0BAA0B;IAC1B,IAAI,cAAkC,CAAC;IACvC,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QAC5B,MAAM,YAAY,GAAG,OAAO,CAAC,KAAK,CAAC,mBAAmB,CAAC,CAAC;QACxD,cAAc,GAAG,YAAY,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAC9D,CAAC;SAAM,CAAC;QACN,MAAM,YAAY,GAAG,OAAO,CAAC,KAAK,CAAC,2BAA2B,CAAC,CAAC;QAChE,cAAc,GAAG,YAAY,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAC9D,CAAC;IAED,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QAC5B,2EAA2E;QAC3E,MAAM,SAAS,GAAG,2DAA2D,CAAC;QAC9E,IAAI,KAA6B,CAAC;QAClC,OAAO,CAAC,KAAK,GAAG,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;YAClD,OAAO,CAAC,IAAI,CAAC;gBACX,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC;gBACb,KAAK,EAAE,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;aAC7B,CAAC,CAAC;QACL,CAAC;IACH,CAAC;SAAM,CAAC;QACN,oEAAoE;QACpE,MAAM,SAAS,GAAG,iEAAiE,CAAC;QACpF,IAAI,KAA6B,CAAC;QAClC,OAAO,CAAC,KAAK,GAAG,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;YAClD,OAAO,CAAC,IAAI,CAAC;gBACX,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC;gBACb,KAAK,EAAE,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;aAC7B,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,EAAE,CAAC;AACtD,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"balance.js","sourceRoot":"","sources":["../../src/tools/balance.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,MAAuB;IACxD,OAAO,MAAM,CAAC,UAAU,EAAE,CAAC;AAC7B,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
interface EstimateResult {
|
|
2
|
+
totalCharacters: number;
|
|
3
|
+
estimatedTokens: number;
|
|
4
|
+
breakdown: Array<{
|
|
5
|
+
language: string;
|
|
6
|
+
multiplier: number;
|
|
7
|
+
tokens: number;
|
|
8
|
+
}>;
|
|
9
|
+
}
|
|
10
|
+
export declare function estimateCost(filePath: string, targetLanguages: string[]): Promise<EstimateResult>;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { parseFile } from '../parsers/index.js';
|
|
3
|
+
const TOKENS_PER_CHAR = {
|
|
4
|
+
'English': 1.0,
|
|
5
|
+
'Spanish': 1.0,
|
|
6
|
+
'French': 1.0,
|
|
7
|
+
'German': 1.0,
|
|
8
|
+
'Chinese (Simplified)': 1.5,
|
|
9
|
+
'Chinese (Traditional)': 1.5,
|
|
10
|
+
'Russian': 1.0,
|
|
11
|
+
'Arabic': 1.2,
|
|
12
|
+
'Portuguese': 1.0,
|
|
13
|
+
'Japanese': 1.5,
|
|
14
|
+
'Italian': 1.0,
|
|
15
|
+
'Korean': 1.5,
|
|
16
|
+
'Turkish': 1.0,
|
|
17
|
+
'Hindi': 1.2,
|
|
18
|
+
'Dutch': 1.0,
|
|
19
|
+
'Polish': 1.0,
|
|
20
|
+
'Swedish': 1.0,
|
|
21
|
+
'Greek': 1.0,
|
|
22
|
+
'Hebrew': 1.2,
|
|
23
|
+
'Indonesian': 1.0,
|
|
24
|
+
'Thai': 1.2,
|
|
25
|
+
};
|
|
26
|
+
export async function estimateCost(filePath, targetLanguages) {
|
|
27
|
+
const content = await readFile(filePath, 'utf-8');
|
|
28
|
+
const filename = filePath.split('/').pop() || filePath;
|
|
29
|
+
const parsed = parseFile(content, filename);
|
|
30
|
+
const totalCharacters = parsed.entries.reduce((sum, e) => sum + e.value.length, 0);
|
|
31
|
+
const breakdown = targetLanguages.map(lang => {
|
|
32
|
+
const multiplier = TOKENS_PER_CHAR[lang] ?? 1.0;
|
|
33
|
+
const tokens = Math.ceil(totalCharacters * multiplier);
|
|
34
|
+
return { language: lang, multiplier, tokens };
|
|
35
|
+
});
|
|
36
|
+
const estimatedTokens = breakdown.reduce((sum, b) => sum + b.tokens, 0);
|
|
37
|
+
return {
|
|
38
|
+
totalCharacters,
|
|
39
|
+
estimatedTokens,
|
|
40
|
+
breakdown,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=estimate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"estimate.js","sourceRoot":"","sources":["../../src/tools/estimate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAEhD,MAAM,eAAe,GAA2B;IAC9C,SAAS,EAAE,GAAG;IACd,SAAS,EAAE,GAAG;IACd,QAAQ,EAAE,GAAG;IACb,QAAQ,EAAE,GAAG;IACb,sBAAsB,EAAE,GAAG;IAC3B,uBAAuB,EAAE,GAAG;IAC5B,SAAS,EAAE,GAAG;IACd,QAAQ,EAAE,GAAG;IACb,YAAY,EAAE,GAAG;IACjB,UAAU,EAAE,GAAG;IACf,SAAS,EAAE,GAAG;IACd,QAAQ,EAAE,GAAG;IACb,SAAS,EAAE,GAAG;IACd,OAAO,EAAE,GAAG;IACZ,OAAO,EAAE,GAAG;IACZ,QAAQ,EAAE,GAAG;IACb,SAAS,EAAE,GAAG;IACd,OAAO,EAAE,GAAG;IACZ,QAAQ,EAAE,GAAG;IACb,YAAY,EAAE,GAAG;IACjB,MAAM,EAAE,GAAG;CACZ,CAAC;AAYF,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,QAAgB,EAAE,eAAyB;IAC5E,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAClD,MAAM,QAAQ,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,IAAI,QAAQ,CAAC;IACvD,MAAM,MAAM,GAAG,SAAS,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;IAE5C,MAAM,eAAe,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IAEnF,MAAM,SAAS,GAAG,eAAe,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE;QAC3C,MAAM,UAAU,GAAG,eAAe,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC;QAChD,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,eAAe,GAAG,UAAU,CAAC,CAAC;QACvD,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,CAAC;IAChD,CAAC,CAAC,CAAC;IAEH,MAAM,eAAe,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IAExE,OAAO;QACL,eAAe;QACf,eAAe;QACf,SAAS;KACV,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { QuestlangClient } from '../client.js';
|
|
2
|
+
interface GlossaryResult {
|
|
3
|
+
id: string;
|
|
4
|
+
message: string;
|
|
5
|
+
}
|
|
6
|
+
export declare function updateGlossary(client: QuestlangClient, term: string, translations: Record<string, string>, type?: string, context?: string, inflections?: string[]): Promise<GlossaryResult>;
|
|
7
|
+
export {};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
const SUPPORTED_LANGUAGES = [
|
|
2
|
+
'English', 'Spanish', 'French', 'German',
|
|
3
|
+
'Chinese (Simplified)', 'Chinese (Traditional)',
|
|
4
|
+
'Russian', 'Arabic', 'Portuguese', 'Japanese',
|
|
5
|
+
'Italian', 'Korean', 'Turkish', 'Hindi',
|
|
6
|
+
'Dutch', 'Polish', 'Swedish', 'Greek',
|
|
7
|
+
'Hebrew', 'Indonesian', 'Thai',
|
|
8
|
+
];
|
|
9
|
+
export async function updateGlossary(client, term, translations, type, context, inflections) {
|
|
10
|
+
// Pad missing languages with empty strings (backend requires all 21)
|
|
11
|
+
const paddedTranslations = {};
|
|
12
|
+
for (const lang of SUPPORTED_LANGUAGES) {
|
|
13
|
+
paddedTranslations[lang] = translations[lang] || '';
|
|
14
|
+
}
|
|
15
|
+
const entry = {
|
|
16
|
+
term,
|
|
17
|
+
translations: paddedTranslations,
|
|
18
|
+
type: type || 'term',
|
|
19
|
+
context,
|
|
20
|
+
inflections,
|
|
21
|
+
};
|
|
22
|
+
const result = await client.addGlossaryEntry(entry);
|
|
23
|
+
return {
|
|
24
|
+
id: (result.id || ''),
|
|
25
|
+
message: `Glossary entry for "${term}" added successfully.`,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=glossary.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"glossary.js","sourceRoot":"","sources":["../../src/tools/glossary.ts"],"names":[],"mappings":"AAGA,MAAM,mBAAmB,GAAG;IAC1B,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ;IACxC,sBAAsB,EAAE,uBAAuB;IAC/C,SAAS,EAAE,QAAQ,EAAE,YAAY,EAAE,UAAU;IAC7C,SAAS,EAAE,QAAQ,EAAE,SAAS,EAAE,OAAO;IACvC,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,OAAO;IACrC,QAAQ,EAAE,YAAY,EAAE,MAAM;CAC/B,CAAC;AAOF,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,MAAuB,EACvB,IAAY,EACZ,YAAoC,EACpC,IAAa,EACb,OAAgB,EAChB,WAAsB;IAEtB,qEAAqE;IACrE,MAAM,kBAAkB,GAA2B,EAAE,CAAC;IACtD,KAAK,MAAM,IAAI,IAAI,mBAAmB,EAAE,CAAC;QACvC,kBAAkB,CAAC,IAAI,CAAC,GAAG,YAAY,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;IACtD,CAAC;IAED,MAAM,KAAK,GAAkB;QAC3B,IAAI;QACJ,YAAY,EAAE,kBAAkB;QAChC,IAAI,EAAE,IAAI,IAAI,MAAM;QACpB,OAAO;QACP,WAAW;KACZ,CAAC;IAEF,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAC;IAEpD,OAAO;QACL,EAAE,EAAE,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAW;QAC/B,OAAO,EAAE,uBAAuB,IAAI,uBAAuB;KAC5D,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { QuestlangClient } from '../client.js';
|
|
2
|
+
interface OrderItem {
|
|
3
|
+
orderId: string;
|
|
4
|
+
status: string;
|
|
5
|
+
progress: number;
|
|
6
|
+
createdAt: string;
|
|
7
|
+
modelName: string;
|
|
8
|
+
}
|
|
9
|
+
export declare function listOrders(client: QuestlangClient, limit?: number): Promise<OrderItem[]>;
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export async function listOrders(client, limit = 10) {
|
|
2
|
+
const data = await client.getOrders();
|
|
3
|
+
// Backend returns { count, orders } wrapper
|
|
4
|
+
const orders = Array.isArray(data) ? data : (data.orders || []);
|
|
5
|
+
return orders.slice(0, limit).map(order => ({
|
|
6
|
+
orderId: (order.id || order.orderId),
|
|
7
|
+
status: order.status,
|
|
8
|
+
progress: (order.translationProgress ?? order.progress ?? 0),
|
|
9
|
+
createdAt: order.createdAt,
|
|
10
|
+
modelName: (order.modelName || ''),
|
|
11
|
+
}));
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=orders.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"orders.js","sourceRoot":"","sources":["../../src/tools/orders.ts"],"names":[],"mappings":"AAUA,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,MAAuB,EAAE,KAAK,GAAG,EAAE;IAClE,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,SAAS,EAAE,CAAC;IACtC,4CAA4C;IAC5C,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAE,IAAgC,CAAC,MAAmC,IAAI,EAAE,CAAC,CAAC;IAE1H,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QAC1C,OAAO,EAAE,CAAC,KAAK,CAAC,EAAE,IAAI,KAAK,CAAC,OAAO,CAAW;QAC9C,MAAM,EAAE,KAAK,CAAC,MAAgB;QAC9B,QAAQ,EAAE,CAAC,KAAK,CAAC,mBAAmB,IAAI,KAAK,CAAC,QAAQ,IAAI,CAAC,CAAW;QACtE,SAAS,EAAE,KAAK,CAAC,SAAmB;QACpC,SAAS,EAAE,CAAC,KAAK,CAAC,SAAS,IAAI,EAAE,CAAW;KAC7C,CAAC,CAAC,CAAC;AACN,CAAC"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { QuestlangClient } from '../client.js';
|
|
2
|
+
export declare function requestQualityReview(client: QuestlangClient, orderId: string, model?: string): Promise<string>;
|
|
3
|
+
export declare function getReviewReport(client: QuestlangClient, orderId: string): Promise<string>;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
export async function requestQualityReview(client, orderId, model) {
|
|
2
|
+
// First estimate cost
|
|
3
|
+
const estimate = await client.estimateReviewCost(orderId, model);
|
|
4
|
+
// Then request review
|
|
5
|
+
const result = await client.requestReview(orderId, model);
|
|
6
|
+
// Return formatted result string
|
|
7
|
+
return `Quality review started for order ${orderId}.\n` +
|
|
8
|
+
`Estimated cost: ${estimate.estimatedTokens} tokens (${estimate.estimatedPriceRub} \u20BD)\n` +
|
|
9
|
+
`Status: ${result.qualityReviewStatus}\n` +
|
|
10
|
+
`Use questlang_get_order_status to check progress.`;
|
|
11
|
+
}
|
|
12
|
+
export async function getReviewReport(client, orderId) {
|
|
13
|
+
const result = await client.getReviewStatus(orderId);
|
|
14
|
+
if (result.qualityReviewStatus === 'none') {
|
|
15
|
+
return 'No quality review has been requested for this order.';
|
|
16
|
+
}
|
|
17
|
+
if (result.qualityReviewStatus === 'processing') {
|
|
18
|
+
return 'Quality review is still in progress. Check back in a few seconds.';
|
|
19
|
+
}
|
|
20
|
+
if (result.qualityReviewStatus === 'failed') {
|
|
21
|
+
return 'Quality review failed. Tokens have been refunded.';
|
|
22
|
+
}
|
|
23
|
+
// Format the report
|
|
24
|
+
const report = result.qualityReviewReport;
|
|
25
|
+
let output = `Quality Review Report for Order ${orderId}\n`;
|
|
26
|
+
output += `Model: ${report.reviewModel}\n`;
|
|
27
|
+
output += `Total issues: ${report.totalIssues}\n\n`;
|
|
28
|
+
const issuesByCategory = report.issuesByCategory;
|
|
29
|
+
if (issuesByCategory) {
|
|
30
|
+
output += 'Issues by category:\n';
|
|
31
|
+
for (const [cat, count] of Object.entries(issuesByCategory)) {
|
|
32
|
+
output += ` ${cat}: ${count}\n`;
|
|
33
|
+
}
|
|
34
|
+
output += '\n';
|
|
35
|
+
}
|
|
36
|
+
const issues = report.issues;
|
|
37
|
+
if (issues?.length) {
|
|
38
|
+
output += 'Issues:\n';
|
|
39
|
+
for (const issue of issues.slice(0, 50)) {
|
|
40
|
+
output += ` [Row ${issue.rowIndex}] [${issue.language}] (${issue.category}/${issue.severity}): ${issue.message}`;
|
|
41
|
+
if (issue.suggestion) {
|
|
42
|
+
output += ` \u2192 ${issue.suggestion}`;
|
|
43
|
+
}
|
|
44
|
+
output += '\n';
|
|
45
|
+
}
|
|
46
|
+
if (issues.length > 50) {
|
|
47
|
+
output += ` ... and ${issues.length - 50} more issues\n`;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return output;
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=review.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"review.js","sourceRoot":"","sources":["../../src/tools/review.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,MAAuB,EACvB,OAAe,EACf,KAAc;IAEd,sBAAsB;IACtB,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,kBAAkB,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IAEjE,sBAAsB;IACtB,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,aAAa,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IAE1D,iCAAiC;IACjC,OAAO,oCAAoC,OAAO,KAAK;QACrD,mBAAmB,QAAQ,CAAC,eAAe,YAAY,QAAQ,CAAC,iBAAiB,YAAY;QAC7F,WAAW,MAAM,CAAC,mBAAmB,IAAI;QACzC,mDAAmD,CAAC;AACxD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,MAAuB,EACvB,OAAe;IAEf,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC;IAErD,IAAI,MAAM,CAAC,mBAAmB,KAAK,MAAM,EAAE,CAAC;QAC1C,OAAO,sDAAsD,CAAC;IAChE,CAAC;IACD,IAAI,MAAM,CAAC,mBAAmB,KAAK,YAAY,EAAE,CAAC;QAChD,OAAO,mEAAmE,CAAC;IAC7E,CAAC;IACD,IAAI,MAAM,CAAC,mBAAmB,KAAK,QAAQ,EAAE,CAAC;QAC5C,OAAO,mDAAmD,CAAC;IAC7D,CAAC;IAED,oBAAoB;IACpB,MAAM,MAAM,GAAG,MAAM,CAAC,mBAA8C,CAAC;IACrE,IAAI,MAAM,GAAG,mCAAmC,OAAO,IAAI,CAAC;IAC5D,MAAM,IAAI,UAAU,MAAM,CAAC,WAAW,IAAI,CAAC;IAC3C,MAAM,IAAI,iBAAiB,MAAM,CAAC,WAAW,MAAM,CAAC;IAEpD,MAAM,gBAAgB,GAAG,MAAM,CAAC,gBAAsD,CAAC;IACvF,IAAI,gBAAgB,EAAE,CAAC;QACrB,MAAM,IAAI,uBAAuB,CAAC;QAClC,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,gBAAgB,CAAC,EAAE,CAAC;YAC5D,MAAM,IAAI,KAAK,GAAG,KAAK,KAAK,IAAI,CAAC;QACnC,CAAC;QACD,MAAM,IAAI,IAAI,CAAC;IACjB,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,CAAC,MAAoD,CAAC;IAC3E,IAAI,MAAM,EAAE,MAAM,EAAE,CAAC;QACnB,MAAM,IAAI,WAAW,CAAC;QACtB,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC;YACxC,MAAM,IAAI,UAAU,KAAK,CAAC,QAAQ,MAAM,KAAK,CAAC,QAAQ,MAAM,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,QAAQ,MAAM,KAAK,CAAC,OAAO,EAAE,CAAC;YAClH,IAAI,KAAK,CAAC,UAAU,EAAE,CAAC;gBACrB,MAAM,IAAI,WAAW,KAAK,CAAC,UAAU,EAAE,CAAC;YAC1C,CAAC;YACD,MAAM,IAAI,IAAI,CAAC;QACjB,CAAC;QACD,IAAI,MAAM,CAAC,MAAM,GAAG,EAAE,EAAE,CAAC;YACvB,MAAM,IAAI,aAAa,MAAM,CAAC,MAAM,GAAG,EAAE,gBAAgB,CAAC;QAC5D,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
interface ScanResult {
|
|
2
|
+
files: Array<{
|
|
3
|
+
path: string;
|
|
4
|
+
format: string;
|
|
5
|
+
keyCount: number;
|
|
6
|
+
samples: string[];
|
|
7
|
+
}>;
|
|
8
|
+
totalKeys: number;
|
|
9
|
+
summary: string;
|
|
10
|
+
}
|
|
11
|
+
export declare function scan(directory: string, maxDepth?: number): Promise<ScanResult>;
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { readdir, readFile } from 'node:fs/promises';
|
|
2
|
+
import { join, basename, extname } from 'node:path';
|
|
3
|
+
import { parseFile, SUPPORTED_EXTENSIONS } from '../parsers/index.js';
|
|
4
|
+
const SKIP_DIRS = new Set(['node_modules', '.git', 'build', 'dist', '__pycache__', '.next', '.nuxt']);
|
|
5
|
+
async function scanDirectory(dir, depth, maxDepth) {
|
|
6
|
+
if (depth > maxDepth)
|
|
7
|
+
return [];
|
|
8
|
+
const results = [];
|
|
9
|
+
let entries;
|
|
10
|
+
try {
|
|
11
|
+
entries = await readdir(dir, { withFileTypes: true });
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
return [];
|
|
15
|
+
}
|
|
16
|
+
for (const entry of entries) {
|
|
17
|
+
const fullPath = join(dir, entry.name);
|
|
18
|
+
if (entry.isDirectory()) {
|
|
19
|
+
if (SKIP_DIRS.has(entry.name))
|
|
20
|
+
continue;
|
|
21
|
+
const subResults = await scanDirectory(fullPath, depth + 1, maxDepth);
|
|
22
|
+
results.push(...subResults);
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
if (!entry.isFile())
|
|
26
|
+
continue;
|
|
27
|
+
const ext = extname(entry.name).toLowerCase();
|
|
28
|
+
if (!SUPPORTED_EXTENSIONS.includes(ext))
|
|
29
|
+
continue;
|
|
30
|
+
// For .xml files, only match strings.xml
|
|
31
|
+
if (ext === '.xml' && basename(entry.name) !== 'strings.xml')
|
|
32
|
+
continue;
|
|
33
|
+
try {
|
|
34
|
+
const content = await readFile(fullPath, 'utf-8');
|
|
35
|
+
const parsed = parseFile(content, entry.name);
|
|
36
|
+
results.push({
|
|
37
|
+
path: fullPath,
|
|
38
|
+
format: parsed.format,
|
|
39
|
+
keyCount: parsed.entries.length,
|
|
40
|
+
samples: parsed.entries.slice(0, 3).map(e => e.key),
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
// Skip files that fail to parse
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return results;
|
|
48
|
+
}
|
|
49
|
+
export async function scan(directory, maxDepth = 5) {
|
|
50
|
+
const files = await scanDirectory(directory, 0, maxDepth);
|
|
51
|
+
const totalKeys = files.reduce((sum, f) => sum + f.keyCount, 0);
|
|
52
|
+
const formatCounts = {};
|
|
53
|
+
for (const f of files) {
|
|
54
|
+
formatCounts[f.format] = (formatCounts[f.format] || 0) + 1;
|
|
55
|
+
}
|
|
56
|
+
const formatSummary = Object.entries(formatCounts)
|
|
57
|
+
.map(([format, count]) => `${count} ${format}`)
|
|
58
|
+
.join(', ');
|
|
59
|
+
return {
|
|
60
|
+
files,
|
|
61
|
+
totalKeys,
|
|
62
|
+
summary: `Found ${files.length} i18n files (${formatSummary}) with ${totalKeys} total keys`,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=scan.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scan.js","sourceRoot":"","sources":["../../src/tools/scan.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { QuestlangClient } from '../client.js';
|
|
2
|
+
interface StatusResult {
|
|
3
|
+
status: string;
|
|
4
|
+
progress: number;
|
|
5
|
+
downloadUrl?: string;
|
|
6
|
+
translatedContent?: string;
|
|
7
|
+
error?: string;
|
|
8
|
+
validationSummary?: string;
|
|
9
|
+
qualityReviewStatus?: string;
|
|
10
|
+
}
|
|
11
|
+
export declare function getOrderStatus(client: QuestlangClient, orderId: string): Promise<StatusResult>;
|
|
12
|
+
export {};
|