@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
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
# TranslationAgent
|
|
2
|
+
|
|
3
|
+
::: info 번역
|
|
4
|
+
모든 비영어 문서는 Claude Sonnet 4를 사용하여 자동으로 번역됩니다.
|
|
5
|
+
:::
|
|
6
|
+
|
|
7
|
+
Self-Refine 알고리즘을 구현하는 저수준 번역 에이전트입니다.
|
|
8
|
+
|
|
9
|
+
## 생성자
|
|
10
|
+
|
|
11
|
+
```typescript
|
|
12
|
+
import { TranslationAgent } from '@llm-translate/cli';
|
|
13
|
+
|
|
14
|
+
const agent = new TranslationAgent(options: TranslationAgentOptions);
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
### 옵션
|
|
18
|
+
|
|
19
|
+
```typescript
|
|
20
|
+
interface TranslationAgentOptions {
|
|
21
|
+
provider: LLMProvider;
|
|
22
|
+
qualityThreshold?: number; // Default: 85
|
|
23
|
+
maxIterations?: number; // Default: 4
|
|
24
|
+
verbose?: boolean; // Default: false
|
|
25
|
+
strictQuality?: boolean; // Default: false
|
|
26
|
+
enableCaching?: boolean; // Default: true for Claude
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## 메서드
|
|
31
|
+
|
|
32
|
+
### translate
|
|
33
|
+
|
|
34
|
+
Self-Refine 루프를 사용하여 콘텐츠를 번역합니다.
|
|
35
|
+
|
|
36
|
+
```typescript
|
|
37
|
+
const result = await agent.translate(request: TranslationRequest);
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
#### 요청
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
interface TranslationRequest {
|
|
44
|
+
content: string; // Source text to translate
|
|
45
|
+
sourceLang: string; // Source language code
|
|
46
|
+
targetLang: string; // Target language code
|
|
47
|
+
glossary?: ResolvedGlossary; // Resolved glossary for target language
|
|
48
|
+
context?: {
|
|
49
|
+
documentPurpose?: string;
|
|
50
|
+
previousChunks?: string[];
|
|
51
|
+
documentSummary?: string;
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
#### 결과
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
interface TranslationResult {
|
|
60
|
+
content: string; // Translated text
|
|
61
|
+
metadata: {
|
|
62
|
+
qualityScore: number;
|
|
63
|
+
qualityThreshold: number;
|
|
64
|
+
thresholdMet: boolean;
|
|
65
|
+
iterations: number;
|
|
66
|
+
tokensUsed: {
|
|
67
|
+
input: number;
|
|
68
|
+
output: number;
|
|
69
|
+
cacheRead?: number;
|
|
70
|
+
cacheWrite?: number;
|
|
71
|
+
};
|
|
72
|
+
duration: number;
|
|
73
|
+
provider: string;
|
|
74
|
+
model: string;
|
|
75
|
+
};
|
|
76
|
+
glossaryCompliance?: {
|
|
77
|
+
applied: string[]; // Terms successfully applied
|
|
78
|
+
missed: string[]; // Terms not found in translation
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Self-Refine 알고리즘
|
|
84
|
+
|
|
85
|
+
에이전트는 다음과 같은 반복적 개선 프로세스를 구현합니다:
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
// Pseudocode
|
|
89
|
+
async translate(request) {
|
|
90
|
+
// 1. Initial translation with glossary
|
|
91
|
+
let translation = await generateInitialTranslation(request);
|
|
92
|
+
let iterations = 1;
|
|
93
|
+
|
|
94
|
+
while (iterations < maxIterations) {
|
|
95
|
+
// 2. Evaluate quality
|
|
96
|
+
const evaluation = await evaluateQuality(translation);
|
|
97
|
+
|
|
98
|
+
if (evaluation.score >= qualityThreshold) {
|
|
99
|
+
break; // Quality met
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// 3. Generate improvement suggestions
|
|
103
|
+
const suggestions = await generateReflection(translation);
|
|
104
|
+
|
|
105
|
+
// 4. Apply improvements
|
|
106
|
+
translation = await improveTranslation(translation, suggestions);
|
|
107
|
+
iterations++;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return { content: translation, metadata: { ... } };
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## 사용 예시
|
|
115
|
+
|
|
116
|
+
### 기본 번역
|
|
117
|
+
|
|
118
|
+
```typescript
|
|
119
|
+
import { TranslationAgent, createClaudeProvider } from '@llm-translate/cli';
|
|
120
|
+
|
|
121
|
+
const provider = createClaudeProvider();
|
|
122
|
+
const agent = new TranslationAgent({
|
|
123
|
+
provider,
|
|
124
|
+
qualityThreshold: 85,
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
const result = await agent.translate({
|
|
128
|
+
content: 'Hello, world!',
|
|
129
|
+
sourceLang: 'en',
|
|
130
|
+
targetLang: 'ko',
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
console.log(result.content); // 안녕하세요, 세계!
|
|
134
|
+
console.log(result.metadata.qualityScore); // 92
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### 용어집 사용
|
|
138
|
+
|
|
139
|
+
```typescript
|
|
140
|
+
import { loadGlossary, resolveGlossary } from '@llm-translate/cli';
|
|
141
|
+
|
|
142
|
+
const glossary = await loadGlossary('./glossary.json');
|
|
143
|
+
const resolved = resolveGlossary(glossary, 'ko');
|
|
144
|
+
|
|
145
|
+
const result = await agent.translate({
|
|
146
|
+
content: 'The component renders a prop.',
|
|
147
|
+
sourceLang: 'en',
|
|
148
|
+
targetLang: 'ko',
|
|
149
|
+
glossary: resolved,
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
console.log(result.glossaryCompliance);
|
|
153
|
+
// { applied: ['component', 'prop'], missed: [] }
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### 컨텍스트 사용
|
|
157
|
+
|
|
158
|
+
```typescript
|
|
159
|
+
const result = await agent.translate({
|
|
160
|
+
content: 'Click the button to continue.',
|
|
161
|
+
sourceLang: 'en',
|
|
162
|
+
targetLang: 'ko',
|
|
163
|
+
context: {
|
|
164
|
+
documentPurpose: 'User interface instructions',
|
|
165
|
+
previousChunks: [
|
|
166
|
+
'이전 단계에서 설정을 완료했습니다.', // Previous translation
|
|
167
|
+
],
|
|
168
|
+
},
|
|
169
|
+
});
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### 엄격한 품질 모드
|
|
173
|
+
|
|
174
|
+
```typescript
|
|
175
|
+
import { TranslationError, ErrorCode } from '@llm-translate/cli';
|
|
176
|
+
|
|
177
|
+
const agent = new TranslationAgent({
|
|
178
|
+
provider,
|
|
179
|
+
qualityThreshold: 95,
|
|
180
|
+
strictQuality: true, // Throw if threshold not met
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
try {
|
|
184
|
+
const result = await agent.translate(request);
|
|
185
|
+
} catch (error) {
|
|
186
|
+
if (error instanceof TranslationError &&
|
|
187
|
+
error.code === ErrorCode.QUALITY_THRESHOLD_NOT_MET) {
|
|
188
|
+
console.log(`Quality: ${error.details.score}/${error.details.threshold}`);
|
|
189
|
+
console.log(`Issues: ${error.details.issues.join(', ')}`);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
## 품질 평가
|
|
195
|
+
|
|
196
|
+
에이전트는 네 가지 기준으로 번역을 평가합니다:
|
|
197
|
+
|
|
198
|
+
| 기준 | 가중치 | 설명 |
|
|
199
|
+
|-----------|--------|-------------|
|
|
200
|
+
| 의미 정확성 | 40% | 의미 보존 |
|
|
201
|
+
| 유창성 | 25% | 자연스러운 언어 흐름 |
|
|
202
|
+
| 용어집 준수 | 20% | 용어 일관성 |
|
|
203
|
+
| 형식 보존 | 15% | 구조 유지 |
|
|
204
|
+
|
|
205
|
+
### 평가 결과
|
|
206
|
+
|
|
207
|
+
```typescript
|
|
208
|
+
interface QualityEvaluation {
|
|
209
|
+
score: number; // 0-100
|
|
210
|
+
breakdown: {
|
|
211
|
+
accuracy: number; // 0-40
|
|
212
|
+
fluency: number; // 0-25
|
|
213
|
+
glossary: number; // 0-20
|
|
214
|
+
format: number; // 0-15
|
|
215
|
+
};
|
|
216
|
+
issues: string[]; // Specific problems identified
|
|
217
|
+
}
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
## 프롬프트 캐싱
|
|
221
|
+
|
|
222
|
+
`enableCaching` 이 true일 때 (Claude의 기본값), 에이전트는 캐싱을 위해 프롬프트를 구조화합니다:
|
|
223
|
+
|
|
224
|
+
```
|
|
225
|
+
┌─────────────────────────────────┐
|
|
226
|
+
│ System Instructions (CACHED) │ ← Reused across chunks
|
|
227
|
+
├─────────────────────────────────┤
|
|
228
|
+
│ Glossary (CACHED) │ ← Reused across chunks
|
|
229
|
+
├─────────────────────────────────┤
|
|
230
|
+
│ Source Text (NOT cached) │ ← Changes per chunk
|
|
231
|
+
└─────────────────────────────────┘
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
이는 다중 청크 문서의 비용을 40-50% 절감할 수 있습니다.
|
|
235
|
+
|
|
236
|
+
## 고급: 사용자 정의 평가
|
|
237
|
+
|
|
238
|
+
기본 평가 로직을 재정의합니다:
|
|
239
|
+
|
|
240
|
+
```typescript
|
|
241
|
+
class CustomAgent extends TranslationAgent {
|
|
242
|
+
protected async evaluateQuality(
|
|
243
|
+
source: string,
|
|
244
|
+
translation: string,
|
|
245
|
+
sourceLang: string,
|
|
246
|
+
targetLang: string
|
|
247
|
+
): Promise<QualityEvaluation> {
|
|
248
|
+
// Custom evaluation logic
|
|
249
|
+
const baseEval = await super.evaluateQuality(
|
|
250
|
+
source, translation, sourceLang, targetLang
|
|
251
|
+
);
|
|
252
|
+
|
|
253
|
+
// Add custom checks
|
|
254
|
+
if (translation.length < source.length * 0.5) {
|
|
255
|
+
baseEval.score -= 10;
|
|
256
|
+
baseEval.issues.push('Translation suspiciously short');
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
return baseEval;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
```
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
# TranslationEngine
|
|
2
|
+
|
|
3
|
+
::: info 번역
|
|
4
|
+
모든 비영어 문서는 Claude Sonnet 4를 사용하여 자동으로 번역됩니다.
|
|
5
|
+
:::
|
|
6
|
+
|
|
7
|
+
번역 작업의 주요 진입점입니다.
|
|
8
|
+
|
|
9
|
+
## Constructor
|
|
10
|
+
|
|
11
|
+
```typescript
|
|
12
|
+
import { TranslationEngine } from '@llm-translate/cli';
|
|
13
|
+
|
|
14
|
+
const engine = new TranslationEngine(options: TranslationEngineOptions);
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
### Options
|
|
18
|
+
|
|
19
|
+
```typescript
|
|
20
|
+
interface TranslationEngineOptions {
|
|
21
|
+
provider: LLMProvider;
|
|
22
|
+
qualityThreshold?: number; // Default: 85
|
|
23
|
+
maxIterations?: number; // Default: 4
|
|
24
|
+
chunkingConfig?: ChunkingConfig;
|
|
25
|
+
verbose?: boolean; // Default: false
|
|
26
|
+
}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Methods
|
|
30
|
+
|
|
31
|
+
### translateFile
|
|
32
|
+
|
|
33
|
+
단일 파일을 번역합니다.
|
|
34
|
+
|
|
35
|
+
```typescript
|
|
36
|
+
const result = await engine.translateFile(options: TranslateFileOptions);
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
#### Options
|
|
40
|
+
|
|
41
|
+
```typescript
|
|
42
|
+
interface TranslateFileOptions {
|
|
43
|
+
input: string; // Input file path
|
|
44
|
+
output?: string; // Output file path (optional)
|
|
45
|
+
sourceLang?: string; // Source language (auto-detect if omitted)
|
|
46
|
+
targetLang: string; // Target language (required)
|
|
47
|
+
glossary?: string | Glossary; // Glossary path or object
|
|
48
|
+
context?: {
|
|
49
|
+
documentPurpose?: string;
|
|
50
|
+
preserveFormatting?: boolean;
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
#### Returns
|
|
56
|
+
|
|
57
|
+
```typescript
|
|
58
|
+
interface TranslateFileResult {
|
|
59
|
+
content: string;
|
|
60
|
+
outputPath?: string;
|
|
61
|
+
metadata: {
|
|
62
|
+
qualityScore: number;
|
|
63
|
+
qualityThreshold: number;
|
|
64
|
+
thresholdMet: boolean;
|
|
65
|
+
iterations: number;
|
|
66
|
+
tokensUsed: {
|
|
67
|
+
input: number;
|
|
68
|
+
output: number;
|
|
69
|
+
cacheRead?: number;
|
|
70
|
+
cacheWrite?: number;
|
|
71
|
+
};
|
|
72
|
+
duration: number;
|
|
73
|
+
chunks: number;
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
#### Example
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
const result = await engine.translateFile({
|
|
82
|
+
input: 'docs/guide.md',
|
|
83
|
+
output: 'docs/guide.ko.md',
|
|
84
|
+
targetLang: 'ko',
|
|
85
|
+
glossary: './glossary.json',
|
|
86
|
+
context: {
|
|
87
|
+
documentPurpose: 'Technical documentation for developers',
|
|
88
|
+
},
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
console.log(`Quality: ${result.metadata.qualityScore}`);
|
|
92
|
+
console.log(`Output: ${result.outputPath}`);
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### translateContent
|
|
96
|
+
|
|
97
|
+
콘텐츠를 직접 번역합니다 (파일 I/O 없이).
|
|
98
|
+
|
|
99
|
+
```typescript
|
|
100
|
+
const result = await engine.translateContent(
|
|
101
|
+
content: string,
|
|
102
|
+
options: TranslateContentOptions
|
|
103
|
+
);
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
#### Options
|
|
107
|
+
|
|
108
|
+
```typescript
|
|
109
|
+
interface TranslateContentOptions {
|
|
110
|
+
sourceLang?: string;
|
|
111
|
+
targetLang: string;
|
|
112
|
+
glossary?: ResolvedGlossary;
|
|
113
|
+
format?: 'markdown' | 'html' | 'text';
|
|
114
|
+
context?: {
|
|
115
|
+
documentPurpose?: string;
|
|
116
|
+
previousChunks?: string[];
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
#### Example
|
|
122
|
+
|
|
123
|
+
```typescript
|
|
124
|
+
const content = `
|
|
125
|
+
# Hello World
|
|
126
|
+
|
|
127
|
+
This is a **markdown** document.
|
|
128
|
+
`;
|
|
129
|
+
|
|
130
|
+
const result = await engine.translateContent(content, {
|
|
131
|
+
targetLang: 'ko',
|
|
132
|
+
format: 'markdown',
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
console.log(result.content);
|
|
136
|
+
// # 안녕하세요
|
|
137
|
+
//
|
|
138
|
+
// 이것은 **마크다운** 문서입니다.
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### translateDirectory
|
|
142
|
+
|
|
143
|
+
디렉토리의 모든 파일을 번역합니다.
|
|
144
|
+
|
|
145
|
+
```typescript
|
|
146
|
+
const results = await engine.translateDirectory(options: TranslateDirectoryOptions);
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
#### Options
|
|
150
|
+
|
|
151
|
+
```typescript
|
|
152
|
+
interface TranslateDirectoryOptions {
|
|
153
|
+
input: string; // Input directory
|
|
154
|
+
output: string; // Output directory
|
|
155
|
+
targetLang: string;
|
|
156
|
+
glossary?: string | Glossary;
|
|
157
|
+
pattern?: string; // Glob pattern (default: '**/*.md')
|
|
158
|
+
exclude?: string[]; // Patterns to exclude
|
|
159
|
+
concurrency?: number; // Parallel processing (default: 3)
|
|
160
|
+
continueOnError?: boolean;
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
#### Returns
|
|
165
|
+
|
|
166
|
+
```typescript
|
|
167
|
+
interface TranslateDirectoryResult {
|
|
168
|
+
successful: TranslateFileResult[];
|
|
169
|
+
failed: Array<{
|
|
170
|
+
file: string;
|
|
171
|
+
error: Error;
|
|
172
|
+
}>;
|
|
173
|
+
summary: {
|
|
174
|
+
total: number;
|
|
175
|
+
successful: number;
|
|
176
|
+
failed: number;
|
|
177
|
+
totalDuration: number;
|
|
178
|
+
averageQuality: number;
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
#### Example
|
|
184
|
+
|
|
185
|
+
```typescript
|
|
186
|
+
const results = await engine.translateDirectory({
|
|
187
|
+
input: './docs',
|
|
188
|
+
output: './docs-ko',
|
|
189
|
+
targetLang: 'ko',
|
|
190
|
+
glossary: './glossary.json',
|
|
191
|
+
concurrency: 5,
|
|
192
|
+
continueOnError: true,
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
console.log(`Translated: ${results.summary.successful}/${results.summary.total}`);
|
|
196
|
+
console.log(`Average quality: ${results.summary.averageQuality}`);
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
## Events
|
|
200
|
+
|
|
201
|
+
TranslationEngine은 EventEmitter를 확장합니다:
|
|
202
|
+
|
|
203
|
+
```typescript
|
|
204
|
+
engine.on('chunk:start', (data) => {
|
|
205
|
+
console.log(`Starting chunk ${data.index}/${data.total}`);
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
engine.on('chunk:complete', (data) => {
|
|
209
|
+
console.log(`Chunk ${data.index}: quality ${data.quality}`);
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
engine.on('file:start', (data) => {
|
|
213
|
+
console.log(`Translating: ${data.file}`);
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
engine.on('file:complete', (data) => {
|
|
217
|
+
console.log(`Completed: ${data.file} (${data.quality})`);
|
|
218
|
+
});
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
## Full Example
|
|
222
|
+
|
|
223
|
+
```typescript
|
|
224
|
+
import {
|
|
225
|
+
TranslationEngine,
|
|
226
|
+
createClaudeProvider,
|
|
227
|
+
loadGlossary,
|
|
228
|
+
resolveGlossary,
|
|
229
|
+
} from '@llm-translate/cli';
|
|
230
|
+
|
|
231
|
+
async function translateDocs() {
|
|
232
|
+
// Setup provider
|
|
233
|
+
const provider = createClaudeProvider({
|
|
234
|
+
apiKey: process.env.ANTHROPIC_API_KEY,
|
|
235
|
+
defaultModel: 'claude-sonnet-4-5-20250929',
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
// Load and resolve glossary
|
|
239
|
+
const glossary = await loadGlossary('./glossary.json');
|
|
240
|
+
|
|
241
|
+
// Create engine
|
|
242
|
+
const engine = new TranslationEngine({
|
|
243
|
+
provider,
|
|
244
|
+
qualityThreshold: 90,
|
|
245
|
+
maxIterations: 5,
|
|
246
|
+
verbose: true,
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
// Track progress
|
|
250
|
+
engine.on('chunk:complete', ({ index, total, quality }) => {
|
|
251
|
+
console.log(`Progress: ${index}/${total} (quality: ${quality})`);
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
// Translate
|
|
255
|
+
try {
|
|
256
|
+
const result = await engine.translateFile({
|
|
257
|
+
input: 'README.md',
|
|
258
|
+
output: 'README.ko.md',
|
|
259
|
+
targetLang: 'ko',
|
|
260
|
+
glossary,
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
console.log('Translation complete!');
|
|
264
|
+
console.log(`Quality: ${result.metadata.qualityScore}`);
|
|
265
|
+
console.log(`Tokens: ${result.metadata.tokensUsed.input} in / ${result.metadata.tokensUsed.output} out`);
|
|
266
|
+
|
|
267
|
+
if (result.metadata.tokensUsed.cacheRead) {
|
|
268
|
+
console.log(`Cache hit: ${result.metadata.tokensUsed.cacheRead} tokens`);
|
|
269
|
+
}
|
|
270
|
+
} catch (error) {
|
|
271
|
+
console.error('Translation failed:', error.message);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
```
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
# API Reference
|
|
2
|
+
|
|
3
|
+
::: info 번역
|
|
4
|
+
모든 비영어 문서는 Claude Sonnet 4를 사용하여 자동으로 번역됩니다.
|
|
5
|
+
:::
|
|
6
|
+
|
|
7
|
+
llm-translate는 Node.js 애플리케이션에서 프로그래밍 방식으로 사용할 수 있습니다.
|
|
8
|
+
|
|
9
|
+
## 설치
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install @llm-translate/cli
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## 빠른 시작
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
import { TranslationEngine, createClaudeProvider } from '@llm-translate/cli';
|
|
19
|
+
|
|
20
|
+
// Create provider
|
|
21
|
+
const provider = createClaudeProvider({
|
|
22
|
+
apiKey: process.env.ANTHROPIC_API_KEY,
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
// Create engine
|
|
26
|
+
const engine = new TranslationEngine({
|
|
27
|
+
provider,
|
|
28
|
+
qualityThreshold: 85,
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
// Translate
|
|
32
|
+
const result = await engine.translateFile({
|
|
33
|
+
input: 'README.md',
|
|
34
|
+
output: 'README.ko.md',
|
|
35
|
+
sourceLang: 'en',
|
|
36
|
+
targetLang: 'ko',
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
console.log(result.metadata);
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## 핵심 클래스
|
|
43
|
+
|
|
44
|
+
### [TranslationEngine](./engine)
|
|
45
|
+
|
|
46
|
+
번역 작업의 주요 진입점입니다.
|
|
47
|
+
|
|
48
|
+
```typescript
|
|
49
|
+
const engine = new TranslationEngine(options);
|
|
50
|
+
await engine.translateFile({ input, output, targetLang });
|
|
51
|
+
await engine.translateContent(content, options);
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### [TranslationAgent](./agent)
|
|
55
|
+
|
|
56
|
+
Self-Refine 루프를 가진 저수준 번역 에이전트입니다.
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
const agent = new TranslationAgent({ provider, qualityThreshold });
|
|
60
|
+
const result = await agent.translate(request);
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### [Providers](./providers)
|
|
64
|
+
|
|
65
|
+
LLM 제공자 구현체입니다.
|
|
66
|
+
|
|
67
|
+
```typescript
|
|
68
|
+
import {
|
|
69
|
+
createClaudeProvider,
|
|
70
|
+
createOpenAIProvider,
|
|
71
|
+
createOllamaProvider,
|
|
72
|
+
} from '@llm-translate/cli';
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## 타입 내보내기
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
import type {
|
|
79
|
+
// Configuration
|
|
80
|
+
TranslationConfig,
|
|
81
|
+
ProviderConfig,
|
|
82
|
+
|
|
83
|
+
// Requests/Results
|
|
84
|
+
TranslationRequest,
|
|
85
|
+
TranslationResult,
|
|
86
|
+
|
|
87
|
+
// Glossary
|
|
88
|
+
Glossary,
|
|
89
|
+
GlossaryTerm,
|
|
90
|
+
ResolvedGlossary,
|
|
91
|
+
|
|
92
|
+
// Quality
|
|
93
|
+
QualityEvaluation,
|
|
94
|
+
|
|
95
|
+
// Provider
|
|
96
|
+
LLMProvider,
|
|
97
|
+
ChatMessage,
|
|
98
|
+
ChatResponse,
|
|
99
|
+
} from '@llm-translate/cli';
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## 유틸리티 함수
|
|
103
|
+
|
|
104
|
+
### Chunking
|
|
105
|
+
|
|
106
|
+
```typescript
|
|
107
|
+
import { chunkContent, chunkMarkdown } from '@llm-translate/cli';
|
|
108
|
+
|
|
109
|
+
const chunks = chunkContent(text, { maxTokens: 1024 });
|
|
110
|
+
const mdChunks = chunkMarkdown(markdown, { maxTokens: 1024 });
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### 용어집
|
|
114
|
+
|
|
115
|
+
```typescript
|
|
116
|
+
import { loadGlossary, resolveGlossary, createGlossaryLookup } from '@llm-translate/cli';
|
|
117
|
+
|
|
118
|
+
const glossary = await loadGlossary('./glossary.json');
|
|
119
|
+
const resolved = resolveGlossary(glossary, 'ko');
|
|
120
|
+
const lookup = createGlossaryLookup(resolved);
|
|
121
|
+
|
|
122
|
+
const terms = lookup.findAll(sourceText);
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### 토큰 추정
|
|
126
|
+
|
|
127
|
+
```typescript
|
|
128
|
+
import { estimateTokens, calculateTokenBudget } from '@llm-translate/cli';
|
|
129
|
+
|
|
130
|
+
const tokens = estimateTokens(text);
|
|
131
|
+
const budget = calculateTokenBudget(text, { glossaryTokens: 500 });
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## 오류 처리
|
|
135
|
+
|
|
136
|
+
```typescript
|
|
137
|
+
import { TranslationError, ErrorCode } from '@llm-translate/cli';
|
|
138
|
+
|
|
139
|
+
try {
|
|
140
|
+
await engine.translateFile(options);
|
|
141
|
+
} catch (error) {
|
|
142
|
+
if (error instanceof TranslationError) {
|
|
143
|
+
switch (error.code) {
|
|
144
|
+
case ErrorCode.FILE_NOT_FOUND:
|
|
145
|
+
// Handle missing file
|
|
146
|
+
break;
|
|
147
|
+
case ErrorCode.QUALITY_THRESHOLD_NOT_MET:
|
|
148
|
+
// Handle quality issue
|
|
149
|
+
console.log('Score:', error.details.score);
|
|
150
|
+
break;
|
|
151
|
+
case ErrorCode.PROVIDER_RATE_LIMITED:
|
|
152
|
+
// Handle rate limit
|
|
153
|
+
break;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## 오류 코드
|
|
160
|
+
|
|
161
|
+
| 코드 | 설명 |
|
|
162
|
+
|------|-------------|
|
|
163
|
+
|`FILE_NOT_FOUND`| 입력 파일이 존재하지 않습니다 |
|
|
164
|
+
|`INVALID_CONFIG`| 구성 검증에 실패했습니다 |
|
|
165
|
+
|`GLOSSARY_NOT_FOUND`| 용어집 파일을 찾을 수 없습니다 |
|
|
166
|
+
|`GLOSSARY_INVALID`| 용어집 검증에 실패했습니다 |
|
|
167
|
+
|`PROVIDER_AUTH_FAILED`| API 키가 유효하지 않습니다 |
|
|
168
|
+
|`PROVIDER_RATE_LIMITED`| 속도 제한을 초과했습니다 |
|
|
169
|
+
|`PROVIDER_ERROR`| 일반적인 제공자 오류입니다 |
|
|
170
|
+
|`QUALITY_THRESHOLD_NOT_MET`| 번역 품질이 임계값 미만입니다 |
|
|
171
|
+
|`PARSE_ERROR`| 문서 파싱에 실패했습니다 |
|