locai 2.6.2 → 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.
Files changed (131) hide show
  1. package/README.md +23 -25
  2. package/dist/cli/helpers.d.ts +34 -2
  3. package/dist/cli/helpers.js +96 -37
  4. package/dist/cli.js +40 -39
  5. package/dist/commands/review.d.ts +33 -8
  6. package/dist/commands/review.js +89 -22
  7. package/dist/commands/translator.d.ts +4 -2
  8. package/dist/commands/translator.js +1 -1
  9. package/dist/config/index.d.ts +53 -66
  10. package/dist/config/index.js +2 -1
  11. package/dist/config/setup.d.ts +3 -2
  12. package/dist/config/setup.js +5 -3
  13. package/dist/core/adapters/android-adapter.d.ts +3 -2
  14. package/dist/core/adapters/android-adapter.js +12 -13
  15. package/dist/core/adapters/base-adapter.d.ts +13 -2
  16. package/dist/core/adapters/ios-adapter.d.ts +3 -2
  17. package/dist/core/adapters/json-adapter.d.ts +4 -3
  18. package/dist/core/adapters/json-adapter.js +1 -3
  19. package/dist/core/adapters/po-adapter.d.ts +3 -2
  20. package/dist/core/adapters/po-adapter.js +4 -5
  21. package/dist/core/adapters/properties-adapter.d.ts +3 -2
  22. package/dist/core/adapters/properties-adapter.js +2 -4
  23. package/dist/core/adapters/yaml-adapter.d.ts +4 -3
  24. package/dist/core/adapters/yaml-adapter.js +1 -3
  25. package/dist/core/context-processor.d.ts +21 -6
  26. package/dist/core/context-processor.js +2 -7
  27. package/dist/core/detection/framework-detector.js +1 -1
  28. package/dist/core/detection/structure-detector.js +1 -1
  29. package/dist/core/fallback-provider.d.ts +3 -4
  30. package/dist/core/fallback-provider.js +15 -21
  31. package/dist/core/orchestrator.d.ts +98 -26
  32. package/dist/core/orchestrator.js +56 -116
  33. package/dist/core/pipeline/Pipeline.d.ts +2 -2
  34. package/dist/core/pipeline/Pipeline.js +1 -3
  35. package/dist/core/pipeline/context.d.ts +69 -13
  36. package/dist/core/pipeline/context.js +1 -7
  37. package/dist/core/pipeline/steps/CacheReadStep.d.ts +10 -7
  38. package/dist/core/pipeline/steps/CacheReadStep.js +1 -12
  39. package/dist/core/pipeline/steps/CacheWriteStep.d.ts +2 -2
  40. package/dist/core/pipeline/steps/CacheWriteStep.js +1 -8
  41. package/dist/core/pipeline/steps/ConfidenceCheckStep.d.ts +5 -0
  42. package/dist/core/pipeline/steps/ConfidenceCheckStep.js +24 -44
  43. package/dist/core/pipeline/steps/ContextEnrichmentStep.js +3 -14
  44. package/dist/core/pipeline/steps/QualityCheckStep.d.ts +1 -1
  45. package/dist/core/pipeline/steps/QualityCheckStep.js +0 -2
  46. package/dist/core/pipeline/steps/ToneCheckStep.js +1 -1
  47. package/dist/core/pipeline/steps/TranslationStep.d.ts +13 -3
  48. package/dist/core/pipeline/steps/TranslationStep.js +21 -23
  49. package/dist/core/pipeline/steps/VectorCacheReadStep.d.ts +9 -1
  50. package/dist/core/pipeline/steps/VectorCacheReadStep.js +3 -5
  51. package/dist/core/pipeline/steps/VectorCacheWriteStep.d.ts +2 -1
  52. package/dist/core/pipeline/steps/VectorCacheWriteStep.js +2 -1
  53. package/dist/core/provider-factory.d.ts +15 -9
  54. package/dist/core/provider-factory.js +42 -32
  55. package/dist/providers/anthropic.d.ts +5 -4
  56. package/dist/providers/anthropic.js +3 -2
  57. package/dist/providers/api-types.d.ts +74 -0
  58. package/dist/providers/api-types.js +5 -0
  59. package/dist/providers/base-provider.d.ts +27 -31
  60. package/dist/providers/base-provider.js +22 -46
  61. package/dist/providers/dashscope.js +0 -2
  62. package/dist/providers/deepseek.js +0 -1
  63. package/dist/providers/gemini.js +0 -1
  64. package/dist/providers/openai.d.ts +3 -3
  65. package/dist/providers/openai.js +0 -1
  66. package/dist/providers/xai.js +0 -1
  67. package/dist/services/embedding-provider.js +4 -2
  68. package/dist/services/language-processor.d.ts +5 -3
  69. package/dist/services/language-processor.js +38 -43
  70. package/dist/services/source-analyzer.d.ts +24 -0
  71. package/dist/services/source-analyzer.js +161 -68
  72. package/dist/services/translation-service.d.ts +31 -13
  73. package/dist/services/translation-service.js +116 -71
  74. package/dist/services/vector-store.d.ts +15 -2
  75. package/dist/services/vector-store.js +14 -10
  76. package/dist/types/index.d.ts +31 -33
  77. package/dist/utils/ai-context-analyzer.d.ts +12 -3
  78. package/dist/utils/ai-context-analyzer.js +18 -10
  79. package/dist/utils/confidence-scorer.d.ts +16 -19
  80. package/dist/utils/confidence-scorer.js +36 -125
  81. package/dist/utils/error-helper.d.ts +27 -6
  82. package/dist/utils/error-helper.js +10 -9
  83. package/dist/utils/file-manager.d.ts +8 -7
  84. package/dist/utils/file-manager.js +22 -23
  85. package/dist/utils/glossary-manager.d.ts +22 -3
  86. package/dist/utils/glossary-manager.js +47 -28
  87. package/dist/utils/graceful-shutdown.d.ts +7 -1
  88. package/dist/utils/graceful-shutdown.js +1 -2
  89. package/dist/utils/input-validator.d.ts +53 -66
  90. package/dist/utils/input-validator.js +100 -151
  91. package/dist/utils/locale-paths.d.ts +7 -0
  92. package/dist/utils/locale-paths.js +16 -0
  93. package/dist/utils/logger.d.ts +8 -8
  94. package/dist/utils/logger.js +7 -15
  95. package/dist/utils/object-transformer.d.ts +20 -5
  96. package/dist/utils/object-transformer.js +20 -28
  97. package/dist/utils/progress-tracker.js +2 -3
  98. package/dist/utils/prompt-templates.d.ts +45 -5
  99. package/dist/utils/prompt-templates.js +61 -5
  100. package/dist/utils/quality/base-checker.d.ts +40 -18
  101. package/dist/utils/quality/base-checker.js +1 -2
  102. package/dist/utils/quality/code-block-checker.d.ts +13 -5
  103. package/dist/utils/quality/index.d.ts +2 -1
  104. package/dist/utils/quality/index.js +8 -27
  105. package/dist/utils/quality/length-checker.d.ts +35 -5
  106. package/dist/utils/quality/length-checker.js +2 -3
  107. package/dist/utils/quality/markdown-checker.d.ts +14 -7
  108. package/dist/utils/quality/special-characters-checker.d.ts +18 -1
  109. package/dist/utils/quality/special-characters-checker.js +0 -1
  110. package/dist/utils/quality/style-guide-checker.d.ts +1 -1
  111. package/dist/utils/quality/style-guide-checker.js +0 -2
  112. package/dist/utils/quality/tone-verifier.js +1 -1
  113. package/dist/utils/rate-limiter.d.ts +17 -1
  114. package/dist/utils/rate-limiter.js +12 -11
  115. package/dist/utils/retry-helper.d.ts +21 -3
  116. package/dist/utils/retry-helper.js +11 -3
  117. package/dist/utils/review-queue.d.ts +45 -0
  118. package/dist/utils/review-queue.js +53 -0
  119. package/dist/utils/state-manager.d.ts +12 -3
  120. package/dist/utils/state-manager.js +32 -7
  121. package/dist/utils/statistics-manager.d.ts +11 -1
  122. package/dist/utils/statistics-manager.js +8 -0
  123. package/dist/utils/ui-manager.d.ts +2 -1
  124. package/dist/utils/ui-manager.js +15 -1
  125. package/package.json +14 -9
  126. package/dist/commands/interactive-review.d.ts +0 -9
  127. package/dist/commands/interactive-review.js +0 -109
  128. package/dist/core/pipeline/steps/__tests__/ToneCheckStep.test.d.ts +0 -1
  129. package/dist/core/pipeline/steps/__tests__/ToneCheckStep.test.js +0 -100
  130. package/dist/utils/quality/__tests__/tone-verifier.test.d.ts +0 -1
  131. package/dist/utils/quality/__tests__/tone-verifier.test.js +0 -91
@@ -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
- export interface TransformedObject {
5
- [key: string]: any;
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: TransformedObject, prefix?: string, maxDepth?: number): Record<string, string | number | boolean | null>;
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, any>): TransformedObject;
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: TransformedObject, source: TransformedObject, overwriteArrays?: boolean): TransformedObject;
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 !== null &&
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
- if (current[part] == null ||
83
- typeof current[part] !== "object" ||
84
- Array.isArray(current[part])) {
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 = current[part];
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
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
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
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
119
+ const source = obj;
131
120
  const result = {};
132
- for (const key of Object.keys(obj)) {
133
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
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
- result[key] = this.mergeObjects(targetValue || {}, sourceValue, overwriteArrays);
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 spinner = final ? "[OK]" : this._spinnerFrames[this._spinnerIndex];
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?: any;
9
+ lengthControl?: LengthControlOptions;
7
10
  targetLang?: string;
8
11
  detectedContext?: {
9
12
  category?: string;
10
13
  confidence?: number;
11
14
  prompt?: string;
12
- existingTranslation?: string;
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, any>;
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) => any;
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) => any;
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
- console.warn(`Error generating length template for mode ${mode}:`, error.message);
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
- console.warn("Error processing categories:", error.message);
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
- console.error(`Error generating prompt for provider ${provider}:`, error.message);
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
- console.error(`Error generating analysis prompt for provider ${provider}:`, error.message);
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
- export interface CheckerOptions {
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
- rules?: Record<string, any>;
12
- styleGuide?: {
13
- formality?: string;
14
- toneOfVoice?: string;
15
- };
16
- context?: Record<string, any>;
17
- [key: string]: any;
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]: any;
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]: any;
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: Issue[];
54
+ issues: QualityIssue[];
34
55
  source?: string;
35
56
  translated?: string;
36
- context?: Record<string, any>;
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: Issue[];
43
- fixes: Fix[];
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
- protected rules: Record<string, boolean | any>;
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, any>): Issue;
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, any>): Fix;
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?: any): ValidationResult;
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,