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.
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 +22 -17
  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
@@ -4,7 +4,6 @@ import gettextParser from "gettext-parser";
4
4
  */
5
5
  export class PoAdapter {
6
6
  extensions = [".po", ".pot"];
7
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
8
7
  async parse(content) {
9
8
  try {
10
9
  const po = gettextParser.po.parse(content);
@@ -22,11 +21,10 @@ export class PoAdapter {
22
21
  return result;
23
22
  }
24
23
  catch (error) {
25
- throw new Error(`PO parse error: ${error.message}`);
24
+ throw new Error(`PO parse error: ${error instanceof Error ? error.message : String(error)}`);
26
25
  }
27
26
  }
28
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
29
- async serialize(data, options = {}) {
27
+ async serialize(data) {
30
28
  const translations = {
31
29
  "": {},
32
30
  };
@@ -34,7 +32,8 @@ export class PoAdapter {
34
32
  for (const [key, value] of Object.entries(data)) {
35
33
  translations[""][key] = {
36
34
  msgid: key,
37
- msgstr: [value],
35
+ // gettext-parser writes falsy msgstr values as "" and stringifies the rest
36
+ msgstr: [value ? String(value) : ""],
38
37
  };
39
38
  }
40
39
  return gettextParser.po
@@ -1,9 +1,10 @@
1
+ import type { TranslationObject } from "../../utils/object-transformer.js";
1
2
  import { FileFormatAdapter } from "./base-adapter.js";
2
3
  /**
3
4
  * Adapter for Java .properties files
4
5
  */
5
6
  export declare class PropertiesAdapter implements FileFormatAdapter {
6
7
  extensions: string[];
7
- parse(content: string): Promise<Record<string, any>>;
8
- serialize(data: Record<string, any>, options?: any): Promise<string>;
8
+ parse(content: string): Promise<TranslationObject>;
9
+ serialize(data: TranslationObject): Promise<string>;
9
10
  }
@@ -4,17 +4,15 @@ import dotProperties from "dot-properties";
4
4
  */
5
5
  export class PropertiesAdapter {
6
6
  extensions = [".properties"];
7
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
8
7
  async parse(content) {
9
8
  try {
10
9
  return dotProperties.parse(content);
11
10
  }
12
11
  catch (error) {
13
- throw new Error(`Properties parse error: ${error.message}`);
12
+ throw new Error(`Properties parse error: ${error instanceof Error ? error.message : String(error)}`);
14
13
  }
15
14
  }
16
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
17
- async serialize(data, options = {}) {
15
+ async serialize(data) {
18
16
  // dot-properties doesn't have a standardized stringify, simple implementation
19
17
  // or used a different library, but let's do a simple one for now or use the lib if it supports it.
20
18
  // Checking docs: dot-properties is mostly a parser. 'properties' npm package is better for writing but we installed dot-properties.
@@ -1,9 +1,10 @@
1
- import { FileFormatAdapter } from "./base-adapter.js";
1
+ import type { TranslationObject } from "../../utils/object-transformer.js";
2
+ import { FileFormatAdapter, SerializeOptions } from "./base-adapter.js";
2
3
  /**
3
4
  * Adapter for YAML files
4
5
  */
5
6
  export declare class YamlAdapter implements FileFormatAdapter {
6
7
  extensions: string[];
7
- parse(content: string): Promise<Record<string, any>>;
8
- serialize(data: Record<string, any>, options?: any): Promise<string>;
8
+ parse(content: string): Promise<TranslationObject>;
9
+ serialize(data: TranslationObject, options?: SerializeOptions): Promise<string>;
9
10
  }
@@ -4,16 +4,14 @@ import yaml from "js-yaml";
4
4
  */
5
5
  export class YamlAdapter {
6
6
  extensions = [".yaml", ".yml"];
7
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
8
7
  async parse(content) {
9
8
  try {
10
9
  return yaml.load(content) || {};
11
10
  }
12
11
  catch (error) {
13
- throw new Error(`YAML parse error: ${error.message}`);
12
+ throw new Error(`YAML parse error: ${error instanceof Error ? error.message : String(error)}`);
14
13
  }
15
14
  }
16
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
17
15
  async serialize(data, options = {}) {
18
16
  return yaml.dump(data, {
19
17
  indent: options.indent || 2,
@@ -1,16 +1,30 @@
1
- interface CategoryConfig {
1
+ /** A content category and the keywords that identify it. */
2
+ export interface CategoryConfig {
2
3
  keywords: string[];
4
+ /** Multiplier applied to this category's keyword matches (default 1.0). */
3
5
  weight?: number;
6
+ /** Instruction added to the translation prompt for texts in this category. */
4
7
  prompt?: string;
5
8
  }
9
+ /** Settings of the AI-based analysis; the whole object is also read by AIContextAnalyzer. */
10
+ export interface ContextAnalysisOptions {
11
+ model?: string;
12
+ temperature?: number;
13
+ maxTokens?: number;
14
+ timeout?: number;
15
+ retries?: number;
16
+ /** Cache analysis results (default true). */
17
+ cacheAnalysis?: boolean;
18
+ }
19
+ /** The `context` configuration section: category detection for translation texts. */
6
20
  export interface ContextConfig {
7
21
  enabled?: boolean;
8
22
  useAI?: boolean;
9
23
  aiProvider?: string;
10
24
  minTextLength?: number;
11
- analysisOptions?: {
12
- cacheAnalysis?: boolean;
13
- };
25
+ allowNewCategories?: boolean;
26
+ debug?: boolean;
27
+ analysisOptions?: ContextAnalysisOptions;
14
28
  categories: Record<string, CategoryConfig>;
15
29
  detection: {
16
30
  threshold: number;
@@ -21,7 +35,8 @@ export interface ContextConfig {
21
35
  prompt: string;
22
36
  };
23
37
  }
24
- interface AnalysisResult {
38
+ /** Detected category of a text. */
39
+ export interface AnalysisResult {
25
40
  category: string;
26
41
  confidence: number;
27
42
  prompt?: string;
@@ -42,7 +57,7 @@ declare class ContextProcessor {
42
57
  * Create a new ContextProcessor instance.
43
58
  * @param config - Configuration object.
44
59
  */
45
- constructor(config: any);
60
+ constructor(config: ContextConfig);
46
61
  /**
47
62
  * Initialize keyword matching patterns from configuration.
48
63
  */
@@ -10,13 +10,11 @@ class ContextProcessor {
10
10
  config;
11
11
  keywordCache;
12
12
  aiAnalyzer;
13
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
14
13
  resultCache;
15
14
  /**
16
15
  * Create a new ContextProcessor instance.
17
16
  * @param config - Configuration object.
18
17
  */
19
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
20
18
  constructor(config) {
21
19
  this.config = config;
22
20
  this.keywordCache = new Map();
@@ -56,7 +54,6 @@ class ContextProcessor {
56
54
  if (!texts || !Array.isArray(texts) || !this.config.enabled) {
57
55
  return texts.map(() => this.getFallback());
58
56
  }
59
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
60
57
  const results = [];
61
58
  const aiTexts = [];
62
59
  const aiIndices = [];
@@ -120,7 +117,8 @@ class ContextProcessor {
120
117
  }
121
118
  }
122
119
  catch (error) {
123
- console.error("Batch AI context analysis failed:", error.message);
120
+ const message = error instanceof Error ? error.message : String(error);
121
+ console.error("Batch AI context analysis failed:", message);
124
122
  }
125
123
  }
126
124
  return results;
@@ -141,7 +139,6 @@ class ContextProcessor {
141
139
  */
142
140
  _performKeywordAnalysis(text) {
143
141
  const lowerText = text.toLowerCase();
144
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
145
142
  const results = new Map();
146
143
  let totalScore = 0;
147
144
  const isShortText = text.length < 500;
@@ -183,7 +180,6 @@ class ContextProcessor {
183
180
  /**
184
181
  * Determine the best category match from analysis results.
185
182
  */
186
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
187
183
  getBestMatch(results, totalScore) {
188
184
  if (totalScore === 0)
189
185
  return this.getFallback();
@@ -212,7 +208,6 @@ class ContextProcessor {
212
208
  /**
213
209
  * Generate a collision-resistant cache key for text and context.
214
210
  */
215
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
216
211
  getCacheKey(text, context) {
217
212
  let keyContent;
218
213
  if (text.length <= 200) {
@@ -34,7 +34,7 @@ export class FrameworkDetector {
34
34
  return { framework: "react", localesDir: "public/locales", fileFormat: "json" };
35
35
  }
36
36
  }
37
- catch (e) {
37
+ catch {
38
38
  // No package.json or invalid
39
39
  }
40
40
  // Check for pubspec.yaml (Flutter)
@@ -23,7 +23,7 @@ export class StructureDetector {
23
23
  }
24
24
  return LocaleStructure.UNKNOWN;
25
25
  }
26
- catch (error) {
26
+ catch {
27
27
  return LocaleStructure.UNKNOWN;
28
28
  }
29
29
  }
@@ -1,9 +1,8 @@
1
- import { TranslateOptions, ProviderConfig } from "../providers/base-provider.js";
1
+ import { TranslateOptions, ProviderConfig, ChatMessage } from "../providers/base-provider.js";
2
2
  export interface ProviderModule {
3
3
  translate(text: string, sourceLang: string, targetLang: string, options?: TranslateOptions): Promise<string>;
4
4
  analyze?(prompt: string, options?: ProviderConfig): Promise<string>;
5
- chat?(messages: any[], options?: ProviderConfig): Promise<string>;
6
- [key: string]: any;
5
+ chat?(messages: ChatMessage[], options?: ProviderConfig): Promise<string>;
7
6
  }
8
7
  export interface ProviderWrapper {
9
8
  name: string;
@@ -41,7 +40,7 @@ declare class FallbackProvider {
41
40
  _calculatePriority(text: string): number;
42
41
  translate(text: string, sourceLang: string, targetLang: string, options?: TranslateOptions): Promise<string>;
43
42
  analyze(prompt: string, options?: ProviderConfig): Promise<string>;
44
- chat(messages: any[], options?: ProviderConfig): Promise<string>;
43
+ chat(messages: ChatMessage[], options?: ProviderConfig): Promise<string>;
45
44
  getStats(): Record<string, ProviderStats>;
46
45
  reset(): void;
47
46
  resetStats(): void;
@@ -43,7 +43,6 @@ class FallbackProvider {
43
43
  return 1;
44
44
  }
45
45
  async translate(text, sourceLang, targetLang, options = {}) {
46
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
47
46
  const errors = [];
48
47
  const startTime = Date.now();
49
48
  const startIndex = this.currentIndex;
@@ -89,7 +88,6 @@ class FallbackProvider {
89
88
  },
90
89
  }), this._calculatePriority(text));
91
90
  const responseTime = Date.now() - providerStartTime;
92
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
93
91
  const stats = this.providerStats.get(providerName);
94
92
  stats.success++;
95
93
  stats.consecutiveFailures = 0;
@@ -102,14 +100,14 @@ class FallbackProvider {
102
100
  return result;
103
101
  }
104
102
  catch (error) {
103
+ const message = error instanceof Error ? error.message : String(error);
105
104
  if (this.providerStats.has(providerName)) {
106
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
107
105
  const stats = this.providerStats.get(providerName);
108
106
  stats.failure++;
109
107
  stats.consecutiveFailures++;
110
108
  stats.lastError = {
111
109
  time: new Date(),
112
- message: error.message,
110
+ message,
113
111
  };
114
112
  if (stats.consecutiveFailures >= 5) {
115
113
  this._disableProvider(providerData, 2 * 60 * 1000);
@@ -119,13 +117,13 @@ class FallbackProvider {
119
117
  this.lastErrorTime = Date.now();
120
118
  errors.push({
121
119
  provider: providerName,
122
- error: error.message,
120
+ error: message,
123
121
  attempt: currentAttempt + 1,
124
122
  });
125
123
  const safeProviderName = `Provider_${(currentProviderIndex % totalProviders) + 1}`;
126
- const safeErrorMessage = error.message.includes("API") || error.message.includes("key")
124
+ const safeErrorMessage = message.includes("API") || message.includes("key")
127
125
  ? "Authentication or API error"
128
- : error.message.substring(0, 100);
126
+ : message.substring(0, 100);
129
127
  console.warn(`${safeProviderName} failed (attempt ${currentAttempt + 1}/${maxAttempts}): ${safeErrorMessage}`);
130
128
  currentAttempt++;
131
129
  this.currentIndex++;
@@ -135,7 +133,6 @@ class FallbackProvider {
135
133
  throw new Error(`All providers failed after ${maxAttempts} attempts (${Date.now() - startTime}ms):\n${JSON.stringify(errors, null, 2)}`);
136
134
  }
137
135
  async analyze(prompt, options = {}) {
138
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
139
136
  const errors = [];
140
137
  const startTime = Date.now();
141
138
  const savedIndex = this.currentIndex;
@@ -185,7 +182,6 @@ class FallbackProvider {
185
182
  },
186
183
  }), this._calculatePriority(prompt));
187
184
  const responseTime = Date.now() - providerStartTime;
188
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
189
185
  const stats = this.providerStats.get(providerName);
190
186
  stats.success++;
191
187
  stats.consecutiveFailures = 0;
@@ -199,14 +195,14 @@ class FallbackProvider {
199
195
  return result;
200
196
  }
201
197
  catch (error) {
198
+ const message = error instanceof Error ? error.message : String(error);
202
199
  if (this.providerStats.has(providerName)) {
203
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
204
200
  const stats = this.providerStats.get(providerName);
205
201
  stats.failure++;
206
202
  stats.consecutiveFailures++;
207
203
  stats.lastError = {
208
204
  time: new Date(),
209
- message: error.message,
205
+ message,
210
206
  };
211
207
  if (stats.consecutiveFailures >= 5) {
212
208
  this._disableProvider(providerData, 2 * 60 * 1000);
@@ -216,13 +212,13 @@ class FallbackProvider {
216
212
  this.lastErrorTime = Date.now();
217
213
  errors.push({
218
214
  provider: providerName,
219
- error: error.message,
215
+ error: message,
220
216
  attempt: currentAttempt + 1,
221
217
  });
222
218
  const safeProviderName = `Provider_${(currentProviderIndex % totalProviders) + 1}`;
223
- const safeErrorMessage = error.message.includes("API") || error.message.includes("key")
219
+ const safeErrorMessage = message.includes("API") || message.includes("key")
224
220
  ? "Authentication or API error"
225
- : error.message.substring(0, 100);
221
+ : message.substring(0, 100);
226
222
  console.warn(`${safeProviderName} failed (attempt ${currentAttempt + 1}/${maxAttempts}): ${safeErrorMessage}`);
227
223
  currentAttempt++;
228
224
  this.currentIndex++;
@@ -232,7 +228,6 @@ class FallbackProvider {
232
228
  throw new Error(`All providers failed for analysis after ${maxAttempts} attempts (${Date.now() - startTime}ms):\n${JSON.stringify(errors, null, 2)}`);
233
229
  }
234
230
  async chat(messages, options = {}) {
235
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
236
231
  const errors = [];
237
232
  const startTime = Date.now();
238
233
  const savedIndex = this.currentIndex;
@@ -283,7 +278,6 @@ class FallbackProvider {
283
278
  }), 1 // Priority 1 (normal)
284
279
  );
285
280
  const responseTime = Date.now() - providerStartTime;
286
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
287
281
  const stats = this.providerStats.get(providerName);
288
282
  stats.success++;
289
283
  stats.consecutiveFailures = 0;
@@ -297,14 +291,14 @@ class FallbackProvider {
297
291
  return result;
298
292
  }
299
293
  catch (error) {
294
+ const message = error instanceof Error ? error.message : String(error);
300
295
  if (this.providerStats.has(providerName)) {
301
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
302
296
  const stats = this.providerStats.get(providerName);
303
297
  stats.failure++;
304
298
  stats.consecutiveFailures++;
305
299
  stats.lastError = {
306
300
  time: new Date(),
307
- message: error.message,
301
+ message,
308
302
  };
309
303
  if (stats.consecutiveFailures >= 5) {
310
304
  this._disableProvider(providerData, 2 * 60 * 1000);
@@ -314,13 +308,13 @@ class FallbackProvider {
314
308
  this.lastErrorTime = Date.now();
315
309
  errors.push({
316
310
  provider: providerName,
317
- error: error.message,
311
+ error: message,
318
312
  attempt: currentAttempt + 1,
319
313
  });
320
314
  const safeProviderName = `Provider_${(currentProviderIndex % totalProviders) + 1}`;
321
- const safeErrorMessage = error.message.includes("API") || error.message.includes("key")
315
+ const safeErrorMessage = message.includes("API") || message.includes("key")
322
316
  ? "Authentication or API error"
323
- : error.message.substring(0, 100);
317
+ : message.substring(0, 100);
324
318
  console.warn(`${safeProviderName} failed (attempt ${currentAttempt + 1}/${maxAttempts}): ${safeErrorMessage}`);
325
319
  currentAttempt++;
326
320
  this.currentIndex++;
@@ -1,21 +1,52 @@
1
- import ProgressTracker from "../utils/progress-tracker.js";
1
+ import { ProviderStatus, RateLimiterConfig } from "../utils/rate-limiter.js";
2
+ import ProgressTracker, { ProgressOptions } from "../utils/progress-tracker.js";
2
3
  import QualityChecker from "../utils/quality/index.js";
4
+ import type { QualityRules } from "../utils/quality/base-checker.js";
5
+ import type { LengthControlOptions } from "../utils/quality/length-checker.js";
6
+ import type { StyleGuideConfig } from "../utils/quality/style-guide-checker.js";
3
7
  import { ContextConfig } from "./context-processor.js";
4
8
  import { LRUCache } from "lru-cache";
5
- import GlossaryManager from "../utils/glossary-manager.js";
6
- import { TranslationResult } from "./pipeline/context.js";
9
+ import GlossaryManager, { GlossaryConfig } from "../utils/glossary-manager.js";
10
+ import type { FlatTranslationValue } from "../utils/object-transformer.js";
11
+ import type { RetryConfig } from "../utils/retry-helper.js";
12
+ import type { ProviderConfig } from "../providers/base-provider.js";
13
+ import { TranslationMeta, TranslationResult } from "./pipeline/context.js";
7
14
  import { CacheStats } from "./pipeline/steps/CacheReadStep.js";
8
15
  import { ConfidenceSettings } from "./pipeline/steps/TranslationStep.js";
9
16
  import { SourceCodeAnalyzer } from "../services/source-analyzer.js";
17
+ /** The `rateLimiter` configuration section. */
18
+ export interface RateLimiterOptions extends RateLimiterConfig {
19
+ enabled?: boolean;
20
+ }
21
+ /** The `retryOptions` configuration section; providers read `maxRetries` and `initialDelay`. */
22
+ export interface ProviderRetryOptions {
23
+ maxRetries?: number;
24
+ initialDelay?: number;
25
+ maxDelay?: number;
26
+ jitter?: boolean;
27
+ retryableErrors?: string[];
28
+ perProviderRetry?: Record<string, Partial<RetryConfig>>;
29
+ }
30
+ /** The `vectorMemory` configuration section. */
31
+ export interface VectorMemoryOptions extends VectorStoreConfig {
32
+ embeddingProvider?: string;
33
+ embeddingModel?: string;
34
+ }
35
+ /**
36
+ * Settings read by the orchestrator and its pipeline steps. The whole object is also
37
+ * forwarded to the provider's `translate()` (see TranslationStep).
38
+ */
10
39
  export interface OrchestratorOptions {
11
40
  context: ContextConfig;
41
+ /** Scan the project's source code for where each key is used (default true). */
12
42
  deepContext?: boolean;
13
- progressOptions?: any;
14
- styleGuide?: any;
15
- lengthControl?: any;
43
+ progressOptions?: ProgressOptions;
44
+ styleGuide?: StyleGuideConfig;
45
+ lengthControl?: LengthControlOptions;
16
46
  qualityChecks?: {
17
47
  enabled?: boolean;
18
- rules?: any;
48
+ rules?: QualityRules;
49
+ autoFix?: boolean;
19
50
  };
20
51
  advanced?: {
21
52
  timeoutMs?: number;
@@ -24,34 +55,49 @@ export interface OrchestratorOptions {
24
55
  autoOptimize?: boolean;
25
56
  debug?: boolean;
26
57
  };
27
- glossary?: any;
28
- rateLimiter?: {
29
- queueStrategy?: string;
30
- queueTimeout?: number;
31
- adaptiveThrottling?: boolean;
32
- providerLimits?: any;
33
- };
58
+ glossary?: GlossaryConfig;
59
+ rateLimiter?: RateLimiterOptions;
34
60
  cacheSize?: number;
35
61
  cacheTTL?: number;
36
62
  updateAgeOnGet?: boolean;
63
+ /** @deprecated Has no effect: expired cache entries are always translated again. */
37
64
  allowStaleCache?: boolean;
65
+ /** @deprecated Has no effect: the cache never refreshes entries in the background. */
38
66
  staleWhileRevalidate?: boolean;
67
+ /** Turns confidence scoring on; translations scoring below it need review (`--min-confidence`). */
39
68
  minConfidence?: number;
69
+ /** Turns confidence scoring on and saves the flagged translations for `locai review`. */
40
70
  saveReviewQueue?: boolean;
41
71
  confidenceScoring?: {
72
+ /** Score every new translation (default false). */
42
73
  enabled?: boolean;
74
+ /** Translations scoring below it need review (default 0.7); the top-level minConfidence wins. */
43
75
  minConfidence?: number;
76
+ /** Save the flagged translations to .localize-cache/review-queue.json for `locai review`. */
44
77
  saveReviewQueue?: boolean;
78
+ /** Translations scoring at or above it are marked auto-approved (default 0.9). */
45
79
  autoApproveThreshold?: number;
80
+ /** Older name of minConfidence; when both are set, the higher one applies. */
46
81
  reviewThreshold?: number;
82
+ /** Translations scoring below it are rejected and not written (default 0.5). */
47
83
  rejectThreshold?: number;
48
84
  };
49
85
  concurrencyLimit?: number;
50
86
  cacheEnabled?: boolean;
87
+ /** Language of the source texts. */
51
88
  source: string;
52
- [key: string]: any;
89
+ vectorMemory?: VectorMemoryOptions;
90
+ /** Analyzer shared between orchestrators; a new one is created when omitted. */
91
+ sourceCodeAnalyzer?: SourceCodeAnalyzer;
92
+ /** Translation provider; the available providers are tried in order when unset. */
93
+ apiProvider?: string;
94
+ useFallback?: boolean;
95
+ fallbackOrder?: string[];
96
+ /** Model settings per provider, keyed by provider name. */
97
+ apiConfig?: Record<string, ProviderConfig>;
98
+ retryOptions?: ProviderRetryOptions;
53
99
  }
54
- import { VectorStore } from "../services/vector-store.js";
100
+ import { VectorStore, VectorStoreConfig } from "../services/vector-store.js";
55
101
  import { EmbeddingProvider } from "../services/embedding-provider.js";
56
102
  import { VectorMemoryStats } from "./pipeline/steps/VectorCacheReadStep.js";
57
103
  interface AdvancedSettings {
@@ -61,21 +107,44 @@ interface AdvancedSettings {
61
107
  autoOptimize: boolean;
62
108
  debug: boolean;
63
109
  }
110
+ /** One key to translate, as passed to {@link Orchestrator.processTranslations}. */
64
111
  interface TranslationItem {
65
112
  key: string;
66
113
  text: string;
67
114
  targetLang: string;
68
- existingTranslation?: any;
69
- [key: string]: any;
115
+ existingTranslation?: FlatTranslationValue;
116
+ }
117
+ /** Cache counters plus the cache's current size, capacity and hit rate. */
118
+ export interface CacheStatus extends CacheStats {
119
+ size: number;
120
+ capacity: number;
121
+ hitRate: number;
122
+ }
123
+ /** Snapshot returned by {@link Orchestrator.getStatus}. */
124
+ export interface OrchestratorStatus {
125
+ /** Empty when the cache is disabled. */
126
+ cache: CacheStatus | Record<string, never>;
127
+ rateLimiter: Record<string, ProviderStatus>;
128
+ concurrency: number;
129
+ advanced: AdvancedSettings;
130
+ confidence: {
131
+ minThreshold?: number;
132
+ autoApproveThreshold: number;
133
+ reviewThreshold: number;
134
+ rejectThreshold: number;
135
+ reviewQueueSize: number;
136
+ autoApprovedCount: number;
137
+ rejectedCount: number;
138
+ } | null;
70
139
  }
71
140
  declare class Orchestrator {
72
141
  private options;
73
142
  private contextProcessor;
74
143
  progress: ProgressTracker;
75
- qualityChecker: QualityChecker | any;
144
+ qualityChecker: QualityChecker | null;
76
145
  advanced: AdvancedSettings;
77
146
  glossaryManager: GlossaryManager;
78
- translationCache: LRUCache<string, any> | null;
147
+ translationCache: LRUCache<string, TranslationResult> | null;
79
148
  cacheStats: CacheStats;
80
149
  vectorStore: VectorStore;
81
150
  embeddingProvider: EmbeddingProvider;
@@ -87,18 +156,21 @@ declare class Orchestrator {
87
156
  private _shutdownCallback;
88
157
  constructor(options: OrchestratorOptions);
89
158
  private _buildPipeline;
90
- processTranslation(key: string, text: string, targetLang: string, contextData: any, existingTranslation: any): Promise<TranslationResult>;
159
+ processTranslation(key: string, text: string, targetLang: string, contextData: TranslationMeta, existingTranslation?: FlatTranslationValue): Promise<TranslationResult>;
160
+ /** Count a scored translation and queue it when it needs review or was rejected. */
161
+ private _recordConfidence;
91
162
  processTranslations(items: TranslationItem[]): Promise<TranslationResult[]>;
92
163
  private _chunkArray;
93
164
  private _applyAutoOptimizations;
94
165
  clearCache(): void;
95
166
  resetCacheStats(): void;
96
- getCacheStats(): any;
97
- _refreshCacheEntry(key: string, context: any): Promise<void>;
98
- getStatus(): any;
167
+ getCacheStats(): CacheStatus | Record<string, never>;
168
+ getStatus(): OrchestratorStatus;
169
+ /**
170
+ * Add the translations this orchestrator flagged to `.localize-cache/review-queue.json`,
171
+ * when saving the review queue is enabled.
172
+ */
99
173
  saveReviewQueue(): void;
100
- private _groupByLanguage;
101
- private _groupByConfidenceLevel;
102
174
  destroy(): void;
103
175
  }
104
176
  export default Orchestrator;