@intlayer/cli 7.0.7 → 7.0.8
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/dist/assets/translation-alignment/ARCHITECTURE.md +518 -0
- package/dist/assets/translation-alignment/IMPROVEMENTS.md +550 -0
- package/dist/assets/translation-alignment/INTEGRATION_EXAMPLE.md +682 -0
- package/dist/assets/translation-alignment/QUICK_START.md +494 -0
- package/dist/assets/translation-alignment/README.md +485 -0
- package/dist/assets/translation-alignment/SUMMARY.md +440 -0
- package/dist/cjs/IntlayerEventListener.cjs +0 -3
- package/dist/cjs/IntlayerEventListener.cjs.map +1 -1
- package/dist/cjs/_virtual/_utils_asset.cjs +0 -3
- package/dist/cjs/build.cjs +0 -2
- package/dist/cjs/build.cjs.map +1 -1
- package/dist/cjs/cli.cjs +6 -7
- package/dist/cjs/cli.cjs.map +1 -1
- package/dist/cjs/config.cjs +0 -1
- package/dist/cjs/config.cjs.map +1 -1
- package/dist/cjs/editor.cjs +0 -4
- package/dist/cjs/editor.cjs.map +1 -1
- package/dist/cjs/fill/fill.cjs +0 -3
- package/dist/cjs/fill/fill.cjs.map +1 -1
- package/dist/cjs/fill/formatAutoFilledFilePath.cjs +0 -1
- package/dist/cjs/fill/formatAutoFilledFilePath.cjs.map +1 -1
- package/dist/cjs/fill/listTranslationsTasks.cjs +0 -6
- package/dist/cjs/fill/listTranslationsTasks.cjs.map +1 -1
- package/dist/cjs/fill/translateDictionary.cjs +0 -6
- package/dist/cjs/fill/translateDictionary.cjs.map +1 -1
- package/dist/cjs/fill/writeFill.cjs +0 -4
- package/dist/cjs/fill/writeFill.cjs.map +1 -1
- package/dist/cjs/getTargetDictionary.cjs +0 -4
- package/dist/cjs/getTargetDictionary.cjs.map +1 -1
- package/dist/cjs/index.cjs +0 -1
- package/dist/cjs/listContentDeclaration.cjs +0 -4
- package/dist/cjs/listContentDeclaration.cjs.map +1 -1
- package/dist/cjs/liveSync.cjs +0 -6
- package/dist/cjs/liveSync.cjs.map +1 -1
- package/dist/cjs/pull.cjs +0 -5
- package/dist/cjs/pull.cjs.map +1 -1
- package/dist/cjs/push/pullLog.cjs +0 -1
- package/dist/cjs/push/pullLog.cjs.map +1 -1
- package/dist/cjs/push/push.cjs +0 -5
- package/dist/cjs/push/push.cjs.map +1 -1
- package/dist/cjs/pushConfig.cjs +0 -2
- package/dist/cjs/pushConfig.cjs.map +1 -1
- package/dist/cjs/pushLog.cjs +0 -1
- package/dist/cjs/pushLog.cjs.map +1 -1
- package/dist/cjs/reviewDoc.cjs +8 -131
- package/dist/cjs/reviewDoc.cjs.map +1 -1
- package/dist/cjs/reviewDocBlockAware.cjs +90 -0
- package/dist/cjs/reviewDocBlockAware.cjs.map +1 -0
- package/dist/cjs/test/index.cjs +0 -2
- package/dist/cjs/test/index.cjs.map +1 -1
- package/dist/cjs/test/listMissingTranslations.cjs +0 -4
- package/dist/cjs/test/listMissingTranslations.cjs.map +1 -1
- package/dist/cjs/translateDoc.cjs +8 -8
- package/dist/cjs/translateDoc.cjs.map +1 -1
- package/dist/cjs/translation-alignment/alignBlocks.cjs +67 -0
- package/dist/cjs/translation-alignment/alignBlocks.cjs.map +1 -0
- package/dist/cjs/translation-alignment/computeSimilarity.cjs +25 -0
- package/dist/cjs/translation-alignment/computeSimilarity.cjs.map +1 -0
- package/dist/cjs/translation-alignment/fingerprintBlock.cjs +23 -0
- package/dist/cjs/translation-alignment/fingerprintBlock.cjs.map +1 -0
- package/dist/cjs/translation-alignment/index.cjs +21 -0
- package/dist/cjs/translation-alignment/mapChangedLinesToBlocks.cjs +18 -0
- package/dist/cjs/translation-alignment/mapChangedLinesToBlocks.cjs.map +1 -0
- package/dist/cjs/translation-alignment/normalizeBlock.cjs +22 -0
- package/dist/cjs/translation-alignment/normalizeBlock.cjs.map +1 -0
- package/dist/cjs/translation-alignment/pipeline.cjs +37 -0
- package/dist/cjs/translation-alignment/pipeline.cjs.map +1 -0
- package/dist/cjs/translation-alignment/planActions.cjs +48 -0
- package/dist/cjs/translation-alignment/planActions.cjs.map +1 -0
- package/dist/cjs/translation-alignment/rebuildDocument.cjs +49 -0
- package/dist/cjs/translation-alignment/rebuildDocument.cjs.map +1 -0
- package/dist/cjs/translation-alignment/segmentDocument.cjs +132 -0
- package/dist/cjs/translation-alignment/segmentDocument.cjs.map +1 -0
- package/dist/cjs/translation-alignment/types.cjs +0 -0
- package/dist/cjs/utils/calculateChunks.cjs +0 -1
- package/dist/cjs/utils/calculateChunks.cjs.map +1 -1
- package/dist/cjs/utils/checkAccess.cjs +0 -2
- package/dist/cjs/utils/checkAccess.cjs.map +1 -1
- package/dist/cjs/utils/checkLastUpdateTime.cjs +0 -1
- package/dist/cjs/utils/checkLastUpdateTime.cjs.map +1 -1
- package/dist/cjs/utils/chunkInference.cjs +0 -2
- package/dist/cjs/utils/chunkInference.cjs.map +1 -1
- package/dist/cjs/utils/getIsFileUpdatedRecently.cjs +0 -1
- package/dist/cjs/utils/getIsFileUpdatedRecently.cjs.map +1 -1
- package/dist/cjs/utils/getParentPackageJSON.cjs +0 -2
- package/dist/cjs/utils/getParentPackageJSON.cjs.map +1 -1
- package/dist/cjs/utils/mapChunksBetweenFiles.cjs +0 -1
- package/dist/cjs/utils/mapChunksBetweenFiles.cjs.map +1 -1
- package/dist/cjs/watch.cjs +0 -2
- package/dist/cjs/watch.cjs.map +1 -1
- package/dist/esm/cli.mjs +6 -3
- package/dist/esm/cli.mjs.map +1 -1
- package/dist/esm/index.mjs +2 -2
- package/dist/esm/reviewDoc.mjs +13 -128
- package/dist/esm/reviewDoc.mjs.map +1 -1
- package/dist/esm/reviewDocBlockAware.mjs +89 -0
- package/dist/esm/reviewDocBlockAware.mjs.map +1 -0
- package/dist/esm/translateDoc.mjs +8 -3
- package/dist/esm/translateDoc.mjs.map +1 -1
- package/dist/esm/translation-alignment/alignBlocks.mjs +67 -0
- package/dist/esm/translation-alignment/alignBlocks.mjs.map +1 -0
- package/dist/esm/translation-alignment/computeSimilarity.mjs +23 -0
- package/dist/esm/translation-alignment/computeSimilarity.mjs.map +1 -0
- package/dist/esm/translation-alignment/fingerprintBlock.mjs +21 -0
- package/dist/esm/translation-alignment/fingerprintBlock.mjs.map +1 -0
- package/dist/esm/translation-alignment/index.mjs +11 -0
- package/dist/esm/translation-alignment/mapChangedLinesToBlocks.mjs +17 -0
- package/dist/esm/translation-alignment/mapChangedLinesToBlocks.mjs.map +1 -0
- package/dist/esm/translation-alignment/normalizeBlock.mjs +21 -0
- package/dist/esm/translation-alignment/normalizeBlock.mjs.map +1 -0
- package/dist/esm/translation-alignment/pipeline.mjs +36 -0
- package/dist/esm/translation-alignment/pipeline.mjs.map +1 -0
- package/dist/esm/translation-alignment/planActions.mjs +47 -0
- package/dist/esm/translation-alignment/planActions.mjs.map +1 -0
- package/dist/esm/translation-alignment/rebuildDocument.mjs +47 -0
- package/dist/esm/translation-alignment/rebuildDocument.mjs.map +1 -0
- package/dist/esm/translation-alignment/segmentDocument.mjs +131 -0
- package/dist/esm/translation-alignment/segmentDocument.mjs.map +1 -0
- package/dist/esm/translation-alignment/types.mjs +0 -0
- package/dist/types/cli.d.ts.map +1 -1
- package/dist/types/index.d.ts +2 -2
- package/dist/types/pull.d.ts.map +1 -1
- package/dist/types/reviewDoc.d.ts +3 -6
- package/dist/types/reviewDoc.d.ts.map +1 -1
- package/dist/types/reviewDocBlockAware.d.ts +19 -0
- package/dist/types/reviewDocBlockAware.d.ts.map +1 -0
- package/dist/types/translateDoc.d.ts +2 -0
- package/dist/types/translateDoc.d.ts.map +1 -1
- package/dist/types/translation-alignment/alignBlocks.d.ts +7 -0
- package/dist/types/translation-alignment/alignBlocks.d.ts.map +1 -0
- package/dist/types/translation-alignment/computeSimilarity.d.ts +6 -0
- package/dist/types/translation-alignment/computeSimilarity.d.ts.map +1 -0
- package/dist/types/translation-alignment/fingerprintBlock.d.ts +7 -0
- package/dist/types/translation-alignment/fingerprintBlock.d.ts.map +1 -0
- package/dist/types/translation-alignment/index.d.ts +11 -0
- package/dist/types/translation-alignment/mapChangedLinesToBlocks.d.ts +7 -0
- package/dist/types/translation-alignment/mapChangedLinesToBlocks.d.ts.map +1 -0
- package/dist/types/translation-alignment/normalizeBlock.d.ts +7 -0
- package/dist/types/translation-alignment/normalizeBlock.d.ts.map +1 -0
- package/dist/types/translation-alignment/pipeline.d.ts +25 -0
- package/dist/types/translation-alignment/pipeline.d.ts.map +1 -0
- package/dist/types/translation-alignment/planActions.d.ts +7 -0
- package/dist/types/translation-alignment/planActions.d.ts.map +1 -0
- package/dist/types/translation-alignment/rebuildDocument.d.ts +32 -0
- package/dist/types/translation-alignment/rebuildDocument.d.ts.map +1 -0
- package/dist/types/translation-alignment/segmentDocument.d.ts +7 -0
- package/dist/types/translation-alignment/segmentDocument.d.ts.map +1 -0
- package/dist/types/translation-alignment/types.d.ts +49 -0
- package/dist/types/translation-alignment/types.d.ts.map +1 -0
- package/package.json +23 -23
|
@@ -0,0 +1,682 @@
|
|
|
1
|
+
# Integration Example
|
|
2
|
+
|
|
3
|
+
This guide shows how to integrate the block-aware translation system into your existing workflow.
|
|
4
|
+
|
|
5
|
+
## Step 1: Update Your Review Function
|
|
6
|
+
|
|
7
|
+
Replace the existing `reviewDoc.ts` call to `reviewFile` with `reviewFileBlockAware` for locales that should use block-aware alignment.
|
|
8
|
+
|
|
9
|
+
### Before
|
|
10
|
+
|
|
11
|
+
```typescript
|
|
12
|
+
// In reviewDoc.ts
|
|
13
|
+
import { reviewFile } from './reviewDoc';
|
|
14
|
+
|
|
15
|
+
await reviewFile(
|
|
16
|
+
absoluteBaseFilePath,
|
|
17
|
+
outputFilePath,
|
|
18
|
+
locale as Locale,
|
|
19
|
+
baseLocale,
|
|
20
|
+
aiOptions,
|
|
21
|
+
configOptions,
|
|
22
|
+
customInstructions,
|
|
23
|
+
changedLines
|
|
24
|
+
);
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### After
|
|
28
|
+
|
|
29
|
+
```typescript
|
|
30
|
+
// In reviewDoc.ts
|
|
31
|
+
import { reviewFile } from './reviewDoc';
|
|
32
|
+
import { reviewFileBlockAware } from './reviewDocBlockAware';
|
|
33
|
+
|
|
34
|
+
// Use block-aware for markdown files
|
|
35
|
+
const useBlockAware = absoluteBaseFilePath.endsWith('.md');
|
|
36
|
+
|
|
37
|
+
if (useBlockAware) {
|
|
38
|
+
await reviewFileBlockAware(
|
|
39
|
+
absoluteBaseFilePath,
|
|
40
|
+
outputFilePath,
|
|
41
|
+
locale as Locale,
|
|
42
|
+
baseLocale,
|
|
43
|
+
aiOptions,
|
|
44
|
+
configOptions,
|
|
45
|
+
customInstructions,
|
|
46
|
+
changedLines
|
|
47
|
+
);
|
|
48
|
+
} else {
|
|
49
|
+
// Fallback to original for non-markdown files
|
|
50
|
+
await reviewFile(
|
|
51
|
+
absoluteBaseFilePath,
|
|
52
|
+
outputFilePath,
|
|
53
|
+
locale as Locale,
|
|
54
|
+
baseLocale,
|
|
55
|
+
aiOptions,
|
|
56
|
+
configOptions,
|
|
57
|
+
customInstructions,
|
|
58
|
+
changedLines
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Step 2: Modify reviewDoc.ts
|
|
64
|
+
|
|
65
|
+
Here's the complete modified section of `reviewDoc.ts`:
|
|
66
|
+
|
|
67
|
+
```typescript
|
|
68
|
+
// Around line 330-386 in reviewDoc.ts
|
|
69
|
+
const allTasks = docList.flatMap((docPath) =>
|
|
70
|
+
locales.map((locale) => async () => {
|
|
71
|
+
appLogger(
|
|
72
|
+
`Reviewing file: ${formatPath(docPath)} to ${formatLocale(locale)}`
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
const absoluteBaseFilePath = join(configuration.content.baseDir, docPath);
|
|
76
|
+
const outputFilePath = getOutputFilePath(
|
|
77
|
+
absoluteBaseFilePath,
|
|
78
|
+
locale,
|
|
79
|
+
baseLocale
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
// Skip if file exists and skipIfExists option is enabled
|
|
83
|
+
if (skipIfExists && existsSync(outputFilePath)) {
|
|
84
|
+
const relativePath = relative(
|
|
85
|
+
configuration.content.baseDir,
|
|
86
|
+
outputFilePath
|
|
87
|
+
);
|
|
88
|
+
appLogger(
|
|
89
|
+
`${colorize('⊘', ANSIColors.YELLOW)} File ${formatPath(relativePath)} already exists, skipping.`
|
|
90
|
+
);
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const fileModificationData = checkFileModifiedRange(outputFilePath, {
|
|
95
|
+
skipIfModifiedBefore,
|
|
96
|
+
skipIfModifiedAfter,
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
if (fileModificationData.isSkipped) {
|
|
100
|
+
appLogger(fileModificationData.message);
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
let changedLines: number[] | undefined;
|
|
105
|
+
if (gitOptions) {
|
|
106
|
+
const gitChangedLines = await listGitLines(
|
|
107
|
+
absoluteBaseFilePath,
|
|
108
|
+
gitOptions
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
appLogger(`Git changed lines: ${gitChangedLines.join(', ')}`);
|
|
112
|
+
changedLines = gitChangedLines;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// NEW: Use block-aware translation for markdown files
|
|
116
|
+
const useBlockAware = absoluteBaseFilePath.endsWith('.md');
|
|
117
|
+
|
|
118
|
+
if (useBlockAware) {
|
|
119
|
+
await reviewFileBlockAware(
|
|
120
|
+
absoluteBaseFilePath,
|
|
121
|
+
outputFilePath,
|
|
122
|
+
locale as Locale,
|
|
123
|
+
baseLocale,
|
|
124
|
+
aiOptions,
|
|
125
|
+
configOptions,
|
|
126
|
+
customInstructions,
|
|
127
|
+
changedLines
|
|
128
|
+
);
|
|
129
|
+
} else {
|
|
130
|
+
await reviewFile(
|
|
131
|
+
absoluteBaseFilePath,
|
|
132
|
+
outputFilePath,
|
|
133
|
+
locale as Locale,
|
|
134
|
+
baseLocale,
|
|
135
|
+
aiOptions,
|
|
136
|
+
configOptions,
|
|
137
|
+
customInstructions,
|
|
138
|
+
changedLines
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
})
|
|
142
|
+
);
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## Step 3: Add Import Statement
|
|
146
|
+
|
|
147
|
+
At the top of `reviewDoc.ts`, add the import for the new function:
|
|
148
|
+
|
|
149
|
+
```typescript
|
|
150
|
+
// Around line 1-35 in reviewDoc.ts
|
|
151
|
+
import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
|
|
152
|
+
import { readFile } from 'node:fs/promises';
|
|
153
|
+
import { dirname, join, relative } from 'node:path';
|
|
154
|
+
import { readAsset } from 'utils:asset';
|
|
155
|
+
import type { AIOptions } from '@intlayer/api';
|
|
156
|
+
import {
|
|
157
|
+
formatLocale,
|
|
158
|
+
formatPath,
|
|
159
|
+
getChunk,
|
|
160
|
+
type ListGitFilesOptions,
|
|
161
|
+
listGitFiles,
|
|
162
|
+
listGitLines,
|
|
163
|
+
parallelize,
|
|
164
|
+
} from '@intlayer/chokidar';
|
|
165
|
+
// ... other imports ...
|
|
166
|
+
|
|
167
|
+
// ADD THIS LINE:
|
|
168
|
+
import { reviewFileBlockAware } from './reviewDocBlockAware';
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
## Step 4: Test with a Sample Document
|
|
172
|
+
|
|
173
|
+
Create a test script to verify the integration:
|
|
174
|
+
|
|
175
|
+
```typescript
|
|
176
|
+
// test-block-aware.ts
|
|
177
|
+
import { reviewFileBlockAware } from './reviewDocBlockAware';
|
|
178
|
+
import { Locales } from '@intlayer/types';
|
|
179
|
+
|
|
180
|
+
const testTranslation = async () => {
|
|
181
|
+
await reviewFileBlockAware(
|
|
182
|
+
'docs/en/test.md',
|
|
183
|
+
'docs/fr/test.md',
|
|
184
|
+
Locales.FRENCH,
|
|
185
|
+
Locales.ENGLISH,
|
|
186
|
+
{
|
|
187
|
+
// Your AI options
|
|
188
|
+
provider: 'openai',
|
|
189
|
+
// ... other options
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
// Your config options
|
|
193
|
+
},
|
|
194
|
+
'Translate technical documentation accurately',
|
|
195
|
+
[10, 15, 20] // Changed lines from git
|
|
196
|
+
);
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
testTranslation().catch(console.error);
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
## Step 5: Monitor Performance
|
|
203
|
+
|
|
204
|
+
Add logging to track the efficiency of the block-aware system:
|
|
205
|
+
|
|
206
|
+
```typescript
|
|
207
|
+
// In reviewDocBlockAware.ts, after buildAlignmentPlan:
|
|
208
|
+
|
|
209
|
+
const reusedCount = plan.actions.filter((a) => a.kind === "reuse").length;
|
|
210
|
+
const reviewCount = plan.actions.filter((a) => a.kind === "review").length;
|
|
211
|
+
const newCount = plan.actions.filter((a) => a.kind === "insert_new").length;
|
|
212
|
+
const deleteCount = plan.actions.filter((a) => a.kind === "delete").length;
|
|
213
|
+
const totalCount = plan.actions.length;
|
|
214
|
+
|
|
215
|
+
const efficiencyRate = totalCount > 0
|
|
216
|
+
? ((reusedCount / totalCount) * 100).toFixed(1)
|
|
217
|
+
: '0.0';
|
|
218
|
+
|
|
219
|
+
applicationLogger(
|
|
220
|
+
`${filePrefix}Efficiency: ${colorizeNumber(efficiencyRate)}% reused (${colorizeNumber(reusedCount)}/${colorizeNumber(totalCount)} blocks)`
|
|
221
|
+
);
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
## Step 6: Configure for Your Locales
|
|
225
|
+
|
|
226
|
+
You might want to enable block-aware translation only for specific locales:
|
|
227
|
+
|
|
228
|
+
```typescript
|
|
229
|
+
// Configuration approach
|
|
230
|
+
const BLOCK_AWARE_LOCALES = [
|
|
231
|
+
Locales.FRENCH,
|
|
232
|
+
Locales.SPANISH,
|
|
233
|
+
Locales.GERMAN,
|
|
234
|
+
// Add more locales as needed
|
|
235
|
+
];
|
|
236
|
+
|
|
237
|
+
const shouldUseBlockAware = (filePath: string, locale: Locale): boolean => {
|
|
238
|
+
return (
|
|
239
|
+
filePath.endsWith('.md') &&
|
|
240
|
+
BLOCK_AWARE_LOCALES.includes(locale)
|
|
241
|
+
);
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
// In your review task:
|
|
245
|
+
if (shouldUseBlockAware(absoluteBaseFilePath, locale)) {
|
|
246
|
+
await reviewFileBlockAware(/* ... */);
|
|
247
|
+
} else {
|
|
248
|
+
await reviewFile(/* ... */);
|
|
249
|
+
}
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
## Step 7: Gradual Rollout
|
|
253
|
+
|
|
254
|
+
For safety, you can roll out gradually:
|
|
255
|
+
|
|
256
|
+
### Phase 1: Parallel Testing (No Impact)
|
|
257
|
+
|
|
258
|
+
```typescript
|
|
259
|
+
// Run both systems and compare results
|
|
260
|
+
const blockAwareOutput = await reviewFileBlockAware(/* ... */);
|
|
261
|
+
const originalOutput = await reviewFile(/* ... */);
|
|
262
|
+
|
|
263
|
+
// Log differences for analysis
|
|
264
|
+
logDifferences(blockAwareOutput, originalOutput);
|
|
265
|
+
|
|
266
|
+
// Use original output for now
|
|
267
|
+
writeFileSync(outputFilePath, originalOutput);
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
### Phase 2: Selective Files
|
|
271
|
+
|
|
272
|
+
```typescript
|
|
273
|
+
const TEST_FILES = [
|
|
274
|
+
'docs/en/getting-started.md',
|
|
275
|
+
'docs/en/api-reference.md',
|
|
276
|
+
];
|
|
277
|
+
|
|
278
|
+
const useBlockAware = TEST_FILES.includes(docPath);
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
### Phase 3: Full Rollout
|
|
282
|
+
|
|
283
|
+
```typescript
|
|
284
|
+
// All markdown files use block-aware
|
|
285
|
+
const useBlockAware = absoluteBaseFilePath.endsWith('.md');
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
## Step 8: Adjust Configuration Based on Results
|
|
289
|
+
|
|
290
|
+
After analyzing real-world performance, tune the similarity thresholds:
|
|
291
|
+
|
|
292
|
+
```typescript
|
|
293
|
+
// In reviewDocBlockAware.ts
|
|
294
|
+
const { plan, segmentsToReview } = buildAlignmentPlan({
|
|
295
|
+
englishText,
|
|
296
|
+
frenchText,
|
|
297
|
+
changedLines,
|
|
298
|
+
similarityOptions: {
|
|
299
|
+
// Adjust these based on your needs:
|
|
300
|
+
minimumMatchForReuse: 0.92, // Higher = more conservative (more reviews)
|
|
301
|
+
minimumMatchForNearDuplicate: 0.82, // For future near-duplicate detection
|
|
302
|
+
},
|
|
303
|
+
});
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
### Finding Optimal Thresholds
|
|
307
|
+
|
|
308
|
+
Run analysis on your corpus:
|
|
309
|
+
|
|
310
|
+
```typescript
|
|
311
|
+
// analyze-thresholds.ts
|
|
312
|
+
const thresholds = [0.85, 0.87, 0.90, 0.92, 0.95];
|
|
313
|
+
const results = new Map();
|
|
314
|
+
|
|
315
|
+
for (const threshold of thresholds) {
|
|
316
|
+
const { plan } = buildAlignmentPlan({
|
|
317
|
+
englishText,
|
|
318
|
+
frenchText,
|
|
319
|
+
changedLines,
|
|
320
|
+
similarityOptions: { minimumMatchForReuse: threshold },
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
const reusedCount = plan.actions.filter(a => a.kind === "reuse").length;
|
|
324
|
+
results.set(threshold, reusedCount);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
console.table(results);
|
|
328
|
+
// Choose threshold with best balance of accuracy and efficiency
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
## Step 9: Handle Edge Cases
|
|
332
|
+
|
|
333
|
+
Add error handling for edge cases:
|
|
334
|
+
|
|
335
|
+
```typescript
|
|
336
|
+
export const reviewFileBlockAware = async (
|
|
337
|
+
baseFilePath: string,
|
|
338
|
+
outputFilePath: string,
|
|
339
|
+
locale: Locale,
|
|
340
|
+
baseLocale: Locale,
|
|
341
|
+
aiOptions?: AIOptions,
|
|
342
|
+
configOptions?: GetConfigurationOptions,
|
|
343
|
+
customInstructions?: string,
|
|
344
|
+
changedLines?: number[]
|
|
345
|
+
) => {
|
|
346
|
+
try {
|
|
347
|
+
const configuration = getConfiguration(configOptions);
|
|
348
|
+
const applicationLogger = getAppLogger(configuration);
|
|
349
|
+
|
|
350
|
+
// Read files with fallbacks
|
|
351
|
+
const englishText = await readFile(baseFilePath, "utf-8").catch((error) => {
|
|
352
|
+
applicationLogger(`Error reading base file: ${error.message}`);
|
|
353
|
+
throw error;
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
const frenchText = await readFile(outputFilePath, "utf-8").catch(() => {
|
|
357
|
+
// If French file doesn't exist, treat as new translation
|
|
358
|
+
applicationLogger(`French file doesn't exist, creating new translation`);
|
|
359
|
+
return "";
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
// Validate inputs
|
|
363
|
+
if (!englishText.trim()) {
|
|
364
|
+
applicationLogger(`Base file is empty, skipping`);
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// Rest of implementation...
|
|
369
|
+
|
|
370
|
+
} catch (error) {
|
|
371
|
+
const configuration = getConfiguration(configOptions);
|
|
372
|
+
const applicationLogger = getAppLogger(configuration);
|
|
373
|
+
applicationLogger(
|
|
374
|
+
`${colorize('✖', ANSIColors.RED)} Error processing ${formatPath(baseFilePath)}: ${error.message}`
|
|
375
|
+
);
|
|
376
|
+
throw error;
|
|
377
|
+
}
|
|
378
|
+
};
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
## Step 10: Create Migration Script
|
|
382
|
+
|
|
383
|
+
For existing translations, create a one-time migration script:
|
|
384
|
+
|
|
385
|
+
```typescript
|
|
386
|
+
// migrate-to-block-aware.ts
|
|
387
|
+
import fg from 'fast-glob';
|
|
388
|
+
import { reviewFileBlockAware } from './reviewDocBlockAware';
|
|
389
|
+
|
|
390
|
+
const migrateAllTranslations = async () => {
|
|
391
|
+
const baseFiles = await fg('docs/en/**/*.md');
|
|
392
|
+
const locales = [Locales.FRENCH, Locales.SPANISH, Locales.GERMAN];
|
|
393
|
+
|
|
394
|
+
for (const baseFile of baseFiles) {
|
|
395
|
+
for (const locale of locales) {
|
|
396
|
+
const outputFile = baseFile.replace('/en/', `/${locale}/`);
|
|
397
|
+
|
|
398
|
+
console.log(`Migrating: ${baseFile} → ${outputFile}`);
|
|
399
|
+
|
|
400
|
+
try {
|
|
401
|
+
await reviewFileBlockAware(
|
|
402
|
+
baseFile,
|
|
403
|
+
outputFile,
|
|
404
|
+
locale,
|
|
405
|
+
Locales.ENGLISH,
|
|
406
|
+
aiOptions,
|
|
407
|
+
configOptions,
|
|
408
|
+
undefined,
|
|
409
|
+
undefined // No changed lines = full review (but with block awareness)
|
|
410
|
+
);
|
|
411
|
+
} catch (error) {
|
|
412
|
+
console.error(`Failed to migrate ${baseFile}:`, error);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
};
|
|
417
|
+
|
|
418
|
+
migrateAllTranslations().catch(console.error);
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
## Complete Integration Example
|
|
422
|
+
|
|
423
|
+
Here's the complete modified `reviewDoc.ts` with block-aware integration:
|
|
424
|
+
|
|
425
|
+
```typescript
|
|
426
|
+
import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
|
|
427
|
+
import { readFile } from 'node:fs/promises';
|
|
428
|
+
import { dirname, join, relative } from 'node:path';
|
|
429
|
+
import { readAsset } from 'utils:asset';
|
|
430
|
+
import type { AIOptions } from '@intlayer/api';
|
|
431
|
+
import {
|
|
432
|
+
formatLocale,
|
|
433
|
+
formatPath,
|
|
434
|
+
getChunk,
|
|
435
|
+
type ListGitFilesOptions,
|
|
436
|
+
listGitFiles,
|
|
437
|
+
listGitLines,
|
|
438
|
+
parallelize,
|
|
439
|
+
} from '@intlayer/chokidar';
|
|
440
|
+
import {
|
|
441
|
+
ANSIColors,
|
|
442
|
+
colon,
|
|
443
|
+
colorize,
|
|
444
|
+
colorizeNumber,
|
|
445
|
+
type GetConfigurationOptions,
|
|
446
|
+
getAppLogger,
|
|
447
|
+
getConfiguration,
|
|
448
|
+
retryManager,
|
|
449
|
+
} from '@intlayer/config';
|
|
450
|
+
import { getLocaleName } from '@intlayer/core';
|
|
451
|
+
import { type Locale, Locales } from '@intlayer/types';
|
|
452
|
+
import fg from 'fast-glob';
|
|
453
|
+
import { chunkText } from './utils/calculateChunks';
|
|
454
|
+
import { checkAIAccess } from './utils/checkAccess';
|
|
455
|
+
import { checkFileModifiedRange } from './utils/checkFileModifiedRange';
|
|
456
|
+
import { chunkInference } from './utils/chunkInference';
|
|
457
|
+
import { fixChunkStartEndChars } from './utils/fixChunkStartEndChars';
|
|
458
|
+
import { getOutputFilePath } from './utils/getOutputFilePath';
|
|
459
|
+
import { mapChunksBetweenFiles } from './utils/mapChunksBetweenFiles';
|
|
460
|
+
import { reviewFileBlockAware } from './reviewDocBlockAware'; // NEW IMPORT
|
|
461
|
+
|
|
462
|
+
// Keep existing reviewFile function...
|
|
463
|
+
export const reviewFile = async (/* ... */) => {
|
|
464
|
+
// Existing implementation
|
|
465
|
+
};
|
|
466
|
+
|
|
467
|
+
// Keep existing ReviewDocOptions type...
|
|
468
|
+
type ReviewDocOptions = {
|
|
469
|
+
docPattern: string[];
|
|
470
|
+
locales: Locale[];
|
|
471
|
+
excludedGlobPattern: string[];
|
|
472
|
+
baseLocale: Locale;
|
|
473
|
+
aiOptions?: AIOptions;
|
|
474
|
+
nbSimultaneousFileProcessed?: number;
|
|
475
|
+
configOptions?: GetConfigurationOptions;
|
|
476
|
+
customInstructions?: string;
|
|
477
|
+
skipIfModifiedBefore?: number | string | Date;
|
|
478
|
+
skipIfModifiedAfter?: number | string | Date;
|
|
479
|
+
skipIfExists?: boolean;
|
|
480
|
+
gitOptions?: ListGitFilesOptions;
|
|
481
|
+
useBlockAware?: boolean; // NEW OPTION
|
|
482
|
+
};
|
|
483
|
+
|
|
484
|
+
export const reviewDoc = async ({
|
|
485
|
+
docPattern,
|
|
486
|
+
locales,
|
|
487
|
+
excludedGlobPattern,
|
|
488
|
+
baseLocale,
|
|
489
|
+
aiOptions,
|
|
490
|
+
nbSimultaneousFileProcessed,
|
|
491
|
+
configOptions,
|
|
492
|
+
customInstructions,
|
|
493
|
+
skipIfModifiedBefore,
|
|
494
|
+
skipIfModifiedAfter,
|
|
495
|
+
skipIfExists,
|
|
496
|
+
gitOptions,
|
|
497
|
+
useBlockAware = true, // NEW: Default to true
|
|
498
|
+
}: ReviewDocOptions) => {
|
|
499
|
+
const configuration = getConfiguration(configOptions);
|
|
500
|
+
const appLogger = getAppLogger(configuration);
|
|
501
|
+
|
|
502
|
+
const hasCMSAuth = await checkAIAccess(configuration, aiOptions);
|
|
503
|
+
if (!hasCMSAuth) return;
|
|
504
|
+
|
|
505
|
+
if (nbSimultaneousFileProcessed && nbSimultaneousFileProcessed > 10) {
|
|
506
|
+
appLogger(
|
|
507
|
+
`Warning: nbSimultaneousFileProcessed is set to ${nbSimultaneousFileProcessed}, which is greater than 10. Setting it to 10.`
|
|
508
|
+
);
|
|
509
|
+
nbSimultaneousFileProcessed = 10;
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
let docList: string[] = await fg(docPattern, {
|
|
513
|
+
ignore: excludedGlobPattern,
|
|
514
|
+
});
|
|
515
|
+
|
|
516
|
+
if (gitOptions) {
|
|
517
|
+
const gitChangedFiles = await listGitFiles(gitOptions);
|
|
518
|
+
if (gitChangedFiles) {
|
|
519
|
+
docList = docList.filter((path) =>
|
|
520
|
+
gitChangedFiles.some((gitFile) => join(process.cwd(), path) === gitFile)
|
|
521
|
+
);
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
appLogger(`Base locale is ${formatLocale(baseLocale)}`);
|
|
526
|
+
appLogger(
|
|
527
|
+
`Reviewing ${colorizeNumber(locales.length)} locales: [ ${formatLocale(locales)} ]`
|
|
528
|
+
);
|
|
529
|
+
appLogger(`Reviewing ${colorizeNumber(docList.length)} files:`);
|
|
530
|
+
appLogger(docList.map((path) => ` - ${formatPath(path)}\n`));
|
|
531
|
+
|
|
532
|
+
const allTasks = docList.flatMap((docPath) =>
|
|
533
|
+
locales.map((locale) => async () => {
|
|
534
|
+
appLogger(
|
|
535
|
+
`Reviewing file: ${formatPath(docPath)} to ${formatLocale(locale)}`
|
|
536
|
+
);
|
|
537
|
+
|
|
538
|
+
const absoluteBaseFilePath = join(configuration.content.baseDir, docPath);
|
|
539
|
+
const outputFilePath = getOutputFilePath(
|
|
540
|
+
absoluteBaseFilePath,
|
|
541
|
+
locale,
|
|
542
|
+
baseLocale
|
|
543
|
+
);
|
|
544
|
+
|
|
545
|
+
if (skipIfExists && existsSync(outputFilePath)) {
|
|
546
|
+
const relativePath = relative(
|
|
547
|
+
configuration.content.baseDir,
|
|
548
|
+
outputFilePath
|
|
549
|
+
);
|
|
550
|
+
appLogger(
|
|
551
|
+
`${colorize('⊘', ANSIColors.YELLOW)} File ${formatPath(relativePath)} already exists, skipping.`
|
|
552
|
+
);
|
|
553
|
+
return;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
const fileModificationData = checkFileModifiedRange(outputFilePath, {
|
|
557
|
+
skipIfModifiedBefore,
|
|
558
|
+
skipIfModifiedAfter,
|
|
559
|
+
});
|
|
560
|
+
|
|
561
|
+
if (fileModificationData.isSkipped) {
|
|
562
|
+
appLogger(fileModificationData.message);
|
|
563
|
+
return;
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
let changedLines: number[] | undefined;
|
|
567
|
+
if (gitOptions) {
|
|
568
|
+
const gitChangedLines = await listGitLines(
|
|
569
|
+
absoluteBaseFilePath,
|
|
570
|
+
gitOptions
|
|
571
|
+
);
|
|
572
|
+
appLogger(`Git changed lines: ${gitChangedLines.join(', ')}`);
|
|
573
|
+
changedLines = gitChangedLines;
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
// NEW: Decide whether to use block-aware translation
|
|
577
|
+
const shouldUseBlockAware =
|
|
578
|
+
useBlockAware && absoluteBaseFilePath.endsWith('.md');
|
|
579
|
+
|
|
580
|
+
if (shouldUseBlockAware) {
|
|
581
|
+
await reviewFileBlockAware(
|
|
582
|
+
absoluteBaseFilePath,
|
|
583
|
+
outputFilePath,
|
|
584
|
+
locale as Locale,
|
|
585
|
+
baseLocale,
|
|
586
|
+
aiOptions,
|
|
587
|
+
configOptions,
|
|
588
|
+
customInstructions,
|
|
589
|
+
changedLines
|
|
590
|
+
);
|
|
591
|
+
} else {
|
|
592
|
+
await reviewFile(
|
|
593
|
+
absoluteBaseFilePath,
|
|
594
|
+
outputFilePath,
|
|
595
|
+
locale as Locale,
|
|
596
|
+
baseLocale,
|
|
597
|
+
aiOptions,
|
|
598
|
+
configOptions,
|
|
599
|
+
customInstructions,
|
|
600
|
+
changedLines
|
|
601
|
+
);
|
|
602
|
+
}
|
|
603
|
+
})
|
|
604
|
+
);
|
|
605
|
+
|
|
606
|
+
await parallelize(
|
|
607
|
+
allTasks,
|
|
608
|
+
(task) => task(),
|
|
609
|
+
nbSimultaneousFileProcessed ?? 3
|
|
610
|
+
);
|
|
611
|
+
};
|
|
612
|
+
```
|
|
613
|
+
|
|
614
|
+
## Testing Checklist
|
|
615
|
+
|
|
616
|
+
Before deploying to production:
|
|
617
|
+
|
|
618
|
+
- [ ] Test with empty French file (new translation)
|
|
619
|
+
- [ ] Test with identical files (should reuse 100%)
|
|
620
|
+
- [ ] Test with minor changes (should have high reuse rate)
|
|
621
|
+
- [ ] Test with reordered paragraphs (should detect reordering)
|
|
622
|
+
- [ ] Test with deleted paragraphs
|
|
623
|
+
- [ ] Test with added paragraphs
|
|
624
|
+
- [ ] Test with Git changed lines integration
|
|
625
|
+
- [ ] Test without Git changed lines (fallback)
|
|
626
|
+
- [ ] Test error handling (missing files, corrupt files)
|
|
627
|
+
- [ ] Verify output formatting matches original
|
|
628
|
+
- [ ] Compare AI token usage (should be lower)
|
|
629
|
+
- [ ] Verify translation quality matches or exceeds original system
|
|
630
|
+
|
|
631
|
+
## Monitoring in Production
|
|
632
|
+
|
|
633
|
+
Add metrics collection:
|
|
634
|
+
|
|
635
|
+
```typescript
|
|
636
|
+
// metrics.ts
|
|
637
|
+
export interface TranslationMetrics {
|
|
638
|
+
fileName: string;
|
|
639
|
+
locale: string;
|
|
640
|
+
totalBlocks: number;
|
|
641
|
+
reusedBlocks: number;
|
|
642
|
+
reviewedBlocks: number;
|
|
643
|
+
newBlocks: number;
|
|
644
|
+
deletedBlocks: number;
|
|
645
|
+
tokensUsed: number;
|
|
646
|
+
processingTimeMs: number;
|
|
647
|
+
efficiencyRate: number;
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
const metrics: TranslationMetrics[] = [];
|
|
651
|
+
|
|
652
|
+
export const collectMetrics = (metric: TranslationMetrics) => {
|
|
653
|
+
metrics.push(metric);
|
|
654
|
+
};
|
|
655
|
+
|
|
656
|
+
export const getAverageEfficiency = (): number => {
|
|
657
|
+
if (metrics.length === 0) return 0;
|
|
658
|
+
const total = metrics.reduce((sum, m) => sum + m.efficiencyRate, 0);
|
|
659
|
+
return total / metrics.length;
|
|
660
|
+
};
|
|
661
|
+
```
|
|
662
|
+
|
|
663
|
+
## Support
|
|
664
|
+
|
|
665
|
+
If you encounter issues:
|
|
666
|
+
|
|
667
|
+
1. Check the logs for alignment details
|
|
668
|
+
2. Verify input files are valid markdown
|
|
669
|
+
3. Ensure Git integration is working if using changed lines
|
|
670
|
+
4. Review similarity thresholds for your content type
|
|
671
|
+
5. Open an issue with sample input/output files
|
|
672
|
+
|
|
673
|
+
## Next Steps
|
|
674
|
+
|
|
675
|
+
After successful integration:
|
|
676
|
+
|
|
677
|
+
1. Monitor efficiency rates and adjust thresholds
|
|
678
|
+
2. Gather feedback on translation quality
|
|
679
|
+
3. Consider extending to other file types (HTML, RST, etc.)
|
|
680
|
+
4. Explore parallel translation for multiple segments
|
|
681
|
+
5. Add visual tools for reviewing alignment decisions
|
|
682
|
+
|