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,6 +1,22 @@
1
- import { Project, Node } from "ts-morph";
1
+ import { Project, Node, ts, } from "ts-morph";
2
2
  import { log } from "../utils/logger.js";
3
3
  import path from "path";
4
+ /** Longest usage snippet kept for a key, in characters. */
5
+ const MAX_SNIPPET_LENGTH = 200;
6
+ /** Source spans wider than this cannot shrink to a snippet, so they are not printed. */
7
+ const MAX_SNIPPET_SOURCE_WIDTH = 10 * MAX_SNIPPET_LENGTH;
8
+ /** Tooling directives (`eslint-disable-next-line`, `@ts-expect-error`, ...) say nothing about the text. */
9
+ const DIRECTIVE_COMMENT = /^(eslint-|@ts-|prettier-ignore\b|istanbul ignore\b|c8 ignore\b|biome-ignore\b)/;
10
+ /** Prints code without comments, for the usage snippet. */
11
+ const printer = ts.createPrinter({ removeComments: true });
12
+ /** Comment text without its markers (`//`, `/*`, `*\/`, JSDoc `*`), on one line. */
13
+ const commentText = (comment) => comment
14
+ .replace(/^\/\/+|^\/\*+|\*+\/$/g, "")
15
+ .replace(/^\s*\*+/gm, "")
16
+ .replace(/\s+/g, " ")
17
+ .trim();
18
+ /** Functions and methods: code in them runs later, not where they are written. */
19
+ const isFunction = (node) => Node.isFunctionLikeDeclaration(node) || Node.isFunctionExpression(node);
4
20
  export class SourceCodeAnalyzer {
5
21
  project;
6
22
  keyUsageMap = new Map();
@@ -39,7 +55,8 @@ export class SourceCodeAnalyzer {
39
55
  log(`Source analysis complete. Context extracted for ${this.keyUsageMap.size} keys.`);
40
56
  }
41
57
  catch (error) {
42
- console.warn(`Error during source analysis: ${error.message}`);
58
+ const message = error instanceof Error ? error.message : String(error);
59
+ console.warn(`Error during source analysis: ${message}`);
43
60
  // Don't throw to prevent blocking the whole app, just log
44
61
  }
45
62
  })();
@@ -75,75 +92,32 @@ export class SourceCodeAnalyzer {
75
92
  }
76
93
  }
77
94
  extractContext(key, node, fullFilePath) {
78
- const relativePath = path.relative(this.rootDir, fullFilePath);
79
- const line = node.getStartLineNumber();
80
- // Extract component name
81
- let componentName = undefined;
82
- // Use getAncestors() to find the parent safely
83
- const componentNode = node
84
- .getAncestors()
85
- .find((n) => Node.isFunctionDeclaration(n) ||
86
- Node.isClassDeclaration(n) ||
87
- Node.isVariableDeclaration(n));
88
- if (componentNode) {
89
- if (Node.isFunctionDeclaration(componentNode) ||
90
- Node.isClassDeclaration(componentNode)) {
91
- componentName = componentNode.getName();
92
- }
93
- else if (Node.isVariableDeclaration(componentNode)) {
94
- componentName = componentNode.getName();
95
- }
96
- }
97
- // Extract comments
98
- // Look for leading comments on the statement
99
- const statement = node
100
- .getAncestors()
101
- .find((n) => Node.isExpressionStatement(n) ||
102
- Node.isVariableStatement(n) ||
103
- Node.isJsxElement(n) ||
104
- Node.isJsxExpression(n));
105
- const comments = [];
106
- const targetNode = statement || node;
107
- const ranges = targetNode.getLeadingCommentRanges();
108
- for (const range of ranges) {
109
- comments.push(range
110
- .getText()
111
- .replace(/^\/\/\s*/, "")
112
- .replace(/^\/\*\s*/, "")
113
- .replace(/\s*\*\/$/, ""));
114
- }
115
- // Get props (simple heuristic for JSX)
95
+ const component = this.findComponent(node);
96
+ const comments = this.getComments(node, component?.declaration);
97
+ // Props of the JSX element the text belongs to
116
98
  const props = {};
117
- const jsxElement = node
118
- .getAncestors()
119
- .find((n) => Node.isJsxOpeningElement(n) || Node.isJsxSelfClosingElement(n));
120
- if (jsxElement &&
121
- (Node.isJsxOpeningElement(jsxElement) || Node.isJsxSelfClosingElement(jsxElement))) {
122
- jsxElement.getAttributes().forEach((attr) => {
123
- if (Node.isJsxAttribute(attr)) {
124
- // Safe name extraction
125
- const nameNode = attr.getNameNode();
126
- const name = nameNode ? nameNode.getText() : "unknown";
127
- const initializer = attr.getInitializer();
128
- let value = initializer ? initializer.getText() : "true";
129
- // Strip quotes if string literal
130
- if ((value.startsWith('"') && value.endsWith('"')) ||
131
- (value.startsWith("'") && value.endsWith("'"))) {
132
- value = value.substring(1, value.length - 1);
133
- }
134
- props[name] = value;
99
+ this.findJsxElement(node)
100
+ ?.getAttributes()
101
+ .forEach((attr) => {
102
+ if (Node.isJsxAttribute(attr)) {
103
+ // Safe name extraction
104
+ const nameNode = attr.getNameNode();
105
+ const name = nameNode ? nameNode.getText() : "unknown";
106
+ const initializer = attr.getInitializer();
107
+ let value = initializer ? initializer.getText() : "true";
108
+ // Strip quotes if string literal
109
+ if ((value.startsWith('"') && value.endsWith('"')) ||
110
+ (value.startsWith("'") && value.endsWith("'"))) {
111
+ value = value.substring(1, value.length - 1);
135
112
  }
136
- });
137
- }
138
- // Code Snippet (surrounding lines)
139
- // Check for node.getParent() to avoid error if node is root (unlikely for call expression)
140
- const parent = node.getParent();
141
- const snippet = parent ? parent.getText() : node.getText();
113
+ props[name] = value;
114
+ }
115
+ });
142
116
  const context = {
143
- filePath: relativePath,
144
- line,
145
- component: componentName,
146
- usageSnippet: snippet.length > 200 ? snippet.substring(0, 200) + "..." : snippet,
117
+ filePath: path.relative(this.rootDir, fullFilePath),
118
+ line: node.getStartLineNumber(),
119
+ component: component?.name,
120
+ usageSnippet: this.getSnippet(node),
147
121
  comments: comments.length > 0 ? comments : undefined,
148
122
  props: Object.keys(props).length > 0 ? props : undefined,
149
123
  };
@@ -152,6 +126,125 @@ export class SourceCodeAnalyzer {
152
126
  }
153
127
  this.keyUsageMap.get(key)?.push(context);
154
128
  }
129
+ /**
130
+ * The component the call is in: the nearest enclosing function, class or function-valued
131
+ * variable whose name starts with an uppercase letter, otherwise the nearest one of any name.
132
+ * A variable counts only when the call is inside a function in its initializer
133
+ * (`const Dialog = () => ...`), so `const label = t("key")` does not name a component.
134
+ */
135
+ findComponent(node) {
136
+ let nearest;
137
+ let insideFunction = false;
138
+ for (const ancestor of node.getAncestors()) {
139
+ let found;
140
+ if (Node.isFunctionDeclaration(ancestor) ||
141
+ Node.isFunctionExpression(ancestor) ||
142
+ Node.isClassDeclaration(ancestor)) {
143
+ const name = ancestor.getName();
144
+ if (name)
145
+ found = { name, declaration: ancestor };
146
+ }
147
+ else if (insideFunction &&
148
+ Node.isVariableDeclaration(ancestor) &&
149
+ Node.isIdentifier(ancestor.getNameNode())) {
150
+ found = {
151
+ name: ancestor.getName(),
152
+ declaration: ancestor.getVariableStatement() ?? ancestor,
153
+ };
154
+ }
155
+ insideFunction ||= isFunction(ancestor);
156
+ if (found && /^[A-Z]/.test(found.name))
157
+ return found;
158
+ nearest ??= found;
159
+ }
160
+ return nearest;
161
+ }
162
+ /**
163
+ * The JSX element the text belongs to: the one whose attribute holds the call
164
+ * (`<Button title={t("key")} />`) or whose child it is (`<Button>{t("key")}</Button>`).
165
+ * A call inside a nested function (`onClick={() => alert(t("key"))}`) belongs to none.
166
+ */
167
+ findJsxElement(node) {
168
+ for (const ancestor of node.getAncestors()) {
169
+ if (Node.isJsxOpeningElement(ancestor) || Node.isJsxSelfClosingElement(ancestor)) {
170
+ return ancestor;
171
+ }
172
+ if (Node.isJsxElement(ancestor))
173
+ return ancestor.getOpeningElement();
174
+ if (Node.isJsxFragment(ancestor) ||
175
+ Node.isStatement(ancestor) ||
176
+ isFunction(ancestor)) {
177
+ return undefined;
178
+ }
179
+ }
180
+ return undefined;
181
+ }
182
+ /**
183
+ * Comments about the call, nearest first: comments right above the call's expression, JSX
184
+ * attribute, JSX element or statement, `{/* ... *\/}` comments right before its JSX element,
185
+ * and the comment above its component. Tooling directives are left out.
186
+ */
187
+ getComments(node, component) {
188
+ // Raw comments by start position; nested nodes share their leading comments
189
+ const found = new Map();
190
+ for (let current = node; current && !Node.isSourceFile(current); current = current.getParent()) {
191
+ for (const range of current.getLeadingCommentRanges()) {
192
+ found.set(range.getPos(), range.getText());
193
+ }
194
+ const parent = current.getParent();
195
+ if (Node.isJsxElement(parent) || Node.isJsxFragment(parent)) {
196
+ // `{/* ... */}` children right before this one. Compiler nodes, so a long list
197
+ // of children is not wrapped again for every call in it.
198
+ const sourceFile = current.getSourceFile().compilerNode;
199
+ const siblings = parent.compilerNode.children;
200
+ const self = current.compilerNode;
201
+ for (let i = siblings.findIndex((child) => child === self) - 1; i >= 0; i--) {
202
+ const sibling = siblings[i];
203
+ if (ts.isJsxText(sibling) && sibling.containsOnlyTriviaWhiteSpaces)
204
+ continue;
205
+ if (!ts.isJsxExpression(sibling) || sibling.expression)
206
+ break;
207
+ const text = sibling.getText(sourceFile);
208
+ for (const match of text.matchAll(/\/\*[\s\S]*?\*\/|\/\/.*/g)) {
209
+ found.set(sibling.getStart(sourceFile) + match.index, match[0]);
210
+ }
211
+ }
212
+ }
213
+ if (Node.isStatement(current))
214
+ break;
215
+ }
216
+ for (const range of component?.getLeadingCommentRanges() ?? []) {
217
+ found.set(range.getPos(), range.getText());
218
+ }
219
+ return [...found.values()]
220
+ .map(commentText)
221
+ .filter((text) => text && !DIRECTIVE_COMMENT.test(text));
222
+ }
223
+ /**
224
+ * The code around the call with comments removed: the largest of the call, its enclosing
225
+ * expressions and its statement that fits in MAX_SNIPPET_LENGTH characters.
226
+ */
227
+ getSnippet(node) {
228
+ const print = (target) => printer
229
+ .printNode(ts.EmitHint.Unspecified, target.compilerNode, target.getSourceFile().compilerNode)
230
+ .replace(/\s+/g, " ")
231
+ .trim();
232
+ let snippet = print(node);
233
+ for (const ancestor of node.getAncestors()) {
234
+ if (Node.isSourceFile(ancestor) || ancestor.getWidth() > MAX_SNIPPET_SOURCE_WIDTH) {
235
+ break;
236
+ }
237
+ const text = print(ancestor);
238
+ if (text.length > MAX_SNIPPET_LENGTH)
239
+ break;
240
+ snippet = text;
241
+ if (Node.isStatement(ancestor))
242
+ break;
243
+ }
244
+ return snippet.length > MAX_SNIPPET_LENGTH
245
+ ? `${snippet.substring(0, MAX_SNIPPET_LENGTH)}...`
246
+ : snippet;
247
+ }
155
248
  getContext(key) {
156
249
  // Return duplicate functionality: prefer the first usage found or merge?
157
250
  // For now return the first usage that has comments, or just the first usage.
@@ -1,6 +1,14 @@
1
+ import { FlatTranslations } from "../utils/object-transformer.js";
1
2
  import Orchestrator from "../core/orchestrator.js";
2
- import StateManager from "../utils/state-manager.js";
3
+ import StateManager, { StateComparison } from "../utils/state-manager.js";
3
4
  import { TranslationOptions, GlobalStats, LanguageProcessResult } from "../types/index.js";
5
+ /** Result of `locai fix` for one source file. */
6
+ export interface FixSummary {
7
+ /** Translations with a length issue. */
8
+ issues: number;
9
+ /** Of those, how many were re-translated successfully. */
10
+ fixed: number;
11
+ }
4
12
  /**
5
13
  * Service for handling file translation operations.
6
14
  * Orchestrates the translation process including state management,
@@ -26,47 +34,56 @@ declare class TranslationService {
26
34
  /**
27
35
  * Initialize translation state and sync analysis.
28
36
  * @param {string} resolvedFile - Path to resolved source file.
29
- * @param {Record<string, any>} flattenedSource - Flattened source content.
37
+ * @param {FlatTranslations} flattenedSource - Flattened source content.
30
38
  * @param {TranslationOptions} options - Translation options.
31
39
  * @returns {Promise<Object>} - Initialized state components.
32
40
  */
33
- initializeTranslationState(resolvedFile: string, flattenedSource: Record<string, any>, options: TranslationOptions): Promise<{
41
+ initializeTranslationState(resolvedFile: string, flattenedSource: FlatTranslations, options: TranslationOptions): Promise<{
34
42
  stateManager: StateManager;
35
43
  projectRoot: string;
44
+ previousState: Record<string, string>;
36
45
  currentState: Record<string, string>;
37
- comparison: import("../utils/state-manager.js").StateComparison;
46
+ comparison: StateComparison;
38
47
  }>;
39
48
  /**
40
49
  * Process all target languages with concurrency.
41
50
  * @param {string} resolvedFile - Path to source file.
42
- * @param {Record<string, any>} flattenedSource - Flattened source content.
51
+ * @param {FlatTranslations} flattenedSource - Flattened source content.
43
52
  * @param {TranslationOptions} options - Translation options.
44
53
  * @param {GlobalStats} globalStats - Global statistics object to update.
45
- * @param {any} comparison - State comparison result.
54
+ * @param {StateComparison} comparison - State comparison result.
46
55
  */
47
- processAllLanguages(resolvedFile: string, flattenedSource: Record<string, any>, options: TranslationOptions, globalStats: GlobalStats, comparison: any): Promise<void>;
56
+ processAllLanguages(resolvedFile: string, flattenedSource: FlatTranslations, options: TranslationOptions, globalStats: GlobalStats, comparison: StateComparison): Promise<void>;
48
57
  /**
49
58
  * Process a single language translation.
50
59
  * @param {string} targetLang - Target language code.
51
60
  * @param {string} sourceFile - Path to source file.
52
- * @param {Record<string, any>} flattenedSource - Flattened source content.
61
+ * @param {FlatTranslations} flattenedSource - Flattened source content.
53
62
  * @param {Orchestrator} orchestrator - Orchestrator instance.
54
63
  * @param {TranslationOptions} options - Translation options.
55
64
  * @param {GlobalStats} globalStats - Global statistics object.
56
- * @param {any} comparison - State comparison result.
65
+ * @param {StateComparison} comparison - State comparison result.
57
66
  * @returns {Promise<LanguageProcessResult>} - Status and messages for this language.
58
67
  */
59
- processLanguage(targetLang: string, sourceFile: string, flattenedSource: Record<string, any>, orchestrator: Orchestrator, options: TranslationOptions, globalStats: GlobalStats, comparison: any): Promise<LanguageProcessResult>;
68
+ processLanguage(targetLang: string, sourceFile: string, flattenedSource: FlatTranslations, orchestrator: Orchestrator, options: TranslationOptions, globalStats: GlobalStats, comparison: StateComparison): Promise<LanguageProcessResult>;
60
69
  /**
61
70
  * Finalize translation process and save state.
62
71
  * @param {StateManager} stateManager - State manager instance.
63
72
  * @param {string} projectRoot - Project root directory.
64
- * @param {any} currentState - Current state object.
73
+ * @param {Record<string, string>} previousState - State loaded at the start of the run.
74
+ * @param {Record<string, string>} currentState - Current state (key → content hash).
75
+ * @param {StateComparison} comparison - New, modified and deleted keys of this run.
65
76
  * @param {GlobalStats} globalStats - Global statistics object.
66
77
  * @param {number} startTime - Process start timestamp.
67
78
  * @param {TranslationOptions} options - Translation options.
68
79
  */
69
- finalizeTranslation(stateManager: StateManager, projectRoot: string, currentState: any, globalStats: GlobalStats, startTime: number, options: TranslationOptions): Promise<void>;
80
+ finalizeTranslation(stateManager: StateManager, projectRoot: string, previousState: Record<string, string>, currentState: Record<string, string>, comparison: StateComparison, globalStats: GlobalStats, startTime: number, options: TranslationOptions): Promise<void>;
81
+ /**
82
+ * Print the confidence scores of the run and, when the review queue is enabled, add the
83
+ * flagged translations to `.localize-cache/review-queue.json`.
84
+ * @param {Orchestrator[]} orchestrators - The run's orchestrators (they share the settings).
85
+ */
86
+ reportConfidence(orchestrators: Orchestrator[]): Promise<void>;
70
87
  /**
71
88
  * Remove deleted keys from all target language files.
72
89
  * @param {string} sourceFile - Path to source file.
@@ -78,9 +95,10 @@ declare class TranslationService {
78
95
  * Validate and fix existing translations that have length issues.
79
96
  * @param {string} file - Path to source file.
80
97
  * @param {TranslationOptions} options - Translation options.
98
+ * @returns {Promise<FixSummary>} - How many length issues were found and fixed.
81
99
  * @throws {Error} If validation process fails.
82
100
  */
83
- validateAndFixExistingTranslations(file: string, options: TranslationOptions): Promise<void>;
101
+ validateAndFixExistingTranslations(file: string, options: TranslationOptions): Promise<FixSummary>;
84
102
  /**
85
103
  * Find locale files
86
104
  */