locai 2.6.1 → 2.7.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 +23 -25
- package/dist/cli/helpers.d.ts +34 -2
- package/dist/cli/helpers.js +96 -37
- package/dist/cli.js +40 -39
- package/dist/commands/review.d.ts +33 -8
- package/dist/commands/review.js +89 -22
- package/dist/commands/translator.d.ts +4 -2
- package/dist/commands/translator.js +1 -1
- package/dist/config/index.d.ts +53 -66
- package/dist/config/index.js +2 -1
- package/dist/config/setup.d.ts +3 -2
- package/dist/config/setup.js +5 -3
- package/dist/core/adapters/android-adapter.d.ts +3 -2
- package/dist/core/adapters/android-adapter.js +12 -13
- package/dist/core/adapters/base-adapter.d.ts +13 -2
- package/dist/core/adapters/ios-adapter.d.ts +3 -2
- package/dist/core/adapters/json-adapter.d.ts +4 -3
- package/dist/core/adapters/json-adapter.js +1 -3
- package/dist/core/adapters/po-adapter.d.ts +3 -2
- package/dist/core/adapters/po-adapter.js +4 -5
- package/dist/core/adapters/properties-adapter.d.ts +3 -2
- package/dist/core/adapters/properties-adapter.js +2 -4
- package/dist/core/adapters/yaml-adapter.d.ts +4 -3
- package/dist/core/adapters/yaml-adapter.js +1 -3
- package/dist/core/context-processor.d.ts +21 -6
- package/dist/core/context-processor.js +2 -7
- package/dist/core/detection/framework-detector.js +1 -1
- package/dist/core/detection/structure-detector.js +1 -1
- package/dist/core/fallback-provider.d.ts +3 -4
- package/dist/core/fallback-provider.js +15 -21
- package/dist/core/orchestrator.d.ts +98 -26
- package/dist/core/orchestrator.js +56 -116
- package/dist/core/pipeline/Pipeline.d.ts +2 -2
- package/dist/core/pipeline/Pipeline.js +1 -3
- package/dist/core/pipeline/context.d.ts +69 -13
- package/dist/core/pipeline/context.js +1 -7
- package/dist/core/pipeline/steps/CacheReadStep.d.ts +10 -7
- package/dist/core/pipeline/steps/CacheReadStep.js +1 -12
- package/dist/core/pipeline/steps/CacheWriteStep.d.ts +2 -2
- package/dist/core/pipeline/steps/CacheWriteStep.js +1 -8
- package/dist/core/pipeline/steps/ConfidenceCheckStep.d.ts +5 -0
- package/dist/core/pipeline/steps/ConfidenceCheckStep.js +24 -44
- package/dist/core/pipeline/steps/ContextEnrichmentStep.js +3 -14
- package/dist/core/pipeline/steps/QualityCheckStep.d.ts +1 -1
- package/dist/core/pipeline/steps/QualityCheckStep.js +0 -2
- package/dist/core/pipeline/steps/ToneCheckStep.js +1 -1
- package/dist/core/pipeline/steps/TranslationStep.d.ts +13 -3
- package/dist/core/pipeline/steps/TranslationStep.js +21 -23
- package/dist/core/pipeline/steps/VectorCacheReadStep.d.ts +9 -1
- package/dist/core/pipeline/steps/VectorCacheReadStep.js +3 -5
- package/dist/core/pipeline/steps/VectorCacheWriteStep.d.ts +2 -1
- package/dist/core/pipeline/steps/VectorCacheWriteStep.js +2 -1
- package/dist/core/provider-factory.d.ts +15 -9
- package/dist/core/provider-factory.js +42 -32
- package/dist/providers/anthropic.d.ts +5 -4
- package/dist/providers/anthropic.js +3 -2
- package/dist/providers/api-types.d.ts +74 -0
- package/dist/providers/api-types.js +5 -0
- package/dist/providers/base-provider.d.ts +27 -31
- package/dist/providers/base-provider.js +22 -46
- package/dist/providers/dashscope.js +0 -2
- package/dist/providers/deepseek.js +0 -1
- package/dist/providers/gemini.js +0 -1
- package/dist/providers/openai.d.ts +3 -3
- package/dist/providers/openai.js +0 -1
- package/dist/providers/xai.js +0 -1
- package/dist/services/embedding-provider.js +4 -2
- package/dist/services/language-processor.d.ts +5 -3
- package/dist/services/language-processor.js +38 -43
- package/dist/services/source-analyzer.d.ts +24 -0
- package/dist/services/source-analyzer.js +161 -68
- package/dist/services/translation-service.d.ts +31 -13
- package/dist/services/translation-service.js +116 -71
- package/dist/services/vector-store.d.ts +15 -2
- package/dist/services/vector-store.js +14 -10
- package/dist/types/index.d.ts +31 -33
- package/dist/utils/ai-context-analyzer.d.ts +12 -3
- package/dist/utils/ai-context-analyzer.js +18 -10
- package/dist/utils/confidence-scorer.d.ts +16 -19
- package/dist/utils/confidence-scorer.js +36 -125
- package/dist/utils/error-helper.d.ts +27 -6
- package/dist/utils/error-helper.js +10 -9
- package/dist/utils/file-manager.d.ts +8 -7
- package/dist/utils/file-manager.js +22 -23
- package/dist/utils/glossary-manager.d.ts +22 -3
- package/dist/utils/glossary-manager.js +47 -28
- package/dist/utils/graceful-shutdown.d.ts +7 -1
- package/dist/utils/graceful-shutdown.js +1 -2
- package/dist/utils/input-validator.d.ts +53 -66
- package/dist/utils/input-validator.js +100 -151
- package/dist/utils/locale-paths.d.ts +7 -0
- package/dist/utils/locale-paths.js +16 -0
- package/dist/utils/logger.d.ts +8 -8
- package/dist/utils/logger.js +7 -15
- package/dist/utils/object-transformer.d.ts +20 -5
- package/dist/utils/object-transformer.js +20 -28
- package/dist/utils/progress-tracker.js +2 -3
- package/dist/utils/prompt-templates.d.ts +45 -5
- package/dist/utils/prompt-templates.js +61 -5
- package/dist/utils/quality/base-checker.d.ts +40 -18
- package/dist/utils/quality/base-checker.js +1 -2
- package/dist/utils/quality/code-block-checker.d.ts +13 -5
- package/dist/utils/quality/index.d.ts +2 -1
- package/dist/utils/quality/index.js +8 -27
- package/dist/utils/quality/length-checker.d.ts +35 -5
- package/dist/utils/quality/length-checker.js +2 -3
- package/dist/utils/quality/markdown-checker.d.ts +14 -7
- package/dist/utils/quality/special-characters-checker.d.ts +18 -1
- package/dist/utils/quality/special-characters-checker.js +0 -1
- package/dist/utils/quality/style-guide-checker.d.ts +1 -1
- package/dist/utils/quality/style-guide-checker.js +0 -2
- package/dist/utils/quality/tone-verifier.js +1 -1
- package/dist/utils/rate-limiter.d.ts +17 -1
- package/dist/utils/rate-limiter.js +12 -11
- package/dist/utils/retry-helper.d.ts +21 -3
- package/dist/utils/retry-helper.js +11 -3
- package/dist/utils/review-queue.d.ts +45 -0
- package/dist/utils/review-queue.js +53 -0
- package/dist/utils/state-manager.d.ts +12 -3
- package/dist/utils/state-manager.js +32 -7
- package/dist/utils/statistics-manager.d.ts +11 -1
- package/dist/utils/statistics-manager.js +8 -0
- package/dist/utils/ui-manager.d.ts +2 -1
- package/dist/utils/ui-manager.js +15 -1
- package/package.json +22 -17
- package/dist/commands/interactive-review.d.ts +0 -9
- package/dist/commands/interactive-review.js +0 -109
- package/dist/core/pipeline/steps/__tests__/ToneCheckStep.test.d.ts +0 -1
- package/dist/core/pipeline/steps/__tests__/ToneCheckStep.test.js +0 -100
- package/dist/utils/quality/__tests__/tone-verifier.test.d.ts +0 -1
- package/dist/utils/quality/__tests__/tone-verifier.test.js +0 -91
package/dist/utils/logger.js
CHANGED
|
@@ -54,14 +54,13 @@ class Logger {
|
|
|
54
54
|
this.initialized = true;
|
|
55
55
|
}
|
|
56
56
|
catch (error) {
|
|
57
|
-
console.warn(`Logger initialization warning: ${error.message}`);
|
|
57
|
+
console.warn(`Logger initialization warning: ${error instanceof Error ? error.message : String(error)}`);
|
|
58
58
|
}
|
|
59
59
|
}
|
|
60
60
|
/**
|
|
61
61
|
* Parse size string (e.g., "10MB") to bytes.
|
|
62
62
|
* @param sizeStr - Size string to parse.
|
|
63
63
|
*/
|
|
64
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
65
64
|
parseSize(sizeStr) {
|
|
66
65
|
if (typeof sizeStr === "number")
|
|
67
66
|
return sizeStr;
|
|
@@ -92,7 +91,7 @@ class Logger {
|
|
|
92
91
|
}
|
|
93
92
|
}
|
|
94
93
|
catch (error) {
|
|
95
|
-
console.warn(`Log rotation failed for ${logPath}: ${error.message}`);
|
|
94
|
+
console.warn(`Log rotation failed for ${logPath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
96
95
|
this.currentLogSizes[level] = 0;
|
|
97
96
|
}
|
|
98
97
|
}
|
|
@@ -118,7 +117,7 @@ class Logger {
|
|
|
118
117
|
}
|
|
119
118
|
}
|
|
120
119
|
catch (error) {
|
|
121
|
-
console.warn(`Log rotation failed for ${logPath}: ${error.message}`);
|
|
120
|
+
console.warn(`Log rotation failed for ${logPath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
122
121
|
}
|
|
123
122
|
}
|
|
124
123
|
/**
|
|
@@ -150,7 +149,7 @@ class Logger {
|
|
|
150
149
|
}
|
|
151
150
|
}
|
|
152
151
|
catch (error) {
|
|
153
|
-
console.warn(`Cleanup old logs failed: ${error.message}`);
|
|
152
|
+
console.warn(`Cleanup old logs failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
154
153
|
}
|
|
155
154
|
}
|
|
156
155
|
/**
|
|
@@ -159,7 +158,6 @@ class Logger {
|
|
|
159
158
|
* @param message - Log message.
|
|
160
159
|
* @param data - Additional data to log.
|
|
161
160
|
*/
|
|
162
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
163
161
|
formatMessage(level, message, data = null) {
|
|
164
162
|
const timestamp = this.config.includeTimestamps ? new Date().toISOString() : null;
|
|
165
163
|
switch (this.config.outputFormat) {
|
|
@@ -187,7 +185,6 @@ class Logger {
|
|
|
187
185
|
* @param message - Log message.
|
|
188
186
|
* @param data - Additional data to log.
|
|
189
187
|
*/
|
|
190
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
191
188
|
async writeToFile(level, message, data = null) {
|
|
192
189
|
if (!this.config.saveErrorLogs)
|
|
193
190
|
return;
|
|
@@ -208,7 +205,7 @@ class Logger {
|
|
|
208
205
|
}
|
|
209
206
|
}
|
|
210
207
|
catch (error) {
|
|
211
|
-
console.warn(`Failed to write to log file: ${error.message}`);
|
|
208
|
+
console.warn(`Failed to write to log file: ${error instanceof Error ? error.message : String(error)}`);
|
|
212
209
|
}
|
|
213
210
|
}
|
|
214
211
|
/**
|
|
@@ -227,7 +224,6 @@ class Logger {
|
|
|
227
224
|
* @param message - Error message.
|
|
228
225
|
* @param data - Additional data.
|
|
229
226
|
*/
|
|
230
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
231
227
|
async error(message, data = null) {
|
|
232
228
|
console.error(`ERROR: ${message}`);
|
|
233
229
|
if (data && this.config.verbose) {
|
|
@@ -240,7 +236,6 @@ class Logger {
|
|
|
240
236
|
* @param message - Warning message.
|
|
241
237
|
* @param data - Additional data.
|
|
242
238
|
*/
|
|
243
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
244
239
|
async warn(message, data = null) {
|
|
245
240
|
console.warn(`WARNING: ${message}`);
|
|
246
241
|
if (data && this.config.verbose) {
|
|
@@ -253,7 +248,6 @@ class Logger {
|
|
|
253
248
|
* @param message - Info message.
|
|
254
249
|
* @param data - Additional data.
|
|
255
250
|
*/
|
|
256
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
257
251
|
async info(message, data = null) {
|
|
258
252
|
if (this.config.diagnosticsLevel !== "minimal" || this.config.verbose) {
|
|
259
253
|
console.log(`INFO: ${message}`);
|
|
@@ -268,7 +262,6 @@ class Logger {
|
|
|
268
262
|
* @param message - Debug message.
|
|
269
263
|
* @param data - Additional data.
|
|
270
264
|
*/
|
|
271
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
272
265
|
async debug(message, data = null) {
|
|
273
266
|
if (this.config.verbose || this.config.diagnosticsLevel === "detailed") {
|
|
274
267
|
console.log(`DEBUG: ${message}`);
|
|
@@ -284,7 +277,6 @@ class Logger {
|
|
|
284
277
|
* @param message - Log message.
|
|
285
278
|
* @param data - Additional data.
|
|
286
279
|
*/
|
|
287
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
288
280
|
async diagnostics(level, message, data = null) {
|
|
289
281
|
const levels = {
|
|
290
282
|
minimal: 1,
|
|
@@ -313,7 +305,7 @@ class Logger {
|
|
|
313
305
|
};
|
|
314
306
|
}
|
|
315
307
|
catch (error) {
|
|
316
|
-
console.warn(`Failed to get stats for ${logPath}: ${error.message}`);
|
|
308
|
+
console.warn(`Failed to get stats for ${logPath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
317
309
|
stats[level] = {
|
|
318
310
|
size: 0,
|
|
319
311
|
sizeFormatted: "0 B",
|
|
@@ -346,7 +338,7 @@ class Logger {
|
|
|
346
338
|
await fsPromises.unlink(logPath);
|
|
347
339
|
}
|
|
348
340
|
catch (error) {
|
|
349
|
-
console.warn(`Failed to clear log ${logPath}: ${error.message}`);
|
|
341
|
+
console.warn(`Failed to clear log ${logPath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
350
342
|
}
|
|
351
343
|
}
|
|
352
344
|
this.currentLogSizes = {};
|
|
@@ -1,9 +1,24 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Utility class for object manipulation.
|
|
3
3
|
*/
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
/**
|
|
5
|
+
* A value in a locale file: a primitive leaf, an array, or a nested object.
|
|
6
|
+
*/
|
|
7
|
+
export type TranslationValue = string | number | boolean | null | TranslationValue[] | TranslationObject;
|
|
8
|
+
/**
|
|
9
|
+
* A (possibly nested) locale object, e.g. `{ "home": { "title": "Hello" } }`.
|
|
10
|
+
*/
|
|
11
|
+
export interface TranslationObject {
|
|
12
|
+
[key: string]: TranslationValue;
|
|
6
13
|
}
|
|
14
|
+
/**
|
|
15
|
+
* A leaf of a flattened locale object. Arrays are kept whole, they are not flattened.
|
|
16
|
+
*/
|
|
17
|
+
export type FlatTranslationValue = Exclude<TranslationValue, TranslationObject>;
|
|
18
|
+
/**
|
|
19
|
+
* A locale object flattened to dot-notation keys, e.g. `{ "home.title": "Hello" }`.
|
|
20
|
+
*/
|
|
21
|
+
export type FlatTranslations = Record<string, FlatTranslationValue>;
|
|
7
22
|
declare class ObjectTransformer {
|
|
8
23
|
/**
|
|
9
24
|
* Flatten a nested object into a single-level object with dot notation keys.
|
|
@@ -12,13 +27,13 @@ declare class ObjectTransformer {
|
|
|
12
27
|
* @param maxDepth - Maximum recursion depth.
|
|
13
28
|
* @returns Flattened object.
|
|
14
29
|
*/
|
|
15
|
-
static flatten(obj:
|
|
30
|
+
static flatten(obj: TranslationObject, prefix?: string, maxDepth?: number): FlatTranslations;
|
|
16
31
|
/**
|
|
17
32
|
* Unflatten an object with dot notation keys into a nested object.
|
|
18
33
|
* @param obj - The flattened object.
|
|
19
34
|
* @returns Nested object.
|
|
20
35
|
*/
|
|
21
|
-
static unflatten(obj: Record<string,
|
|
36
|
+
static unflatten(obj: Record<string, TranslationValue>): TranslationObject;
|
|
22
37
|
/**
|
|
23
38
|
* Create a deep copy of an object.
|
|
24
39
|
* @param obj - The object to clone.
|
|
@@ -33,6 +48,6 @@ declare class ObjectTransformer {
|
|
|
33
48
|
* @param overwriteArrays - Whether to overwrite arrays or merge them.
|
|
34
49
|
* @returns Merged object.
|
|
35
50
|
*/
|
|
36
|
-
static mergeObjects(target:
|
|
51
|
+
static mergeObjects(target: TranslationObject, source: TranslationObject, overwriteArrays?: boolean): TranslationObject;
|
|
37
52
|
}
|
|
38
53
|
export default ObjectTransformer;
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Utility class for object manipulation.
|
|
3
3
|
*/
|
|
4
|
+
const isPlainObject = (value) => value !== null &&
|
|
5
|
+
typeof value === "object" &&
|
|
6
|
+
!Array.isArray(value) &&
|
|
7
|
+
value.constructor === Object;
|
|
4
8
|
class ObjectTransformer {
|
|
5
9
|
/**
|
|
6
10
|
* Flatten a nested object into a single-level object with dot notation keys.
|
|
@@ -11,10 +15,8 @@ class ObjectTransformer {
|
|
|
11
15
|
*/
|
|
12
16
|
static flatten(obj, prefix = "", maxDepth = 20) {
|
|
13
17
|
if (!obj || typeof obj !== "object" || Array.isArray(obj)) {
|
|
14
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
15
18
|
return prefix ? { [prefix]: obj } : {};
|
|
16
19
|
}
|
|
17
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
18
20
|
const result = {};
|
|
19
21
|
const stack = [{ obj, prefix, depth: 0 }];
|
|
20
22
|
while (stack.length > 0) {
|
|
@@ -32,10 +34,7 @@ class ObjectTransformer {
|
|
|
32
34
|
const key = keys[i];
|
|
33
35
|
const value = currentObj[key];
|
|
34
36
|
const newKey = currentPrefix ? `${currentPrefix}.${key}` : key;
|
|
35
|
-
if (value
|
|
36
|
-
typeof value === "object" &&
|
|
37
|
-
!Array.isArray(value) &&
|
|
38
|
-
value.constructor === Object) {
|
|
37
|
+
if (isPlainObject(value)) {
|
|
39
38
|
stack.push({ obj: value, prefix: newKey, depth: depth + 1 });
|
|
40
39
|
}
|
|
41
40
|
else {
|
|
@@ -50,7 +49,6 @@ class ObjectTransformer {
|
|
|
50
49
|
* @param obj - The flattened object.
|
|
51
50
|
* @returns Nested object.
|
|
52
51
|
*/
|
|
53
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
54
52
|
static unflatten(obj) {
|
|
55
53
|
if (!obj || typeof obj !== "object") {
|
|
56
54
|
return {};
|
|
@@ -79,12 +77,12 @@ class ObjectTransformer {
|
|
|
79
77
|
const lastIndex = partsLength - 1;
|
|
80
78
|
for (let i = 0; i < lastIndex; i++) {
|
|
81
79
|
const part = keyParts[i];
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
current[part] =
|
|
80
|
+
let next = current[part];
|
|
81
|
+
if (next == null || typeof next !== "object" || Array.isArray(next)) {
|
|
82
|
+
next = {};
|
|
83
|
+
current[part] = next;
|
|
86
84
|
}
|
|
87
|
-
current =
|
|
85
|
+
current = next;
|
|
88
86
|
}
|
|
89
87
|
current[keyParts[lastIndex]] = value;
|
|
90
88
|
}
|
|
@@ -96,44 +94,33 @@ class ObjectTransformer {
|
|
|
96
94
|
* @param seen - Internal set to track circular references.
|
|
97
95
|
* @returns Deep copy of the object.
|
|
98
96
|
*/
|
|
99
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
100
97
|
static deepClone(obj, seen = new WeakSet()) {
|
|
101
98
|
if (obj === null || typeof obj !== "object") {
|
|
102
99
|
return obj;
|
|
103
100
|
}
|
|
104
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
105
101
|
if (seen.has(obj)) {
|
|
106
|
-
//
|
|
102
|
+
// Circular reference: break the cycle with an empty object
|
|
107
103
|
return {};
|
|
108
104
|
}
|
|
109
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
110
105
|
seen.add(obj);
|
|
111
106
|
if (Array.isArray(obj)) {
|
|
112
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
113
107
|
const result = obj.map((item) => this.deepClone(item, seen));
|
|
114
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
115
108
|
seen.delete(obj);
|
|
116
109
|
return result;
|
|
117
110
|
}
|
|
118
111
|
if (obj instanceof Date) {
|
|
119
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
120
112
|
seen.delete(obj);
|
|
121
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
122
113
|
return new Date(obj.getTime());
|
|
123
114
|
}
|
|
124
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
125
115
|
if (obj.constructor !== Object) {
|
|
126
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
127
116
|
seen.delete(obj);
|
|
128
117
|
return obj;
|
|
129
118
|
}
|
|
130
|
-
|
|
119
|
+
const source = obj;
|
|
131
120
|
const result = {};
|
|
132
|
-
for (const key of Object.keys(
|
|
133
|
-
|
|
134
|
-
result[key] = this.deepClone(obj[key], seen);
|
|
121
|
+
for (const key of Object.keys(source)) {
|
|
122
|
+
result[key] = this.deepClone(source[key], seen);
|
|
135
123
|
}
|
|
136
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
137
124
|
seen.delete(obj);
|
|
138
125
|
return result;
|
|
139
126
|
}
|
|
@@ -165,7 +152,12 @@ class ObjectTransformer {
|
|
|
165
152
|
else if (sourceValue &&
|
|
166
153
|
typeof sourceValue === "object" &&
|
|
167
154
|
sourceValue.constructor === Object) {
|
|
168
|
-
|
|
155
|
+
// Merge into an existing object; anything else at this key (primitive, array,
|
|
156
|
+
// missing) is replaced by a copy of the source object.
|
|
157
|
+
result[key] =
|
|
158
|
+
targetValue !== undefined && isPlainObject(targetValue)
|
|
159
|
+
? this.mergeObjects(targetValue, sourceValue, overwriteArrays)
|
|
160
|
+
: this.deepClone(sourceValue);
|
|
169
161
|
}
|
|
170
162
|
else {
|
|
171
163
|
result[key] = sourceValue;
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
*/
|
|
4
4
|
class ProgressTracker {
|
|
5
5
|
logToConsole;
|
|
6
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
7
6
|
logFrequency;
|
|
8
7
|
_isUpdating;
|
|
9
8
|
_pendingUpdates;
|
|
@@ -15,7 +14,6 @@ class ProgressTracker {
|
|
|
15
14
|
success;
|
|
16
15
|
failed;
|
|
17
16
|
startTime;
|
|
18
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
19
17
|
endTime;
|
|
20
18
|
lastUpdateTime;
|
|
21
19
|
recentOperationTimes;
|
|
@@ -216,7 +214,8 @@ class ProgressTracker {
|
|
|
216
214
|
etaText = ` | ETA: ${Math.floor(eta / 3600)}h ${Math.floor((eta % 3600) / 60)}m`;
|
|
217
215
|
}
|
|
218
216
|
}
|
|
219
|
-
const
|
|
217
|
+
const finalLabel = this.failed > 0 ? "[ERR]" : "[OK]";
|
|
218
|
+
const spinner = final ? finalLabel : this._spinnerFrames[this._spinnerIndex];
|
|
220
219
|
const text = `${spinner} ${langInfo}${bar} ${percent.toFixed(1)}% | ${this.completed}/${this.total} items | OK ${this.success} | ERR ${this.failed}${etaText}`;
|
|
221
220
|
process.stderr.write("\r\x1b[K" + text);
|
|
222
221
|
if (final) {
|
|
@@ -1,37 +1,77 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Prompt template utilities for translation and analysis
|
|
3
3
|
*/
|
|
4
|
+
import type { CodeContext } from "../types/context.js";
|
|
5
|
+
import type { FlatTranslationValue } from "./object-transformer.js";
|
|
6
|
+
import type { LengthControlOptions } from "./quality/length-checker.js";
|
|
4
7
|
export interface PromptOptions {
|
|
5
8
|
mode?: "smart" | "strict" | "flexible" | "exact" | "relaxed";
|
|
6
|
-
lengthControl?:
|
|
9
|
+
lengthControl?: LengthControlOptions;
|
|
7
10
|
targetLang?: string;
|
|
8
11
|
detectedContext?: {
|
|
9
12
|
category?: string;
|
|
10
13
|
confidence?: number;
|
|
11
14
|
prompt?: string;
|
|
12
|
-
|
|
15
|
+
/** Current value in the target file; only a string triggers a revision request */
|
|
16
|
+
existingTranslation?: FlatTranslationValue;
|
|
13
17
|
similarTranslation?: {
|
|
14
18
|
source: string;
|
|
15
19
|
target: string;
|
|
16
20
|
similarity: number;
|
|
17
21
|
};
|
|
22
|
+
/** Where the key is used in the project's source code (deep context) */
|
|
23
|
+
code?: CodeContext;
|
|
18
24
|
};
|
|
19
25
|
styleGuide?: {
|
|
20
26
|
formality?: string;
|
|
21
27
|
toneOfVoice?: string;
|
|
22
28
|
};
|
|
23
|
-
categories?: Record<string,
|
|
29
|
+
categories?: Record<string, unknown>;
|
|
24
30
|
allowNewCategories?: boolean;
|
|
25
31
|
model?: string;
|
|
26
32
|
temperature?: number;
|
|
27
33
|
maxTokens?: number;
|
|
28
34
|
}
|
|
35
|
+
export interface ChatMessage {
|
|
36
|
+
role: "system" | "user";
|
|
37
|
+
content: string;
|
|
38
|
+
}
|
|
39
|
+
/** OpenAI-compatible chat payload (OpenAI, DeepSeek, xAI, DashScope translation) */
|
|
40
|
+
export interface ChatPrompt {
|
|
41
|
+
messages: ChatMessage[];
|
|
42
|
+
}
|
|
43
|
+
/** Anthropic Messages API payload: system prompt is a top-level field */
|
|
44
|
+
export interface AnthropicPrompt {
|
|
45
|
+
system: string;
|
|
46
|
+
messages: ChatMessage[];
|
|
47
|
+
}
|
|
48
|
+
/** Gemini generateContent payload */
|
|
49
|
+
export interface GeminiPrompt {
|
|
50
|
+
contents: {
|
|
51
|
+
parts: {
|
|
52
|
+
text: string;
|
|
53
|
+
}[];
|
|
54
|
+
}[];
|
|
55
|
+
}
|
|
56
|
+
/** DashScope native analysis payload */
|
|
57
|
+
export interface DashScopeAnalysisPrompt {
|
|
58
|
+
model: string;
|
|
59
|
+
input: {
|
|
60
|
+
messages: ChatMessage[];
|
|
61
|
+
};
|
|
62
|
+
parameters: {
|
|
63
|
+
temperature: number;
|
|
64
|
+
max_tokens: number;
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
export type TranslationPromptPayload = ChatPrompt | AnthropicPrompt | GeminiPrompt;
|
|
68
|
+
export type AnalysisPromptPayload = TranslationPromptPayload | DashScopeAnalysisPrompt;
|
|
29
69
|
/**
|
|
30
70
|
* Get translation prompt
|
|
31
71
|
*/
|
|
32
|
-
declare const getPrompt: (provider: string, sourceLang: string, targetLang: string, text: string, options?: PromptOptions) =>
|
|
72
|
+
declare const getPrompt: (provider: string, sourceLang: string, targetLang: string, text: string, options?: PromptOptions) => TranslationPromptPayload;
|
|
33
73
|
/**
|
|
34
74
|
* Get analysis prompt
|
|
35
75
|
*/
|
|
36
|
-
declare const getAnalysisPrompt: (provider: string, text: string, options?: PromptOptions) =>
|
|
76
|
+
declare const getAnalysisPrompt: (provider: string, text: string, options?: PromptOptions) => AnalysisPromptPayload;
|
|
37
77
|
export { getPrompt, getAnalysisPrompt };
|
|
@@ -52,11 +52,63 @@ const getLengthInstructions = (options) => {
|
|
|
52
52
|
return templateFn();
|
|
53
53
|
}
|
|
54
54
|
catch (error) {
|
|
55
|
-
|
|
55
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
56
|
+
console.warn(`Error generating length template for mode ${mode}:`, message);
|
|
56
57
|
}
|
|
57
58
|
}
|
|
58
59
|
return "TRANSLATION LENGTH: Keep translation concise and natural.";
|
|
59
60
|
};
|
|
61
|
+
/** What the prompt carries of a key's source-code usage (deep context). */
|
|
62
|
+
const CODE_CONTEXT_LIMITS = {
|
|
63
|
+
/** Characters of the file path, of each comment and of the code snippet */
|
|
64
|
+
textLength: 200,
|
|
65
|
+
/** Characters of the component name and of each prop value */
|
|
66
|
+
nameLength: 100,
|
|
67
|
+
comments: 3,
|
|
68
|
+
props: 10,
|
|
69
|
+
};
|
|
70
|
+
/** `text` on one line, cut to `max` characters. */
|
|
71
|
+
const clip = (text, max) => {
|
|
72
|
+
const line = text.replace(/\s+/g, " ").trim();
|
|
73
|
+
return line.length > max ? `${line.substring(0, max)}...` : line;
|
|
74
|
+
};
|
|
75
|
+
/**
|
|
76
|
+
* Where the key is used in the project's source code: file, component, comments, JSX props and
|
|
77
|
+
* a code snippet, cut to CODE_CONTEXT_LIMITS. Empty when the key was not found in the code.
|
|
78
|
+
*/
|
|
79
|
+
const getCodeContextInstructions = (code) => {
|
|
80
|
+
if (!code)
|
|
81
|
+
return "";
|
|
82
|
+
const { textLength, nameLength } = CODE_CONTEXT_LIMITS;
|
|
83
|
+
const lines = [`- File: ${clip(code.filePath, textLength)}:${code.line}`];
|
|
84
|
+
if (code.component) {
|
|
85
|
+
lines.push(`- Component: ${clip(code.component, nameLength)}`);
|
|
86
|
+
}
|
|
87
|
+
const comments = (code.comments ?? [])
|
|
88
|
+
.map((comment) => clip(comment, textLength))
|
|
89
|
+
.filter((comment) => comment.length > 0)
|
|
90
|
+
.slice(0, CODE_CONTEXT_LIMITS.comments);
|
|
91
|
+
if (comments.length > 0) {
|
|
92
|
+
lines.push(`- Comments: ${comments.map((comment) => `"${comment}"`).join("; ")}`);
|
|
93
|
+
}
|
|
94
|
+
const props = Object.entries(code.props ?? {})
|
|
95
|
+
.slice(0, CODE_CONTEXT_LIMITS.props)
|
|
96
|
+
.map(([name, value]) =>
|
|
97
|
+
// Expression values keep their braces: onClick={onDelete}
|
|
98
|
+
value.startsWith("{")
|
|
99
|
+
? `${name}=${clip(value, nameLength)}`
|
|
100
|
+
: `${name}="${clip(value, nameLength)}"`);
|
|
101
|
+
if (props.length > 0) {
|
|
102
|
+
lines.push(`- JSX props: ${props.join(" ")}`);
|
|
103
|
+
}
|
|
104
|
+
if (code.usageSnippet) {
|
|
105
|
+
lines.push(`- Code: ${clip(code.usageSnippet, textLength)}`);
|
|
106
|
+
}
|
|
107
|
+
return `
|
|
108
|
+
SOURCE CODE CONTEXT (where this text is used in the app; use it only to understand the text, do not translate it or include it in your answer):
|
|
109
|
+
${lines.join("\n")}
|
|
110
|
+
`;
|
|
111
|
+
};
|
|
60
112
|
/**
|
|
61
113
|
* Base translation prompt template
|
|
62
114
|
*/
|
|
@@ -113,13 +165,14 @@ Please maintain consistency with this style and vocabulary where appropriate.`;
|
|
|
113
165
|
}
|
|
114
166
|
const formality = options.styleGuide?.formality || "neutral";
|
|
115
167
|
const toneOfVoice = options.styleGuide?.toneOfVoice || "professional";
|
|
168
|
+
const codeContextInstructions = getCodeContextInstructions(options.detectedContext?.code);
|
|
116
169
|
return `
|
|
117
170
|
Translation Task: ${sourceLang} → ${targetLang}
|
|
118
171
|
${additionalInstructions}
|
|
119
172
|
|
|
120
173
|
Category: ${safeContext.category}
|
|
121
174
|
Context Instructions: ${safeContext.prompt}
|
|
122
|
-
|
|
175
|
+
${codeContextInstructions}
|
|
123
176
|
LENGTH CONTROL:
|
|
124
177
|
${lengthInstructions}
|
|
125
178
|
|
|
@@ -164,7 +217,8 @@ const baseAnalysisPromptTemplate = (text, options = {}) => {
|
|
|
164
217
|
}
|
|
165
218
|
}
|
|
166
219
|
catch (error) {
|
|
167
|
-
|
|
220
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
221
|
+
console.warn("Error processing categories:", message);
|
|
168
222
|
}
|
|
169
223
|
}
|
|
170
224
|
const maxTextLength = 1500;
|
|
@@ -401,7 +455,8 @@ const getPrompt = (provider, sourceLang, targetLang, text, options = {}) => {
|
|
|
401
455
|
return promptGenerator(sourceLang, targetLang, text, options);
|
|
402
456
|
}
|
|
403
457
|
catch (error) {
|
|
404
|
-
|
|
458
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
459
|
+
console.error(`Error generating prompt for provider ${provider}:`, message);
|
|
405
460
|
return translationPrompts.default(sourceLang, targetLang, text, options);
|
|
406
461
|
}
|
|
407
462
|
};
|
|
@@ -426,7 +481,8 @@ const getAnalysisPrompt = (provider, text, options = {}) => {
|
|
|
426
481
|
return promptGenerator(text, options);
|
|
427
482
|
}
|
|
428
483
|
catch (error) {
|
|
429
|
-
|
|
484
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
485
|
+
console.error(`Error generating analysis prompt for provider ${provider}:`, message);
|
|
430
486
|
return analysisPrompts.default(text, options);
|
|
431
487
|
}
|
|
432
488
|
};
|
|
@@ -2,45 +2,66 @@
|
|
|
2
2
|
* Base Quality Checker.
|
|
3
3
|
* Common infrastructure for all quality checkers.
|
|
4
4
|
*/
|
|
5
|
-
|
|
5
|
+
import type { ContextConfig } from "../../core/context-processor.js";
|
|
6
|
+
import type { CodeBlockFixResult, CodeBlockIssue } from "./code-block-checker.js";
|
|
7
|
+
import type { HtmlTagFixResult, HtmlTagIssue } from "./html-tag-checker.js";
|
|
8
|
+
import type { LengthCheckOptions, LengthControlOptions, LengthIssue } from "./length-checker.js";
|
|
9
|
+
import type { MarkdownFixResult, MarkdownIssue } from "./markdown-checker.js";
|
|
10
|
+
import type { PlaceholderFixResult, PlaceholderIssue } from "./placeholder-checker.js";
|
|
11
|
+
import type { PunctuationFixResult, PunctuationIssue } from "./punctuation-checker.js";
|
|
12
|
+
import type { QuoteFixResult, QuoteIssue } from "./quote-balance-checker.js";
|
|
13
|
+
import type { SpecialCharFixResult, SpecialCharIssue } from "./special-characters-checker.js";
|
|
14
|
+
import type { StyleFixResult, StyleGuideConfig, StyleIssue } from "./style-guide-checker.js";
|
|
15
|
+
/** Toggles for the individual quality checks (the `qualityChecks.rules` config section) */
|
|
16
|
+
export interface QualityRules {
|
|
6
17
|
placeholderConsistency?: boolean;
|
|
7
18
|
htmlTagsConsistency?: boolean;
|
|
8
19
|
punctuationCheck?: boolean;
|
|
20
|
+
quoteBalanceCheck?: boolean;
|
|
9
21
|
lengthValidation?: boolean;
|
|
10
22
|
sanitizeOutput?: boolean;
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
23
|
+
markdownPreservation?: boolean;
|
|
24
|
+
codeBlockPreservation?: boolean;
|
|
25
|
+
specialCharacters?: boolean;
|
|
26
|
+
}
|
|
27
|
+
export interface CheckerOptions extends QualityRules {
|
|
28
|
+
/** Master switch for quality checks (the `qualityChecks.enabled` config value) */
|
|
29
|
+
enabled?: boolean;
|
|
30
|
+
styleGuide?: StyleGuideConfig;
|
|
31
|
+
context?: Partial<ContextConfig>;
|
|
32
|
+
lengthControl?: LengthControlOptions;
|
|
18
33
|
}
|
|
34
|
+
/** Generic issue record built by {@link BaseChecker.createIssue} */
|
|
19
35
|
export interface Issue {
|
|
20
36
|
type: string;
|
|
21
37
|
message: string;
|
|
22
38
|
timestamp: string;
|
|
23
|
-
[key: string]:
|
|
39
|
+
[key: string]: unknown;
|
|
24
40
|
}
|
|
41
|
+
/** Generic fix record built by {@link BaseChecker.createFix} */
|
|
25
42
|
export interface Fix {
|
|
26
43
|
type: string;
|
|
27
44
|
message: string;
|
|
28
45
|
timestamp: string;
|
|
29
|
-
[key: string]:
|
|
46
|
+
[key: string]: unknown;
|
|
30
47
|
}
|
|
48
|
+
/** Any issue reported by one of the quality checkers */
|
|
49
|
+
export type QualityIssue = PlaceholderIssue | HtmlTagIssue | PunctuationIssue | QuoteIssue | LengthIssue | StyleIssue | MarkdownIssue | CodeBlockIssue | SpecialCharIssue;
|
|
50
|
+
/** Any fix applied by one of the quality checkers */
|
|
51
|
+
export type QualityFix = PlaceholderFixResult["appliedFixes"][number] | HtmlTagFixResult["appliedFixes"][number] | PunctuationFixResult["appliedFixes"][number] | QuoteFixResult["appliedFixes"][number] | StyleFixResult["appliedFixes"][number] | MarkdownFixResult["appliedFixes"][number] | CodeBlockFixResult["appliedFixes"][number] | SpecialCharFixResult["appliedFixes"][number];
|
|
31
52
|
export interface ValidationResult {
|
|
32
53
|
isValid: boolean;
|
|
33
|
-
issues:
|
|
54
|
+
issues: QualityIssue[];
|
|
34
55
|
source?: string;
|
|
35
56
|
translated?: string;
|
|
36
|
-
context?:
|
|
57
|
+
context?: Partial<ContextConfig>;
|
|
37
58
|
}
|
|
38
59
|
export interface FixResult {
|
|
39
60
|
originalText: string;
|
|
40
61
|
fixedText: string;
|
|
41
62
|
isModified: boolean;
|
|
42
|
-
issues:
|
|
43
|
-
fixes:
|
|
63
|
+
issues: QualityIssue[];
|
|
64
|
+
fixes: QualityFix[];
|
|
44
65
|
metadata?: {
|
|
45
66
|
sourceLength: number;
|
|
46
67
|
originalLength: number;
|
|
@@ -49,7 +70,8 @@ export interface FixResult {
|
|
|
49
70
|
};
|
|
50
71
|
}
|
|
51
72
|
declare class BaseChecker {
|
|
52
|
-
|
|
73
|
+
/** Effective options: rule defaults overridden by the constructor options */
|
|
74
|
+
readonly rules: CheckerOptions;
|
|
53
75
|
protected styleGuide: {
|
|
54
76
|
formality: string;
|
|
55
77
|
toneOfVoice: string;
|
|
@@ -62,15 +84,15 @@ declare class BaseChecker {
|
|
|
62
84
|
/**
|
|
63
85
|
* Create an issue object.
|
|
64
86
|
*/
|
|
65
|
-
createIssue(type: string, message: string, details?: Record<string,
|
|
87
|
+
createIssue(type: string, message: string, details?: Record<string, unknown>): Issue;
|
|
66
88
|
/**
|
|
67
89
|
* Create a fix object.
|
|
68
90
|
*/
|
|
69
|
-
createFix(type: string, message: string, details?: Record<string,
|
|
91
|
+
createFix(type: string, message: string, details?: Record<string, unknown>): Fix;
|
|
70
92
|
/**
|
|
71
93
|
* Validate translation.
|
|
72
94
|
*/
|
|
73
|
-
validate(_sourceText: string, _translatedText: string, _options?:
|
|
95
|
+
validate(_sourceText: string, _translatedText: string, _options?: LengthCheckOptions): ValidationResult;
|
|
74
96
|
/**
|
|
75
97
|
* Validate and fix translation.
|
|
76
98
|
*/
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* Common infrastructure for all quality checkers.
|
|
4
4
|
*/
|
|
5
5
|
class BaseChecker {
|
|
6
|
+
/** Effective options: rule defaults overridden by the constructor options */
|
|
6
7
|
rules;
|
|
7
8
|
styleGuide;
|
|
8
9
|
/**
|
|
@@ -48,7 +49,6 @@ class BaseChecker {
|
|
|
48
49
|
/**
|
|
49
50
|
* Validate translation.
|
|
50
51
|
*/
|
|
51
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
52
52
|
validate(_sourceText, _translatedText, _options) {
|
|
53
53
|
const issues = [];
|
|
54
54
|
return {
|
|
@@ -59,7 +59,6 @@ class BaseChecker {
|
|
|
59
59
|
/**
|
|
60
60
|
* Validate and fix translation.
|
|
61
61
|
*/
|
|
62
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
63
62
|
validateAndFix(sourceText, translatedText) {
|
|
64
63
|
return {
|
|
65
64
|
originalText: translatedText,
|