@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.
Files changed (150) hide show
  1. package/dist/assets/translation-alignment/ARCHITECTURE.md +518 -0
  2. package/dist/assets/translation-alignment/IMPROVEMENTS.md +550 -0
  3. package/dist/assets/translation-alignment/INTEGRATION_EXAMPLE.md +682 -0
  4. package/dist/assets/translation-alignment/QUICK_START.md +494 -0
  5. package/dist/assets/translation-alignment/README.md +485 -0
  6. package/dist/assets/translation-alignment/SUMMARY.md +440 -0
  7. package/dist/cjs/IntlayerEventListener.cjs +0 -3
  8. package/dist/cjs/IntlayerEventListener.cjs.map +1 -1
  9. package/dist/cjs/_virtual/_utils_asset.cjs +0 -3
  10. package/dist/cjs/build.cjs +0 -2
  11. package/dist/cjs/build.cjs.map +1 -1
  12. package/dist/cjs/cli.cjs +6 -7
  13. package/dist/cjs/cli.cjs.map +1 -1
  14. package/dist/cjs/config.cjs +0 -1
  15. package/dist/cjs/config.cjs.map +1 -1
  16. package/dist/cjs/editor.cjs +0 -4
  17. package/dist/cjs/editor.cjs.map +1 -1
  18. package/dist/cjs/fill/fill.cjs +0 -3
  19. package/dist/cjs/fill/fill.cjs.map +1 -1
  20. package/dist/cjs/fill/formatAutoFilledFilePath.cjs +0 -1
  21. package/dist/cjs/fill/formatAutoFilledFilePath.cjs.map +1 -1
  22. package/dist/cjs/fill/listTranslationsTasks.cjs +0 -6
  23. package/dist/cjs/fill/listTranslationsTasks.cjs.map +1 -1
  24. package/dist/cjs/fill/translateDictionary.cjs +0 -6
  25. package/dist/cjs/fill/translateDictionary.cjs.map +1 -1
  26. package/dist/cjs/fill/writeFill.cjs +0 -4
  27. package/dist/cjs/fill/writeFill.cjs.map +1 -1
  28. package/dist/cjs/getTargetDictionary.cjs +0 -4
  29. package/dist/cjs/getTargetDictionary.cjs.map +1 -1
  30. package/dist/cjs/index.cjs +0 -1
  31. package/dist/cjs/listContentDeclaration.cjs +0 -4
  32. package/dist/cjs/listContentDeclaration.cjs.map +1 -1
  33. package/dist/cjs/liveSync.cjs +0 -6
  34. package/dist/cjs/liveSync.cjs.map +1 -1
  35. package/dist/cjs/pull.cjs +0 -5
  36. package/dist/cjs/pull.cjs.map +1 -1
  37. package/dist/cjs/push/pullLog.cjs +0 -1
  38. package/dist/cjs/push/pullLog.cjs.map +1 -1
  39. package/dist/cjs/push/push.cjs +0 -5
  40. package/dist/cjs/push/push.cjs.map +1 -1
  41. package/dist/cjs/pushConfig.cjs +0 -2
  42. package/dist/cjs/pushConfig.cjs.map +1 -1
  43. package/dist/cjs/pushLog.cjs +0 -1
  44. package/dist/cjs/pushLog.cjs.map +1 -1
  45. package/dist/cjs/reviewDoc.cjs +8 -131
  46. package/dist/cjs/reviewDoc.cjs.map +1 -1
  47. package/dist/cjs/reviewDocBlockAware.cjs +90 -0
  48. package/dist/cjs/reviewDocBlockAware.cjs.map +1 -0
  49. package/dist/cjs/test/index.cjs +0 -2
  50. package/dist/cjs/test/index.cjs.map +1 -1
  51. package/dist/cjs/test/listMissingTranslations.cjs +0 -4
  52. package/dist/cjs/test/listMissingTranslations.cjs.map +1 -1
  53. package/dist/cjs/translateDoc.cjs +8 -8
  54. package/dist/cjs/translateDoc.cjs.map +1 -1
  55. package/dist/cjs/translation-alignment/alignBlocks.cjs +67 -0
  56. package/dist/cjs/translation-alignment/alignBlocks.cjs.map +1 -0
  57. package/dist/cjs/translation-alignment/computeSimilarity.cjs +25 -0
  58. package/dist/cjs/translation-alignment/computeSimilarity.cjs.map +1 -0
  59. package/dist/cjs/translation-alignment/fingerprintBlock.cjs +23 -0
  60. package/dist/cjs/translation-alignment/fingerprintBlock.cjs.map +1 -0
  61. package/dist/cjs/translation-alignment/index.cjs +21 -0
  62. package/dist/cjs/translation-alignment/mapChangedLinesToBlocks.cjs +18 -0
  63. package/dist/cjs/translation-alignment/mapChangedLinesToBlocks.cjs.map +1 -0
  64. package/dist/cjs/translation-alignment/normalizeBlock.cjs +22 -0
  65. package/dist/cjs/translation-alignment/normalizeBlock.cjs.map +1 -0
  66. package/dist/cjs/translation-alignment/pipeline.cjs +37 -0
  67. package/dist/cjs/translation-alignment/pipeline.cjs.map +1 -0
  68. package/dist/cjs/translation-alignment/planActions.cjs +48 -0
  69. package/dist/cjs/translation-alignment/planActions.cjs.map +1 -0
  70. package/dist/cjs/translation-alignment/rebuildDocument.cjs +49 -0
  71. package/dist/cjs/translation-alignment/rebuildDocument.cjs.map +1 -0
  72. package/dist/cjs/translation-alignment/segmentDocument.cjs +132 -0
  73. package/dist/cjs/translation-alignment/segmentDocument.cjs.map +1 -0
  74. package/dist/cjs/translation-alignment/types.cjs +0 -0
  75. package/dist/cjs/utils/calculateChunks.cjs +0 -1
  76. package/dist/cjs/utils/calculateChunks.cjs.map +1 -1
  77. package/dist/cjs/utils/checkAccess.cjs +0 -2
  78. package/dist/cjs/utils/checkAccess.cjs.map +1 -1
  79. package/dist/cjs/utils/checkLastUpdateTime.cjs +0 -1
  80. package/dist/cjs/utils/checkLastUpdateTime.cjs.map +1 -1
  81. package/dist/cjs/utils/chunkInference.cjs +0 -2
  82. package/dist/cjs/utils/chunkInference.cjs.map +1 -1
  83. package/dist/cjs/utils/getIsFileUpdatedRecently.cjs +0 -1
  84. package/dist/cjs/utils/getIsFileUpdatedRecently.cjs.map +1 -1
  85. package/dist/cjs/utils/getParentPackageJSON.cjs +0 -2
  86. package/dist/cjs/utils/getParentPackageJSON.cjs.map +1 -1
  87. package/dist/cjs/utils/mapChunksBetweenFiles.cjs +0 -1
  88. package/dist/cjs/utils/mapChunksBetweenFiles.cjs.map +1 -1
  89. package/dist/cjs/watch.cjs +0 -2
  90. package/dist/cjs/watch.cjs.map +1 -1
  91. package/dist/esm/cli.mjs +6 -3
  92. package/dist/esm/cli.mjs.map +1 -1
  93. package/dist/esm/index.mjs +2 -2
  94. package/dist/esm/reviewDoc.mjs +13 -128
  95. package/dist/esm/reviewDoc.mjs.map +1 -1
  96. package/dist/esm/reviewDocBlockAware.mjs +89 -0
  97. package/dist/esm/reviewDocBlockAware.mjs.map +1 -0
  98. package/dist/esm/translateDoc.mjs +8 -3
  99. package/dist/esm/translateDoc.mjs.map +1 -1
  100. package/dist/esm/translation-alignment/alignBlocks.mjs +67 -0
  101. package/dist/esm/translation-alignment/alignBlocks.mjs.map +1 -0
  102. package/dist/esm/translation-alignment/computeSimilarity.mjs +23 -0
  103. package/dist/esm/translation-alignment/computeSimilarity.mjs.map +1 -0
  104. package/dist/esm/translation-alignment/fingerprintBlock.mjs +21 -0
  105. package/dist/esm/translation-alignment/fingerprintBlock.mjs.map +1 -0
  106. package/dist/esm/translation-alignment/index.mjs +11 -0
  107. package/dist/esm/translation-alignment/mapChangedLinesToBlocks.mjs +17 -0
  108. package/dist/esm/translation-alignment/mapChangedLinesToBlocks.mjs.map +1 -0
  109. package/dist/esm/translation-alignment/normalizeBlock.mjs +21 -0
  110. package/dist/esm/translation-alignment/normalizeBlock.mjs.map +1 -0
  111. package/dist/esm/translation-alignment/pipeline.mjs +36 -0
  112. package/dist/esm/translation-alignment/pipeline.mjs.map +1 -0
  113. package/dist/esm/translation-alignment/planActions.mjs +47 -0
  114. package/dist/esm/translation-alignment/planActions.mjs.map +1 -0
  115. package/dist/esm/translation-alignment/rebuildDocument.mjs +47 -0
  116. package/dist/esm/translation-alignment/rebuildDocument.mjs.map +1 -0
  117. package/dist/esm/translation-alignment/segmentDocument.mjs +131 -0
  118. package/dist/esm/translation-alignment/segmentDocument.mjs.map +1 -0
  119. package/dist/esm/translation-alignment/types.mjs +0 -0
  120. package/dist/types/cli.d.ts.map +1 -1
  121. package/dist/types/index.d.ts +2 -2
  122. package/dist/types/pull.d.ts.map +1 -1
  123. package/dist/types/reviewDoc.d.ts +3 -6
  124. package/dist/types/reviewDoc.d.ts.map +1 -1
  125. package/dist/types/reviewDocBlockAware.d.ts +19 -0
  126. package/dist/types/reviewDocBlockAware.d.ts.map +1 -0
  127. package/dist/types/translateDoc.d.ts +2 -0
  128. package/dist/types/translateDoc.d.ts.map +1 -1
  129. package/dist/types/translation-alignment/alignBlocks.d.ts +7 -0
  130. package/dist/types/translation-alignment/alignBlocks.d.ts.map +1 -0
  131. package/dist/types/translation-alignment/computeSimilarity.d.ts +6 -0
  132. package/dist/types/translation-alignment/computeSimilarity.d.ts.map +1 -0
  133. package/dist/types/translation-alignment/fingerprintBlock.d.ts +7 -0
  134. package/dist/types/translation-alignment/fingerprintBlock.d.ts.map +1 -0
  135. package/dist/types/translation-alignment/index.d.ts +11 -0
  136. package/dist/types/translation-alignment/mapChangedLinesToBlocks.d.ts +7 -0
  137. package/dist/types/translation-alignment/mapChangedLinesToBlocks.d.ts.map +1 -0
  138. package/dist/types/translation-alignment/normalizeBlock.d.ts +7 -0
  139. package/dist/types/translation-alignment/normalizeBlock.d.ts.map +1 -0
  140. package/dist/types/translation-alignment/pipeline.d.ts +25 -0
  141. package/dist/types/translation-alignment/pipeline.d.ts.map +1 -0
  142. package/dist/types/translation-alignment/planActions.d.ts +7 -0
  143. package/dist/types/translation-alignment/planActions.d.ts.map +1 -0
  144. package/dist/types/translation-alignment/rebuildDocument.d.ts +32 -0
  145. package/dist/types/translation-alignment/rebuildDocument.d.ts.map +1 -0
  146. package/dist/types/translation-alignment/segmentDocument.d.ts +7 -0
  147. package/dist/types/translation-alignment/segmentDocument.d.ts.map +1 -0
  148. package/dist/types/translation-alignment/types.d.ts +49 -0
  149. package/dist/types/translation-alignment/types.d.ts.map +1 -0
  150. 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
+