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
@@ -1,8 +1,19 @@
1
+ /** Details for a code block count mismatch */
2
+ export interface CodeBlockCountDetails {
3
+ sourceCount: number;
4
+ translatedCount: number;
5
+ }
6
+ /** Details for a code block whose content changed */
7
+ export interface CodeBlockModifiedDetails {
8
+ index: number;
9
+ sourceCode: string;
10
+ translatedCode: string;
11
+ }
1
12
  export interface CodeBlockIssue {
2
13
  type: "code-block-count" | "code-block-modified" | "code-block-restore";
3
14
  severity: "error" | "critical";
4
15
  message: string;
5
- details?: any;
16
+ details?: CodeBlockCountDetails | CodeBlockModifiedDetails;
6
17
  }
7
18
  export interface CodeBlockFixResult {
8
19
  text: string;
@@ -40,10 +51,7 @@ declare class CodeBlockChecker {
40
51
  /**
41
52
  * Restore missing code blocks from source.
42
53
  */
43
- restoreCodeBlocks(sourceText: string, translatedText: string, sourceCodeBlocks: CodeBlock[]): {
44
- text: string;
45
- appliedFixes: any[];
46
- };
54
+ restoreCodeBlocks(sourceText: string, translatedText: string, sourceCodeBlocks: CodeBlock[]): Pick<CodeBlockFixResult, "text" | "appliedFixes">;
47
55
  /**
48
56
  * Protect code blocks before translation.
49
57
  * Replaces code blocks with tokens to prevent translation.
@@ -1,4 +1,5 @@
1
1
  import BaseChecker, { CheckerOptions, FixResult, ValidationResult } from "./base-checker.js";
2
+ import { LengthCheckOptions } from "./length-checker.js";
2
3
  /**
3
4
  * Main Quality Checker Class.
4
5
  * Orchestrates various quality checks and fixes for translated text.
@@ -26,7 +27,7 @@ declare class QualityChecker extends BaseChecker {
26
27
  /**
27
28
  * Validate translated text against source text.
28
29
  */
29
- validate(sourceText: string, translatedText: string, options?: Record<string, any>): ValidationResult;
30
+ validate(sourceText: string, translatedText: string, options?: LengthCheckOptions): ValidationResult;
30
31
  /**
31
32
  * Validate and attempt to fix translated text.
32
33
  */
@@ -43,7 +43,10 @@ class QualityChecker extends BaseChecker {
43
43
  this.lengthChecker = new LengthChecker();
44
44
  this.textSanitizer = new TextSanitizer();
45
45
  this.quoteBalanceChecker = new QuoteBalanceChecker();
46
- this.styleGuideChecker = new StyleGuideChecker(this.styleGuide);
46
+ this.styleGuideChecker = new StyleGuideChecker({
47
+ ...this.styleGuide,
48
+ conventions: this.rules.styleGuide?.conventions,
49
+ });
47
50
  this.markdownChecker = new MarkdownChecker();
48
51
  this.codeBlockChecker = new CodeBlockChecker();
49
52
  this.specialCharactersChecker = new SpecialCharactersChecker();
@@ -54,51 +57,37 @@ class QualityChecker extends BaseChecker {
54
57
  validate(sourceText, translatedText, options = {}) {
55
58
  const issues = [];
56
59
  if (this.rules.placeholderConsistency) {
57
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
58
60
  issues.push(...this.placeholderChecker.checkPlaceholders(sourceText, translatedText));
59
61
  }
60
62
  if (this.rules.htmlTagsConsistency) {
61
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
62
63
  issues.push(...this.htmlTagChecker.checkHtmlTags(sourceText, translatedText));
63
64
  }
64
65
  if (this.rules.punctuationCheck) {
65
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
66
66
  issues.push(...this.punctuationChecker.checkPunctuation(sourceText, translatedText));
67
67
  }
68
68
  // Quote balance validation
69
69
  if (this.rules.quoteBalanceCheck !== false) {
70
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
71
70
  issues.push(...this.quoteBalanceChecker.checkQuoteBalance(translatedText));
72
- issues.push(
73
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
74
- ...this.quoteBalanceChecker.validateQuoteConsistency(sourceText, translatedText));
71
+ issues.push(...this.quoteBalanceChecker.validateQuoteConsistency(sourceText, translatedText));
75
72
  }
76
73
  if (this.rules.lengthValidation) {
77
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
78
74
  issues.push(...this.lengthChecker.checkLength(sourceText, translatedText, options));
79
75
  }
80
76
  // Style guide validation
81
77
  if (this.styleGuide && this.styleGuideChecker) {
82
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
83
78
  issues.push(...this.styleGuideChecker.checkStyleGuide(sourceText, translatedText));
84
79
  }
85
80
  // Markdown preservation
86
81
  if (this.rules.markdownPreservation) {
87
- issues.push(
88
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
89
- ...this.markdownChecker.checkMarkdownPreservation(sourceText, translatedText));
82
+ issues.push(...this.markdownChecker.checkMarkdownPreservation(sourceText, translatedText));
90
83
  }
91
84
  // Code block preservation
92
85
  if (this.rules.codeBlockPreservation) {
93
- issues.push(
94
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
95
- ...this.codeBlockChecker.checkCodeBlockPreservation(sourceText, translatedText));
86
+ issues.push(...this.codeBlockChecker.checkCodeBlockPreservation(sourceText, translatedText));
96
87
  }
97
88
  // Special characters preservation
98
89
  if (this.rules.specialCharacters) {
99
- issues.push(
100
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
101
- ...this.specialCharactersChecker.checkSpecialCharacters(sourceText, translatedText));
90
+ issues.push(...this.specialCharactersChecker.checkSpecialCharacters(sourceText, translatedText));
102
91
  }
103
92
  if (this.rules.sanitizeOutput) {
104
93
  translatedText = this.sanitizeTranslation(translatedText);
@@ -124,21 +113,18 @@ class QualityChecker extends BaseChecker {
124
113
  if (this.rules.placeholderConsistency) {
125
114
  const result = this.placeholderChecker.fixPlaceholders(sourceText, fixedText);
126
115
  fixedText = result.text;
127
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
128
116
  issues.push(...result.foundIssues);
129
117
  fixes.push(...result.appliedFixes);
130
118
  }
131
119
  if (this.rules.htmlTagsConsistency) {
132
120
  const result = this.htmlTagChecker.fixHtmlTags(sourceText, fixedText);
133
121
  fixedText = result.text;
134
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
135
122
  issues.push(...result.foundIssues);
136
123
  fixes.push(...result.appliedFixes);
137
124
  }
138
125
  if (this.rules.punctuationCheck) {
139
126
  const result = this.punctuationChecker.fixPunctuation(sourceText, fixedText);
140
127
  fixedText = result.text;
141
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
142
128
  issues.push(...result.foundIssues);
143
129
  fixes.push(...result.appliedFixes);
144
130
  }
@@ -146,7 +132,6 @@ class QualityChecker extends BaseChecker {
146
132
  if (this.rules.quoteBalanceCheck !== false) {
147
133
  const result = this.quoteBalanceChecker.fixQuoteBalance(fixedText);
148
134
  fixedText = result.text;
149
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
150
135
  issues.push(...result.foundIssues);
151
136
  fixes.push(...result.appliedFixes);
152
137
  }
@@ -154,7 +139,6 @@ class QualityChecker extends BaseChecker {
154
139
  if (this.styleGuide && this.styleGuideChecker) {
155
140
  const result = this.styleGuideChecker.fixStyleGuide(sourceText, fixedText);
156
141
  fixedText = result.text;
157
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
158
142
  issues.push(...result.foundIssues);
159
143
  fixes.push(...result.appliedFixes);
160
144
  }
@@ -162,7 +146,6 @@ class QualityChecker extends BaseChecker {
162
146
  if (this.rules.markdownPreservation) {
163
147
  const result = this.markdownChecker.fixMarkdownPreservation(sourceText, fixedText);
164
148
  fixedText = result.text;
165
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
166
149
  issues.push(...result.foundIssues);
167
150
  fixes.push(...result.appliedFixes);
168
151
  }
@@ -170,7 +153,6 @@ class QualityChecker extends BaseChecker {
170
153
  if (this.rules.codeBlockPreservation) {
171
154
  const result = this.codeBlockChecker.fixCodeBlockPreservation(sourceText, fixedText);
172
155
  fixedText = result.text;
173
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
174
156
  issues.push(...result.foundIssues);
175
157
  fixes.push(...result.appliedFixes);
176
158
  }
@@ -178,7 +160,6 @@ class QualityChecker extends BaseChecker {
178
160
  if (this.rules.specialCharacters) {
179
161
  const result = this.specialCharactersChecker.fixSpecialCharacters(sourceText, fixedText);
180
162
  fixedText = result.text;
181
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
182
163
  issues.push(...result.foundIssues);
183
164
  fixes.push(...result.appliedFixes);
184
165
  }
@@ -1,5 +1,35 @@
1
+ export type LengthMode = "strict" | "flexible" | "exact" | "relaxed" | "smart";
2
+ /** Modes whose tolerance is a single symmetric deviation */
3
+ export type StandardLengthMode = Exclude<LengthMode, "smart">;
4
+ /** Allowed deviation as a fraction of the source length (e.g. max 0.2 = up to 20% longer) */
5
+ export interface LengthDeviationBounds {
6
+ min?: number;
7
+ max?: number;
8
+ }
9
+ export interface SmartLengthRules {
10
+ /** Symmetric tolerance used when no language/context bound applies */
11
+ default?: number;
12
+ byLanguage?: Record<string, LengthDeviationBounds>;
13
+ byContext?: Record<string, LengthDeviationBounds>;
14
+ }
15
+ export interface LengthControlRules extends Partial<Record<StandardLengthMode, number>> {
16
+ smart?: SmartLengthRules;
17
+ }
18
+ /** The `lengthControl` section of the configuration */
19
+ export interface LengthControlOptions {
20
+ mode?: LengthMode;
21
+ rules?: LengthControlRules;
22
+ }
23
+ /** Options consulted by {@link LengthChecker.checkLength} */
24
+ export interface LengthCheckOptions {
25
+ lengthControl?: LengthControlOptions;
26
+ targetLang?: string;
27
+ detectedContext?: {
28
+ category?: string;
29
+ };
30
+ }
1
31
  export interface LengthConfig {
2
- mode: "strict" | "flexible" | "exact" | "relaxed" | "smart";
32
+ mode: LengthMode;
3
33
  maxDeviation: number;
4
34
  minDeviation: number;
5
35
  targetLang?: string;
@@ -34,7 +64,7 @@ declare class LengthChecker {
34
64
  /**
35
65
  * Check if translated text length is acceptable.
36
66
  */
37
- checkLength(source: string, translated: string, options?: any): LengthIssue[];
67
+ checkLength(source: string, translated: string, options?: LengthCheckOptions): LengthIssue[];
38
68
  /**
39
69
  * Calculate length of text ignoring whitespace.
40
70
  */
@@ -42,15 +72,15 @@ declare class LengthChecker {
42
72
  /**
43
73
  * Get configuration for length check.
44
74
  */
45
- getConfig(options: any): LengthConfig;
75
+ getConfig(options: LengthCheckOptions): LengthConfig;
46
76
  /**
47
77
  * Get configuration for smart mode.
48
78
  */
49
- getSmartModeConfig(lengthControl: any, targetLang: string, context: string): LengthConfig;
79
+ getSmartModeConfig(lengthControl: LengthControlOptions, targetLang: string, context: string): LengthConfig;
50
80
  /**
51
81
  * Get configuration for standard mode.
52
82
  */
53
- getStandardModeConfig(lengthControl: any, mode: any, targetLang: string, context: string): LengthConfig;
83
+ getStandardModeConfig(lengthControl: LengthControlOptions, mode: StandardLengthMode, targetLang: string, context: string): LengthConfig;
54
84
  /**
55
85
  * Calculate allowed length ratio range.
56
86
  */
@@ -52,7 +52,7 @@ class LengthChecker {
52
52
  type: "length",
53
53
  severity: "error",
54
54
  message: "Length validation failed due to technical error",
55
- details: { error: error.message },
55
+ details: { error: error instanceof Error ? error.message : String(error) },
56
56
  },
57
57
  ];
58
58
  }
@@ -100,8 +100,7 @@ class LengthChecker {
100
100
  getStandardModeConfig(lengthControl, mode, targetLang, context) {
101
101
  const deviation = lengthControl.rules?.[mode] || this.defaultConfig.maxDeviation;
102
102
  return {
103
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
104
- mode: mode,
103
+ mode,
105
104
  targetLang,
106
105
  context,
107
106
  maxDeviation: deviation,
@@ -3,8 +3,19 @@ export interface MarkdownIssue {
3
3
  severity: "error" | "warning" | "critical";
4
4
  message: string;
5
5
  pattern?: string;
6
- details?: any;
6
+ details?: {
7
+ sourceCount: number;
8
+ translatedCount: number;
9
+ };
10
+ }
11
+ /** A markdown link captured from source text */
12
+ export interface MarkdownLink {
13
+ full: string;
14
+ text: string;
15
+ url: string;
7
16
  }
17
+ /** A captured markdown element: the matched text, or the parts of a link */
18
+ export type MarkdownElement = string | MarkdownLink;
8
19
  export interface MarkdownFixResult {
9
20
  text: string;
10
21
  foundIssues: MarkdownIssue[];
@@ -35,15 +46,11 @@ declare class MarkdownChecker {
35
46
  /**
36
47
  * Extract markdown elements from text.
37
48
  */
38
- extractMarkdownElements(text: string): Record<string, any[]>;
49
+ extractMarkdownElements(text: string): Record<string, MarkdownElement[]>;
39
50
  /**
40
51
  * Restore markdown elements that were lost in translation.
41
52
  */
42
- restoreMarkdownElements(text: string, elements: any[], type: string): {
43
- text: string;
44
- foundIssues: MarkdownIssue[];
45
- appliedFixes: any[];
46
- };
53
+ restoreMarkdownElements(text: string, elements: MarkdownElement[], type: string): MarkdownFixResult;
47
54
  /**
48
55
  * Fix broken markdown links.
49
56
  */
@@ -1,9 +1,26 @@
1
+ /** Details for a per-category character count mismatch */
2
+ export interface SpecialCharCountDetails {
3
+ sourceChars: string[];
4
+ translatedChars: string[];
5
+ missing: string[];
6
+ extra: string[];
7
+ }
8
+ /** Details for a special character that disappeared from the translation */
9
+ export interface SpecialCharSubstitutionDetails {
10
+ originalChar: string;
11
+ charCode: number;
12
+ }
13
+ /** Details for an emoji count mismatch */
14
+ export interface EmojiDetails {
15
+ sourceEmojis: string[];
16
+ translatedEmojis: string[];
17
+ }
1
18
  export interface SpecialCharIssue {
2
19
  type: "special-characters" | "special-character-substitution" | "emoji-preservation" | "missing-special-chars" | "character-substitution";
3
20
  category?: string;
4
21
  severity: "error" | "warning";
5
22
  message: string;
6
- details?: any;
23
+ details?: SpecialCharCountDetails | SpecialCharSubstitutionDetails | EmojiDetails;
7
24
  }
8
25
  export interface SpecialCharFixResult {
9
26
  text: string;
@@ -200,7 +200,6 @@ class SpecialCharactersChecker {
200
200
  if (check && fixedText.includes(wrong)) {
201
201
  const count = (fixedText.match(new RegExp(wrong, "g")) || []).length;
202
202
  foundIssues.push({
203
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
204
203
  type: "character-substitution",
205
204
  severity: "warning", // Added severity
206
205
  message: `Found ${count} instances of "${wrong}" that should be "${correct}"`,
@@ -8,7 +8,7 @@ export interface StyleGuideConfig {
8
8
  conventions?: {
9
9
  useOxfordComma?: boolean;
10
10
  useSentenceCase?: boolean;
11
- [key: string]: any;
11
+ [convention: string]: boolean | undefined;
12
12
  };
13
13
  }
14
14
  export interface StyleIssue {
@@ -21,7 +21,6 @@ class StyleGuideChecker {
21
21
  /**
22
22
  * Check if text follows style guide conventions.
23
23
  */
24
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
25
24
  checkStyleGuide(_sourceText, translatedText) {
26
25
  const issues = [];
27
26
  if (this.styleGuide.conventions.useOxfordComma) {
@@ -85,7 +84,6 @@ class StyleGuideChecker {
85
84
  /**
86
85
  * Fix style guide issues.
87
86
  */
88
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
89
87
  fixStyleGuide(sourceText, translatedText) {
90
88
  let fixedText = translatedText;
91
89
  const foundIssues = [];
@@ -14,7 +14,7 @@ export class ToneVerifier {
14
14
  // Try to get a specific "judge" provider if configured, otherwise default
15
15
  this.provider = ProviderFactory.getProvider(providerName, true);
16
16
  }
17
- catch (error) {
17
+ catch {
18
18
  // If specific provider fails, might fall back to any available
19
19
  const available = ProviderFactory.getAvailableProviders();
20
20
  if (available.length > 0) {
@@ -7,6 +7,22 @@ export interface RateLimiterConfig {
7
7
  concurrency?: number;
8
8
  }>;
9
9
  }
10
+ export interface ProviderStatus {
11
+ queueSize: number;
12
+ processing: number;
13
+ requestsUsed: number;
14
+ requestsLimit: number;
15
+ resetIn: number;
16
+ errorRate: number;
17
+ adjustments: {
18
+ rpm: number;
19
+ concurrency: number;
20
+ };
21
+ config: {
22
+ maxConcurrent: number;
23
+ rpm: number;
24
+ };
25
+ }
10
26
  /**
11
27
  * Handles API rate limiting and request throttling.
12
28
  * Supports provider-specific limits and adaptive throttling.
@@ -57,7 +73,7 @@ declare class RateLimiter {
57
73
  * Get complete rate limiter status.
58
74
  * @returns Status object including active metrics.
59
75
  */
60
- getStatus(): Record<string, any>;
76
+ getStatus(): Record<string, ProviderStatus>;
61
77
  /**
62
78
  * Get current configuration.
63
79
  * @returns Configuration object.
@@ -122,7 +122,6 @@ class RateLimiter {
122
122
  * @param priority - Task priority (higher is better).
123
123
  * @returns Task result.
124
124
  */
125
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
126
125
  async enqueue(provider, task, priority = 0) {
127
126
  provider = provider.toLowerCase();
128
127
  if (!this.providers[provider]) {
@@ -131,7 +130,8 @@ class RateLimiter {
131
130
  return new Promise((resolve, reject) => {
132
131
  const queueItem = {
133
132
  task,
134
- resolve,
133
+ // Only ever called with this item's own task result, which is a T.
134
+ resolve: resolve,
135
135
  reject,
136
136
  timestamp: Date.now(),
137
137
  priority,
@@ -194,7 +194,7 @@ class RateLimiter {
194
194
  item.reject(new Error(`Request timed out in queue after ${this.config.queueTimeout}ms`));
195
195
  }
196
196
  catch (error) {
197
- console.error(`Failed to reject timed out request: ${error.message}`);
197
+ console.error(`Failed to reject timed out request: ${error instanceof Error ? error.message : String(error)}`);
198
198
  }
199
199
  });
200
200
  });
@@ -228,12 +228,15 @@ class RateLimiter {
228
228
  }
229
229
  catch (error) {
230
230
  this._trackErrorRate(provider, true);
231
- console.error(`Provider ${provider} task failed:`, {
232
- error: error.message,
233
- queueTime: queueTime,
234
- processingTime: performance.now() - startTime,
235
- queueSize: queue.length,
236
- });
231
+ // The caller reports the failure; the per-attempt details are for --debug
232
+ if (process.env.DEBUG === "true") {
233
+ console.error(`Provider ${provider} task failed:`, {
234
+ error: error instanceof Error ? error.message : String(error),
235
+ queueTime: queueTime,
236
+ processingTime: performance.now() - startTime,
237
+ queueSize: queue.length,
238
+ });
239
+ }
237
240
  reject(error);
238
241
  }
239
242
  finally {
@@ -337,9 +340,7 @@ class RateLimiter {
337
340
  * Get complete rate limiter status.
338
341
  * @returns Status object including active metrics.
339
342
  */
340
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
341
343
  getStatus() {
342
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
343
344
  const status = {};
344
345
  Object.entries(this.providers).forEach(([name, config]) => {
345
346
  const errorRates = this.metrics.errorRates[name];
@@ -1,6 +1,24 @@
1
1
  /**
2
2
  * Retry helper for API operations with exponential backoff
3
3
  */
4
+ /**
5
+ * Error-like value thrown by a retried operation. The retry logic reads
6
+ * `status` / `code` / `message` and annotates the error with retry metadata.
7
+ */
8
+ export interface RetryableError {
9
+ status?: number;
10
+ code?: string;
11
+ message?: string;
12
+ retryInfo?: RetryInfo;
13
+ retryStats?: RetryStats;
14
+ }
15
+ export interface RetryInfo {
16
+ attemptNumber: number;
17
+ maxRetries: number;
18
+ willRetry: boolean;
19
+ context: Record<string, unknown>;
20
+ provider?: string;
21
+ }
4
22
  export interface RetryOptions {
5
23
  provider?: string;
6
24
  perProviderRetry?: Record<string, Partial<RetryConfig>>;
@@ -9,8 +27,8 @@ export interface RetryOptions {
9
27
  initialDelay?: number;
10
28
  maxDelay?: number;
11
29
  context?: string;
12
- logContext?: Record<string, any>;
13
- retryCondition?: (error: any, attempts: number, maxRetries: number) => boolean | Promise<boolean>;
30
+ logContext?: Record<string, unknown>;
31
+ retryCondition?: (error: RetryableError, attempts: number, maxRetries: number) => boolean | Promise<boolean>;
14
32
  }
15
33
  export interface RetryConfig {
16
34
  maxRetries: number;
@@ -40,7 +58,7 @@ declare class RetryHelper {
40
58
  * Default retry condition based on error type.
41
59
  * Checks against retryableErrors list.
42
60
  */
43
- static defaultRetryCondition(error: any, retryableErrors?: string[]): boolean;
61
+ static defaultRetryCondition(error: RetryableError, retryableErrors?: string[]): boolean;
44
62
  /**
45
63
  * Promise-based delay.
46
64
  */
@@ -60,7 +60,12 @@ class RetryHelper {
60
60
  }
61
61
  return result;
62
62
  }
63
- catch (error) {
63
+ catch (caught) {
64
+ // Errors are annotated with retry metadata below, which a primitive rejection
65
+ // (a thrown string, undefined) cannot hold; wrap those so the message survives.
66
+ const error = typeof caught === "object" && caught !== null
67
+ ? caught
68
+ : new Error(String(caught));
64
69
  attempts++;
65
70
  lastError = error;
66
71
  if (!error.retryInfo) {
@@ -126,7 +131,10 @@ class RetryHelper {
126
131
  return true;
127
132
  }
128
133
  // Check for server errors (5xx)
129
- if (retryableErrors.includes("server") && error.status >= 500 && error.status < 600) {
134
+ if (retryableErrors.includes("server") &&
135
+ error.status !== undefined &&
136
+ error.status >= 500 &&
137
+ error.status < 600) {
130
138
  return true;
131
139
  }
132
140
  // Check for unknown/general errors
@@ -152,7 +160,7 @@ class RetryHelper {
152
160
  error.message?.includes("connection")) {
153
161
  return true;
154
162
  }
155
- return error.status >= 500 || !error.status;
163
+ return (error.status !== undefined && error.status >= 500) || !error.status;
156
164
  }
157
165
  return false;
158
166
  }
@@ -0,0 +1,45 @@
1
+ import type { ConfidenceResult } from "./confidence-scorer.js";
2
+ /** One key in one target language. */
3
+ export interface TranslationRef {
4
+ language: string;
5
+ key: string;
6
+ }
7
+ /** A translation flagged by confidence scoring, queued for `locai review`. */
8
+ export interface ReviewQueueItem extends TranslationRef {
9
+ source: string;
10
+ translation: string;
11
+ confidence: ConfidenceResult;
12
+ sourceLang: string;
13
+ category: string;
14
+ /** Scored below rejectThreshold, so the translation was not written to the locale file. */
15
+ rejected: boolean;
16
+ timestamp: string;
17
+ }
18
+ /** Content of `.localize-cache/review-queue.json`. */
19
+ export interface ReviewQueueFile {
20
+ timestamp: string;
21
+ items: ReviewQueueItem[];
22
+ }
23
+ /** The review queue file of the project in the working directory. */
24
+ export declare const reviewQueuePath: () => string;
25
+ /**
26
+ * Read the review queue.
27
+ * @returns The queued items, or null when there is no queue file.
28
+ */
29
+ export declare function readReviewQueue(): ReviewQueueItem[] | null;
30
+ /**
31
+ * Add the flagged translations of a run to the review queue. Queued items for translations the
32
+ * run scored again are dropped first: the new score replaces them.
33
+ * @param scored - Every translation the run scored.
34
+ * @param flagged - The translations the run flagged for review or rejected.
35
+ * @returns The queue file and the number of items it now holds.
36
+ */
37
+ export declare function updateReviewQueue(scored: TranslationRef[], flagged: ReviewQueueItem[]): {
38
+ file: string;
39
+ size: number;
40
+ };
41
+ /**
42
+ * Remove reviewed translations from the review queue.
43
+ * @param reviewed - The translations a review approved, edited or rejected.
44
+ */
45
+ export declare function removeFromReviewQueue(reviewed: TranslationRef[]): void;
@@ -0,0 +1,53 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ /** The review queue file of the project in the working directory. */
4
+ export const reviewQueuePath = () => path.join(process.cwd(), ".localize-cache", "review-queue.json");
5
+ /** Identity of a queued translation: its language and key. */
6
+ const refId = ({ language, key }) => JSON.stringify([language, key]);
7
+ /**
8
+ * Read the review queue.
9
+ * @returns The queued items, or null when there is no queue file.
10
+ */
11
+ export function readReviewQueue() {
12
+ const file = reviewQueuePath();
13
+ if (!fs.existsSync(file)) {
14
+ return null;
15
+ }
16
+ const data = JSON.parse(fs.readFileSync(file, "utf8"));
17
+ return data.items || [];
18
+ }
19
+ /** Write `items` as the review queue. */
20
+ function writeReviewQueue(items) {
21
+ const file = reviewQueuePath();
22
+ fs.mkdirSync(path.dirname(file), { recursive: true });
23
+ const data = { timestamp: new Date().toISOString(), items };
24
+ fs.writeFileSync(file, JSON.stringify(data, null, 2));
25
+ return file;
26
+ }
27
+ /**
28
+ * Add the flagged translations of a run to the review queue. Queued items for translations the
29
+ * run scored again are dropped first: the new score replaces them.
30
+ * @param scored - Every translation the run scored.
31
+ * @param flagged - The translations the run flagged for review or rejected.
32
+ * @returns The queue file and the number of items it now holds.
33
+ */
34
+ export function updateReviewQueue(scored, flagged) {
35
+ const rescored = new Set(scored.map(refId));
36
+ const items = [
37
+ ...(readReviewQueue() || []).filter((item) => !rescored.has(refId(item))),
38
+ ...flagged,
39
+ ];
40
+ return { file: writeReviewQueue(items), size: items.length };
41
+ }
42
+ /**
43
+ * Remove reviewed translations from the review queue.
44
+ * @param reviewed - The translations a review approved, edited or rejected.
45
+ */
46
+ export function removeFromReviewQueue(reviewed) {
47
+ const items = readReviewQueue();
48
+ if (!items || reviewed.length === 0) {
49
+ return;
50
+ }
51
+ const done = new Set(reviewed.map(refId));
52
+ writeReviewQueue(items.filter((item) => !done.has(refId(item))));
53
+ }