@intlayer/cli 7.5.11 → 7.5.13

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 (70) hide show
  1. package/dist/cjs/cli.cjs +4 -4
  2. package/dist/cjs/cli.cjs.map +1 -1
  3. package/dist/cjs/index.cjs +4 -5
  4. package/dist/cjs/{reviewDoc.cjs → reviewDoc/reviewDoc.cjs} +17 -15
  5. package/dist/cjs/reviewDoc/reviewDoc.cjs.map +1 -0
  6. package/dist/cjs/{reviewDocBlockAware.cjs → reviewDoc/reviewDocBlockAware.cjs} +12 -8
  7. package/dist/cjs/reviewDoc/reviewDocBlockAware.cjs.map +1 -0
  8. package/dist/cjs/translateDoc/index.cjs +8 -0
  9. package/dist/cjs/translateDoc/translateDoc.cjs +74 -0
  10. package/dist/cjs/translateDoc/translateDoc.cjs.map +1 -0
  11. package/dist/cjs/translateDoc/translateFile.cjs +103 -0
  12. package/dist/cjs/translateDoc/translateFile.cjs.map +1 -0
  13. package/dist/cjs/translateDoc/types.cjs +0 -0
  14. package/dist/cjs/translateDoc/validation.cjs +49 -0
  15. package/dist/cjs/translateDoc/validation.cjs.map +1 -0
  16. package/dist/cjs/translation-alignment/planActions.cjs +2 -4
  17. package/dist/cjs/translation-alignment/planActions.cjs.map +1 -1
  18. package/dist/cjs/translation-alignment/segmentDocument.cjs +35 -101
  19. package/dist/cjs/translation-alignment/segmentDocument.cjs.map +1 -1
  20. package/dist/esm/cli.mjs +2 -2
  21. package/dist/esm/cli.mjs.map +1 -1
  22. package/dist/esm/index.mjs +3 -3
  23. package/dist/esm/{reviewDoc.mjs → reviewDoc/reviewDoc.mjs} +14 -12
  24. package/dist/esm/reviewDoc/reviewDoc.mjs.map +1 -0
  25. package/dist/esm/{reviewDocBlockAware.mjs → reviewDoc/reviewDocBlockAware.mjs} +11 -7
  26. package/dist/esm/reviewDoc/reviewDocBlockAware.mjs.map +1 -0
  27. package/dist/esm/translateDoc/index.mjs +5 -0
  28. package/dist/esm/translateDoc/translateDoc.mjs +72 -0
  29. package/dist/esm/translateDoc/translateDoc.mjs.map +1 -0
  30. package/dist/esm/translateDoc/translateFile.mjs +102 -0
  31. package/dist/esm/translateDoc/translateFile.mjs.map +1 -0
  32. package/dist/esm/translateDoc/types.mjs +0 -0
  33. package/dist/esm/translateDoc/validation.mjs +47 -0
  34. package/dist/esm/translateDoc/validation.mjs.map +1 -0
  35. package/dist/esm/translation-alignment/planActions.mjs +2 -4
  36. package/dist/esm/translation-alignment/planActions.mjs.map +1 -1
  37. package/dist/esm/translation-alignment/segmentDocument.mjs +35 -101
  38. package/dist/esm/translation-alignment/segmentDocument.mjs.map +1 -1
  39. package/dist/types/cli.d.ts.map +1 -1
  40. package/dist/types/index.d.ts +3 -3
  41. package/dist/types/{reviewDoc.d.ts → reviewDoc/reviewDoc.d.ts} +1 -1
  42. package/dist/types/reviewDoc/reviewDoc.d.ts.map +1 -0
  43. package/dist/types/{reviewDocBlockAware.d.ts → reviewDoc/reviewDocBlockAware.d.ts} +2 -2
  44. package/dist/types/reviewDoc/reviewDocBlockAware.d.ts.map +1 -0
  45. package/dist/types/translateDoc/index.d.ts +5 -0
  46. package/dist/types/translateDoc/translateDoc.d.ts +21 -0
  47. package/dist/types/translateDoc/translateDoc.d.ts.map +1 -0
  48. package/dist/types/translateDoc/translateFile.d.ts +21 -0
  49. package/dist/types/translateDoc/translateFile.d.ts.map +1 -0
  50. package/dist/types/translateDoc/types.d.ts +47 -0
  51. package/dist/types/translateDoc/types.d.ts.map +1 -0
  52. package/dist/types/translateDoc/validation.d.ts +16 -0
  53. package/dist/types/translateDoc/validation.d.ts.map +1 -0
  54. package/dist/types/translation-alignment/planActions.d.ts +2 -2
  55. package/dist/types/translation-alignment/planActions.d.ts.map +1 -1
  56. package/dist/types/translation-alignment/rebuildDocument.d.ts.map +1 -1
  57. package/dist/types/translation-alignment/segmentDocument.d.ts.map +1 -1
  58. package/package.json +11 -11
  59. package/dist/cjs/reviewDoc.cjs.map +0 -1
  60. package/dist/cjs/reviewDocBlockAware.cjs.map +0 -1
  61. package/dist/cjs/translateDoc.cjs +0 -163
  62. package/dist/cjs/translateDoc.cjs.map +0 -1
  63. package/dist/esm/reviewDoc.mjs.map +0 -1
  64. package/dist/esm/reviewDocBlockAware.mjs.map +0 -1
  65. package/dist/esm/translateDoc.mjs +0 -160
  66. package/dist/esm/translateDoc.mjs.map +0 -1
  67. package/dist/types/reviewDoc.d.ts.map +0 -1
  68. package/dist/types/reviewDocBlockAware.d.ts.map +0 -1
  69. package/dist/types/translateDoc.d.ts +0 -56
  70. package/dist/types/translateDoc.d.ts.map +0 -1
@@ -7,11 +7,11 @@ import { liveSync } from "./liveSync.js";
7
7
  import { pull } from "./pull.js";
8
8
  import { push } from "./push/push.js";
9
9
  import { pushConfig } from "./pushConfig.js";
10
- import { reviewDoc } from "./reviewDoc.js";
10
+ import { reviewDoc } from "./reviewDoc/reviewDoc.js";
11
11
  import { listMissingTranslations, listMissingTranslationsWithConfig } from "./test/listMissingTranslations.js";
12
12
  import { testMissingTranslations } from "./test/test.js";
13
13
  import "./test/index.js";
14
14
  import { transform } from "./transform.js";
15
- import { translateDoc, translateFile } from "./translateDoc.js";
15
+ import { translateDoc } from "./translateDoc/translateDoc.js";
16
16
  export * from "@intlayer/chokidar";
17
- export { ConfigurationOptions, FillOptions, build, dirname, fill, listContentDeclaration, listContentDeclarationRows, listMissingTranslations, listMissingTranslationsWithConfig, liveSync, pull, push, pushConfig, reviewDoc, setAPI, startEditor, testMissingTranslations, transform, translateDoc, translateFile };
17
+ export { ConfigurationOptions, FillOptions, build, dirname, fill, listContentDeclaration, listContentDeclarationRows, listMissingTranslations, listMissingTranslationsWithConfig, liveSync, pull, push, pushConfig, reviewDoc, setAPI, startEditor, testMissingTranslations, transform, translateDoc };
@@ -3,7 +3,7 @@ import { GetConfigurationOptions } from "@intlayer/config";
3
3
  import { AIOptions } from "@intlayer/api";
4
4
  import { ListGitFilesOptions } from "@intlayer/chokidar";
5
5
 
6
- //#region src/reviewDoc.d.ts
6
+ //#region src/reviewDoc/reviewDoc.d.ts
7
7
  type ReviewDocOptions = {
8
8
  docPattern: string[];
9
9
  locales: Locale[];
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reviewDoc.d.ts","names":[],"sources":["../../../src/reviewDoc/reviewDoc.ts"],"sourcesContent":[],"mappings":";;;;;;KA0BK,gBAAA;;EAAA,OAAA,EAEM,MAFN,EAAA;EAEM,mBAAA,EAAA,MAAA,EAAA;EAEG,UAAA,EAAA,MAAA;EACA,SAAA,CAAA,EAAA,SAAA;EAEI,2BAAA,CAAA,EAAA,MAAA;EAEyB,aAAA,CAAA,EAFzB,uBAEyB;EACD,kBAAA,CAAA,EAAA,MAAA;EAE3B,oBAAA,CAAA,EAAA,MAAA,GAAA,MAAA,GAH4B,IAG5B;EAAmB,mBAAA,CAAA,EAAA,MAAA,GAAA,MAAA,GAFQ,IAER;EAOrB,YAqIZ,CAAA,EAAA,OAAA;EArI+B,UAAA,CAAA,EAPjB,mBAOiB;CAAA;;;;;AAAA,cAAnB,SAAmB,EAAA,CAAA;EAAA,UAAA;EAAA,OAAA;EAAA,mBAAA;EAAA,UAAA;EAAA,SAAA;EAAA,2BAAA;EAAA,aAAA;EAAA,kBAAA;EAAA,oBAAA;EAAA,mBAAA;EAAA,YAAA;EAAA;AAAA,CAAA,EAa7B,gBAb6B,EAAA,GAab,OAba,CAAA,IAAA,CAAA"}
@@ -1,10 +1,10 @@
1
- import { AIClient } from "./utils/setupAI.js";
1
+ import { AIClient } from "../utils/setupAI.js";
2
2
  import { Locale } from "@intlayer/types";
3
3
  import { GetConfigurationOptions } from "@intlayer/config";
4
4
  import { AIOptions } from "@intlayer/api";
5
5
  import { AIConfig } from "@intlayer/ai";
6
6
 
7
- //#region src/reviewDocBlockAware.d.ts
7
+ //#region src/reviewDoc/reviewDocBlockAware.d.ts
8
8
 
9
9
  /**
10
10
  * Review a file using block-aware alignment.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reviewDocBlockAware.d.ts","names":[],"sources":["../../../src/reviewDoc/reviewDocBlockAware.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;AAqCA;;;;;;;AAUqB,cAVR,oBAUQ,EAAA,CAAA,YAAA,EAAA,MAAA,EAAA,cAAA,EAAA,MAAA,EAAA,MAAA,EAPX,MAOW,EAAA,UAAA,EANP,MAMO,EAAA,SAAA,CAAA,EALP,SAKO,EAAA,aAAA,CAAA,EAJH,uBAIG,EAAA,kBAAA,CAAA,EAAA,MAAA,EAAA,YAAA,CAAA,EAAA,MAAA,EAAA,EAAA,QAAA,CAAA,EADR,QACQ,EAAA,QAAA,CAAA,EAAR,QAAQ,EAAA,GAAA,OAAA,CAAA,IAAA,CAAA"}
@@ -0,0 +1,5 @@
1
+ import { ErrorState, FlushStrategy, LimitFunction, TranslateDocOptions, TranslateFileOptions } from "./types.js";
2
+ import { translateDoc } from "./translateDoc.js";
3
+ import { translateFile } from "./translateFile.js";
4
+ import { sanitizeChunk, validateTranslation } from "./validation.js";
5
+ export { ErrorState, FlushStrategy, LimitFunction, TranslateDocOptions, TranslateFileOptions, sanitizeChunk, translateDoc, translateFile, validateTranslation };
@@ -0,0 +1,21 @@
1
+ import { TranslateDocOptions } from "./types.js";
2
+
3
+ //#region src/translateDoc/translateDoc.d.ts
4
+ declare const translateDoc: ({
5
+ docPattern,
6
+ locales,
7
+ excludedGlobPattern,
8
+ baseLocale,
9
+ aiOptions,
10
+ nbSimultaneousFileProcessed,
11
+ configOptions,
12
+ customInstructions,
13
+ skipIfModifiedBefore,
14
+ skipIfModifiedAfter,
15
+ skipIfExists,
16
+ gitOptions,
17
+ flushStrategy
18
+ }: TranslateDocOptions) => Promise<void>;
19
+ //#endregion
20
+ export { translateDoc };
21
+ //# sourceMappingURL=translateDoc.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"translateDoc.d.ts","names":[],"sources":["../../../src/translateDoc/translateDoc.ts"],"sourcesContent":[],"mappings":";;;cAmBa;;;;;;;;;;;;;;GAcV,wBAAmB"}
@@ -0,0 +1,21 @@
1
+ import { TranslateFileOptions } from "./types.js";
2
+
3
+ //#region src/translateDoc/translateFile.d.ts
4
+ declare const translateFile: ({
5
+ baseFilePath,
6
+ outputFilePath,
7
+ locale,
8
+ baseLocale,
9
+ configuration,
10
+ errorState,
11
+ aiOptions,
12
+ customInstructions,
13
+ aiClient,
14
+ aiConfig,
15
+ flushStrategy,
16
+ onChunkReceive,
17
+ limit
18
+ }: TranslateFileOptions) => Promise<string | null>;
19
+ //#endregion
20
+ export { translateFile };
21
+ //# sourceMappingURL=translateFile.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"translateFile.d.ts","names":[],"sources":["../../../src/translateDoc/translateFile.ts"],"sourcesContent":[],"mappings":";;;cAoBa;;;;;;;;;;;;;;GAcV,yBAAuB"}
@@ -0,0 +1,47 @@
1
+ import { AIClient } from "../utils/setupAI.js";
2
+ import { IntlayerConfig, Locale } from "@intlayer/types";
3
+ import { GetConfigurationOptions } from "@intlayer/config";
4
+ import { ListGitFilesOptions } from "@intlayer/chokidar";
5
+ import { AIConfig, AIOptions } from "@intlayer/ai";
6
+
7
+ //#region src/translateDoc/types.d.ts
8
+ type ErrorState = {
9
+ count: number;
10
+ maxErrors: number;
11
+ shouldStop: boolean;
12
+ };
13
+ type FlushStrategy = 'incremental' | 'end' | 'none';
14
+ type LimitFunction = (fn: () => Promise<any>, ...args: any[]) => Promise<any>;
15
+ type TranslateFileOptions = {
16
+ baseFilePath: string;
17
+ outputFilePath: string;
18
+ locale: Locale;
19
+ baseLocale: Locale;
20
+ configuration: IntlayerConfig;
21
+ errorState: ErrorState;
22
+ aiOptions?: AIOptions;
23
+ customInstructions?: string;
24
+ aiClient?: AIClient;
25
+ aiConfig?: AIConfig;
26
+ flushStrategy?: FlushStrategy;
27
+ onChunkReceive?: (chunk: string, index: number, total: number) => void;
28
+ limit?: LimitFunction;
29
+ };
30
+ type TranslateDocOptions = {
31
+ docPattern: string[];
32
+ locales: Locale[];
33
+ excludedGlobPattern: string[];
34
+ baseLocale: Locale;
35
+ aiOptions?: AIOptions;
36
+ nbSimultaneousFileProcessed?: number;
37
+ configOptions?: GetConfigurationOptions;
38
+ customInstructions?: string;
39
+ skipIfModifiedBefore?: number | string | Date;
40
+ skipIfModifiedAfter?: number | string | Date;
41
+ skipIfExists?: boolean;
42
+ gitOptions?: ListGitFilesOptions;
43
+ flushStrategy?: FlushStrategy;
44
+ };
45
+ //#endregion
46
+ export { ErrorState, FlushStrategy, LimitFunction, TranslateDocOptions, TranslateFileOptions };
47
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","names":[],"sources":["../../../src/translateDoc/types.ts"],"sourcesContent":[],"mappings":";;;;;;;KAMY,UAAA;;EAAA,SAAA,EAAA,MAAU;EAMV,UAAA,EAAA,OAAa;AAGzB,CAAA;AAKY,KARA,aAAA,GAQoB,aAAA,GAAA,KAAA,GAAA,MAAA;AAGtB,KARE,aAAA,GAQF,CAAA,EAAA,EAAA,GAAA,GAPE,OAOF,CAAA,GAAA,CAAA,EAAA,GAAA,IAAA,EAAA,GAAA,EAAA,EAAA,GALL,OAKK,CAAA,GAAA,CAAA;AACI,KAJF,oBAAA,GAIE;EACG,YAAA,EAAA,MAAA;EACH,cAAA,EAAA,MAAA;EACA,MAAA,EAJJ,MAII;EAED,UAAA,EALC,MAKD;EACA,aAAA,EALI,cAKJ;EACK,UAAA,EALJ,UAKI;EAER,SAAA,CAAA,EANI,SAMJ;EAAa,kBAAA,CAAA,EAAA,MAAA;EAGX,QAAA,CAAA,EAPC,QAOD;EAED,QAAA,CAAA,EARE,QAQF;EAEG,aAAA,CAAA,EATI,aASJ;EACA,cAAA,CAAA,EAAA,CAAA,KAAA,EAAA,MAAA,EAAA,KAAA,EAAA,MAAA,EAAA,KAAA,EAAA,MAAA,EAAA,GAAA,IAAA;EAEI,KAAA,CAAA,EAVR,aAUQ;CAEyB;AACD,KAV9B,mBAAA,GAU8B;EAE3B,UAAA,EAAA,MAAA,EAAA;EACG,OAAA,EAXP,MAWO,EAAA;EAAa,mBAAA,EAAA,MAAA,EAAA;cATjB;cACA;;kBAEI;;2CAEyB;0CACD;;eAE3B;kBACG"}
@@ -0,0 +1,16 @@
1
+ import { Logger } from "@intlayer/config";
2
+
3
+ //#region src/translateDoc/validation.d.ts
4
+
5
+ /**
6
+ * Validates that the translated content matches the structure of the original.
7
+ * Throws an error if a mismatch is found, triggering a retry.
8
+ */
9
+ declare const validateTranslation: (original: string, translated: string, logger: Logger) => boolean;
10
+ /**
11
+ * Clean common AI artifacts
12
+ */
13
+ declare const sanitizeChunk: (translated: string, original: string) => string;
14
+ //#endregion
15
+ export { sanitizeChunk, validateTranslation };
16
+ //# sourceMappingURL=validation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validation.d.ts","names":[],"sources":["../../../src/translateDoc/validation.ts"],"sourcesContent":[],"mappings":";;;;;;AAMA;AAoEA;cApEa,oEAGH;;;;cAiEG"}
@@ -1,7 +1,7 @@
1
- import { AlignmentPair, AlignmentPlan, SimilarityOptions } from "./types.js";
1
+ import { AlignmentPair, AlignmentPlan } from "./types.js";
2
2
 
3
3
  //#region src/translation-alignment/planActions.d.ts
4
- declare const planAlignmentActions: (alignment: AlignmentPair[], changedEnglishBlockIndexes: Set<number>, similarityOptions: SimilarityOptions) => AlignmentPlan;
4
+ declare const planAlignmentActions: (alignment: AlignmentPair[], changedEnglishBlockIndexes: Set<number>) => AlignmentPlan;
5
5
  //#endregion
6
6
  export { planAlignmentActions };
7
7
  //# sourceMappingURL=planActions.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"planActions.d.ts","names":[],"sources":["../../../src/translation-alignment/planActions.ts"],"sourcesContent":[],"mappings":";;;cAOa,kCACA,6CACiB,gCACT,sBAClB"}
1
+ {"version":3,"file":"planActions.d.ts","names":[],"sources":["../../../src/translation-alignment/planActions.ts"],"sourcesContent":[],"mappings":";;;cAEa,kCACA,6CACiB,gBAC3B"}
@@ -1 +1 @@
1
- {"version":3,"file":"rebuildDocument.d.ts","names":[],"sources":["../../../src/translation-alignment/rebuildDocument.ts"],"sourcesContent":[],"mappings":";;;KAEY,eAAA;gBACI;EADJ,eAAA,EAAA,MAAe,GAAA,IACX;EAKJ,WAAA,EAAA,MAAY;CACP;AACD,KAFJ,YAAA,GAEI;EACR,aAAA,EAFS,kBAET,EAAA;EAAa,YAAA,EADL,kBACK,EAAA;EAGT,IAAA,EAHJ,aAGiB;AAQzB,CAAA;AAAyC,KAR7B,aAAA,GAQ6B;EAAA,gBAAA,EAPrB,eAOqB,EAAA;CAAA;;;;AAiCzC;AACQ,cAlCK,wBAkCL,EAAA,CAAA;EAAA,aAAA;EAAA,YAAA;EAAA;AAAA,CAAA,EA9BL,YA8BK,EAAA,GA9BU,aA8BV;;;;cADK,8BACL,6BACQ,wCACI"}
1
+ {"version":3,"file":"rebuildDocument.d.ts","names":[],"sources":["../../../src/translation-alignment/rebuildDocument.ts"],"sourcesContent":[],"mappings":";;;KAEY,eAAA;gBACI;EADJ,eAAA,EAAA,MAAe,GAAA,IAAA;EAMf,WAAA,EAAA,MAAY;CACP;AACD,KAFJ,YAAA,GAEI;EACR,aAAA,EAFS,kBAET,EAAA;EAAa,YAAA,EADL,kBACK,EAAA;EAGT,IAAA,EAHJ,aAGiB;AAQzB,CAAA;AAAyC,KAR7B,aAAA,GAQ6B;EAAA,gBAAA,EAPrB,eAOqB,EAAA;CAAA;;;;AAiCzC;AACQ,cAlCK,wBAkCL,EAAA,CAAA;EAAA,aAAA;EAAA,YAAA;EAAA;AAAA,CAAA,EA9BL,YA8BK,EAAA,GA9BU,aA8BV;;;;cADK,8BACL,6BACQ,wCACI"}
@@ -1 +1 @@
1
- {"version":3,"file":"segmentDocument.d.ts","names":[],"sources":["../../../src/translation-alignment/segmentDocument.ts"],"sourcesContent":[],"mappings":";;;cAwBa,mCAAkC"}
1
+ {"version":3,"file":"segmentDocument.d.ts","names":[],"sources":["../../../src/translation-alignment/segmentDocument.ts"],"sourcesContent":[],"mappings":";;;cAUa,mCAAkC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intlayer/cli",
3
- "version": "7.5.11",
3
+ "version": "7.5.13",
4
4
  "private": false,
5
5
  "description": "Provides uniform command-line interface scripts for Intlayer, used in packages like intlayer-cli and intlayer.",
6
6
  "keywords": [
@@ -68,15 +68,15 @@
68
68
  },
69
69
  "dependencies": {
70
70
  "@clack/prompts": "^0.11.0",
71
- "@intlayer/ai": "7.5.11",
72
- "@intlayer/api": "7.5.11",
73
- "@intlayer/chokidar": "7.5.11",
74
- "@intlayer/config": "7.5.11",
75
- "@intlayer/core": "7.5.11",
76
- "@intlayer/dictionaries-entry": "7.5.11",
77
- "@intlayer/remote-dictionaries-entry": "7.5.11",
78
- "@intlayer/types": "7.5.11",
79
- "@intlayer/unmerged-dictionaries-entry": "7.5.11",
71
+ "@intlayer/ai": "7.5.13",
72
+ "@intlayer/api": "7.5.13",
73
+ "@intlayer/chokidar": "7.5.13",
74
+ "@intlayer/config": "7.5.13",
75
+ "@intlayer/core": "7.5.13",
76
+ "@intlayer/dictionaries-entry": "7.5.13",
77
+ "@intlayer/remote-dictionaries-entry": "7.5.13",
78
+ "@intlayer/types": "7.5.13",
79
+ "@intlayer/unmerged-dictionaries-entry": "7.5.13",
80
80
  "commander": "14.0.1",
81
81
  "eventsource": "3.0.7",
82
82
  "fast-glob": "3.3.3"
@@ -92,7 +92,7 @@
92
92
  "vitest": "4.0.16"
93
93
  },
94
94
  "peerDependencies": {
95
- "@intlayer/ai": "7.5.11"
95
+ "@intlayer/ai": "7.5.13"
96
96
  },
97
97
  "peerDependenciesMeta": {
98
98
  "@intlayer/ai": {
@@ -1 +0,0 @@
1
- {"version":3,"file":"reviewDoc.cjs","names":["setupAI","docList: string[]","getOutputFilePath","ANSIColors","checkFileModifiedRange","changedLines: number[] | undefined","reviewFileBlockAware"],"sources":["../../src/reviewDoc.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\nimport { join, relative } from 'node:path';\nimport type { AIOptions } from '@intlayer/api'; // OAuth handled by API proxy\nimport {\n formatLocale,\n formatPath,\n type ListGitFilesOptions,\n listGitFiles,\n listGitLines,\n parallelize,\n} from '@intlayer/chokidar';\nimport {\n ANSIColors,\n colorize,\n colorizeNumber,\n type GetConfigurationOptions,\n getAppLogger,\n getConfiguration,\n} from '@intlayer/config';\nimport type { Locale } from '@intlayer/types';\nimport fg from 'fast-glob';\nimport { reviewFileBlockAware } from './reviewDocBlockAware';\nimport { checkFileModifiedRange } from './utils/checkFileModifiedRange';\nimport { getOutputFilePath } from './utils/getOutputFilePath';\nimport { setupAI } from './utils/setupAI';\n\ntype ReviewDocOptions = {\n docPattern: string[];\n locales: Locale[];\n excludedGlobPattern: string[];\n baseLocale: Locale;\n aiOptions?: AIOptions;\n nbSimultaneousFileProcessed?: number;\n configOptions?: GetConfigurationOptions;\n customInstructions?: string;\n skipIfModifiedBefore?: number | string | Date;\n skipIfModifiedAfter?: number | string | Date;\n skipIfExists?: boolean;\n gitOptions?: ListGitFilesOptions;\n};\n\n/**\n * Main audit function: scans all .md files in \"en/\" (unless you specified DOC_LIST),\n * then audits them to each locale in LOCALE_LIST.\n */\nexport const reviewDoc = async ({\n docPattern,\n locales,\n excludedGlobPattern,\n baseLocale,\n aiOptions,\n nbSimultaneousFileProcessed,\n configOptions,\n customInstructions,\n skipIfModifiedBefore,\n skipIfModifiedAfter,\n skipIfExists,\n gitOptions,\n}: ReviewDocOptions) => {\n const configuration = getConfiguration(configOptions);\n const appLogger = getAppLogger(configuration);\n\n const aiResult = await setupAI(configuration, aiOptions);\n\n if (!aiResult?.hasAIAccess) return;\n\n const { aiClient, aiConfig } = aiResult;\n\n if (nbSimultaneousFileProcessed && nbSimultaneousFileProcessed > 10) {\n appLogger(\n `Warning: nbSimultaneousFileProcessed is set to ${nbSimultaneousFileProcessed}, which is greater than 10. Setting it to 10.`\n );\n nbSimultaneousFileProcessed = 10; // Limit the number of simultaneous file processed to 10\n }\n\n let docList: string[] = await fg(docPattern, {\n ignore: excludedGlobPattern,\n });\n\n if (gitOptions) {\n const gitChangedFiles = await listGitFiles(gitOptions);\n\n if (gitChangedFiles) {\n // Convert dictionary file paths to be relative to git root for comparison\n\n // Filter dictionaries based on git changed files\n docList = docList.filter((path) =>\n gitChangedFiles.some((gitFile) => join(process.cwd(), path) === gitFile)\n );\n }\n }\n\n // OAuth handled by API proxy internally\n\n appLogger(`Base locale is ${formatLocale(baseLocale)}`);\n appLogger(\n `Reviewing ${colorizeNumber(locales.length)} locales: [ ${formatLocale(locales)} ]`\n );\n\n appLogger(`Reviewing ${colorizeNumber(docList.length)} files:`);\n appLogger(docList.map((path) => ` - ${formatPath(path)}\\n`));\n\n // Create all tasks to be processed\n const allTasks = docList.flatMap((docPath) =>\n locales.map((locale) => async () => {\n appLogger(\n `Reviewing file: ${formatPath(docPath)} to ${formatLocale(locale)}`\n );\n\n const absoluteBaseFilePath = join(configuration.content.baseDir, docPath);\n const outputFilePath = getOutputFilePath(\n absoluteBaseFilePath,\n locale,\n baseLocale\n );\n\n // Skip if file exists and skipIfExists option is enabled\n if (skipIfExists && existsSync(outputFilePath)) {\n const relativePath = relative(\n configuration.content.baseDir,\n outputFilePath\n );\n appLogger(\n `${colorize('⊘', ANSIColors.YELLOW)} File ${formatPath(relativePath)} already exists, skipping.`\n );\n return;\n }\n\n const fileModificationData = checkFileModifiedRange(outputFilePath, {\n skipIfModifiedBefore,\n skipIfModifiedAfter,\n });\n\n if (fileModificationData.isSkipped) {\n appLogger(fileModificationData.message);\n return;\n }\n\n let changedLines: number[] | undefined;\n // FIXED: Enable git optimization that was previously commented out\n if (gitOptions) {\n const gitChangedLines = await listGitLines(\n absoluteBaseFilePath,\n gitOptions\n );\n\n appLogger(`Git changed lines: ${gitChangedLines.join(', ')}`);\n changedLines = gitChangedLines;\n }\n\n await reviewFileBlockAware(\n absoluteBaseFilePath,\n outputFilePath,\n locale as Locale,\n baseLocale,\n aiOptions,\n configOptions,\n customInstructions,\n changedLines,\n aiClient,\n aiConfig\n );\n })\n );\n\n await parallelize(\n allTasks,\n (task) => task(),\n nbSimultaneousFileProcessed ?? 3\n );\n};\n"],"mappings":";;;;;;;;;;;;;;;;;AA6CA,MAAa,YAAY,OAAO,EAC9B,YACA,SACA,qBACA,YACA,WACA,6BACA,eACA,oBACA,sBACA,qBACA,cACA,iBACsB;CACtB,MAAM,uDAAiC,cAAc;CACrD,MAAM,+CAAyB,cAAc;CAE7C,MAAM,WAAW,MAAMA,8BAAQ,eAAe,UAAU;AAExD,KAAI,CAAC,UAAU,YAAa;CAE5B,MAAM,EAAE,UAAU,aAAa;AAE/B,KAAI,+BAA+B,8BAA8B,IAAI;AACnE,YACE,kDAAkD,4BAA4B,+CAC/E;AACD,gCAA8B;;CAGhC,IAAIC,UAAoB,6BAAS,YAAY,EAC3C,QAAQ,qBACT,CAAC;AAEF,KAAI,YAAY;EACd,MAAM,kBAAkB,2CAAmB,WAAW;AAEtD,MAAI,gBAIF,WAAU,QAAQ,QAAQ,SACxB,gBAAgB,MAAM,gCAAiB,QAAQ,KAAK,EAAE,KAAK,KAAK,QAAQ,CACzE;;AAML,WAAU,uDAA+B,WAAW,GAAG;AACvD,WACE,kDAA4B,QAAQ,OAAO,CAAC,mDAA2B,QAAQ,CAAC,IACjF;AAED,WAAU,kDAA4B,QAAQ,OAAO,CAAC,SAAS;AAC/D,WAAU,QAAQ,KAAK,SAAS,yCAAiB,KAAK,CAAC,IAAI,CAAC;AAiE5D,2CA9DiB,QAAQ,SAAS,YAChC,QAAQ,KAAK,WAAW,YAAY;AAClC,YACE,sDAA8B,QAAQ,CAAC,2CAAmB,OAAO,GAClE;EAED,MAAM,2CAA4B,cAAc,QAAQ,SAAS,QAAQ;EACzE,MAAM,iBAAiBC,kDACrB,sBACA,QACA,WACD;AAGD,MAAI,wCAA2B,eAAe,EAAE;GAC9C,MAAM,uCACJ,cAAc,QAAQ,SACtB,eACD;AACD,aACE,kCAAY,KAAKC,4BAAW,OAAO,CAAC,2CAAmB,aAAa,CAAC,4BACtE;AACD;;EAGF,MAAM,uBAAuBC,4DAAuB,gBAAgB;GAClE;GACA;GACD,CAAC;AAEF,MAAI,qBAAqB,WAAW;AAClC,aAAU,qBAAqB,QAAQ;AACvC;;EAGF,IAAIC;AAEJ,MAAI,YAAY;GACd,MAAM,kBAAkB,2CACtB,sBACA,WACD;AAED,aAAU,sBAAsB,gBAAgB,KAAK,KAAK,GAAG;AAC7D,kBAAe;;AAGjB,QAAMC,iDACJ,sBACA,gBACA,QACA,YACA,WACA,eACA,oBACA,cACA,UACA,SACD;GACD,CACH,GAIE,SAAS,MAAM,EAChB,+BAA+B,EAChC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"reviewDocBlockAware.cjs","names":["readAsset","ANSIColors","buildAlignmentPlan","mergeReviewedSegments","chunkInference","Locales","fixChunkStartEndChars"],"sources":["../../src/reviewDocBlockAware.ts"],"sourcesContent":["import { mkdirSync, writeFileSync } from 'node:fs';\nimport { readFile } from 'node:fs/promises';\nimport { dirname } from 'node:path';\nimport { readAsset } from 'utils:asset';\nimport type { AIConfig } from '@intlayer/ai';\nimport type { AIOptions } from '@intlayer/api';\nimport { formatLocale, formatPath } from '@intlayer/chokidar';\nimport {\n ANSIColors,\n colon,\n colorize,\n colorizeNumber,\n type GetConfigurationOptions,\n getAppLogger,\n getConfiguration,\n retryManager,\n} from '@intlayer/config';\nimport { getLocaleName } from '@intlayer/core';\nimport { type Locale, Locales } from '@intlayer/types';\nimport {\n buildAlignmentPlan,\n mergeReviewedSegments,\n} from './translation-alignment/pipeline';\nimport { chunkInference } from './utils/chunkInference';\nimport { fixChunkStartEndChars } from './utils/fixChunkStartEndChars';\nimport type { AIClient } from './utils/setupAI';\n\n/**\n * Review a file using block-aware alignment.\n * This approach:\n * 1. Segments both English and French documents into semantic blocks\n * 2. Aligns blocks using structure (special chars, numbers) and context\n * 3. Detects which blocks changed, were added, or deleted\n * 4. Only sends changed/new blocks to AI for translation\n * 5. Handles reordering automatically\n */\nexport const reviewFileBlockAware = async (\n baseFilePath: string,\n outputFilePath: string,\n locale: Locale,\n baseLocale: Locale,\n aiOptions?: AIOptions,\n configOptions?: GetConfigurationOptions,\n customInstructions?: string,\n changedLines?: number[],\n aiClient?: AIClient,\n aiConfig?: AIConfig\n) => {\n const configuration = getConfiguration(configOptions);\n const applicationLogger = getAppLogger(configuration);\n\n const englishText = await readFile(baseFilePath, 'utf-8');\n const frenchText = await readFile(outputFilePath, 'utf-8').catch(() => '');\n\n const basePrompt = readAsset('./prompts/REVIEW_PROMPT.md', 'utf-8')\n .replaceAll('{{localeName}}', `${formatLocale(locale, false)}`)\n .replaceAll('{{baseLocaleName}}', `${formatLocale(baseLocale, false)}`)\n .replace('{{applicationContext}}', aiOptions?.applicationContext ?? '-')\n .replace('{{customInstructions}}', customInstructions ?? '-');\n\n const filePrefixText = `${ANSIColors.GREY_DARK}[${formatPath(baseFilePath)}${ANSIColors.GREY_DARK}] `;\n const filePrefix = [\n colon(filePrefixText, { colSize: 40 }),\n `→ ${ANSIColors.RESET}`,\n ].join('');\n const prefixText = `${ANSIColors.GREY_DARK}[${formatPath(baseFilePath)}${ANSIColors.GREY_DARK}][${formatLocale(locale)}${ANSIColors.GREY_DARK}] `;\n const prefix = [\n colon(prefixText, { colSize: 40 }),\n `→ ${ANSIColors.RESET}`,\n ].join('');\n\n // Build block-aware alignment and plan\n const { englishBlocks, frenchBlocks, plan, segmentsToReview } =\n buildAlignmentPlan({\n englishText,\n frenchText,\n changedLines,\n });\n\n applicationLogger(\n `${filePrefix}Block-aware alignment complete. Total blocks: EN=${colorizeNumber(englishBlocks.length)}, FR=${colorizeNumber(frenchBlocks.length)}`\n );\n applicationLogger(\n `${filePrefix}Actions: reuse=${colorizeNumber(plan.actions.filter((a) => a.kind === 'reuse').length)}, review=${colorizeNumber(plan.actions.filter((a) => a.kind === 'review').length)}, new=${colorizeNumber(plan.actions.filter((a) => a.kind === 'insert_new').length)}, delete=${colorizeNumber(plan.actions.filter((a) => a.kind === 'delete').length)}`\n );\n\n if (segmentsToReview.length === 0) {\n applicationLogger(\n `${filePrefix}No segments need review, reusing existing translation`\n );\n mkdirSync(dirname(outputFilePath), { recursive: true });\n writeFileSync(\n outputFilePath,\n mergeReviewedSegments(plan, frenchBlocks, new Map())\n );\n applicationLogger(\n `${colorize('✔', ANSIColors.GREEN)} File ${formatPath(outputFilePath)} updated successfully (no changes needed).`\n );\n return;\n }\n\n applicationLogger(\n `${filePrefix}Segments to review: ${colorizeNumber(segmentsToReview.length)}`\n );\n\n // Review segments that need AI translation\n const reviewedSegmentsMap = new Map<number, string>();\n\n for (const segment of segmentsToReview) {\n const segmentNumber = segmentsToReview.indexOf(segment) + 1;\n const englishBlock = segment.englishBlock;\n\n const getBaseChunkContextPrompt = () =>\n `**BLOCK ${segmentNumber} of ${segmentsToReview.length}** is the base block in ${formatLocale(baseLocale, false)} as reference.\\n` +\n `///chunksStart///\\n` +\n englishBlock.content +\n `///chunksEnd///`;\n\n const getFrenchChunkPrompt = () =>\n `**BLOCK ${segmentNumber} of ${segmentsToReview.length}** is the current block to review in ${formatLocale(locale, false)}.\\n` +\n `///chunksStart///\\n` +\n (segment.frenchBlockText ?? '') +\n `///chunksEnd///`;\n\n const reviewedChunkResult = await retryManager(async () => {\n const result = await chunkInference(\n [\n { role: 'system', content: basePrompt },\n { role: 'system', content: getBaseChunkContextPrompt() },\n { role: 'system', content: getFrenchChunkPrompt() },\n {\n role: 'system',\n content: `The next user message will be the **BLOCK ${colorizeNumber(segmentNumber)} of ${colorizeNumber(segmentsToReview.length)}** that should be translated in ${getLocaleName(locale, Locales.ENGLISH)} (${locale}).`,\n },\n { role: 'user', content: englishBlock.content },\n ],\n aiOptions,\n configuration,\n aiClient,\n aiConfig\n );\n\n applicationLogger(\n `${prefix}${colorizeNumber(result.tokenUsed)} tokens used - Block ${colorizeNumber(segmentNumber)} of ${colorizeNumber(segmentsToReview.length)}`\n );\n\n const fixed = fixChunkStartEndChars(\n result?.fileContent,\n englishBlock.content\n );\n return fixed;\n })();\n\n reviewedSegmentsMap.set(segment.actionIndex, reviewedChunkResult);\n }\n\n // Merge reviewed segments back into final document\n const finalFrenchOutput = mergeReviewedSegments(\n plan,\n frenchBlocks,\n reviewedSegmentsMap\n );\n\n mkdirSync(dirname(outputFilePath), { recursive: true });\n writeFileSync(outputFilePath, finalFrenchOutput);\n\n applicationLogger(\n `${colorize('✔', ANSIColors.GREEN)} File ${formatPath(outputFilePath)} created/updated successfully.`\n );\n};\n"],"mappings":"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"}
@@ -1,163 +0,0 @@
1
- const require_rolldown_runtime = require('./_virtual/rolldown_runtime.cjs');
2
- const require_utils_setupAI = require('./utils/setupAI.cjs');
3
- const require__utils_asset = require('./_virtual/_utils_asset.cjs');
4
- const require_utils_chunkInference = require('./utils/chunkInference.cjs');
5
- const require_utils_fixChunkStartEndChars = require('./utils/fixChunkStartEndChars.cjs');
6
- const require_utils_checkFileModifiedRange = require('./utils/checkFileModifiedRange.cjs');
7
- const require_utils_getOutputFilePath = require('./utils/getOutputFilePath.cjs');
8
- const require_utils_calculateChunks = require('./utils/calculateChunks.cjs');
9
- let _intlayer_chokidar = require("@intlayer/chokidar");
10
- let _intlayer_config = require("@intlayer/config");
11
- let node_path = require("node:path");
12
- let node_fs = require("node:fs");
13
- let node_fs_promises = require("node:fs/promises");
14
- let fast_glob = require("fast-glob");
15
- fast_glob = require_rolldown_runtime.__toESM(fast_glob);
16
-
17
- //#region src/translateDoc.ts
18
- /**
19
- * Translate a single file for a given locale
20
- * Returns TRUE if successful, FALSE if failed/skipped
21
- */
22
- const translateFile = async (baseFilePath, outputFilePath, locale, baseLocale, configuration, errorState, aiOptions, customInstructions, aiClient, aiConfig) => {
23
- if (errorState.shouldStop) return false;
24
- const appLogger = (0, _intlayer_config.getAppLogger)(configuration, { config: { prefix: "" } });
25
- try {
26
- const fileContent = await (0, node_fs_promises.readFile)(baseFilePath, "utf-8");
27
- let fileResultContent = fileContent;
28
- const filePrefix = [(0, _intlayer_config.colon)(`${_intlayer_config.ANSIColors.GREY_DARK}[${(0, _intlayer_chokidar.formatPath)(baseFilePath)}${_intlayer_config.ANSIColors.GREY_DARK}] `, { colSize: 40 }), `→ ${_intlayer_config.ANSIColors.RESET}`].join("");
29
- const prefix = [(0, _intlayer_config.colon)(`${_intlayer_config.ANSIColors.GREY_DARK}[${(0, _intlayer_chokidar.formatPath)(baseFilePath)}${_intlayer_config.ANSIColors.GREY_DARK}][${(0, _intlayer_chokidar.formatLocale)(locale)}${_intlayer_config.ANSIColors.GREY_DARK}] `, { colSize: 40 }), `→ ${_intlayer_config.ANSIColors.RESET}`].join("");
30
- const chunks = require_utils_calculateChunks.chunkText(fileContent);
31
- appLogger(`${filePrefix}Base file splitted into ${(0, _intlayer_config.colorizeNumber)(chunks.length)} chunks`);
32
- const translatedParts = [];
33
- const basePrompt = require__utils_asset.readAsset("./prompts/TRANSLATE_PROMPT.md", "utf-8").replaceAll("{{localeName}}", `${(0, _intlayer_chokidar.formatLocale)(locale, false)}`).replaceAll("{{baseLocaleName}}", `${(0, _intlayer_chokidar.formatLocale)(baseLocale, false)}`).replace("{{applicationContext}}", aiOptions?.applicationContext ?? "-").replace("{{customInstructions}}", customInstructions ?? "-");
34
- for await (const [i, chunk] of chunks.entries()) {
35
- if (errorState.shouldStop) return false;
36
- const isFirstChunk = i === 0;
37
- const fileToTranslateCurrentChunk = chunk.content;
38
- const getPrevChunkPrompt = () => `**CHUNK ${i} of ${chunks.length}** that has been translated in ${(0, _intlayer_chokidar.formatLocale)(locale)}:\n///chunkStart///` + (0, _intlayer_chokidar.getChunk)(translatedParts.join(""), chunks[i - 1]) + `///chunkEnd///`;
39
- const getBaseChunkContextPrompt = () => `**CHUNK ${i + 1} to ${Math.min(i + 3, chunks.length)} of ${chunks.length}** is the base chunk in ${(0, _intlayer_chokidar.formatLocale)(baseLocale, false)} as reference.\n///chunksStart///` + (chunks[i - 1]?.content ?? "") + chunks[i].content + (chunks[i + 1]?.content ?? "") + `///chunksEnd///`;
40
- const chunkTranslation = await (0, _intlayer_config.retryManager)(async () => {
41
- const result = await require_utils_chunkInference.chunkInference([
42
- {
43
- role: "system",
44
- content: basePrompt
45
- },
46
- {
47
- role: "system",
48
- content: getBaseChunkContextPrompt()
49
- },
50
- ...isFirstChunk ? [] : [{
51
- role: "system",
52
- content: getPrevChunkPrompt()
53
- }],
54
- {
55
- role: "system",
56
- content: `The next user message will be the **CHUNK ${(0, _intlayer_config.colorizeNumber)(i + 1)} of ${(0, _intlayer_config.colorizeNumber)(chunks.length)}** in ${(0, _intlayer_chokidar.formatLocale)(baseLocale, false)} to translate in ${(0, _intlayer_chokidar.formatLocale)(locale, false)}.\n
57
- STRICT INSTRUCTIONS:
58
- 1. Translate ONLY the content of this specific chunk.
59
- 2. Do NOT repeat the content from the previous chunk.
60
- 3. Start the translation exactly where the previous chunk ended.
61
- 4. Preserve all code blocks and formatting exactly.`
62
- },
63
- {
64
- role: "user",
65
- content: fileToTranslateCurrentChunk
66
- }
67
- ], aiOptions, configuration, aiClient, aiConfig);
68
- appLogger([
69
- `${prefix}`,
70
- `${_intlayer_config.ANSIColors.GREY_DARK}[Chunk `,
71
- (0, _intlayer_config.colorizeNumber)(i + 1),
72
- `${_intlayer_config.ANSIColors.GREY_DARK} of `,
73
- (0, _intlayer_config.colorizeNumber)(chunks.length),
74
- `${_intlayer_config.ANSIColors.GREY_DARK}] →${_intlayer_config.ANSIColors.RESET} `,
75
- `${(0, _intlayer_config.colorizeNumber)(result.tokenUsed)} tokens used`
76
- ].join(""));
77
- return require_utils_fixChunkStartEndChars.fixChunkStartEndChars(result?.fileContent, fileToTranslateCurrentChunk);
78
- })();
79
- fileResultContent = fileResultContent.replace(fileToTranslateCurrentChunk, chunkTranslation);
80
- }
81
- const finalContent = translatedParts.join("");
82
- (0, node_fs.mkdirSync)((0, node_path.dirname)(outputFilePath), { recursive: true });
83
- (0, node_fs.writeFileSync)(outputFilePath, finalContent);
84
- const relativePath = (0, node_path.relative)(configuration.content.baseDir, outputFilePath);
85
- appLogger(`${(0, _intlayer_config.colorize)("✔", _intlayer_config.ANSIColors.GREEN)} File ${(0, _intlayer_chokidar.formatPath)(relativePath)} created/updated successfully.`);
86
- return true;
87
- } catch (error) {
88
- errorState.count++;
89
- const errorString = JSON.stringify(error);
90
- const errorMessage = error?.message ?? "";
91
- if (errorString.includes("AI_ACCESS_DENIED") || errorMessage.includes("Access keys") || errorMessage.includes("Access denied") || errorMessage.includes("Invalid Access keys")) {
92
- errorState.count = errorState.maxErrors + 1;
93
- appLogger(`${(0, _intlayer_config.colorize)("✖", _intlayer_config.ANSIColors.RED)} Critical Authentication Error. Aborting all tasks.`);
94
- }
95
- if (errorState.count >= errorState.maxErrors && !errorState.shouldStop) {
96
- errorState.shouldStop = true;
97
- appLogger(`${(0, _intlayer_config.colorize)("✖", _intlayer_config.ANSIColors.RED)} Too many errors (${errorState.count}). Stopping process.`);
98
- }
99
- return false;
100
- }
101
- };
102
- /**
103
- * Main translate function: scans all .md files in "en/" (unless you specified DOC_LIST),
104
- * then translates them to each locale in LOCALE_LIST.
105
- */
106
- const translateDoc = async ({ docPattern, locales, excludedGlobPattern, baseLocale, aiOptions, nbSimultaneousFileProcessed, configOptions, customInstructions, skipIfModifiedBefore, skipIfModifiedAfter, skipIfExists, gitOptions }) => {
107
- const configuration = (0, _intlayer_config.getConfiguration)(configOptions);
108
- const appLogger = (0, _intlayer_config.getAppLogger)(configuration);
109
- if (nbSimultaneousFileProcessed && nbSimultaneousFileProcessed > 10) {
110
- appLogger(`Warning: nbSimultaneousFileProcessed is set to ${nbSimultaneousFileProcessed}, which is greater than 10. Setting it to 10.`);
111
- nbSimultaneousFileProcessed = 10;
112
- }
113
- let docList = await (0, fast_glob.default)(docPattern, { ignore: excludedGlobPattern });
114
- const aiResult = await require_utils_setupAI.setupAI(configuration, aiOptions);
115
- if (!aiResult?.hasAIAccess) return;
116
- const { aiClient, aiConfig } = aiResult;
117
- if (gitOptions) {
118
- const gitChangedFiles = await (0, _intlayer_chokidar.listGitFiles)(gitOptions);
119
- if (gitChangedFiles) docList = docList.filter((path) => gitChangedFiles.some((gitFile) => (0, node_path.join)(process.cwd(), path) === gitFile));
120
- }
121
- appLogger(`Base locale is ${(0, _intlayer_chokidar.formatLocale)(baseLocale)}`);
122
- appLogger(`Translating ${(0, _intlayer_config.colorizeNumber)(locales.length)} locales: [ ${(0, _intlayer_chokidar.formatLocale)(locales)} ]`);
123
- appLogger(`Translating ${(0, _intlayer_config.colorizeNumber)(docList.length)} files:`);
124
- docList.forEach((path) => {
125
- appLogger(` - ${(0, _intlayer_chokidar.formatPath)(path)}`);
126
- });
127
- const errorState = {
128
- count: 0,
129
- maxErrors: 5,
130
- shouldStop: false
131
- };
132
- await (0, _intlayer_chokidar.parallelize)(docList.flatMap((docPath) => locales.map((locale) => async () => {
133
- if (errorState.shouldStop) return;
134
- appLogger(`Translating file: ${(0, _intlayer_chokidar.formatPath)(docPath)} to ${(0, _intlayer_chokidar.formatLocale)(locale)}`);
135
- const absoluteBaseFilePath = (0, node_path.join)(configuration.content.baseDir, docPath);
136
- const outputFilePath = require_utils_getOutputFilePath.getOutputFilePath(absoluteBaseFilePath, locale, baseLocale);
137
- if (skipIfExists && (0, node_fs.existsSync)(outputFilePath)) {
138
- const relativePath = (0, node_path.relative)(configuration.content.baseDir, outputFilePath);
139
- appLogger(`${(0, _intlayer_config.colorize)("⊘", _intlayer_config.ANSIColors.YELLOW)} File ${(0, _intlayer_chokidar.formatPath)(relativePath)} already exists, skipping.`);
140
- return;
141
- }
142
- if (!(0, node_fs.existsSync)(outputFilePath)) {
143
- appLogger(`File ${(0, _intlayer_chokidar.formatPath)((0, node_path.relative)(configuration.content.baseDir, outputFilePath))} does not exist, creating it...`);
144
- (0, node_fs.mkdirSync)((0, node_path.dirname)(outputFilePath), { recursive: true });
145
- (0, node_fs.writeFileSync)(outputFilePath, "");
146
- }
147
- const fileModificationData = require_utils_checkFileModifiedRange.checkFileModifiedRange(outputFilePath, {
148
- skipIfModifiedBefore,
149
- skipIfModifiedAfter
150
- });
151
- if (fileModificationData.isSkipped) {
152
- appLogger(fileModificationData.message);
153
- return;
154
- }
155
- await translateFile(absoluteBaseFilePath, outputFilePath, locale, baseLocale, configuration, errorState, aiOptions, customInstructions, aiClient, aiConfig);
156
- })), (task) => task(), nbSimultaneousFileProcessed ?? 3);
157
- if (errorState.count > 0) appLogger(`Process finished with ${(0, _intlayer_config.colorizeNumber)(errorState.count)} error${errorState.count === 1 ? "" : "s"}.`);
158
- };
159
-
160
- //#endregion
161
- exports.translateDoc = translateDoc;
162
- exports.translateFile = translateFile;
163
- //# sourceMappingURL=translateDoc.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"translateDoc.cjs","names":["ANSIColors","chunkText","translatedParts: string[]","readAsset","chunkInference","fixChunkStartEndChars","error: any","docList: string[]","setupAI","errorState: ErrorState","getOutputFilePath","checkFileModifiedRange"],"sources":["../../src/translateDoc.ts"],"sourcesContent":["import { existsSync, mkdirSync, writeFileSync } from 'node:fs';\nimport { readFile } from 'node:fs/promises';\nimport { dirname, join, relative } from 'node:path';\nimport { readAsset } from 'utils:asset';\nimport type { AIConfig, AIOptions } from '@intlayer/ai';\nimport {\n formatLocale,\n formatPath,\n getChunk,\n type ListGitFilesOptions,\n listGitFiles,\n parallelize,\n} from '@intlayer/chokidar';\nimport {\n ANSIColors,\n colon,\n colorize,\n colorizeNumber,\n type GetConfigurationOptions,\n getAppLogger,\n getConfiguration,\n retryManager,\n} from '@intlayer/config';\nimport type { IntlayerConfig, Locale } from '@intlayer/types';\nimport fg from 'fast-glob';\nimport { chunkText } from './utils/calculateChunks';\nimport { checkFileModifiedRange } from './utils/checkFileModifiedRange';\nimport { chunkInference } from './utils/chunkInference';\nimport { fixChunkStartEndChars } from './utils/fixChunkStartEndChars';\nimport { getOutputFilePath } from './utils/getOutputFilePath';\nimport { type AIClient, setupAI } from './utils/setupAI';\n\n/**\n * Shared error state for circuit breaker pattern\n */\ntype ErrorState = {\n count: number;\n maxErrors: number;\n shouldStop: boolean;\n};\n\n/**\n * Translate a single file for a given locale\n * Returns TRUE if successful, FALSE if failed/skipped\n */\nexport const translateFile = async (\n baseFilePath: string,\n outputFilePath: string,\n locale: Locale,\n baseLocale: Locale,\n configuration: IntlayerConfig,\n errorState: ErrorState,\n aiOptions?: AIOptions,\n customInstructions?: string,\n aiClient?: AIClient,\n aiConfig?: AIConfig\n): Promise<boolean> => {\n // Circuit Breaker Check\n if (errorState.shouldStop) {\n return false;\n }\n\n const appLogger = getAppLogger(configuration, {\n config: {\n prefix: '',\n },\n });\n\n try {\n // Read File\n const fileContent = await readFile(baseFilePath, 'utf-8');\n\n let fileResultContent = fileContent;\n\n // Prepare formatting\n const filePrefixText = `${ANSIColors.GREY_DARK}[${formatPath(baseFilePath)}${ANSIColors.GREY_DARK}] `;\n const filePrefix = [\n colon(filePrefixText, { colSize: 40 }),\n `→ ${ANSIColors.RESET}`,\n ].join('');\n\n const prefixText = `${ANSIColors.GREY_DARK}[${formatPath(baseFilePath)}${ANSIColors.GREY_DARK}][${formatLocale(locale)}${ANSIColors.GREY_DARK}] `;\n const prefix = [\n colon(prefixText, { colSize: 40 }),\n `→ ${ANSIColors.RESET}`,\n ].join('');\n\n // Chunking\n const chunks = chunkText(fileContent);\n appLogger(\n `${filePrefix}Base file splitted into ${colorizeNumber(chunks.length)} chunks`\n );\n\n // Instead of replacing content in a string, we push to an array\n const translatedParts: string[] = [];\n\n // Prepare Base Prompt\n const basePrompt = readAsset('./prompts/TRANSLATE_PROMPT.md', 'utf-8')\n .replaceAll('{{localeName}}', `${formatLocale(locale, false)}`)\n .replaceAll('{{baseLocaleName}}', `${formatLocale(baseLocale, false)}`)\n .replace('{{applicationContext}}', aiOptions?.applicationContext ?? '-')\n .replace('{{customInstructions}}', customInstructions ?? '-');\n\n // Iterate and Translate\n for await (const [i, chunk] of chunks.entries()) {\n // Circuit Breaker Check inside the loop (in case error happened elsewhere while processing)\n if (errorState.shouldStop) return false;\n\n const isFirstChunk = i === 0;\n const fileToTranslateCurrentChunk = chunk.content;\n\n // Build the chunk-specific prompt\n const getPrevChunkPrompt = () =>\n `**CHUNK ${i} of ${chunks.length}** that has been translated in ${formatLocale(locale)}:\\n` +\n `///chunkStart///` +\n getChunk(translatedParts.join(''), chunks[i - 1]) +\n `///chunkEnd///`;\n\n const getBaseChunkContextPrompt = () =>\n `**CHUNK ${i + 1} to ${Math.min(i + 3, chunks.length)} of ${chunks.length}** is the base chunk in ${formatLocale(baseLocale, false)} as reference.\\n` +\n `///chunksStart///` +\n (chunks[i - 1]?.content ?? '') +\n chunks[i].content +\n (chunks[i + 1]?.content ?? '') +\n `///chunksEnd///`;\n\n // Make the actual translation call\n const chunkTranslation = await retryManager(async () => {\n const result = await chunkInference(\n [\n { role: 'system', content: basePrompt },\n { role: 'system', content: getBaseChunkContextPrompt() },\n ...(isFirstChunk\n ? []\n : [{ role: 'system', content: getPrevChunkPrompt() } as const]),\n {\n role: 'system',\n content: `The next user message will be the **CHUNK ${colorizeNumber(i + 1)} of ${colorizeNumber(chunks.length)}** in ${formatLocale(baseLocale, false)} to translate in ${formatLocale(locale, false)}.\\n\n STRICT INSTRUCTIONS:\n 1. Translate ONLY the content of this specific chunk. \n 2. Do NOT repeat the content from the previous chunk.\n 3. Start the translation exactly where the previous chunk ended.\n 4. Preserve all code blocks and formatting exactly.`,\n },\n { role: 'user', content: fileToTranslateCurrentChunk },\n ],\n aiOptions,\n configuration,\n aiClient,\n aiConfig\n );\n\n appLogger(\n [\n `${prefix}`,\n `${ANSIColors.GREY_DARK}[Chunk `,\n colorizeNumber(i + 1),\n `${ANSIColors.GREY_DARK} of `,\n colorizeNumber(chunks.length),\n `${ANSIColors.GREY_DARK}] →${ANSIColors.RESET} `,\n `${colorizeNumber(result.tokenUsed)} tokens used`,\n ].join('')\n );\n\n const fixedTranslatedChunkResult = fixChunkStartEndChars(\n result?.fileContent,\n fileToTranslateCurrentChunk\n );\n\n return fixedTranslatedChunkResult;\n })();\n\n // Replace the chunk in the file content\n fileResultContent = fileResultContent.replace(\n fileToTranslateCurrentChunk,\n chunkTranslation\n );\n }\n\n // Write final file by joining parts\n const finalContent = translatedParts.join('');\n\n mkdirSync(dirname(outputFilePath), { recursive: true });\n writeFileSync(outputFilePath, finalContent);\n\n const relativePath = relative(\n configuration.content.baseDir,\n outputFilePath\n );\n\n appLogger(\n `${colorize('✔', ANSIColors.GREEN)} File ${formatPath(relativePath)} created/updated successfully.`\n );\n\n return true; // Success\n } catch (error: any) {\n // Handle Errors and Update State\n\n errorState.count++;\n\n // If it's an Access Denied error, stop immediately (set count to max)\n const errorString = JSON.stringify(error);\n const errorMessage = error?.message ?? '';\n if (\n errorString.includes('AI_ACCESS_DENIED') ||\n errorMessage.includes('Access keys') ||\n errorMessage.includes('Access denied') ||\n errorMessage.includes('Invalid Access keys')\n ) {\n errorState.count = errorState.maxErrors + 1;\n appLogger(\n `${colorize('✖', ANSIColors.RED)} Critical Authentication Error. Aborting all tasks.`\n );\n }\n\n if (errorState.count >= errorState.maxErrors && !errorState.shouldStop) {\n errorState.shouldStop = true;\n appLogger(\n `${colorize('✖', ANSIColors.RED)} Too many errors (${errorState.count}). Stopping process.`\n );\n }\n\n return false; // Failed\n }\n};\n\ntype TranslateDocOptions = {\n docPattern: string[];\n locales: Locale[];\n excludedGlobPattern: string[];\n baseLocale: Locale;\n aiOptions?: AIOptions;\n nbSimultaneousFileProcessed?: number;\n configOptions?: GetConfigurationOptions;\n customInstructions?: string;\n skipIfModifiedBefore?: number | string | Date;\n skipIfModifiedAfter?: number | string | Date;\n skipIfExists?: boolean;\n gitOptions?: ListGitFilesOptions;\n};\n\n/**\n * Main translate function: scans all .md files in \"en/\" (unless you specified DOC_LIST),\n * then translates them to each locale in LOCALE_LIST.\n */\nexport const translateDoc = async ({\n docPattern,\n locales,\n excludedGlobPattern,\n baseLocale,\n aiOptions,\n nbSimultaneousFileProcessed,\n configOptions,\n customInstructions,\n skipIfModifiedBefore,\n skipIfModifiedAfter,\n skipIfExists,\n gitOptions,\n}: TranslateDocOptions) => {\n const configuration = getConfiguration(configOptions);\n const appLogger = getAppLogger(configuration);\n\n if (nbSimultaneousFileProcessed && nbSimultaneousFileProcessed > 10) {\n appLogger(\n `Warning: nbSimultaneousFileProcessed is set to ${nbSimultaneousFileProcessed}, which is greater than 10. Setting it to 10.`\n );\n nbSimultaneousFileProcessed = 10; // Limit the number of simultaneous file processed to 10\n }\n\n let docList: string[] = await fg(docPattern, {\n ignore: excludedGlobPattern,\n });\n\n const aiResult = await setupAI(configuration, aiOptions);\n\n if (!aiResult?.hasAIAccess) return;\n\n const { aiClient, aiConfig } = aiResult;\n\n if (gitOptions) {\n const gitChangedFiles = await listGitFiles(gitOptions);\n\n if (gitChangedFiles) {\n // Convert dictionary file paths to be relative to git root for comparison\n\n // Filter dictionaries based on git changed files\n docList = docList.filter((path) =>\n gitChangedFiles.some((gitFile) => join(process.cwd(), path) === gitFile)\n );\n }\n }\n\n // OAuth handled by API proxy internally\n\n appLogger(`Base locale is ${formatLocale(baseLocale)}`);\n appLogger(\n `Translating ${colorizeNumber(locales.length)} locales: [ ${formatLocale(locales)} ]`\n );\n\n appLogger(`Translating ${colorizeNumber(docList.length)} files:`);\n docList.forEach((path) => {\n appLogger(` - ${formatPath(path)}`);\n });\n\n // Initialize Error State\n const MAX_ALLOWED_ERRORS = 5;\n const errorState: ErrorState = {\n count: 0,\n maxErrors: MAX_ALLOWED_ERRORS,\n shouldStop: false,\n };\n\n // Create all tasks to be processed\n const allTasks = docList.flatMap((docPath) =>\n locales.map((locale) => async () => {\n // Early exit if too many errors\n if (errorState.shouldStop) return;\n\n appLogger(\n `Translating file: ${formatPath(docPath)} to ${formatLocale(locale)}`\n );\n\n const absoluteBaseFilePath = join(configuration.content.baseDir, docPath);\n const outputFilePath = getOutputFilePath(\n absoluteBaseFilePath,\n locale,\n baseLocale\n );\n\n // Skip if file exists and skipIfExists option is enabled\n if (skipIfExists && existsSync(outputFilePath)) {\n const relativePath = relative(\n configuration.content.baseDir,\n outputFilePath\n );\n appLogger(\n `${colorize('⊘', ANSIColors.YELLOW)} File ${formatPath(relativePath)} already exists, skipping.`\n );\n return;\n }\n\n // check if the file exist, otherwise create it\n if (!existsSync(outputFilePath)) {\n const relativePath = relative(\n configuration.content.baseDir,\n outputFilePath\n );\n appLogger(\n `File ${formatPath(relativePath)} does not exist, creating it...`\n );\n mkdirSync(dirname(outputFilePath), { recursive: true });\n writeFileSync(outputFilePath, '');\n }\n\n const fileModificationData = checkFileModifiedRange(outputFilePath, {\n skipIfModifiedBefore,\n skipIfModifiedAfter,\n });\n\n if (fileModificationData.isSkipped) {\n appLogger(fileModificationData.message);\n return;\n }\n\n // Call translateFile with errorState\n await translateFile(\n absoluteBaseFilePath,\n outputFilePath,\n locale as Locale,\n baseLocale,\n configuration,\n errorState,\n aiOptions,\n customInstructions,\n aiClient,\n aiConfig\n );\n })\n );\n\n await parallelize(\n allTasks,\n (task) => task(),\n nbSimultaneousFileProcessed ?? 3\n );\n\n if (errorState.count > 0) {\n appLogger(\n `Process finished with ${colorizeNumber(errorState.count)} error${errorState.count === 1 ? '' : 's'}.`\n );\n }\n};\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"reviewDoc.mjs","names":["docList: string[]","changedLines: number[] | undefined"],"sources":["../../src/reviewDoc.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\nimport { join, relative } from 'node:path';\nimport type { AIOptions } from '@intlayer/api'; // OAuth handled by API proxy\nimport {\n formatLocale,\n formatPath,\n type ListGitFilesOptions,\n listGitFiles,\n listGitLines,\n parallelize,\n} from '@intlayer/chokidar';\nimport {\n ANSIColors,\n colorize,\n colorizeNumber,\n type GetConfigurationOptions,\n getAppLogger,\n getConfiguration,\n} from '@intlayer/config';\nimport type { Locale } from '@intlayer/types';\nimport fg from 'fast-glob';\nimport { reviewFileBlockAware } from './reviewDocBlockAware';\nimport { checkFileModifiedRange } from './utils/checkFileModifiedRange';\nimport { getOutputFilePath } from './utils/getOutputFilePath';\nimport { setupAI } from './utils/setupAI';\n\ntype ReviewDocOptions = {\n docPattern: string[];\n locales: Locale[];\n excludedGlobPattern: string[];\n baseLocale: Locale;\n aiOptions?: AIOptions;\n nbSimultaneousFileProcessed?: number;\n configOptions?: GetConfigurationOptions;\n customInstructions?: string;\n skipIfModifiedBefore?: number | string | Date;\n skipIfModifiedAfter?: number | string | Date;\n skipIfExists?: boolean;\n gitOptions?: ListGitFilesOptions;\n};\n\n/**\n * Main audit function: scans all .md files in \"en/\" (unless you specified DOC_LIST),\n * then audits them to each locale in LOCALE_LIST.\n */\nexport const reviewDoc = async ({\n docPattern,\n locales,\n excludedGlobPattern,\n baseLocale,\n aiOptions,\n nbSimultaneousFileProcessed,\n configOptions,\n customInstructions,\n skipIfModifiedBefore,\n skipIfModifiedAfter,\n skipIfExists,\n gitOptions,\n}: ReviewDocOptions) => {\n const configuration = getConfiguration(configOptions);\n const appLogger = getAppLogger(configuration);\n\n const aiResult = await setupAI(configuration, aiOptions);\n\n if (!aiResult?.hasAIAccess) return;\n\n const { aiClient, aiConfig } = aiResult;\n\n if (nbSimultaneousFileProcessed && nbSimultaneousFileProcessed > 10) {\n appLogger(\n `Warning: nbSimultaneousFileProcessed is set to ${nbSimultaneousFileProcessed}, which is greater than 10. Setting it to 10.`\n );\n nbSimultaneousFileProcessed = 10; // Limit the number of simultaneous file processed to 10\n }\n\n let docList: string[] = await fg(docPattern, {\n ignore: excludedGlobPattern,\n });\n\n if (gitOptions) {\n const gitChangedFiles = await listGitFiles(gitOptions);\n\n if (gitChangedFiles) {\n // Convert dictionary file paths to be relative to git root for comparison\n\n // Filter dictionaries based on git changed files\n docList = docList.filter((path) =>\n gitChangedFiles.some((gitFile) => join(process.cwd(), path) === gitFile)\n );\n }\n }\n\n // OAuth handled by API proxy internally\n\n appLogger(`Base locale is ${formatLocale(baseLocale)}`);\n appLogger(\n `Reviewing ${colorizeNumber(locales.length)} locales: [ ${formatLocale(locales)} ]`\n );\n\n appLogger(`Reviewing ${colorizeNumber(docList.length)} files:`);\n appLogger(docList.map((path) => ` - ${formatPath(path)}\\n`));\n\n // Create all tasks to be processed\n const allTasks = docList.flatMap((docPath) =>\n locales.map((locale) => async () => {\n appLogger(\n `Reviewing file: ${formatPath(docPath)} to ${formatLocale(locale)}`\n );\n\n const absoluteBaseFilePath = join(configuration.content.baseDir, docPath);\n const outputFilePath = getOutputFilePath(\n absoluteBaseFilePath,\n locale,\n baseLocale\n );\n\n // Skip if file exists and skipIfExists option is enabled\n if (skipIfExists && existsSync(outputFilePath)) {\n const relativePath = relative(\n configuration.content.baseDir,\n outputFilePath\n );\n appLogger(\n `${colorize('⊘', ANSIColors.YELLOW)} File ${formatPath(relativePath)} already exists, skipping.`\n );\n return;\n }\n\n const fileModificationData = checkFileModifiedRange(outputFilePath, {\n skipIfModifiedBefore,\n skipIfModifiedAfter,\n });\n\n if (fileModificationData.isSkipped) {\n appLogger(fileModificationData.message);\n return;\n }\n\n let changedLines: number[] | undefined;\n // FIXED: Enable git optimization that was previously commented out\n if (gitOptions) {\n const gitChangedLines = await listGitLines(\n absoluteBaseFilePath,\n gitOptions\n );\n\n appLogger(`Git changed lines: ${gitChangedLines.join(', ')}`);\n changedLines = gitChangedLines;\n }\n\n await reviewFileBlockAware(\n absoluteBaseFilePath,\n outputFilePath,\n locale as Locale,\n baseLocale,\n aiOptions,\n configOptions,\n customInstructions,\n changedLines,\n aiClient,\n aiConfig\n );\n })\n );\n\n await parallelize(\n allTasks,\n (task) => task(),\n nbSimultaneousFileProcessed ?? 3\n );\n};\n"],"mappings":";;;;;;;;;;;;;;;AA6CA,MAAa,YAAY,OAAO,EAC9B,YACA,SACA,qBACA,YACA,WACA,6BACA,eACA,oBACA,sBACA,qBACA,cACA,iBACsB;CACtB,MAAM,gBAAgB,iBAAiB,cAAc;CACrD,MAAM,YAAY,aAAa,cAAc;CAE7C,MAAM,WAAW,MAAM,QAAQ,eAAe,UAAU;AAExD,KAAI,CAAC,UAAU,YAAa;CAE5B,MAAM,EAAE,UAAU,aAAa;AAE/B,KAAI,+BAA+B,8BAA8B,IAAI;AACnE,YACE,kDAAkD,4BAA4B,+CAC/E;AACD,gCAA8B;;CAGhC,IAAIA,UAAoB,MAAM,GAAG,YAAY,EAC3C,QAAQ,qBACT,CAAC;AAEF,KAAI,YAAY;EACd,MAAM,kBAAkB,MAAM,aAAa,WAAW;AAEtD,MAAI,gBAIF,WAAU,QAAQ,QAAQ,SACxB,gBAAgB,MAAM,YAAY,KAAK,QAAQ,KAAK,EAAE,KAAK,KAAK,QAAQ,CACzE;;AAML,WAAU,kBAAkB,aAAa,WAAW,GAAG;AACvD,WACE,aAAa,eAAe,QAAQ,OAAO,CAAC,cAAc,aAAa,QAAQ,CAAC,IACjF;AAED,WAAU,aAAa,eAAe,QAAQ,OAAO,CAAC,SAAS;AAC/D,WAAU,QAAQ,KAAK,SAAS,MAAM,WAAW,KAAK,CAAC,IAAI,CAAC;AAiE5D,OAAM,YA9DW,QAAQ,SAAS,YAChC,QAAQ,KAAK,WAAW,YAAY;AAClC,YACE,mBAAmB,WAAW,QAAQ,CAAC,MAAM,aAAa,OAAO,GAClE;EAED,MAAM,uBAAuB,KAAK,cAAc,QAAQ,SAAS,QAAQ;EACzE,MAAM,iBAAiB,kBACrB,sBACA,QACA,WACD;AAGD,MAAI,gBAAgB,WAAW,eAAe,EAAE;GAC9C,MAAM,eAAe,SACnB,cAAc,QAAQ,SACtB,eACD;AACD,aACE,GAAG,SAAS,KAAK,WAAW,OAAO,CAAC,QAAQ,WAAW,aAAa,CAAC,4BACtE;AACD;;EAGF,MAAM,uBAAuB,uBAAuB,gBAAgB;GAClE;GACA;GACD,CAAC;AAEF,MAAI,qBAAqB,WAAW;AAClC,aAAU,qBAAqB,QAAQ;AACvC;;EAGF,IAAIC;AAEJ,MAAI,YAAY;GACd,MAAM,kBAAkB,MAAM,aAC5B,sBACA,WACD;AAED,aAAU,sBAAsB,gBAAgB,KAAK,KAAK,GAAG;AAC7D,kBAAe;;AAGjB,QAAM,qBACJ,sBACA,gBACA,QACA,YACA,WACA,eACA,oBACA,cACA,UACA,SACD;GACD,CACH,GAIE,SAAS,MAAM,EAChB,+BAA+B,EAChC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"reviewDocBlockAware.mjs","names":[],"sources":["../../src/reviewDocBlockAware.ts"],"sourcesContent":["import { mkdirSync, writeFileSync } from 'node:fs';\nimport { readFile } from 'node:fs/promises';\nimport { dirname } from 'node:path';\nimport { readAsset } from 'utils:asset';\nimport type { AIConfig } from '@intlayer/ai';\nimport type { AIOptions } from '@intlayer/api';\nimport { formatLocale, formatPath } from '@intlayer/chokidar';\nimport {\n ANSIColors,\n colon,\n colorize,\n colorizeNumber,\n type GetConfigurationOptions,\n getAppLogger,\n getConfiguration,\n retryManager,\n} from '@intlayer/config';\nimport { getLocaleName } from '@intlayer/core';\nimport { type Locale, Locales } from '@intlayer/types';\nimport {\n buildAlignmentPlan,\n mergeReviewedSegments,\n} from './translation-alignment/pipeline';\nimport { chunkInference } from './utils/chunkInference';\nimport { fixChunkStartEndChars } from './utils/fixChunkStartEndChars';\nimport type { AIClient } from './utils/setupAI';\n\n/**\n * Review a file using block-aware alignment.\n * This approach:\n * 1. Segments both English and French documents into semantic blocks\n * 2. Aligns blocks using structure (special chars, numbers) and context\n * 3. Detects which blocks changed, were added, or deleted\n * 4. Only sends changed/new blocks to AI for translation\n * 5. Handles reordering automatically\n */\nexport const reviewFileBlockAware = async (\n baseFilePath: string,\n outputFilePath: string,\n locale: Locale,\n baseLocale: Locale,\n aiOptions?: AIOptions,\n configOptions?: GetConfigurationOptions,\n customInstructions?: string,\n changedLines?: number[],\n aiClient?: AIClient,\n aiConfig?: AIConfig\n) => {\n const configuration = getConfiguration(configOptions);\n const applicationLogger = getAppLogger(configuration);\n\n const englishText = await readFile(baseFilePath, 'utf-8');\n const frenchText = await readFile(outputFilePath, 'utf-8').catch(() => '');\n\n const basePrompt = readAsset('./prompts/REVIEW_PROMPT.md', 'utf-8')\n .replaceAll('{{localeName}}', `${formatLocale(locale, false)}`)\n .replaceAll('{{baseLocaleName}}', `${formatLocale(baseLocale, false)}`)\n .replace('{{applicationContext}}', aiOptions?.applicationContext ?? '-')\n .replace('{{customInstructions}}', customInstructions ?? '-');\n\n const filePrefixText = `${ANSIColors.GREY_DARK}[${formatPath(baseFilePath)}${ANSIColors.GREY_DARK}] `;\n const filePrefix = [\n colon(filePrefixText, { colSize: 40 }),\n `→ ${ANSIColors.RESET}`,\n ].join('');\n const prefixText = `${ANSIColors.GREY_DARK}[${formatPath(baseFilePath)}${ANSIColors.GREY_DARK}][${formatLocale(locale)}${ANSIColors.GREY_DARK}] `;\n const prefix = [\n colon(prefixText, { colSize: 40 }),\n `→ ${ANSIColors.RESET}`,\n ].join('');\n\n // Build block-aware alignment and plan\n const { englishBlocks, frenchBlocks, plan, segmentsToReview } =\n buildAlignmentPlan({\n englishText,\n frenchText,\n changedLines,\n });\n\n applicationLogger(\n `${filePrefix}Block-aware alignment complete. Total blocks: EN=${colorizeNumber(englishBlocks.length)}, FR=${colorizeNumber(frenchBlocks.length)}`\n );\n applicationLogger(\n `${filePrefix}Actions: reuse=${colorizeNumber(plan.actions.filter((a) => a.kind === 'reuse').length)}, review=${colorizeNumber(plan.actions.filter((a) => a.kind === 'review').length)}, new=${colorizeNumber(plan.actions.filter((a) => a.kind === 'insert_new').length)}, delete=${colorizeNumber(plan.actions.filter((a) => a.kind === 'delete').length)}`\n );\n\n if (segmentsToReview.length === 0) {\n applicationLogger(\n `${filePrefix}No segments need review, reusing existing translation`\n );\n mkdirSync(dirname(outputFilePath), { recursive: true });\n writeFileSync(\n outputFilePath,\n mergeReviewedSegments(plan, frenchBlocks, new Map())\n );\n applicationLogger(\n `${colorize('✔', ANSIColors.GREEN)} File ${formatPath(outputFilePath)} updated successfully (no changes needed).`\n );\n return;\n }\n\n applicationLogger(\n `${filePrefix}Segments to review: ${colorizeNumber(segmentsToReview.length)}`\n );\n\n // Review segments that need AI translation\n const reviewedSegmentsMap = new Map<number, string>();\n\n for (const segment of segmentsToReview) {\n const segmentNumber = segmentsToReview.indexOf(segment) + 1;\n const englishBlock = segment.englishBlock;\n\n const getBaseChunkContextPrompt = () =>\n `**BLOCK ${segmentNumber} of ${segmentsToReview.length}** is the base block in ${formatLocale(baseLocale, false)} as reference.\\n` +\n `///chunksStart///\\n` +\n englishBlock.content +\n `///chunksEnd///`;\n\n const getFrenchChunkPrompt = () =>\n `**BLOCK ${segmentNumber} of ${segmentsToReview.length}** is the current block to review in ${formatLocale(locale, false)}.\\n` +\n `///chunksStart///\\n` +\n (segment.frenchBlockText ?? '') +\n `///chunksEnd///`;\n\n const reviewedChunkResult = await retryManager(async () => {\n const result = await chunkInference(\n [\n { role: 'system', content: basePrompt },\n { role: 'system', content: getBaseChunkContextPrompt() },\n { role: 'system', content: getFrenchChunkPrompt() },\n {\n role: 'system',\n content: `The next user message will be the **BLOCK ${colorizeNumber(segmentNumber)} of ${colorizeNumber(segmentsToReview.length)}** that should be translated in ${getLocaleName(locale, Locales.ENGLISH)} (${locale}).`,\n },\n { role: 'user', content: englishBlock.content },\n ],\n aiOptions,\n configuration,\n aiClient,\n aiConfig\n );\n\n applicationLogger(\n `${prefix}${colorizeNumber(result.tokenUsed)} tokens used - Block ${colorizeNumber(segmentNumber)} of ${colorizeNumber(segmentsToReview.length)}`\n );\n\n const fixed = fixChunkStartEndChars(\n result?.fileContent,\n englishBlock.content\n );\n return fixed;\n })();\n\n reviewedSegmentsMap.set(segment.actionIndex, reviewedChunkResult);\n }\n\n // Merge reviewed segments back into final document\n const finalFrenchOutput = mergeReviewedSegments(\n plan,\n frenchBlocks,\n reviewedSegmentsMap\n );\n\n mkdirSync(dirname(outputFilePath), { recursive: true });\n writeFileSync(outputFilePath, finalFrenchOutput);\n\n applicationLogger(\n `${colorize('✔', ANSIColors.GREEN)} File ${formatPath(outputFilePath)} created/updated successfully.`\n );\n};\n"],"mappings":"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"}