@llm-translate/cli 1.0.0-next.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.dockerignore +51 -0
- package/.env.example +33 -0
- package/.github/workflows/docs-pages.yml +57 -0
- package/.github/workflows/release.yml +49 -0
- package/.translaterc.json +44 -0
- package/CLAUDE.md +243 -0
- package/Dockerfile +55 -0
- package/README.md +371 -0
- package/RFC.md +1595 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +4494 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/index.d.ts +1152 -0
- package/dist/index.js +3841 -0
- package/dist/index.js.map +1 -0
- package/docker-compose.yml +56 -0
- package/docs/.vitepress/config.ts +161 -0
- package/docs/api/agent.md +262 -0
- package/docs/api/engine.md +274 -0
- package/docs/api/index.md +171 -0
- package/docs/api/providers.md +304 -0
- package/docs/changelog.md +64 -0
- package/docs/cli/dir.md +243 -0
- package/docs/cli/file.md +213 -0
- package/docs/cli/glossary.md +273 -0
- package/docs/cli/index.md +129 -0
- package/docs/cli/init.md +158 -0
- package/docs/cli/serve.md +211 -0
- package/docs/glossary.json +235 -0
- package/docs/guide/chunking.md +272 -0
- package/docs/guide/configuration.md +139 -0
- package/docs/guide/cost-optimization.md +237 -0
- package/docs/guide/docker.md +371 -0
- package/docs/guide/getting-started.md +150 -0
- package/docs/guide/glossary.md +241 -0
- package/docs/guide/index.md +86 -0
- package/docs/guide/ollama.md +515 -0
- package/docs/guide/prompt-caching.md +221 -0
- package/docs/guide/providers.md +232 -0
- package/docs/guide/quality-control.md +206 -0
- package/docs/guide/vitepress-integration.md +265 -0
- package/docs/index.md +63 -0
- package/docs/ja/api/agent.md +262 -0
- package/docs/ja/api/engine.md +274 -0
- package/docs/ja/api/index.md +171 -0
- package/docs/ja/api/providers.md +304 -0
- package/docs/ja/changelog.md +64 -0
- package/docs/ja/cli/dir.md +243 -0
- package/docs/ja/cli/file.md +213 -0
- package/docs/ja/cli/glossary.md +273 -0
- package/docs/ja/cli/index.md +111 -0
- package/docs/ja/cli/init.md +158 -0
- package/docs/ja/guide/chunking.md +271 -0
- package/docs/ja/guide/configuration.md +139 -0
- package/docs/ja/guide/cost-optimization.md +30 -0
- package/docs/ja/guide/getting-started.md +150 -0
- package/docs/ja/guide/glossary.md +214 -0
- package/docs/ja/guide/index.md +32 -0
- package/docs/ja/guide/ollama.md +410 -0
- package/docs/ja/guide/prompt-caching.md +221 -0
- package/docs/ja/guide/providers.md +232 -0
- package/docs/ja/guide/quality-control.md +137 -0
- package/docs/ja/guide/vitepress-integration.md +265 -0
- package/docs/ja/index.md +58 -0
- package/docs/ko/api/agent.md +262 -0
- package/docs/ko/api/engine.md +274 -0
- package/docs/ko/api/index.md +171 -0
- package/docs/ko/api/providers.md +304 -0
- package/docs/ko/changelog.md +64 -0
- package/docs/ko/cli/dir.md +243 -0
- package/docs/ko/cli/file.md +213 -0
- package/docs/ko/cli/glossary.md +273 -0
- package/docs/ko/cli/index.md +111 -0
- package/docs/ko/cli/init.md +158 -0
- package/docs/ko/guide/chunking.md +271 -0
- package/docs/ko/guide/configuration.md +139 -0
- package/docs/ko/guide/cost-optimization.md +30 -0
- package/docs/ko/guide/getting-started.md +150 -0
- package/docs/ko/guide/glossary.md +214 -0
- package/docs/ko/guide/index.md +32 -0
- package/docs/ko/guide/ollama.md +410 -0
- package/docs/ko/guide/prompt-caching.md +221 -0
- package/docs/ko/guide/providers.md +232 -0
- package/docs/ko/guide/quality-control.md +137 -0
- package/docs/ko/guide/vitepress-integration.md +265 -0
- package/docs/ko/index.md +58 -0
- package/docs/zh/api/agent.md +262 -0
- package/docs/zh/api/engine.md +274 -0
- package/docs/zh/api/index.md +171 -0
- package/docs/zh/api/providers.md +304 -0
- package/docs/zh/changelog.md +64 -0
- package/docs/zh/cli/dir.md +243 -0
- package/docs/zh/cli/file.md +213 -0
- package/docs/zh/cli/glossary.md +273 -0
- package/docs/zh/cli/index.md +111 -0
- package/docs/zh/cli/init.md +158 -0
- package/docs/zh/guide/chunking.md +271 -0
- package/docs/zh/guide/configuration.md +139 -0
- package/docs/zh/guide/cost-optimization.md +30 -0
- package/docs/zh/guide/getting-started.md +150 -0
- package/docs/zh/guide/glossary.md +214 -0
- package/docs/zh/guide/index.md +32 -0
- package/docs/zh/guide/ollama.md +410 -0
- package/docs/zh/guide/prompt-caching.md +221 -0
- package/docs/zh/guide/providers.md +232 -0
- package/docs/zh/guide/quality-control.md +137 -0
- package/docs/zh/guide/vitepress-integration.md +265 -0
- package/docs/zh/index.md +58 -0
- package/package.json +91 -0
- package/release.config.mjs +15 -0
- package/schemas/glossary.schema.json +110 -0
- package/src/cli/commands/dir.ts +469 -0
- package/src/cli/commands/file.ts +291 -0
- package/src/cli/commands/glossary.ts +221 -0
- package/src/cli/commands/init.ts +68 -0
- package/src/cli/commands/serve.ts +60 -0
- package/src/cli/index.ts +64 -0
- package/src/cli/options.ts +59 -0
- package/src/core/agent.ts +1119 -0
- package/src/core/chunker.ts +391 -0
- package/src/core/engine.ts +634 -0
- package/src/errors.ts +188 -0
- package/src/index.ts +147 -0
- package/src/integrations/vitepress.ts +549 -0
- package/src/parsers/markdown.ts +383 -0
- package/src/providers/claude.ts +259 -0
- package/src/providers/interface.ts +109 -0
- package/src/providers/ollama.ts +379 -0
- package/src/providers/openai.ts +308 -0
- package/src/providers/registry.ts +153 -0
- package/src/server/index.ts +152 -0
- package/src/server/middleware/auth.ts +93 -0
- package/src/server/middleware/logger.ts +90 -0
- package/src/server/routes/health.ts +84 -0
- package/src/server/routes/translate.ts +210 -0
- package/src/server/types.ts +138 -0
- package/src/services/cache.ts +899 -0
- package/src/services/config.ts +217 -0
- package/src/services/glossary.ts +247 -0
- package/src/types/analysis.ts +164 -0
- package/src/types/index.ts +265 -0
- package/src/types/modes.ts +121 -0
- package/src/types/mqm.ts +157 -0
- package/src/utils/logger.ts +141 -0
- package/src/utils/tokens.ts +116 -0
- package/tests/fixtures/glossaries/ml-glossary.json +53 -0
- package/tests/fixtures/input/lynq-installation.ko.md +350 -0
- package/tests/fixtures/input/lynq-installation.md +350 -0
- package/tests/fixtures/input/simple.ko.md +27 -0
- package/tests/fixtures/input/simple.md +27 -0
- package/tests/unit/chunker.test.ts +229 -0
- package/tests/unit/glossary.test.ts +146 -0
- package/tests/unit/markdown.test.ts +205 -0
- package/tests/unit/tokens.test.ts +81 -0
- package/tsconfig.json +28 -0
- package/tsup.config.ts +34 -0
- package/vitest.config.ts +16 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,1152 @@
|
|
|
1
|
+
import { Root } from 'mdast';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Translation Mode Configurations
|
|
5
|
+
*
|
|
6
|
+
* Defines preset configurations for different translation quality/speed tradeoffs
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Available translation modes
|
|
10
|
+
*/
|
|
11
|
+
type TranslationMode = 'fast' | 'balanced' | 'quality';
|
|
12
|
+
|
|
13
|
+
type ProviderName = 'claude' | 'openai' | 'ollama' | 'custom';
|
|
14
|
+
interface TranslateConfig {
|
|
15
|
+
version: string;
|
|
16
|
+
project?: {
|
|
17
|
+
name: string;
|
|
18
|
+
description: string;
|
|
19
|
+
purpose: string;
|
|
20
|
+
};
|
|
21
|
+
languages: {
|
|
22
|
+
source: string;
|
|
23
|
+
targets: string[];
|
|
24
|
+
/** Per-language style instructions (e.g., { "ko": "경어체", "ja": "です・ます調" }) */
|
|
25
|
+
styles?: Record<string, string>;
|
|
26
|
+
};
|
|
27
|
+
provider: {
|
|
28
|
+
default: ProviderName;
|
|
29
|
+
model?: string;
|
|
30
|
+
fallback?: ProviderName[];
|
|
31
|
+
apiKeys?: Record<ProviderName, string>;
|
|
32
|
+
};
|
|
33
|
+
quality: {
|
|
34
|
+
threshold: number;
|
|
35
|
+
maxIterations: number;
|
|
36
|
+
evaluationMethod: 'llm' | 'embedding' | 'hybrid';
|
|
37
|
+
};
|
|
38
|
+
chunking: {
|
|
39
|
+
maxTokens: number;
|
|
40
|
+
overlapTokens: number;
|
|
41
|
+
preserveStructure: boolean;
|
|
42
|
+
};
|
|
43
|
+
glossary?: {
|
|
44
|
+
path: string;
|
|
45
|
+
strict: boolean;
|
|
46
|
+
};
|
|
47
|
+
paths: {
|
|
48
|
+
output: string;
|
|
49
|
+
cache?: string;
|
|
50
|
+
};
|
|
51
|
+
ignore?: string[];
|
|
52
|
+
}
|
|
53
|
+
interface Glossary {
|
|
54
|
+
metadata: {
|
|
55
|
+
name: string;
|
|
56
|
+
sourceLang: string;
|
|
57
|
+
targetLangs: string[];
|
|
58
|
+
version: string;
|
|
59
|
+
domain?: string;
|
|
60
|
+
};
|
|
61
|
+
terms: GlossaryTerm[];
|
|
62
|
+
}
|
|
63
|
+
interface GlossaryTerm {
|
|
64
|
+
source: string;
|
|
65
|
+
targets: Record<string, string>;
|
|
66
|
+
context?: string;
|
|
67
|
+
caseSensitive?: boolean;
|
|
68
|
+
doNotTranslate?: boolean;
|
|
69
|
+
doNotTranslateFor?: string[];
|
|
70
|
+
partOfSpeech?: 'noun' | 'verb' | 'adjective' | 'other';
|
|
71
|
+
notes?: string;
|
|
72
|
+
}
|
|
73
|
+
interface ResolvedGlossary {
|
|
74
|
+
metadata: {
|
|
75
|
+
name: string;
|
|
76
|
+
sourceLang: string;
|
|
77
|
+
targetLang: string;
|
|
78
|
+
version: string;
|
|
79
|
+
domain?: string;
|
|
80
|
+
};
|
|
81
|
+
terms: ResolvedGlossaryTerm[];
|
|
82
|
+
}
|
|
83
|
+
interface ResolvedGlossaryTerm {
|
|
84
|
+
source: string;
|
|
85
|
+
target: string;
|
|
86
|
+
context?: string;
|
|
87
|
+
caseSensitive: boolean;
|
|
88
|
+
doNotTranslate: boolean;
|
|
89
|
+
}
|
|
90
|
+
type DocumentFormat = 'markdown' | 'html' | 'text';
|
|
91
|
+
interface TranslationRequest {
|
|
92
|
+
content: string;
|
|
93
|
+
sourceLang: string;
|
|
94
|
+
targetLang: string;
|
|
95
|
+
format: DocumentFormat;
|
|
96
|
+
glossary?: ResolvedGlossary;
|
|
97
|
+
context?: {
|
|
98
|
+
documentPurpose?: string;
|
|
99
|
+
/** Per-language style instruction (e.g., "경어체", "です・ます調") */
|
|
100
|
+
styleInstruction?: string;
|
|
101
|
+
previousChunks?: string[];
|
|
102
|
+
documentSummary?: string;
|
|
103
|
+
};
|
|
104
|
+
options?: {
|
|
105
|
+
qualityThreshold?: number;
|
|
106
|
+
maxIterations?: number;
|
|
107
|
+
preserveFormatting?: boolean;
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
interface TranslationResult {
|
|
111
|
+
content: string;
|
|
112
|
+
metadata: {
|
|
113
|
+
qualityScore: number;
|
|
114
|
+
qualityThreshold: number;
|
|
115
|
+
thresholdMet: boolean;
|
|
116
|
+
iterations: number;
|
|
117
|
+
tokensUsed: {
|
|
118
|
+
input: number;
|
|
119
|
+
output: number;
|
|
120
|
+
/** Tokens read from cache (90% cost reduction) */
|
|
121
|
+
cacheRead?: number;
|
|
122
|
+
/** Tokens written to cache (25% cost increase for first write) */
|
|
123
|
+
cacheWrite?: number;
|
|
124
|
+
};
|
|
125
|
+
duration: number;
|
|
126
|
+
provider: string;
|
|
127
|
+
model: string;
|
|
128
|
+
};
|
|
129
|
+
glossaryCompliance?: {
|
|
130
|
+
applied: string[];
|
|
131
|
+
missed: string[];
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
interface ChunkResult {
|
|
135
|
+
original: string;
|
|
136
|
+
translated: string;
|
|
137
|
+
startOffset: number;
|
|
138
|
+
endOffset: number;
|
|
139
|
+
qualityScore: number;
|
|
140
|
+
iterations?: number;
|
|
141
|
+
tokensUsed?: {
|
|
142
|
+
input: number;
|
|
143
|
+
output: number;
|
|
144
|
+
/** Number of cache hits for this chunk */
|
|
145
|
+
cacheRead?: number;
|
|
146
|
+
};
|
|
147
|
+
/** Whether this chunk was retrieved from cache */
|
|
148
|
+
cached?: boolean;
|
|
149
|
+
}
|
|
150
|
+
interface DocumentResult {
|
|
151
|
+
content: string;
|
|
152
|
+
chunks: ChunkResult[];
|
|
153
|
+
metadata: {
|
|
154
|
+
totalTokensUsed: number;
|
|
155
|
+
totalDuration: number;
|
|
156
|
+
averageQuality: number;
|
|
157
|
+
provider: string;
|
|
158
|
+
model: string;
|
|
159
|
+
totalIterations: number;
|
|
160
|
+
tokensUsed: {
|
|
161
|
+
input: number;
|
|
162
|
+
output: number;
|
|
163
|
+
/** Tokens read from cache (90% cost reduction) */
|
|
164
|
+
cacheRead?: number;
|
|
165
|
+
/** Tokens written to cache (25% cost increase for first write) */
|
|
166
|
+
cacheWrite?: number;
|
|
167
|
+
};
|
|
168
|
+
/** Cache statistics */
|
|
169
|
+
cache?: {
|
|
170
|
+
hits: number;
|
|
171
|
+
misses: number;
|
|
172
|
+
};
|
|
173
|
+
};
|
|
174
|
+
glossaryCompliance?: {
|
|
175
|
+
applied: string[];
|
|
176
|
+
missed: string[];
|
|
177
|
+
compliant: boolean;
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
interface Chunk {
|
|
181
|
+
id: string;
|
|
182
|
+
content: string;
|
|
183
|
+
type: 'translatable' | 'preserve';
|
|
184
|
+
startOffset: number;
|
|
185
|
+
endOffset: number;
|
|
186
|
+
metadata?: {
|
|
187
|
+
headerHierarchy?: string[];
|
|
188
|
+
previousContext?: string;
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
interface ChunkingConfig {
|
|
192
|
+
maxTokens: number;
|
|
193
|
+
overlapTokens: number;
|
|
194
|
+
separators: string[];
|
|
195
|
+
preservePatterns: RegExp[];
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Legacy simple quality evaluation (for fast mode or fallback)
|
|
199
|
+
*/
|
|
200
|
+
interface SimpleQualityEvaluation {
|
|
201
|
+
score: number;
|
|
202
|
+
breakdown: {
|
|
203
|
+
accuracy: number;
|
|
204
|
+
fluency: number;
|
|
205
|
+
glossary: number;
|
|
206
|
+
format: number;
|
|
207
|
+
};
|
|
208
|
+
issues: string[];
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Combined quality evaluation type (supports both MQM and simple)
|
|
213
|
+
*/
|
|
214
|
+
type QualityEvaluation = SimpleQualityEvaluation;
|
|
215
|
+
interface CacheEntry {
|
|
216
|
+
sourceHash: string;
|
|
217
|
+
sourceLang: string;
|
|
218
|
+
targetLang: string;
|
|
219
|
+
glossaryHash: string;
|
|
220
|
+
translation: string;
|
|
221
|
+
qualityScore: number;
|
|
222
|
+
createdAt: string;
|
|
223
|
+
provider: string;
|
|
224
|
+
model: string;
|
|
225
|
+
}
|
|
226
|
+
interface CacheIndex {
|
|
227
|
+
version: string;
|
|
228
|
+
entries: Record<string, CacheEntry>;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
type ChatRole = 'system' | 'user' | 'assistant';
|
|
232
|
+
/**
|
|
233
|
+
* Content part with optional cache control for prompt caching
|
|
234
|
+
*/
|
|
235
|
+
interface CacheableTextPart {
|
|
236
|
+
type: 'text';
|
|
237
|
+
text: string;
|
|
238
|
+
/** Enable prompt caching for this content part (Claude only) */
|
|
239
|
+
cacheControl?: {
|
|
240
|
+
type: 'ephemeral';
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
interface ChatMessage {
|
|
244
|
+
role: ChatRole;
|
|
245
|
+
/** Content can be a string or array of cacheable parts */
|
|
246
|
+
content: string | CacheableTextPart[];
|
|
247
|
+
}
|
|
248
|
+
interface ChatRequest {
|
|
249
|
+
messages: ChatMessage[];
|
|
250
|
+
model?: string;
|
|
251
|
+
temperature?: number;
|
|
252
|
+
maxTokens?: number;
|
|
253
|
+
}
|
|
254
|
+
interface ChatResponse {
|
|
255
|
+
content: string;
|
|
256
|
+
usage: {
|
|
257
|
+
inputTokens: number;
|
|
258
|
+
outputTokens: number;
|
|
259
|
+
/** Tokens read from cache (90% cost reduction) */
|
|
260
|
+
cacheReadTokens?: number;
|
|
261
|
+
/** Tokens written to cache (25% cost increase for first write) */
|
|
262
|
+
cacheWriteTokens?: number;
|
|
263
|
+
};
|
|
264
|
+
model: string;
|
|
265
|
+
finishReason: 'stop' | 'length' | 'error';
|
|
266
|
+
}
|
|
267
|
+
interface ModelInfo {
|
|
268
|
+
maxContextTokens: number;
|
|
269
|
+
supportsStreaming: boolean;
|
|
270
|
+
costPer1kInput?: number;
|
|
271
|
+
costPer1kOutput?: number;
|
|
272
|
+
}
|
|
273
|
+
interface ProviderConfig {
|
|
274
|
+
apiKey?: string;
|
|
275
|
+
baseUrl?: string;
|
|
276
|
+
defaultModel?: string;
|
|
277
|
+
timeout?: number;
|
|
278
|
+
}
|
|
279
|
+
interface LLMProvider {
|
|
280
|
+
readonly name: ProviderName;
|
|
281
|
+
/**
|
|
282
|
+
* The default model used by this provider
|
|
283
|
+
*/
|
|
284
|
+
readonly defaultModel: string;
|
|
285
|
+
/**
|
|
286
|
+
* Send a chat request and receive a complete response
|
|
287
|
+
*/
|
|
288
|
+
chat(request: ChatRequest): Promise<ChatResponse>;
|
|
289
|
+
/**
|
|
290
|
+
* Send a chat request and receive a streaming response
|
|
291
|
+
*/
|
|
292
|
+
stream(request: ChatRequest): AsyncIterable<string>;
|
|
293
|
+
/**
|
|
294
|
+
* Count the number of tokens in a text string
|
|
295
|
+
*/
|
|
296
|
+
countTokens(text: string): number;
|
|
297
|
+
/**
|
|
298
|
+
* Get information about a specific model
|
|
299
|
+
*/
|
|
300
|
+
getModelInfo(model?: string): ModelInfo;
|
|
301
|
+
}
|
|
302
|
+
type ProviderFactory = (config: ProviderConfig) => LLMProvider;
|
|
303
|
+
|
|
304
|
+
declare enum ErrorCode {
|
|
305
|
+
CONFIG_NOT_FOUND = "CONFIG_NOT_FOUND",
|
|
306
|
+
CONFIG_INVALID = "CONFIG_INVALID",
|
|
307
|
+
GLOSSARY_NOT_FOUND = "GLOSSARY_NOT_FOUND",
|
|
308
|
+
GLOSSARY_INVALID = "GLOSSARY_INVALID",
|
|
309
|
+
PROVIDER_NOT_FOUND = "PROVIDER_NOT_FOUND",
|
|
310
|
+
PROVIDER_AUTH_FAILED = "PROVIDER_AUTH_FAILED",
|
|
311
|
+
PROVIDER_RATE_LIMITED = "PROVIDER_RATE_LIMITED",
|
|
312
|
+
PROVIDER_ERROR = "PROVIDER_ERROR",
|
|
313
|
+
QUALITY_THRESHOLD_NOT_MET = "QUALITY_THRESHOLD_NOT_MET",
|
|
314
|
+
GLOSSARY_COMPLIANCE_FAILED = "GLOSSARY_COMPLIANCE_FAILED",
|
|
315
|
+
FILE_NOT_FOUND = "FILE_NOT_FOUND",
|
|
316
|
+
FILE_READ_ERROR = "FILE_READ_ERROR",
|
|
317
|
+
FILE_WRITE_ERROR = "FILE_WRITE_ERROR",
|
|
318
|
+
UNSUPPORTED_FORMAT = "UNSUPPORTED_FORMAT",
|
|
319
|
+
CHUNK_TOO_LARGE = "CHUNK_TOO_LARGE",
|
|
320
|
+
UNKNOWN_ERROR = "UNKNOWN_ERROR"
|
|
321
|
+
}
|
|
322
|
+
declare class TranslationError extends Error {
|
|
323
|
+
readonly code: ErrorCode;
|
|
324
|
+
readonly details?: Record<string, unknown>;
|
|
325
|
+
constructor(code: ErrorCode, details?: Record<string, unknown>, customMessage?: string);
|
|
326
|
+
/**
|
|
327
|
+
* Create a JSON representation of the error
|
|
328
|
+
*/
|
|
329
|
+
toJSON(): Record<string, unknown>;
|
|
330
|
+
}
|
|
331
|
+
declare function isTranslationError(error: unknown): error is TranslationError;
|
|
332
|
+
declare function isErrorCode(error: unknown, code: ErrorCode): boolean;
|
|
333
|
+
declare const ExitCode: {
|
|
334
|
+
readonly SUCCESS: 0;
|
|
335
|
+
readonly GENERAL_ERROR: 1;
|
|
336
|
+
readonly INVALID_ARGUMENTS: 2;
|
|
337
|
+
readonly FILE_NOT_FOUND: 3;
|
|
338
|
+
readonly QUALITY_THRESHOLD_NOT_MET: 4;
|
|
339
|
+
readonly PROVIDER_ERROR: 5;
|
|
340
|
+
readonly GLOSSARY_VALIDATION_FAILED: 6;
|
|
341
|
+
};
|
|
342
|
+
type ExitCode = (typeof ExitCode)[keyof typeof ExitCode];
|
|
343
|
+
/**
|
|
344
|
+
* Map error codes to exit codes
|
|
345
|
+
*/
|
|
346
|
+
declare function getExitCode(error: TranslationError): ExitCode;
|
|
347
|
+
|
|
348
|
+
interface LoadConfigOptions {
|
|
349
|
+
configPath?: string;
|
|
350
|
+
cwd?: string;
|
|
351
|
+
}
|
|
352
|
+
declare function loadConfig(options?: LoadConfigOptions): Promise<TranslateConfig>;
|
|
353
|
+
interface CLIOverrides {
|
|
354
|
+
sourceLang?: string;
|
|
355
|
+
targetLang?: string;
|
|
356
|
+
provider?: ProviderName;
|
|
357
|
+
model?: string;
|
|
358
|
+
quality?: number;
|
|
359
|
+
maxIterations?: number;
|
|
360
|
+
chunkSize?: number;
|
|
361
|
+
glossary?: string;
|
|
362
|
+
output?: string;
|
|
363
|
+
noCache?: boolean;
|
|
364
|
+
}
|
|
365
|
+
declare function mergeConfig(config: TranslateConfig, overrides: CLIOverrides): TranslateConfig;
|
|
366
|
+
|
|
367
|
+
declare function loadGlossary(path: string): Promise<Glossary>;
|
|
368
|
+
declare function resolveGlossary(glossary: Glossary, targetLang: string): ResolvedGlossary;
|
|
369
|
+
interface GlossaryLookup {
|
|
370
|
+
/**
|
|
371
|
+
* Find a term in the glossary
|
|
372
|
+
*/
|
|
373
|
+
find(text: string): ResolvedGlossaryTerm | undefined;
|
|
374
|
+
/**
|
|
375
|
+
* Find all matching terms in a text
|
|
376
|
+
*/
|
|
377
|
+
findAll(text: string): ResolvedGlossaryTerm[];
|
|
378
|
+
/**
|
|
379
|
+
* Get all terms
|
|
380
|
+
*/
|
|
381
|
+
getTerms(): ResolvedGlossaryTerm[];
|
|
382
|
+
/**
|
|
383
|
+
* Format glossary for prompt injection
|
|
384
|
+
*/
|
|
385
|
+
formatForPrompt(): string;
|
|
386
|
+
}
|
|
387
|
+
declare function createGlossaryLookup(glossary: ResolvedGlossary): GlossaryLookup;
|
|
388
|
+
interface ComplianceResult {
|
|
389
|
+
applied: string[];
|
|
390
|
+
missed: string[];
|
|
391
|
+
score: number;
|
|
392
|
+
}
|
|
393
|
+
declare function checkGlossaryCompliance(sourceText: string, translatedText: string, glossary: ResolvedGlossary): ComplianceResult;
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* Translation Cache Service
|
|
397
|
+
*
|
|
398
|
+
* File-based caching using content hashes to avoid re-translating unchanged content.
|
|
399
|
+
* Cache entries are indexed by a composite key of content hash, target language,
|
|
400
|
+
* glossary hash, provider, and model.
|
|
401
|
+
*/
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* Context provided to invalidation policies for evaluation
|
|
405
|
+
*/
|
|
406
|
+
interface InvalidationContext {
|
|
407
|
+
/** Current glossary hash (if glossary is used) */
|
|
408
|
+
glossaryHash?: string;
|
|
409
|
+
/** Previous glossary hash (stored in cache metadata) */
|
|
410
|
+
previousGlossaryHash?: string;
|
|
411
|
+
/** Provider name */
|
|
412
|
+
provider?: string;
|
|
413
|
+
/** Model name */
|
|
414
|
+
model?: string;
|
|
415
|
+
/** Cache entry creation timestamp */
|
|
416
|
+
entryCreatedAt?: string;
|
|
417
|
+
/** Current timestamp */
|
|
418
|
+
currentTime?: Date;
|
|
419
|
+
/** Custom metadata for policy-specific checks */
|
|
420
|
+
metadata?: Record<string, unknown>;
|
|
421
|
+
}
|
|
422
|
+
/**
|
|
423
|
+
* Result of an invalidation check
|
|
424
|
+
*/
|
|
425
|
+
interface InvalidationResult {
|
|
426
|
+
/** Whether invalidation should occur */
|
|
427
|
+
shouldInvalidate: boolean;
|
|
428
|
+
/** Reason for invalidation (for logging) */
|
|
429
|
+
reason?: string;
|
|
430
|
+
/** Scope of invalidation */
|
|
431
|
+
scope: 'all' | 'matching' | 'none';
|
|
432
|
+
/** Filter function for 'matching' scope - returns true if entry should be invalidated */
|
|
433
|
+
filter?: (entry: CacheEntry, key: string) => boolean;
|
|
434
|
+
}
|
|
435
|
+
/**
|
|
436
|
+
* Abstract invalidation policy interface
|
|
437
|
+
*/
|
|
438
|
+
interface InvalidationPolicy {
|
|
439
|
+
/** Policy name for logging */
|
|
440
|
+
readonly name: string;
|
|
441
|
+
/** Check if cache should be invalidated */
|
|
442
|
+
check(context: InvalidationContext): InvalidationResult;
|
|
443
|
+
}
|
|
444
|
+
/**
|
|
445
|
+
* Invalidates all cache entries when glossary changes
|
|
446
|
+
*/
|
|
447
|
+
declare class GlossaryChangePolicy implements InvalidationPolicy {
|
|
448
|
+
readonly name = "GlossaryChangePolicy";
|
|
449
|
+
private readonly mode;
|
|
450
|
+
/**
|
|
451
|
+
* @param mode - 'all' invalidates entire cache, 'matching' only entries with old glossary hash
|
|
452
|
+
*/
|
|
453
|
+
constructor(mode?: 'all' | 'matching');
|
|
454
|
+
check(context: InvalidationContext): InvalidationResult;
|
|
455
|
+
}
|
|
456
|
+
/**
|
|
457
|
+
* Invalidates cache entries older than specified TTL
|
|
458
|
+
*/
|
|
459
|
+
declare class TTLPolicy implements InvalidationPolicy {
|
|
460
|
+
readonly name = "TTLPolicy";
|
|
461
|
+
private readonly ttlMs;
|
|
462
|
+
/**
|
|
463
|
+
* @param ttlMs - Time-to-live in milliseconds
|
|
464
|
+
*/
|
|
465
|
+
constructor(ttlMs: number);
|
|
466
|
+
/**
|
|
467
|
+
* Create policy with TTL in hours
|
|
468
|
+
*/
|
|
469
|
+
static hours(hours: number): TTLPolicy;
|
|
470
|
+
/**
|
|
471
|
+
* Create policy with TTL in days
|
|
472
|
+
*/
|
|
473
|
+
static days(days: number): TTLPolicy;
|
|
474
|
+
check(context: InvalidationContext): InvalidationResult;
|
|
475
|
+
}
|
|
476
|
+
/**
|
|
477
|
+
* Invalidates cache entries when provider or model changes
|
|
478
|
+
*/
|
|
479
|
+
declare class ProviderChangePolicy implements InvalidationPolicy {
|
|
480
|
+
readonly name = "ProviderChangePolicy";
|
|
481
|
+
private readonly checkModel;
|
|
482
|
+
/**
|
|
483
|
+
* @param checkModel - If true, also invalidate when model changes within same provider
|
|
484
|
+
*/
|
|
485
|
+
constructor(checkModel?: boolean);
|
|
486
|
+
check(context: InvalidationContext): InvalidationResult;
|
|
487
|
+
}
|
|
488
|
+
/**
|
|
489
|
+
* Invalidates cache entries below a quality threshold
|
|
490
|
+
*/
|
|
491
|
+
declare class QualityThresholdPolicy implements InvalidationPolicy {
|
|
492
|
+
readonly name = "QualityThresholdPolicy";
|
|
493
|
+
private readonly threshold;
|
|
494
|
+
/**
|
|
495
|
+
* @param threshold - Minimum quality score (0-100)
|
|
496
|
+
*/
|
|
497
|
+
constructor(threshold: number);
|
|
498
|
+
check(_context: InvalidationContext): InvalidationResult;
|
|
499
|
+
}
|
|
500
|
+
/**
|
|
501
|
+
* Combines multiple policies - invalidates if ANY policy triggers
|
|
502
|
+
*/
|
|
503
|
+
declare class CompositePolicy implements InvalidationPolicy {
|
|
504
|
+
readonly name = "CompositePolicy";
|
|
505
|
+
private readonly policies;
|
|
506
|
+
private readonly mode;
|
|
507
|
+
/**
|
|
508
|
+
* @param policies - Array of policies to combine
|
|
509
|
+
* @param mode - 'any' triggers if any policy matches, 'all' requires all policies to match
|
|
510
|
+
*/
|
|
511
|
+
constructor(policies: InvalidationPolicy[], mode?: 'any' | 'all');
|
|
512
|
+
check(context: InvalidationContext): InvalidationResult;
|
|
513
|
+
}
|
|
514
|
+
interface CacheOptions {
|
|
515
|
+
/** Cache directory path */
|
|
516
|
+
cacheDir: string;
|
|
517
|
+
/** Enable verbose logging */
|
|
518
|
+
verbose?: boolean;
|
|
519
|
+
/** Invalidation policies to apply */
|
|
520
|
+
invalidationPolicies?: InvalidationPolicy[];
|
|
521
|
+
}
|
|
522
|
+
interface CacheKey {
|
|
523
|
+
/** Source content (will be hashed) */
|
|
524
|
+
content: string;
|
|
525
|
+
/** Source language */
|
|
526
|
+
sourceLang: string;
|
|
527
|
+
/** Target language */
|
|
528
|
+
targetLang: string;
|
|
529
|
+
/** Glossary content or hash (will be hashed if string) */
|
|
530
|
+
glossary?: string;
|
|
531
|
+
/** Provider name */
|
|
532
|
+
provider: string;
|
|
533
|
+
/** Model name */
|
|
534
|
+
model: string;
|
|
535
|
+
}
|
|
536
|
+
interface CacheStats {
|
|
537
|
+
/** Total number of cached entries */
|
|
538
|
+
entries: number;
|
|
539
|
+
/** Total cache size in bytes */
|
|
540
|
+
sizeBytes: number;
|
|
541
|
+
/** Cache version */
|
|
542
|
+
version: string;
|
|
543
|
+
/** Number of entries invalidated by policies */
|
|
544
|
+
invalidated?: number;
|
|
545
|
+
}
|
|
546
|
+
/**
|
|
547
|
+
* Cache metadata stored separately for tracking state across sessions
|
|
548
|
+
*/
|
|
549
|
+
interface CacheMetadata {
|
|
550
|
+
/** Last known glossary hash */
|
|
551
|
+
glossaryHash?: string;
|
|
552
|
+
/** Last known provider */
|
|
553
|
+
provider?: string;
|
|
554
|
+
/** Last known model */
|
|
555
|
+
model?: string;
|
|
556
|
+
/** Last invalidation timestamp */
|
|
557
|
+
lastInvalidation?: string;
|
|
558
|
+
/** Custom metadata */
|
|
559
|
+
custom?: Record<string, unknown>;
|
|
560
|
+
}
|
|
561
|
+
interface CacheResult {
|
|
562
|
+
/** Whether the entry was found in cache */
|
|
563
|
+
hit: boolean;
|
|
564
|
+
/** The cached entry if found */
|
|
565
|
+
entry?: CacheEntry;
|
|
566
|
+
}
|
|
567
|
+
/**
|
|
568
|
+
* Generate SHA-256 hash of content
|
|
569
|
+
*/
|
|
570
|
+
declare function hashContent(content: string): string;
|
|
571
|
+
/**
|
|
572
|
+
* Generate cache key from components
|
|
573
|
+
*/
|
|
574
|
+
declare function generateCacheKey(key: CacheKey): string;
|
|
575
|
+
declare class CacheManager {
|
|
576
|
+
private cacheDir;
|
|
577
|
+
private indexPath;
|
|
578
|
+
private entriesDir;
|
|
579
|
+
private metadataPath;
|
|
580
|
+
private verbose;
|
|
581
|
+
private index;
|
|
582
|
+
private policies;
|
|
583
|
+
private metadata;
|
|
584
|
+
constructor(options: CacheOptions);
|
|
585
|
+
/**
|
|
586
|
+
* Initialize cache directory and load index
|
|
587
|
+
*/
|
|
588
|
+
private ensureInitialized;
|
|
589
|
+
/**
|
|
590
|
+
* Load cache metadata from disk
|
|
591
|
+
*/
|
|
592
|
+
private loadMetadata;
|
|
593
|
+
/**
|
|
594
|
+
* Save cache metadata to disk
|
|
595
|
+
*/
|
|
596
|
+
private saveMetadata;
|
|
597
|
+
/**
|
|
598
|
+
* Update cache metadata
|
|
599
|
+
*/
|
|
600
|
+
updateMetadata(updates: Partial<CacheMetadata>): void;
|
|
601
|
+
/**
|
|
602
|
+
* Get current cache metadata
|
|
603
|
+
*/
|
|
604
|
+
getMetadata(): CacheMetadata;
|
|
605
|
+
/**
|
|
606
|
+
* Apply all configured invalidation policies
|
|
607
|
+
* @returns Number of entries invalidated
|
|
608
|
+
*/
|
|
609
|
+
applyPolicies(context: InvalidationContext): number;
|
|
610
|
+
/**
|
|
611
|
+
* Invalidate entries matching a filter function
|
|
612
|
+
* @returns Number of entries invalidated
|
|
613
|
+
*/
|
|
614
|
+
invalidateMatching(filter: (entry: CacheEntry, key: string) => boolean): number;
|
|
615
|
+
/**
|
|
616
|
+
* Add an invalidation policy at runtime
|
|
617
|
+
*/
|
|
618
|
+
addPolicy(policy: InvalidationPolicy): void;
|
|
619
|
+
/**
|
|
620
|
+
* Remove an invalidation policy by name
|
|
621
|
+
*/
|
|
622
|
+
removePolicy(name: string): boolean;
|
|
623
|
+
/**
|
|
624
|
+
* Get all configured policies
|
|
625
|
+
*/
|
|
626
|
+
getPolicies(): InvalidationPolicy[];
|
|
627
|
+
/**
|
|
628
|
+
* Save index to disk
|
|
629
|
+
*/
|
|
630
|
+
private saveIndex;
|
|
631
|
+
/**
|
|
632
|
+
* Get cached translation if available
|
|
633
|
+
*/
|
|
634
|
+
get(key: CacheKey): CacheResult;
|
|
635
|
+
/**
|
|
636
|
+
* Store translation in cache
|
|
637
|
+
*/
|
|
638
|
+
set(key: CacheKey, translation: string, qualityScore: number): void;
|
|
639
|
+
/**
|
|
640
|
+
* Check if entry exists in cache
|
|
641
|
+
*/
|
|
642
|
+
has(key: CacheKey): boolean;
|
|
643
|
+
/**
|
|
644
|
+
* Remove entry from cache
|
|
645
|
+
*/
|
|
646
|
+
delete(key: CacheKey): boolean;
|
|
647
|
+
/**
|
|
648
|
+
* Clear entire cache (synchronous)
|
|
649
|
+
*/
|
|
650
|
+
private clearSync;
|
|
651
|
+
/**
|
|
652
|
+
* Clear entire cache
|
|
653
|
+
*/
|
|
654
|
+
clear(): void;
|
|
655
|
+
/**
|
|
656
|
+
* Get cache statistics
|
|
657
|
+
*/
|
|
658
|
+
getStats(): CacheStats;
|
|
659
|
+
/**
|
|
660
|
+
* Get all cached entries (for debugging)
|
|
661
|
+
*/
|
|
662
|
+
getAllEntries(): Record<string, CacheEntry>;
|
|
663
|
+
}
|
|
664
|
+
/**
|
|
665
|
+
* Create a cache manager instance
|
|
666
|
+
*/
|
|
667
|
+
declare function createCacheManager(options: CacheOptions): CacheManager;
|
|
668
|
+
/**
|
|
669
|
+
* Create a no-op cache manager that never caches (for --no-cache mode)
|
|
670
|
+
*/
|
|
671
|
+
declare function createNullCacheManager(): CacheManager & {
|
|
672
|
+
isNull: true;
|
|
673
|
+
};
|
|
674
|
+
/**
|
|
675
|
+
* Create default invalidation policies for typical translation workflow
|
|
676
|
+
*/
|
|
677
|
+
declare function createDefaultPolicies(): InvalidationPolicy[];
|
|
678
|
+
/**
|
|
679
|
+
* Create strict invalidation policies (more aggressive invalidation)
|
|
680
|
+
*/
|
|
681
|
+
declare function createStrictPolicies(qualityThreshold?: number): InvalidationPolicy[];
|
|
682
|
+
/**
|
|
683
|
+
* Create minimal invalidation policies (only glossary changes)
|
|
684
|
+
*/
|
|
685
|
+
declare function createMinimalPolicies(): InvalidationPolicy[];
|
|
686
|
+
|
|
687
|
+
declare function registerProvider(name: ProviderName, factory: ProviderFactory): void;
|
|
688
|
+
declare function getProvider(name: ProviderName, config?: ProviderConfig): LLMProvider;
|
|
689
|
+
declare function hasProvider(name: ProviderName): boolean;
|
|
690
|
+
declare function getAvailableProviders(): ProviderName[];
|
|
691
|
+
declare function getProviderConfigFromEnv(name: ProviderName): ProviderConfig;
|
|
692
|
+
interface CreateProviderOptions {
|
|
693
|
+
primary: ProviderName;
|
|
694
|
+
fallback?: ProviderName[];
|
|
695
|
+
config?: Partial<Record<ProviderName, ProviderConfig>>;
|
|
696
|
+
}
|
|
697
|
+
declare function createProviderWithFallback(options: CreateProviderOptions): LLMProvider;
|
|
698
|
+
|
|
699
|
+
declare class ClaudeProvider implements LLMProvider {
|
|
700
|
+
readonly name: ProviderName;
|
|
701
|
+
readonly defaultModel: string;
|
|
702
|
+
private readonly client;
|
|
703
|
+
constructor(config?: ProviderConfig);
|
|
704
|
+
chat(request: ChatRequest): Promise<ChatResponse>;
|
|
705
|
+
/**
|
|
706
|
+
* Convert messages to Vercel AI SDK format with cache control support
|
|
707
|
+
*/
|
|
708
|
+
private convertMessages;
|
|
709
|
+
stream(request: ChatRequest): AsyncIterable<string>;
|
|
710
|
+
countTokens(text: string): number;
|
|
711
|
+
getModelInfo(model?: string): ModelInfo;
|
|
712
|
+
private handleError;
|
|
713
|
+
}
|
|
714
|
+
declare function createClaudeProvider(config?: ProviderConfig): LLMProvider;
|
|
715
|
+
|
|
716
|
+
interface ChunkerOptions {
|
|
717
|
+
maxTokens?: number;
|
|
718
|
+
overlapTokens?: number;
|
|
719
|
+
preserveCodeBlocks?: boolean;
|
|
720
|
+
}
|
|
721
|
+
/**
|
|
722
|
+
* Split content into chunks that respect token limits
|
|
723
|
+
*/
|
|
724
|
+
declare function chunkContent(content: string, options?: ChunkerOptions): Chunk[];
|
|
725
|
+
/**
|
|
726
|
+
* Reassemble chunks back into a document
|
|
727
|
+
* Note: Chunks should not have overlapping content - overlap is only used for context metadata
|
|
728
|
+
*/
|
|
729
|
+
declare function reassembleChunks(chunks: Chunk[]): string;
|
|
730
|
+
/**
|
|
731
|
+
* Get chunk statistics
|
|
732
|
+
*/
|
|
733
|
+
declare function getChunkStats(chunks: Chunk[]): {
|
|
734
|
+
totalChunks: number;
|
|
735
|
+
translatableChunks: number;
|
|
736
|
+
preservedChunks: number;
|
|
737
|
+
totalTokens: number;
|
|
738
|
+
averageTokens: number;
|
|
739
|
+
};
|
|
740
|
+
|
|
741
|
+
interface TranslationAgentOptions {
|
|
742
|
+
provider: LLMProvider;
|
|
743
|
+
qualityThreshold?: number;
|
|
744
|
+
maxIterations?: number;
|
|
745
|
+
verbose?: boolean;
|
|
746
|
+
/** If true, throw error when quality threshold is not met after max iterations */
|
|
747
|
+
strictQuality?: boolean;
|
|
748
|
+
/** Enable prompt caching for Claude provider (default: true) */
|
|
749
|
+
enableCaching?: boolean;
|
|
750
|
+
/** Translation mode: fast, balanced, quality (default: balanced) */
|
|
751
|
+
mode?: TranslationMode;
|
|
752
|
+
/** Enable pre-translation analysis (MAPS-style) - overrides mode setting */
|
|
753
|
+
enableAnalysis?: boolean;
|
|
754
|
+
/** Use MQM-based evaluation - overrides mode setting */
|
|
755
|
+
useMQMEvaluation?: boolean;
|
|
756
|
+
}
|
|
757
|
+
declare class TranslationAgent {
|
|
758
|
+
private provider;
|
|
759
|
+
private qualityThreshold;
|
|
760
|
+
private maxIterations;
|
|
761
|
+
private verbose;
|
|
762
|
+
private strictQuality;
|
|
763
|
+
private enableCaching;
|
|
764
|
+
private enableAnalysis;
|
|
765
|
+
private useMQMEvaluation;
|
|
766
|
+
constructor(options: TranslationAgentOptions);
|
|
767
|
+
/**
|
|
768
|
+
* Translate content using Self-Refine loop with optional MAPS analysis and MQM evaluation
|
|
769
|
+
*/
|
|
770
|
+
translate(request: TranslationRequest): Promise<TranslationResult>;
|
|
771
|
+
private generateInitialTranslation;
|
|
772
|
+
private generateReflection;
|
|
773
|
+
private improveTranslation;
|
|
774
|
+
private evaluateQuality;
|
|
775
|
+
/**
|
|
776
|
+
* Pre-translation analysis using MAPS-style approach
|
|
777
|
+
* Identifies key terms, ambiguous phrases, and translation challenges
|
|
778
|
+
*/
|
|
779
|
+
private analyzeSource;
|
|
780
|
+
/**
|
|
781
|
+
* Evaluate translation quality using MQM framework
|
|
782
|
+
* Returns structured error annotations for targeted refinement
|
|
783
|
+
*/
|
|
784
|
+
private evaluateQualityMQM;
|
|
785
|
+
/**
|
|
786
|
+
* Refine translation based on MQM error annotations
|
|
787
|
+
* Applies targeted fixes for identified errors
|
|
788
|
+
*/
|
|
789
|
+
private refineWithMQM;
|
|
790
|
+
/**
|
|
791
|
+
* Clean up translation output by removing prompt artifacts
|
|
792
|
+
* Uses guardrails to detect and remove any trailing prompt-like content
|
|
793
|
+
*/
|
|
794
|
+
private cleanTranslationOutput;
|
|
795
|
+
/**
|
|
796
|
+
* Preserve leading/trailing whitespace from source text in translated text
|
|
797
|
+
* This ensures document structure (line breaks between sections) is maintained
|
|
798
|
+
*/
|
|
799
|
+
private preserveWhitespace;
|
|
800
|
+
private checkGlossaryCompliance;
|
|
801
|
+
}
|
|
802
|
+
declare function createTranslationAgent(options: TranslationAgentOptions): TranslationAgent;
|
|
803
|
+
|
|
804
|
+
interface TranslationEngineOptions {
|
|
805
|
+
config: TranslateConfig;
|
|
806
|
+
provider?: LLMProvider;
|
|
807
|
+
verbose?: boolean;
|
|
808
|
+
/** Disable caching (--no-cache mode) */
|
|
809
|
+
noCache?: boolean;
|
|
810
|
+
}
|
|
811
|
+
interface TranslateFileOptions {
|
|
812
|
+
content: string;
|
|
813
|
+
sourceLang: string;
|
|
814
|
+
targetLang: string;
|
|
815
|
+
format?: DocumentFormat;
|
|
816
|
+
glossaryPath?: string;
|
|
817
|
+
qualityThreshold?: number;
|
|
818
|
+
maxIterations?: number;
|
|
819
|
+
context?: string;
|
|
820
|
+
/** Per-language style instruction (e.g., "경어체", "です・ます調"). Falls back to config.languages.styles[targetLang] if not specified. */
|
|
821
|
+
styleInstruction?: string;
|
|
822
|
+
/** If true, throw error when quality threshold is not met */
|
|
823
|
+
strictQuality?: boolean;
|
|
824
|
+
/** If true, throw error when glossary terms are missed */
|
|
825
|
+
strictGlossary?: boolean;
|
|
826
|
+
}
|
|
827
|
+
declare class TranslationEngine {
|
|
828
|
+
private config;
|
|
829
|
+
private provider;
|
|
830
|
+
private verbose;
|
|
831
|
+
private cache;
|
|
832
|
+
private cacheHits;
|
|
833
|
+
private cacheMisses;
|
|
834
|
+
constructor(options: TranslationEngineOptions);
|
|
835
|
+
/**
|
|
836
|
+
* Translate a single file/content
|
|
837
|
+
*/
|
|
838
|
+
translateContent(options: TranslateFileOptions): Promise<DocumentResult>;
|
|
839
|
+
/**
|
|
840
|
+
* Check glossary compliance for the entire document
|
|
841
|
+
*/
|
|
842
|
+
private checkDocumentGlossaryCompliance;
|
|
843
|
+
private translateMarkdown;
|
|
844
|
+
private translatePlainText;
|
|
845
|
+
private translateChunk;
|
|
846
|
+
private detectFormat;
|
|
847
|
+
}
|
|
848
|
+
declare function createTranslationEngine(options: TranslationEngineOptions): TranslationEngine;
|
|
849
|
+
declare function translateText(content: string, sourceLang: string, targetLang: string, options?: {
|
|
850
|
+
provider?: LLMProvider;
|
|
851
|
+
glossaryPath?: string;
|
|
852
|
+
qualityThreshold?: number;
|
|
853
|
+
maxIterations?: number;
|
|
854
|
+
verbose?: boolean;
|
|
855
|
+
}): Promise<string>;
|
|
856
|
+
|
|
857
|
+
interface ParsedDocument {
|
|
858
|
+
/** Original markdown content */
|
|
859
|
+
original: string;
|
|
860
|
+
/** AST representation */
|
|
861
|
+
ast: Root;
|
|
862
|
+
/** Extracted text nodes for translation */
|
|
863
|
+
textNodes: TextNode[];
|
|
864
|
+
}
|
|
865
|
+
interface TextNode {
|
|
866
|
+
/** Unique identifier for this node */
|
|
867
|
+
id: string;
|
|
868
|
+
/** Text content to translate */
|
|
869
|
+
content: string;
|
|
870
|
+
/** Node type in AST */
|
|
871
|
+
type: string;
|
|
872
|
+
/** Position in source document */
|
|
873
|
+
position?: {
|
|
874
|
+
start: {
|
|
875
|
+
line: number;
|
|
876
|
+
column: number;
|
|
877
|
+
offset?: number;
|
|
878
|
+
};
|
|
879
|
+
end: {
|
|
880
|
+
line: number;
|
|
881
|
+
column: number;
|
|
882
|
+
offset?: number;
|
|
883
|
+
};
|
|
884
|
+
};
|
|
885
|
+
/** Path to node in AST (for restoration) */
|
|
886
|
+
path: number[];
|
|
887
|
+
/** Whether this node should be translated */
|
|
888
|
+
translatable: boolean;
|
|
889
|
+
}
|
|
890
|
+
interface TranslationMap {
|
|
891
|
+
[nodeId: string]: string;
|
|
892
|
+
}
|
|
893
|
+
/**
|
|
894
|
+
* Parse markdown content and extract translatable text nodes
|
|
895
|
+
*/
|
|
896
|
+
declare function parseMarkdown(content: string): Promise<ParsedDocument>;
|
|
897
|
+
/**
|
|
898
|
+
* Apply translations to AST and stringify back to markdown
|
|
899
|
+
*/
|
|
900
|
+
declare function applyTranslations(document: ParsedDocument, translations: TranslationMap): Promise<string>;
|
|
901
|
+
/**
|
|
902
|
+
* Get only translatable text from a parsed document
|
|
903
|
+
*/
|
|
904
|
+
declare function getTranslatableText(document: ParsedDocument): string[];
|
|
905
|
+
/**
|
|
906
|
+
* Create a translation map from an array of translations
|
|
907
|
+
* (in same order as getTranslatableText output)
|
|
908
|
+
*/
|
|
909
|
+
declare function createTranslationMap(document: ParsedDocument, translations: string[]): TranslationMap;
|
|
910
|
+
/**
|
|
911
|
+
* Extract full text content for translation (preserving structure markers)
|
|
912
|
+
*
|
|
913
|
+
* Processing order is important:
|
|
914
|
+
* 1. First, handle fenced code blocks (must be at line start with newline after opener)
|
|
915
|
+
* 2. Then, handle multi-backtick inline code (for examples like ` ```js...``` `)
|
|
916
|
+
* 3. Then, handle single-backtick inline code
|
|
917
|
+
* 4. Finally, handle link URLs
|
|
918
|
+
*/
|
|
919
|
+
declare function extractTextForTranslation(content: string): {
|
|
920
|
+
text: string;
|
|
921
|
+
preservedSections: Map<string, string>;
|
|
922
|
+
};
|
|
923
|
+
/**
|
|
924
|
+
* Restore preserved sections after translation
|
|
925
|
+
*
|
|
926
|
+
* Uses flexible regex matching to handle cases where LLM may have:
|
|
927
|
+
* - Added spaces around placeholders
|
|
928
|
+
* - Changed case
|
|
929
|
+
* - Added extra underscores
|
|
930
|
+
*/
|
|
931
|
+
declare function restorePreservedSections(translatedText: string, preservedSections: Map<string, string>): string;
|
|
932
|
+
/**
|
|
933
|
+
* Simple markdown translation that preserves structure
|
|
934
|
+
* This is the main function to use for translating markdown content
|
|
935
|
+
*/
|
|
936
|
+
declare function translateMarkdownContent(content: string, translateFn: (text: string) => Promise<string>): Promise<string>;
|
|
937
|
+
|
|
938
|
+
/**
|
|
939
|
+
* Approximate token count for a given text.
|
|
940
|
+
* This is a rough estimate - actual token counts may vary by model.
|
|
941
|
+
*
|
|
942
|
+
* Rules of thumb:
|
|
943
|
+
* - English: ~4 characters per token
|
|
944
|
+
* - CJK (Korean, Japanese, Chinese): ~1.5 characters per token
|
|
945
|
+
* - Code: ~3.5 characters per token
|
|
946
|
+
*/
|
|
947
|
+
declare function estimateTokens(text: string): number;
|
|
948
|
+
/**
|
|
949
|
+
* Check if text exceeds a token limit
|
|
950
|
+
*/
|
|
951
|
+
declare function exceedsTokenLimit(text: string, limit: number): boolean;
|
|
952
|
+
/**
|
|
953
|
+
* Truncate text to approximately fit within a token limit
|
|
954
|
+
*/
|
|
955
|
+
declare function truncateToTokenLimit(text: string, limit: number): string;
|
|
956
|
+
|
|
957
|
+
type LogLevel = 'debug' | 'info' | 'warn' | 'error';
|
|
958
|
+
interface LoggerConfig {
|
|
959
|
+
level: LogLevel;
|
|
960
|
+
quiet: boolean;
|
|
961
|
+
json: boolean;
|
|
962
|
+
}
|
|
963
|
+
declare function configureLogger(options: Partial<LoggerConfig>): void;
|
|
964
|
+
declare const logger: {
|
|
965
|
+
debug(message: string, data?: Record<string, unknown>): void;
|
|
966
|
+
info(message: string, data?: Record<string, unknown>): void;
|
|
967
|
+
warn(message: string, data?: Record<string, unknown>): void;
|
|
968
|
+
error(message: string, data?: Record<string, unknown>): void;
|
|
969
|
+
success(message: string): void;
|
|
970
|
+
progress(current: number, total: number, message: string): void;
|
|
971
|
+
};
|
|
972
|
+
declare function createTimer(): {
|
|
973
|
+
elapsed: () => number;
|
|
974
|
+
format: () => string;
|
|
975
|
+
};
|
|
976
|
+
|
|
977
|
+
/**
|
|
978
|
+
* VitePress Integration Helpers
|
|
979
|
+
*
|
|
980
|
+
* Helper functions to auto-generate VitePress i18n configuration
|
|
981
|
+
* based on translated document structure.
|
|
982
|
+
*
|
|
983
|
+
* @example
|
|
984
|
+
* ```typescript
|
|
985
|
+
* // docs/.vitepress/config.ts
|
|
986
|
+
* import { defineConfig } from 'vitepress';
|
|
987
|
+
* import { generateLocaleConfig } from 'llm-translate/vitepress';
|
|
988
|
+
*
|
|
989
|
+
* const locales = await generateLocaleConfig('./docs', {
|
|
990
|
+
* defaultLocale: 'en',
|
|
991
|
+
* locales: ['ko', 'ja'],
|
|
992
|
+
* });
|
|
993
|
+
*
|
|
994
|
+
* export default defineConfig({ locales });
|
|
995
|
+
* ```
|
|
996
|
+
*/
|
|
997
|
+
/**
|
|
998
|
+
* Navigation item with a link
|
|
999
|
+
* Compatible with VitePress DefaultTheme.NavItemWithLink
|
|
1000
|
+
*/
|
|
1001
|
+
interface NavItemWithLink {
|
|
1002
|
+
text: string;
|
|
1003
|
+
link: string;
|
|
1004
|
+
activeMatch?: string;
|
|
1005
|
+
rel?: string;
|
|
1006
|
+
target?: string;
|
|
1007
|
+
noIcon?: boolean;
|
|
1008
|
+
}
|
|
1009
|
+
/**
|
|
1010
|
+
* Navigation item with children
|
|
1011
|
+
* Compatible with VitePress DefaultTheme.NavItemWithChildren
|
|
1012
|
+
*/
|
|
1013
|
+
interface NavItemWithChildren {
|
|
1014
|
+
text?: string;
|
|
1015
|
+
items: NavItemWithLink[];
|
|
1016
|
+
activeMatch?: string;
|
|
1017
|
+
}
|
|
1018
|
+
/**
|
|
1019
|
+
* Navigation item type
|
|
1020
|
+
* Compatible with VitePress DefaultTheme.NavItem
|
|
1021
|
+
*/
|
|
1022
|
+
type NavItem = NavItemWithLink | NavItemWithChildren;
|
|
1023
|
+
/**
|
|
1024
|
+
* Sidebar item
|
|
1025
|
+
* Compatible with VitePress DefaultTheme.SidebarItem
|
|
1026
|
+
*/
|
|
1027
|
+
interface SidebarItem {
|
|
1028
|
+
text?: string;
|
|
1029
|
+
link?: string;
|
|
1030
|
+
items?: SidebarItem[];
|
|
1031
|
+
collapsed?: boolean;
|
|
1032
|
+
base?: string;
|
|
1033
|
+
docFooterText?: string;
|
|
1034
|
+
rel?: string;
|
|
1035
|
+
target?: string;
|
|
1036
|
+
}
|
|
1037
|
+
/**
|
|
1038
|
+
* Theme configuration
|
|
1039
|
+
* Compatible with VitePress DefaultTheme.Config
|
|
1040
|
+
*/
|
|
1041
|
+
interface ThemeConfig {
|
|
1042
|
+
nav?: NavItem[];
|
|
1043
|
+
sidebar?: SidebarItem[] | Record<string, SidebarItem[]>;
|
|
1044
|
+
editLink?: {
|
|
1045
|
+
pattern: string;
|
|
1046
|
+
text?: string;
|
|
1047
|
+
};
|
|
1048
|
+
docFooter?: {
|
|
1049
|
+
prev?: string | false;
|
|
1050
|
+
next?: string | false;
|
|
1051
|
+
};
|
|
1052
|
+
outline?: {
|
|
1053
|
+
level?: number | [number, number] | 'deep';
|
|
1054
|
+
label?: string;
|
|
1055
|
+
};
|
|
1056
|
+
lastUpdated?: {
|
|
1057
|
+
text?: string;
|
|
1058
|
+
formatOptions?: Intl.DateTimeFormatOptions;
|
|
1059
|
+
};
|
|
1060
|
+
returnToTopLabel?: string;
|
|
1061
|
+
sidebarMenuLabel?: string;
|
|
1062
|
+
darkModeSwitchLabel?: string;
|
|
1063
|
+
langMenuLabel?: string;
|
|
1064
|
+
}
|
|
1065
|
+
/**
|
|
1066
|
+
* Locale configuration
|
|
1067
|
+
* Compatible with VitePress LocaleConfig
|
|
1068
|
+
*/
|
|
1069
|
+
interface LocaleConfig {
|
|
1070
|
+
label: string;
|
|
1071
|
+
lang?: string;
|
|
1072
|
+
link?: string;
|
|
1073
|
+
description?: string;
|
|
1074
|
+
themeConfig?: ThemeConfig;
|
|
1075
|
+
}
|
|
1076
|
+
/**
|
|
1077
|
+
* Options for generating locale configuration
|
|
1078
|
+
*/
|
|
1079
|
+
interface GenerateOptions {
|
|
1080
|
+
/** Default locale code (e.g., 'en') */
|
|
1081
|
+
defaultLocale?: string;
|
|
1082
|
+
/** List of locale codes to generate (e.g., ['ko', 'ja']) */
|
|
1083
|
+
locales?: string[];
|
|
1084
|
+
/** Locale display labels */
|
|
1085
|
+
labels?: Record<string, string>;
|
|
1086
|
+
/** Locale lang codes for HTML */
|
|
1087
|
+
langCodes?: Record<string, string>;
|
|
1088
|
+
/** Locale descriptions */
|
|
1089
|
+
descriptions?: Record<string, string>;
|
|
1090
|
+
/** Directories to include in sidebar (default: auto-detect) */
|
|
1091
|
+
sidebarDirs?: string[];
|
|
1092
|
+
/** Use title from file's first heading */
|
|
1093
|
+
useTitleFromHeading?: boolean;
|
|
1094
|
+
/** Custom locale translations */
|
|
1095
|
+
translations?: Record<string, LocaleTranslations>;
|
|
1096
|
+
}
|
|
1097
|
+
interface LocaleTranslations {
|
|
1098
|
+
editLinkText?: string;
|
|
1099
|
+
docFooter?: {
|
|
1100
|
+
prev: string;
|
|
1101
|
+
next: string;
|
|
1102
|
+
};
|
|
1103
|
+
outline?: {
|
|
1104
|
+
label: string;
|
|
1105
|
+
};
|
|
1106
|
+
lastUpdated?: {
|
|
1107
|
+
text: string;
|
|
1108
|
+
};
|
|
1109
|
+
returnToTopLabel?: string;
|
|
1110
|
+
sidebarMenuLabel?: string;
|
|
1111
|
+
darkModeSwitchLabel?: string;
|
|
1112
|
+
}
|
|
1113
|
+
/**
|
|
1114
|
+
* Auto-detect available locales by scanning for locale directories
|
|
1115
|
+
*/
|
|
1116
|
+
declare function detectLocales(docsDir: string, defaultLocale?: string): string[];
|
|
1117
|
+
/**
|
|
1118
|
+
* Auto-detect sidebar directories by looking at root structure
|
|
1119
|
+
*/
|
|
1120
|
+
declare function detectSidebarDirs(docsDir: string): string[];
|
|
1121
|
+
/**
|
|
1122
|
+
* Generate locale configuration for a specific locale
|
|
1123
|
+
*/
|
|
1124
|
+
declare function generateLocale(docsDir: string, locale: string, options?: GenerateOptions): LocaleConfig;
|
|
1125
|
+
/**
|
|
1126
|
+
* Generate complete locale configuration for VitePress
|
|
1127
|
+
*
|
|
1128
|
+
* @example
|
|
1129
|
+
* ```typescript
|
|
1130
|
+
* import { defineConfig } from 'vitepress';
|
|
1131
|
+
* import { generateLocaleConfig } from 'llm-translate';
|
|
1132
|
+
*
|
|
1133
|
+
* const locales = generateLocaleConfig('./docs', {
|
|
1134
|
+
* defaultLocale: 'en',
|
|
1135
|
+
* locales: ['ko', 'ja'],
|
|
1136
|
+
* });
|
|
1137
|
+
*
|
|
1138
|
+
* export default defineConfig({
|
|
1139
|
+
* locales,
|
|
1140
|
+
* // ... other config
|
|
1141
|
+
* });
|
|
1142
|
+
* ```
|
|
1143
|
+
*/
|
|
1144
|
+
declare function generateLocaleConfig(docsDir: string, options?: GenerateOptions): Record<string, LocaleConfig>;
|
|
1145
|
+
/**
|
|
1146
|
+
* Generate sidebar configuration only
|
|
1147
|
+
*
|
|
1148
|
+
* Useful when you want to manually configure nav but auto-generate sidebar.
|
|
1149
|
+
*/
|
|
1150
|
+
declare function generateSidebarConfig(docsDir: string, options?: Omit<GenerateOptions, 'descriptions' | 'translations'>): Record<string, Record<string, SidebarItem[]>>;
|
|
1151
|
+
|
|
1152
|
+
export { type CacheEntry, type CacheIndex, type CacheKey, CacheManager, type CacheMetadata, type CacheOptions, type CacheResult, type CacheStats, type ChatMessage, type ChatRequest, type ChatResponse, type Chunk, type ChunkResult, type ChunkingConfig, ClaudeProvider, CompositePolicy, type DocumentFormat, type DocumentResult, ErrorCode, ExitCode, type GenerateOptions, type Glossary, GlossaryChangePolicy, type GlossaryTerm, type InvalidationContext, type InvalidationPolicy, type InvalidationResult, type LLMProvider, type LocaleConfig, type LocaleTranslations, type ModelInfo, type NavItem, type NavItemWithChildren, type NavItemWithLink, ProviderChangePolicy, type ProviderConfig, type ProviderName, type QualityEvaluation, QualityThresholdPolicy, type ResolvedGlossary, type ResolvedGlossaryTerm, type SidebarItem, TTLPolicy, type ThemeConfig, type TranslateConfig, TranslationAgent, TranslationEngine, TranslationError, type TranslationRequest, type TranslationResult, applyTranslations, checkGlossaryCompliance, chunkContent, configureLogger, createCacheManager, createClaudeProvider, createDefaultPolicies, createGlossaryLookup, createMinimalPolicies, createNullCacheManager, createProviderWithFallback, createStrictPolicies, createTimer, createTranslationAgent, createTranslationEngine, createTranslationMap, detectLocales, detectSidebarDirs, estimateTokens, exceedsTokenLimit, extractTextForTranslation, generateCacheKey, generateLocale, generateLocaleConfig, generateSidebarConfig, getAvailableProviders, getChunkStats, getExitCode, getProvider, getProviderConfigFromEnv, getTranslatableText, hasProvider, hashContent, isErrorCode, isTranslationError, loadConfig, loadGlossary, logger, mergeConfig, parseMarkdown, reassembleChunks, registerProvider, resolveGlossary, restorePreservedSections, translateMarkdownContent, translateText, truncateToTokenLimit };
|