@intlayer/cli 8.4.4 → 8.4.6

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 (305) hide show
  1. package/dist/cjs/IntlayerEventListener.cjs +186 -1
  2. package/dist/cjs/IntlayerEventListener.cjs.map +1 -1
  3. package/dist/cjs/_virtual/_rolldown/runtime.cjs +29 -0
  4. package/dist/cjs/_virtual/_utils_asset.cjs +98 -0
  5. package/dist/cjs/auth/login.cjs +90 -2
  6. package/dist/cjs/auth/login.cjs.map +1 -1
  7. package/dist/cjs/build.cjs +30 -1
  8. package/dist/cjs/build.cjs.map +1 -1
  9. package/dist/cjs/ci.cjs +76 -1
  10. package/dist/cjs/ci.cjs.map +1 -1
  11. package/dist/cjs/cli.cjs +485 -1
  12. package/dist/cjs/cli.cjs.map +1 -1
  13. package/dist/cjs/config.cjs +15 -1
  14. package/dist/cjs/config.cjs.map +1 -1
  15. package/dist/cjs/editor.cjs +50 -1
  16. package/dist/cjs/editor.cjs.map +1 -1
  17. package/dist/cjs/extract.cjs +108 -1
  18. package/dist/cjs/extract.cjs.map +1 -1
  19. package/dist/cjs/fill/deepMergeContent.cjs +27 -1
  20. package/dist/cjs/fill/deepMergeContent.cjs.map +1 -1
  21. package/dist/cjs/fill/fill.cjs +89 -1
  22. package/dist/cjs/fill/fill.cjs.map +1 -1
  23. package/dist/cjs/fill/formatAutoFilledFilePath.cjs +32 -1
  24. package/dist/cjs/fill/formatAutoFilledFilePath.cjs.map +1 -1
  25. package/dist/cjs/fill/formatFillData.cjs +91 -1
  26. package/dist/cjs/fill/formatFillData.cjs.map +1 -1
  27. package/dist/cjs/fill/getAvailableLocalesInDictionary.cjs +26 -1
  28. package/dist/cjs/fill/getAvailableLocalesInDictionary.cjs.map +1 -1
  29. package/dist/cjs/fill/getFilterMissingContentPerLocale.cjs +51 -1
  30. package/dist/cjs/fill/getFilterMissingContentPerLocale.cjs.map +1 -1
  31. package/dist/cjs/fill/index.cjs +6 -1
  32. package/dist/cjs/fill/listTranslationsTasks.cjs +72 -1
  33. package/dist/cjs/fill/listTranslationsTasks.cjs.map +1 -1
  34. package/dist/cjs/fill/translateDictionary.cjs +241 -1
  35. package/dist/cjs/fill/translateDictionary.cjs.map +1 -1
  36. package/dist/cjs/fill/writeFill.cjs +53 -1
  37. package/dist/cjs/fill/writeFill.cjs.map +1 -1
  38. package/dist/cjs/getTargetDictionary.cjs +36 -1
  39. package/dist/cjs/getTargetDictionary.cjs.map +1 -1
  40. package/dist/cjs/index.cjs +44 -1
  41. package/dist/cjs/init.cjs +22 -1
  42. package/dist/cjs/init.cjs.map +1 -1
  43. package/dist/cjs/initMCP.cjs +69 -1
  44. package/dist/cjs/initMCP.cjs.map +1 -1
  45. package/dist/cjs/initSkills.cjs +93 -1
  46. package/dist/cjs/initSkills.cjs.map +1 -1
  47. package/dist/cjs/listContentDeclaration.cjs +42 -1
  48. package/dist/cjs/listContentDeclaration.cjs.map +1 -1
  49. package/dist/cjs/listProjects.cjs +28 -1
  50. package/dist/cjs/listProjects.cjs.map +1 -1
  51. package/dist/cjs/liveSync.cjs +153 -8
  52. package/dist/cjs/liveSync.cjs.map +1 -1
  53. package/dist/cjs/pull.cjs +153 -1
  54. package/dist/cjs/pull.cjs.map +1 -1
  55. package/dist/cjs/push/pullLog.cjs +105 -3
  56. package/dist/cjs/push/pullLog.cjs.map +1 -1
  57. package/dist/cjs/push/push.cjs +212 -1
  58. package/dist/cjs/push/push.cjs.map +1 -1
  59. package/dist/cjs/pushConfig.cjs +22 -1
  60. package/dist/cjs/pushConfig.cjs.map +1 -1
  61. package/dist/cjs/pushLog.cjs +86 -3
  62. package/dist/cjs/pushLog.cjs.map +1 -1
  63. package/dist/cjs/reviewDoc/reviewDoc.cjs +73 -1
  64. package/dist/cjs/reviewDoc/reviewDoc.cjs.map +1 -1
  65. package/dist/cjs/reviewDoc/reviewDocBlockAware.cjs +98 -1
  66. package/dist/cjs/reviewDoc/reviewDocBlockAware.cjs.map +1 -1
  67. package/dist/cjs/searchDoc.cjs +41 -1
  68. package/dist/cjs/searchDoc.cjs.map +1 -1
  69. package/dist/cjs/test/index.cjs +7 -1
  70. package/dist/cjs/test/listMissingTranslations.cjs +66 -1
  71. package/dist/cjs/test/listMissingTranslations.cjs.map +1 -1
  72. package/dist/cjs/test/test.cjs +56 -1
  73. package/dist/cjs/test/test.cjs.map +1 -1
  74. package/dist/cjs/translateDoc/index.cjs +9 -1
  75. package/dist/cjs/translateDoc/translateDoc.cjs +79 -1
  76. package/dist/cjs/translateDoc/translateDoc.cjs.map +1 -1
  77. package/dist/cjs/translateDoc/translateFile.cjs +106 -2
  78. package/dist/cjs/translateDoc/translateFile.cjs.map +1 -1
  79. package/dist/cjs/translateDoc/validation.cjs +49 -5
  80. package/dist/cjs/translateDoc/validation.cjs.map +1 -1
  81. package/dist/cjs/translation-alignment/alignBlocks.cjs +67 -1
  82. package/dist/cjs/translation-alignment/alignBlocks.cjs.map +1 -1
  83. package/dist/cjs/translation-alignment/computeSimilarity.cjs +25 -1
  84. package/dist/cjs/translation-alignment/computeSimilarity.cjs.map +1 -1
  85. package/dist/cjs/translation-alignment/fingerprintBlock.cjs +23 -1
  86. package/dist/cjs/translation-alignment/fingerprintBlock.cjs.map +1 -1
  87. package/dist/cjs/translation-alignment/index.cjs +22 -1
  88. package/dist/cjs/translation-alignment/mapChangedLinesToBlocks.cjs +18 -1
  89. package/dist/cjs/translation-alignment/mapChangedLinesToBlocks.cjs.map +1 -1
  90. package/dist/cjs/translation-alignment/normalizeBlock.cjs +22 -1
  91. package/dist/cjs/translation-alignment/normalizeBlock.cjs.map +1 -1
  92. package/dist/cjs/translation-alignment/pipeline.cjs +37 -1
  93. package/dist/cjs/translation-alignment/pipeline.cjs.map +1 -1
  94. package/dist/cjs/translation-alignment/planActions.cjs +46 -1
  95. package/dist/cjs/translation-alignment/planActions.cjs.map +1 -1
  96. package/dist/cjs/translation-alignment/rebuildDocument.cjs +49 -2
  97. package/dist/cjs/translation-alignment/rebuildDocument.cjs.map +1 -1
  98. package/dist/cjs/translation-alignment/segmentDocument.cjs +66 -5
  99. package/dist/cjs/translation-alignment/segmentDocument.cjs.map +1 -1
  100. package/dist/cjs/utils/calculateChunks.cjs +89 -2
  101. package/dist/cjs/utils/calculateChunks.cjs.map +1 -1
  102. package/dist/cjs/utils/checkAccess.cjs +83 -1
  103. package/dist/cjs/utils/checkAccess.cjs.map +1 -1
  104. package/dist/cjs/utils/checkConfigConsistency.cjs +16 -1
  105. package/dist/cjs/utils/checkConfigConsistency.cjs.map +1 -1
  106. package/dist/cjs/utils/checkFileModifiedRange.cjs +81 -1
  107. package/dist/cjs/utils/checkFileModifiedRange.cjs.map +1 -1
  108. package/dist/cjs/utils/checkLastUpdateTime.cjs +19 -1
  109. package/dist/cjs/utils/checkLastUpdateTime.cjs.map +1 -1
  110. package/dist/cjs/utils/chunkInference.cjs +45 -1
  111. package/dist/cjs/utils/chunkInference.cjs.map +1 -1
  112. package/dist/cjs/utils/fixChunkStartEndChars.cjs +27 -3
  113. package/dist/cjs/utils/fixChunkStartEndChars.cjs.map +1 -1
  114. package/dist/cjs/utils/formatTimeDiff.cjs +20 -1
  115. package/dist/cjs/utils/formatTimeDiff.cjs.map +1 -1
  116. package/dist/cjs/utils/getIsFileUpdatedRecently.cjs +16 -1
  117. package/dist/cjs/utils/getIsFileUpdatedRecently.cjs.map +1 -1
  118. package/dist/cjs/utils/getOutputFilePath.cjs +108 -1
  119. package/dist/cjs/utils/getOutputFilePath.cjs.map +1 -1
  120. package/dist/cjs/utils/getParentPackageJSON.cjs +20 -1
  121. package/dist/cjs/utils/getParentPackageJSON.cjs.map +1 -1
  122. package/dist/cjs/utils/listSpecialChars.cjs +54 -2
  123. package/dist/cjs/utils/listSpecialChars.cjs.map +1 -1
  124. package/dist/cjs/utils/mapChunksBetweenFiles.cjs +102 -1
  125. package/dist/cjs/utils/mapChunksBetweenFiles.cjs.map +1 -1
  126. package/dist/cjs/utils/openBrowser.cjs +19 -1
  127. package/dist/cjs/utils/openBrowser.cjs.map +1 -1
  128. package/dist/cjs/utils/reorderParagraphs.cjs +91 -3
  129. package/dist/cjs/utils/reorderParagraphs.cjs.map +1 -1
  130. package/dist/cjs/utils/setupAI.cjs +66 -1
  131. package/dist/cjs/utils/setupAI.cjs.map +1 -1
  132. package/dist/cjs/watch.cjs +47 -1
  133. package/dist/cjs/watch.cjs.map +1 -1
  134. package/dist/esm/IntlayerEventListener.mjs +183 -1
  135. package/dist/esm/IntlayerEventListener.mjs.map +1 -1
  136. package/dist/esm/_virtual/_rolldown/runtime.mjs +8 -0
  137. package/dist/esm/_virtual/_utils_asset.mjs +97 -0
  138. package/dist/esm/auth/login.mjs +86 -2
  139. package/dist/esm/auth/login.mjs.map +1 -1
  140. package/dist/esm/build.mjs +28 -1
  141. package/dist/esm/build.mjs.map +1 -1
  142. package/dist/esm/ci.mjs +74 -1
  143. package/dist/esm/ci.mjs.map +1 -1
  144. package/dist/esm/cli.mjs +482 -1
  145. package/dist/esm/cli.mjs.map +1 -1
  146. package/dist/esm/config.mjs +13 -1
  147. package/dist/esm/config.mjs.map +1 -1
  148. package/dist/esm/editor.mjs +50 -1
  149. package/dist/esm/editor.mjs.map +1 -0
  150. package/dist/esm/extract.mjs +104 -1
  151. package/dist/esm/extract.mjs.map +1 -1
  152. package/dist/esm/fill/deepMergeContent.mjs +25 -1
  153. package/dist/esm/fill/deepMergeContent.mjs.map +1 -1
  154. package/dist/esm/fill/fill.mjs +86 -1
  155. package/dist/esm/fill/fill.mjs.map +1 -1
  156. package/dist/esm/fill/formatAutoFilledFilePath.mjs +30 -1
  157. package/dist/esm/fill/formatAutoFilledFilePath.mjs.map +1 -1
  158. package/dist/esm/fill/formatFillData.mjs +89 -1
  159. package/dist/esm/fill/formatFillData.mjs.map +1 -1
  160. package/dist/esm/fill/getAvailableLocalesInDictionary.mjs +24 -1
  161. package/dist/esm/fill/getAvailableLocalesInDictionary.mjs.map +1 -1
  162. package/dist/esm/fill/getFilterMissingContentPerLocale.mjs +49 -1
  163. package/dist/esm/fill/getFilterMissingContentPerLocale.mjs.map +1 -1
  164. package/dist/esm/fill/index.mjs +4 -1
  165. package/dist/esm/fill/listTranslationsTasks.mjs +69 -1
  166. package/dist/esm/fill/listTranslationsTasks.mjs.map +1 -1
  167. package/dist/esm/fill/translateDictionary.mjs +238 -1
  168. package/dist/esm/fill/translateDictionary.mjs.map +1 -1
  169. package/dist/esm/fill/writeFill.mjs +51 -1
  170. package/dist/esm/fill/writeFill.mjs.map +1 -1
  171. package/dist/esm/getTargetDictionary.mjs +33 -1
  172. package/dist/esm/getTargetDictionary.mjs.map +1 -1
  173. package/dist/esm/index.mjs +20 -1
  174. package/dist/esm/init.mjs +19 -1
  175. package/dist/esm/init.mjs.map +1 -1
  176. package/dist/esm/initMCP.mjs +65 -1
  177. package/dist/esm/initMCP.mjs.map +1 -1
  178. package/dist/esm/initSkills.mjs +87 -1
  179. package/dist/esm/initSkills.mjs.map +1 -1
  180. package/dist/esm/listContentDeclaration.mjs +39 -1
  181. package/dist/esm/listContentDeclaration.mjs.map +1 -1
  182. package/dist/esm/listProjects.mjs +26 -1
  183. package/dist/esm/listProjects.mjs.map +1 -1
  184. package/dist/esm/liveSync.mjs +150 -8
  185. package/dist/esm/liveSync.mjs.map +1 -1
  186. package/dist/esm/pull.mjs +150 -1
  187. package/dist/esm/pull.mjs.map +1 -1
  188. package/dist/esm/push/pullLog.mjs +102 -3
  189. package/dist/esm/push/pullLog.mjs.map +1 -1
  190. package/dist/esm/push/push.mjs +208 -1
  191. package/dist/esm/push/push.mjs.map +1 -1
  192. package/dist/esm/pushConfig.mjs +20 -1
  193. package/dist/esm/pushConfig.mjs.map +1 -1
  194. package/dist/esm/pushLog.mjs +83 -3
  195. package/dist/esm/pushLog.mjs.map +1 -1
  196. package/dist/esm/reviewDoc/reviewDoc.mjs +69 -1
  197. package/dist/esm/reviewDoc/reviewDoc.mjs.map +1 -1
  198. package/dist/esm/reviewDoc/reviewDocBlockAware.mjs +95 -1
  199. package/dist/esm/reviewDoc/reviewDocBlockAware.mjs.map +1 -1
  200. package/dist/esm/searchDoc.mjs +39 -1
  201. package/dist/esm/searchDoc.mjs.map +1 -1
  202. package/dist/esm/test/index.mjs +4 -1
  203. package/dist/esm/test/listMissingTranslations.mjs +63 -1
  204. package/dist/esm/test/listMissingTranslations.mjs.map +1 -1
  205. package/dist/esm/test/test.mjs +53 -1
  206. package/dist/esm/test/test.mjs.map +1 -1
  207. package/dist/esm/translateDoc/index.mjs +5 -1
  208. package/dist/esm/translateDoc/translateDoc.mjs +75 -1
  209. package/dist/esm/translateDoc/translateDoc.mjs.map +1 -1
  210. package/dist/esm/translateDoc/translateFile.mjs +103 -2
  211. package/dist/esm/translateDoc/translateFile.mjs.map +1 -1
  212. package/dist/esm/translateDoc/validation.mjs +46 -5
  213. package/dist/esm/translateDoc/validation.mjs.map +1 -1
  214. package/dist/esm/translation-alignment/alignBlocks.mjs +66 -1
  215. package/dist/esm/translation-alignment/alignBlocks.mjs.map +1 -1
  216. package/dist/esm/translation-alignment/computeSimilarity.mjs +22 -1
  217. package/dist/esm/translation-alignment/computeSimilarity.mjs.map +1 -1
  218. package/dist/esm/translation-alignment/fingerprintBlock.mjs +20 -1
  219. package/dist/esm/translation-alignment/fingerprintBlock.mjs.map +1 -1
  220. package/dist/esm/translation-alignment/index.mjs +11 -1
  221. package/dist/esm/translation-alignment/mapChangedLinesToBlocks.mjs +16 -1
  222. package/dist/esm/translation-alignment/mapChangedLinesToBlocks.mjs.map +1 -1
  223. package/dist/esm/translation-alignment/normalizeBlock.mjs +20 -1
  224. package/dist/esm/translation-alignment/normalizeBlock.mjs.map +1 -1
  225. package/dist/esm/translation-alignment/pipeline.mjs +35 -1
  226. package/dist/esm/translation-alignment/pipeline.mjs.map +1 -1
  227. package/dist/esm/translation-alignment/planActions.mjs +44 -1
  228. package/dist/esm/translation-alignment/planActions.mjs.map +1 -1
  229. package/dist/esm/translation-alignment/rebuildDocument.mjs +46 -2
  230. package/dist/esm/translation-alignment/rebuildDocument.mjs.map +1 -1
  231. package/dist/esm/translation-alignment/segmentDocument.mjs +64 -5
  232. package/dist/esm/translation-alignment/segmentDocument.mjs.map +1 -1
  233. package/dist/esm/utils/calculateChunks.mjs +87 -2
  234. package/dist/esm/utils/calculateChunks.mjs.map +1 -1
  235. package/dist/esm/utils/checkAccess.mjs +79 -1
  236. package/dist/esm/utils/checkAccess.mjs.map +1 -1
  237. package/dist/esm/utils/checkConfigConsistency.mjs +14 -1
  238. package/dist/esm/utils/checkConfigConsistency.mjs.map +1 -1
  239. package/dist/esm/utils/checkFileModifiedRange.mjs +80 -1
  240. package/dist/esm/utils/checkFileModifiedRange.mjs.map +1 -1
  241. package/dist/esm/utils/checkLastUpdateTime.mjs +17 -1
  242. package/dist/esm/utils/checkLastUpdateTime.mjs.map +1 -1
  243. package/dist/esm/utils/chunkInference.mjs +43 -1
  244. package/dist/esm/utils/chunkInference.mjs.map +1 -1
  245. package/dist/esm/utils/fixChunkStartEndChars.mjs +25 -3
  246. package/dist/esm/utils/fixChunkStartEndChars.mjs.map +1 -1
  247. package/dist/esm/utils/formatTimeDiff.mjs +18 -1
  248. package/dist/esm/utils/formatTimeDiff.mjs.map +1 -1
  249. package/dist/esm/utils/getIsFileUpdatedRecently.mjs +14 -1
  250. package/dist/esm/utils/getIsFileUpdatedRecently.mjs.map +1 -1
  251. package/dist/esm/utils/getOutputFilePath.mjs +104 -1
  252. package/dist/esm/utils/getOutputFilePath.mjs.map +1 -1
  253. package/dist/esm/utils/getParentPackageJSON.mjs +18 -1
  254. package/dist/esm/utils/getParentPackageJSON.mjs.map +1 -1
  255. package/dist/esm/utils/listSpecialChars.mjs +52 -2
  256. package/dist/esm/utils/listSpecialChars.mjs.map +1 -1
  257. package/dist/esm/utils/mapChunksBetweenFiles.mjs +100 -1
  258. package/dist/esm/utils/mapChunksBetweenFiles.mjs.map +1 -1
  259. package/dist/esm/utils/openBrowser.mjs +17 -1
  260. package/dist/esm/utils/openBrowser.mjs.map +1 -1
  261. package/dist/esm/utils/reorderParagraphs.mjs +90 -3
  262. package/dist/esm/utils/reorderParagraphs.mjs.map +1 -1
  263. package/dist/esm/utils/setupAI.mjs +63 -1
  264. package/dist/esm/utils/setupAI.mjs.map +1 -1
  265. package/dist/esm/watch.mjs +45 -1
  266. package/dist/esm/watch.mjs.map +1 -1
  267. package/dist/types/fill/fill.d.ts +1 -1
  268. package/dist/types/fill/fill.d.ts.map +1 -1
  269. package/dist/types/fill/formatFillData.d.ts.map +1 -1
  270. package/dist/types/fill/translateDictionary.d.ts +1 -1
  271. package/dist/types/getTargetDictionary.d.ts +19 -2
  272. package/dist/types/getTargetDictionary.d.ts.map +1 -0
  273. package/dist/types/index.d.ts +2 -2
  274. package/dist/types/reviewDoc/reviewDocBlockAware.d.ts +1 -1
  275. package/dist/types/test/index.d.ts +2 -2
  276. package/dist/types/test/listMissingTranslations.d.ts +28 -2
  277. package/dist/types/test/listMissingTranslations.d.ts.map +1 -0
  278. package/dist/types/test/test.d.ts +11 -2
  279. package/dist/types/test/test.d.ts.map +1 -0
  280. package/dist/types/translateDoc/index.d.ts +1 -1
  281. package/dist/types/translateDoc/translateDoc.d.ts +1 -1
  282. package/dist/types/translateDoc/translateFile.d.ts +1 -1
  283. package/dist/types/translateDoc/types.d.ts +48 -2
  284. package/dist/types/translateDoc/types.d.ts.map +1 -0
  285. package/dist/types/utils/chunkInference.d.ts +1 -1
  286. package/dist/types/utils/getOutputFilePath.d.ts +18 -1
  287. package/dist/types/utils/getOutputFilePath.d.ts.map +1 -1
  288. package/dist/types/utils/setupAI.d.ts +21 -2
  289. package/dist/types/utils/setupAI.d.ts.map +1 -0
  290. package/package.json +12 -12
  291. package/dist/cjs/_utils_asset-ghp_Cjwk.cjs +0 -2
  292. package/dist/cjs/chunk-Bmb41Sf3.cjs +0 -1
  293. package/dist/esm/_utils_asset-B187VPMw.mjs +0 -2
  294. package/dist/esm/editor-D8BGlLzF.mjs +0 -2
  295. package/dist/esm/editor-D8BGlLzF.mjs.map +0 -1
  296. package/dist/types/getTargetDictionary-RBSRtaQj.d.ts +0 -19
  297. package/dist/types/getTargetDictionary-RBSRtaQj.d.ts.map +0 -1
  298. package/dist/types/listMissingTranslations-DxKw7nqI.d.ts +0 -28
  299. package/dist/types/listMissingTranslations-DxKw7nqI.d.ts.map +0 -1
  300. package/dist/types/setupAI-Bosjx7ah.d.ts +0 -21
  301. package/dist/types/setupAI-Bosjx7ah.d.ts.map +0 -1
  302. package/dist/types/test-DUTiJR5_.d.ts +0 -11
  303. package/dist/types/test-DUTiJR5_.d.ts.map +0 -1
  304. package/dist/types/types-BKvc3FmV.d.ts +0 -48
  305. package/dist/types/types-BKvc3FmV.d.ts.map +0 -1
@@ -1,2 +1,239 @@
1
- import{deepMergeContent as e}from"./deepMergeContent.mjs";import{getFilterMissingContentPerLocale as t}from"./getFilterMissingContentPerLocale.mjs";import{basename as n}from"node:path";import{chunkJSON as r,formatLocale as i,mergeChunks as a,reconstructFromSingleChunk as o,reduceObjectFormat as s,verifyIdenticObjectFormat as c}from"@intlayer/chokidar/utils";import*as l from"@intlayer/config/colors";import{colon as u,colorize as d,colorizeNumber as f,colorizePath as p,getAppLogger as m}from"@intlayer/config/logger";import{getUnmergedDictionaries as h}from"@intlayer/unmerged-dictionaries-entry";import{getFilterMissingTranslationsDictionary as g,getMultilingualDictionary as _,getPerLocaleDictionary as v,insertContentInDictionary as y}from"@intlayer/core/plugins";import{getIntlayerAPIProxy as b}from"@intlayer/api";import{retryManager as x}from"@intlayer/config/utils";const S=e=>!e.description||!e.title||!e.tags,C=e=>{if(typeof e!=`object`||!e||Array.isArray(e))return{content:e,nulls:void 0,hasNulls:!1};let t={},n={},r=!1;for(let[i,a]of Object.entries(e))if(a===null)n[i]=null,r=!0;else{let e=C(a);t[i]=e.content,e.hasNulls&&(n[i]=e.nulls,r=!0)}return{content:t,nulls:r?n:void 0,hasNulls:r}},w=1e3*10;let T=0;const E=async(E,D,O)=>{let k=m(D),A=b(void 0,D),{mode:j,aiOptions:M,fillMetadata:N,aiClient:P,aiConfig:F}={mode:`complete`,fillMetadata:!0,...O},I=()=>{T=0,O?.onSuccess?.()};return await x(async()=>{let m=h(D),b=m[E.dictionaryKey].find(e=>e.localId===E.dictionaryLocalId);if(!b)return k(`${E.dictionaryPreset}Dictionary not found in unmergedDictionariesRecord. Skipping.`,{level:`warn`}),{...E,dictionaryOutput:null};let L;if(N&&(S(b)||j===`review`)){let e=v(b,D.internationalization.defaultLocale);k(`${E.dictionaryPreset} Filling missing metadata for ${p(n(b.filePath))}`,{level:`info`});let t=async()=>P&&F?{data:await P.auditDictionaryMetadata({fileContent:JSON.stringify(e),aiConfig:F})}:await A.ai.auditContentDeclarationMetadata({fileContent:JSON.stringify(e),aiOptions:M});L=(O?.onHandle?await O.onHandle(t):await t()).data?.fileContent}let R=await Promise.all(E.targetLocales.map(async h=>{let _=structuredClone(b),y;if(typeof b.locale==`string`){let e=b.filePath?.replace(RegExp(`/${E.sourceLocale}/`,`g`),`/${h}/`),n=e?m[E.dictionaryKey]?.find(t=>t.filePath===e&&t.locale===h):void 0;y=n??{key:b.key,content:{},filePath:e,locale:h},j===`complete`&&(_=t(_,n))}else j===`complete`&&(_=g(_,h)),_=v(_,E.sourceLocale),y=v(b,h);let S=u([d(`[`,l.GREY_DARK),i(h),d(`]`,l.GREY_DARK)].join(``),{colSize:18}),D=(e,t)=>t<=1?``:u([d(`[`,l.GREY_DARK),f(e+1),d(`/${t}`,l.GREY_DARK),d(`]`,l.GREY_DARK)].join(``),{colSize:5});k(`${E.dictionaryPreset}${S} Preparing ${p(n(y.filePath))}`,{level:`info`});let N=typeof _.content==`object`&&_.content!==null||Array.isArray(_.content),{content:R,nulls:z}=C(N?_.content:{__INTLAYER_ROOT_PRIMITIVE_CONTENT__:_.content}),B=r(R,7e3),V=B.length;V>1&&k(`${E.dictionaryPreset}${S} Split into ${f(V)} chunks for translation`,{level:`info`});let H=[],U=B.map(e=>{let t=D(e.index,e.total);V>1&&k(`${E.dictionaryPreset}${S}${t} Translating chunk`,{level:`info`});let n=o(e),r=s(N?y.content:{__INTLAYER_ROOT_PRIMITIVE_CONTENT__:y.content},n),i=async()=>await x(async()=>{let e;if(e=P&&F?await P.translateJSON({entryFileContent:n,presetOutputContent:r,dictionaryDescription:_.description??L?.description??``,entryLocale:E.sourceLocale,outputLocale:h,mode:j,aiConfig:F}):await A.ai.translateJSON({entryFileContent:n,presetOutputContent:r,dictionaryDescription:_.description??L?.description??``,entryLocale:E.sourceLocale,outputLocale:h,mode:j,aiOptions:M}).then(e=>e.data),!e?.fileContent)throw Error(`No content result`);let{isIdentic:t}=c(e.fileContent,n);if(!t)throw Error(`Translation result does not match expected format`);return I(),e.fileContent},{maxRetry:3,delay:w,onError:({error:t,attempt:n,maxRetry:r})=>{let i=D(e.index,e.total);k(`${E.dictionaryPreset}${S}${i} ${d(`Error filling:`,l.RED)} ${d(typeof t==`string`?t:JSON.stringify(t),l.GREY_DARK)} - Attempt ${f(n+1)} of ${f(r)}`,{level:`error`}),T+=1,T>=10&&(k(`There is something wrong.`,{level:`error`}),process.exit(1))}})();return(O?.onHandle?O.onHandle(i):i()).then(t=>({chunk:e,result:t}))});(await Promise.all(U)).sort((e,t)=>e.chunk.index-t.chunk.index).forEach(({result:e})=>{H.push(e)});let W=a(H);z&&(W=e(W,z));let G={..._,content:W}.content;return N||(G=G?.__INTLAYER_ROOT_PRIMITIVE_CONTENT__),typeof b.locale==`string`&&(G=e(y.content??{},G)),[h,G]})),z=Object.fromEntries(R),B={..._(b.locale?{...b,key:b.key,content:{}}:b),locale:void 0,...L};for(let e of E.targetLocales)z[e]&&(B=y(B,z[e],e));if(k(`${E.dictionaryPreset} ${d(`Translation completed successfully`,l.GREEN)} for ${p(n(B.filePath))}`,{level:`info`}),b.locale&&(b.fill===!0||b.fill===void 0)&&b.location===`local`){let e=b.filePath.split(`.`).slice(0,-1),t=e[e.length-1];return JSON.parse(JSON.stringify({...E,dictionaryOutput:{...B,fill:void 0,filled:!0}}).replaceAll(RegExp(`\\.${t}\\.[a-zA-Z0-9]+`,`g`),`.filled.${t}.json`))}return{...E,dictionaryOutput:B}},{maxRetry:2,delay:w,onError:({error:e,attempt:t,maxRetry:n})=>k(`${E.dictionaryPreset} ${d(`Error:`,l.RED)} ${d(typeof e==`string`?e:JSON.stringify(e),l.GREY_DARK)} - Attempt ${f(t+1)} of ${f(n)}`,{level:`error`}),onMaxTryReached:({error:e})=>k(`${E.dictionaryPreset} ${d(`Maximum number of retries reached:`,l.RED)} ${d(typeof e==`string`?e:JSON.stringify(e),l.GREY_DARK)}`,{level:`error`})})()};export{E as translateDictionary};
1
+ import { deepMergeContent } from "./deepMergeContent.mjs";
2
+ import { getFilterMissingContentPerLocale } from "./getFilterMissingContentPerLocale.mjs";
3
+ import { basename } from "node:path";
4
+ import { chunkJSON, formatLocale, mergeChunks, reconstructFromSingleChunk, reduceObjectFormat, verifyIdenticObjectFormat } from "@intlayer/chokidar/utils";
5
+ import * as ANSIColors from "@intlayer/config/colors";
6
+ import { colon, colorize, colorizeNumber, colorizePath, getAppLogger } from "@intlayer/config/logger";
7
+ import { getUnmergedDictionaries } from "@intlayer/unmerged-dictionaries-entry";
8
+ import { getFilterMissingTranslationsDictionary, getMultilingualDictionary, getPerLocaleDictionary, insertContentInDictionary } from "@intlayer/core/plugins";
9
+ import { getIntlayerAPIProxy } from "@intlayer/api";
10
+ import { retryManager } from "@intlayer/config/utils";
11
+
12
+ //#region src/fill/translateDictionary.ts
13
+ const hasMissingMetadata = (dictionary) => !dictionary.description || !dictionary.title || !dictionary.tags;
14
+ /**
15
+ * Recursively strips null values from an object, returning the cleaned content
16
+ * and a separate object containing only the null-valued paths so they can be
17
+ * re-injected after AI translation (nulls don't need translation).
18
+ */
19
+ const stripNullValues = (obj) => {
20
+ if (typeof obj !== "object" || obj === null || Array.isArray(obj)) return {
21
+ content: obj,
22
+ nulls: void 0,
23
+ hasNulls: false
24
+ };
25
+ const content = {};
26
+ const nulls = {};
27
+ let hasNulls = false;
28
+ for (const [key, value] of Object.entries(obj)) if (value === null) {
29
+ nulls[key] = null;
30
+ hasNulls = true;
31
+ } else {
32
+ const child = stripNullValues(value);
33
+ content[key] = child.content;
34
+ if (child.hasNulls) {
35
+ nulls[key] = child.nulls;
36
+ hasNulls = true;
37
+ }
38
+ }
39
+ return {
40
+ content,
41
+ nulls: hasNulls ? nulls : void 0,
42
+ hasNulls
43
+ };
44
+ };
45
+ const CHUNK_SIZE = 7e3;
46
+ const GROUP_MAX_RETRY = 2;
47
+ const MAX_RETRY = 3;
48
+ const RETRY_DELAY = 1e3 * 10;
49
+ const MAX_FOLLOWING_ERRORS = 10;
50
+ let followingErrors = 0;
51
+ const translateDictionary = async (task, configuration, options) => {
52
+ const appLogger = getAppLogger(configuration);
53
+ const intlayerAPI = getIntlayerAPIProxy(void 0, configuration);
54
+ const { mode, aiOptions, fillMetadata, aiClient, aiConfig } = {
55
+ mode: "complete",
56
+ fillMetadata: true,
57
+ ...options
58
+ };
59
+ const notifySuccess = () => {
60
+ followingErrors = 0;
61
+ options?.onSuccess?.();
62
+ };
63
+ return await retryManager(async () => {
64
+ const unmergedDictionariesRecord = getUnmergedDictionaries(configuration);
65
+ const baseUnmergedDictionary = unmergedDictionariesRecord[task.dictionaryKey].find((dict) => dict.localId === task.dictionaryLocalId);
66
+ if (!baseUnmergedDictionary) {
67
+ appLogger(`${task.dictionaryPreset}Dictionary not found in unmergedDictionariesRecord. Skipping.`, { level: "warn" });
68
+ return {
69
+ ...task,
70
+ dictionaryOutput: null
71
+ };
72
+ }
73
+ let metadata;
74
+ if (fillMetadata && (hasMissingMetadata(baseUnmergedDictionary) || mode === "review")) {
75
+ const defaultLocaleDictionary = getPerLocaleDictionary(baseUnmergedDictionary, configuration.internationalization.defaultLocale);
76
+ appLogger(`${task.dictionaryPreset} Filling missing metadata for ${colorizePath(basename(baseUnmergedDictionary.filePath))}`, { level: "info" });
77
+ const runAudit = async () => {
78
+ if (aiClient && aiConfig) return { data: await aiClient.auditDictionaryMetadata({
79
+ fileContent: JSON.stringify(defaultLocaleDictionary),
80
+ aiConfig
81
+ }) };
82
+ return await intlayerAPI.ai.auditContentDeclarationMetadata({
83
+ fileContent: JSON.stringify(defaultLocaleDictionary),
84
+ aiOptions
85
+ });
86
+ };
87
+ metadata = (options?.onHandle ? await options.onHandle(runAudit) : await runAudit()).data?.fileContent;
88
+ }
89
+ const translatedContentResults = await Promise.all(task.targetLocales.map(async (targetLocale) => {
90
+ /**
91
+ * In complete mode, for large dictionaries, we want to filter all content that is already translated
92
+ *
93
+ * targetLocale: fr
94
+ *
95
+ * { test1: t({ ar: 'Hello', en: 'Hello', fr: 'Bonjour' } }) -> {}
96
+ * { test2: t({ ar: 'Hello', en: 'Hello' }) } -> { test2: t({ ar: 'Hello', en: 'Hello' }) }
97
+ *
98
+ */
99
+ let dictionaryToProcess = structuredClone(baseUnmergedDictionary);
100
+ let targetLocaleDictionary;
101
+ if (typeof baseUnmergedDictionary.locale === "string") {
102
+ const targetLocaleFilePath = baseUnmergedDictionary.filePath?.replace(new RegExp(`/${task.sourceLocale}/`, "g"), `/${targetLocale}/`);
103
+ const targetUnmergedDictionary = targetLocaleFilePath ? unmergedDictionariesRecord[task.dictionaryKey]?.find((dict) => dict.filePath === targetLocaleFilePath && dict.locale === targetLocale) : void 0;
104
+ targetLocaleDictionary = targetUnmergedDictionary ?? {
105
+ key: baseUnmergedDictionary.key,
106
+ content: {},
107
+ filePath: targetLocaleFilePath,
108
+ locale: targetLocale
109
+ };
110
+ if (mode === "complete") dictionaryToProcess = getFilterMissingContentPerLocale(dictionaryToProcess, targetUnmergedDictionary);
111
+ } else {
112
+ if (mode === "complete") dictionaryToProcess = getFilterMissingTranslationsDictionary(dictionaryToProcess, targetLocale);
113
+ dictionaryToProcess = getPerLocaleDictionary(dictionaryToProcess, task.sourceLocale);
114
+ targetLocaleDictionary = getPerLocaleDictionary(baseUnmergedDictionary, targetLocale);
115
+ }
116
+ const localePreset = colon([
117
+ colorize("[", ANSIColors.GREY_DARK),
118
+ formatLocale(targetLocale),
119
+ colorize("]", ANSIColors.GREY_DARK)
120
+ ].join(""), { colSize: 18 });
121
+ const createChunkPreset = (chunkIndex, totalChunks) => {
122
+ if (totalChunks <= 1) return "";
123
+ return colon([
124
+ colorize("[", ANSIColors.GREY_DARK),
125
+ colorizeNumber(chunkIndex + 1),
126
+ colorize(`/${totalChunks}`, ANSIColors.GREY_DARK),
127
+ colorize("]", ANSIColors.GREY_DARK)
128
+ ].join(""), { colSize: 5 });
129
+ };
130
+ appLogger(`${task.dictionaryPreset}${localePreset} Preparing ${colorizePath(basename(targetLocaleDictionary.filePath))}`, { level: "info" });
131
+ const isContentStructured = typeof dictionaryToProcess.content === "object" && dictionaryToProcess.content !== null || Array.isArray(dictionaryToProcess.content);
132
+ const { content: contentToProcess, nulls: strippedNullValues } = stripNullValues(isContentStructured ? dictionaryToProcess.content : { __INTLAYER_ROOT_PRIMITIVE_CONTENT__: dictionaryToProcess.content });
133
+ const chunkedJsonContent = chunkJSON(contentToProcess, CHUNK_SIZE);
134
+ const nbOfChunks = chunkedJsonContent.length;
135
+ if (nbOfChunks > 1) appLogger(`${task.dictionaryPreset}${localePreset} Split into ${colorizeNumber(nbOfChunks)} chunks for translation`, { level: "info" });
136
+ const chunkResult = [];
137
+ const chunkPromises = chunkedJsonContent.map((chunk) => {
138
+ const chunkPreset = createChunkPreset(chunk.index, chunk.total);
139
+ if (nbOfChunks > 1) appLogger(`${task.dictionaryPreset}${localePreset}${chunkPreset} Translating chunk`, { level: "info" });
140
+ const chunkContent = reconstructFromSingleChunk(chunk);
141
+ const presetOutputContent = reduceObjectFormat(isContentStructured ? targetLocaleDictionary.content : { __INTLAYER_ROOT_PRIMITIVE_CONTENT__: targetLocaleDictionary.content }, chunkContent);
142
+ const executeTranslation = async () => {
143
+ return await retryManager(async () => {
144
+ let translationResult;
145
+ if (aiClient && aiConfig) translationResult = await aiClient.translateJSON({
146
+ entryFileContent: chunkContent,
147
+ presetOutputContent,
148
+ dictionaryDescription: dictionaryToProcess.description ?? metadata?.description ?? "",
149
+ entryLocale: task.sourceLocale,
150
+ outputLocale: targetLocale,
151
+ mode,
152
+ aiConfig
153
+ });
154
+ else translationResult = await intlayerAPI.ai.translateJSON({
155
+ entryFileContent: chunkContent,
156
+ presetOutputContent,
157
+ dictionaryDescription: dictionaryToProcess.description ?? metadata?.description ?? "",
158
+ entryLocale: task.sourceLocale,
159
+ outputLocale: targetLocale,
160
+ mode,
161
+ aiOptions
162
+ }).then((result) => result.data);
163
+ if (!translationResult?.fileContent) throw new Error("No content result");
164
+ const { isIdentic } = verifyIdenticObjectFormat(translationResult.fileContent, chunkContent);
165
+ if (!isIdentic) throw new Error("Translation result does not match expected format");
166
+ notifySuccess();
167
+ return translationResult.fileContent;
168
+ }, {
169
+ maxRetry: MAX_RETRY,
170
+ delay: RETRY_DELAY,
171
+ onError: ({ error, attempt, maxRetry }) => {
172
+ const chunkPreset = createChunkPreset(chunk.index, chunk.total);
173
+ appLogger(`${task.dictionaryPreset}${localePreset}${chunkPreset} ${colorize("Error filling:", ANSIColors.RED)} ${colorize(typeof error === "string" ? error : JSON.stringify(error), ANSIColors.GREY_DARK)} - Attempt ${colorizeNumber(attempt + 1)} of ${colorizeNumber(maxRetry)}`, { level: "error" });
174
+ followingErrors += 1;
175
+ if (followingErrors >= MAX_FOLLOWING_ERRORS) {
176
+ appLogger(`There is something wrong.`, { level: "error" });
177
+ process.exit(1);
178
+ }
179
+ }
180
+ })();
181
+ };
182
+ return (options?.onHandle ? options.onHandle(executeTranslation) : executeTranslation()).then((result) => ({
183
+ chunk,
184
+ result
185
+ }));
186
+ });
187
+ (await Promise.all(chunkPromises)).sort((chunkA, chunkB) => chunkA.chunk.index - chunkB.chunk.index).forEach(({ result }) => {
188
+ chunkResult.push(result);
189
+ });
190
+ let mergedContent = mergeChunks(chunkResult);
191
+ if (strippedNullValues) mergedContent = deepMergeContent(mergedContent, strippedNullValues);
192
+ let finalContent = {
193
+ ...dictionaryToProcess,
194
+ content: mergedContent
195
+ }.content;
196
+ if (!isContentStructured) finalContent = finalContent?.__INTLAYER_ROOT_PRIMITIVE_CONTENT__;
197
+ if (typeof baseUnmergedDictionary.locale === "string") finalContent = deepMergeContent(targetLocaleDictionary.content ?? {}, finalContent);
198
+ return [targetLocale, finalContent];
199
+ }));
200
+ const translatedContent = Object.fromEntries(translatedContentResults);
201
+ let dictionaryOutput = {
202
+ ...getMultilingualDictionary(baseUnmergedDictionary.locale ? {
203
+ ...baseUnmergedDictionary,
204
+ key: baseUnmergedDictionary.key,
205
+ content: {}
206
+ } : baseUnmergedDictionary),
207
+ locale: void 0,
208
+ ...metadata
209
+ };
210
+ for (const targetLocale of task.targetLocales) if (translatedContent[targetLocale]) dictionaryOutput = insertContentInDictionary(dictionaryOutput, translatedContent[targetLocale], targetLocale);
211
+ appLogger(`${task.dictionaryPreset} ${colorize("Translation completed successfully", ANSIColors.GREEN)} for ${colorizePath(basename(dictionaryOutput.filePath))}`, { level: "info" });
212
+ const effectiveFillForCheck = baseUnmergedDictionary.fill ?? configuration.dictionary?.fill;
213
+ if (baseUnmergedDictionary.locale && (effectiveFillForCheck === true || effectiveFillForCheck === void 0) && baseUnmergedDictionary.location === "local") {
214
+ const dictionaryFilePath = baseUnmergedDictionary.filePath.split(".").slice(0, -1);
215
+ const contentIndex = dictionaryFilePath[dictionaryFilePath.length - 1];
216
+ return JSON.parse(JSON.stringify({
217
+ ...task,
218
+ dictionaryOutput: {
219
+ ...dictionaryOutput,
220
+ fill: void 0,
221
+ filled: true
222
+ }
223
+ }).replaceAll(new RegExp(`\\.${contentIndex}\\.[a-zA-Z0-9]+`, "g"), `.filled.${contentIndex}.json`));
224
+ }
225
+ return {
226
+ ...task,
227
+ dictionaryOutput
228
+ };
229
+ }, {
230
+ maxRetry: GROUP_MAX_RETRY,
231
+ delay: RETRY_DELAY,
232
+ onError: ({ error, attempt, maxRetry }) => appLogger(`${task.dictionaryPreset} ${colorize("Error:", ANSIColors.RED)} ${colorize(typeof error === "string" ? error : JSON.stringify(error), ANSIColors.GREY_DARK)} - Attempt ${colorizeNumber(attempt + 1)} of ${colorizeNumber(maxRetry)}`, { level: "error" }),
233
+ onMaxTryReached: ({ error }) => appLogger(`${task.dictionaryPreset} ${colorize("Maximum number of retries reached:", ANSIColors.RED)} ${colorize(typeof error === "string" ? error : JSON.stringify(error), ANSIColors.GREY_DARK)}`, { level: "error" })
234
+ })();
235
+ };
236
+
237
+ //#endregion
238
+ export { translateDictionary };
2
239
  //# sourceMappingURL=translateDictionary.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"translateDictionary.mjs","names":[],"sources":["../../../src/fill/translateDictionary.ts"],"sourcesContent":["import { basename } from 'node:path';\nimport type { AIConfig } from '@intlayer/ai';\nimport { type AIOptions, getIntlayerAPIProxy } from '@intlayer/api';\nimport {\n chunkJSON,\n formatLocale,\n type JsonChunk,\n mergeChunks,\n reconstructFromSingleChunk,\n reduceObjectFormat,\n verifyIdenticObjectFormat,\n} from '@intlayer/chokidar/utils';\nimport * as ANSIColors from '@intlayer/config/colors';\nimport {\n colon,\n colorize,\n colorizeNumber,\n colorizePath,\n getAppLogger,\n} from '@intlayer/config/logger';\nimport { retryManager } from '@intlayer/config/utils';\nimport {\n getFilterMissingTranslationsDictionary,\n getMultilingualDictionary,\n getPerLocaleDictionary,\n insertContentInDictionary,\n} from '@intlayer/core/plugins';\nimport type { Locale } from '@intlayer/types/allLocales';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport type { Dictionary } from '@intlayer/types/dictionary';\nimport { getUnmergedDictionaries } from '@intlayer/unmerged-dictionaries-entry';\nimport type { AIClient } from '../utils/setupAI';\nimport { deepMergeContent } from './deepMergeContent';\nimport { getFilterMissingContentPerLocale } from './getFilterMissingContentPerLocale';\nimport type { TranslationTask } from './listTranslationsTasks';\n\ntype TranslateDictionaryResult = TranslationTask & {\n dictionaryOutput: Dictionary | null;\n};\n\ntype TranslateDictionaryOptions = {\n mode: 'complete' | 'review';\n aiOptions?: AIOptions;\n fillMetadata?: boolean;\n onHandle?: ReturnType<\n typeof import('@intlayer/chokidar/utils').getGlobalLimiter\n >;\n onSuccess?: () => void;\n onError?: (error: unknown) => void;\n getAbortError?: () => Error | null;\n aiClient?: AIClient;\n aiConfig?: AIConfig;\n};\n\nconst hasMissingMetadata = (dictionary: Dictionary) =>\n !dictionary.description || !dictionary.title || !dictionary.tags;\n\n/**\n * Recursively strips null values from an object, returning the cleaned content\n * and a separate object containing only the null-valued paths so they can be\n * re-injected after AI translation (nulls don't need translation).\n */\nconst stripNullValues = (\n obj: any\n): { content: any; nulls: any; hasNulls: boolean } => {\n if (typeof obj !== 'object' || obj === null || Array.isArray(obj)) {\n return { content: obj, nulls: undefined, hasNulls: false };\n }\n\n const content: any = {};\n const nulls: any = {};\n let hasNulls = false;\n\n for (const [key, value] of Object.entries(obj)) {\n if (value === null) {\n nulls[key] = null;\n hasNulls = true;\n } else {\n const child = stripNullValues(value);\n content[key] = child.content;\n if (child.hasNulls) {\n nulls[key] = child.nulls;\n hasNulls = true;\n }\n }\n }\n\n return { content, nulls: hasNulls ? nulls : undefined, hasNulls };\n};\n\nconst CHUNK_SIZE = 7000; // GPT-5 Mini safe input size\nconst GROUP_MAX_RETRY = 2;\nconst MAX_RETRY = 3;\nconst RETRY_DELAY = 1000 * 10; // 10 seconds\n\nconst MAX_FOLLOWING_ERRORS = 10; // 10 errors in a row, hard exit the process\nlet followingErrors = 0;\n\nexport const translateDictionary = async (\n task: TranslationTask,\n configuration: IntlayerConfig,\n options?: TranslateDictionaryOptions\n): Promise<TranslateDictionaryResult> => {\n const appLogger = getAppLogger(configuration);\n const intlayerAPI = getIntlayerAPIProxy(undefined, configuration);\n\n const { mode, aiOptions, fillMetadata, aiClient, aiConfig } = {\n mode: 'complete',\n fillMetadata: true,\n ...options,\n } as const;\n\n const notifySuccess = () => {\n followingErrors = 0;\n options?.onSuccess?.();\n };\n\n const result = await retryManager(\n async () => {\n const unmergedDictionariesRecord = getUnmergedDictionaries(configuration);\n\n const baseUnmergedDictionary: Dictionary | undefined =\n unmergedDictionariesRecord[task.dictionaryKey].find(\n (dict) => dict.localId === task.dictionaryLocalId\n );\n\n if (!baseUnmergedDictionary) {\n appLogger(\n `${task.dictionaryPreset}Dictionary not found in unmergedDictionariesRecord. Skipping.`,\n {\n level: 'warn',\n }\n );\n return { ...task, dictionaryOutput: null };\n }\n\n let metadata:\n | Pick<Dictionary, 'description' | 'title' | 'tags'>\n | undefined;\n\n if (\n fillMetadata &&\n (hasMissingMetadata(baseUnmergedDictionary) || mode === 'review')\n ) {\n const defaultLocaleDictionary = getPerLocaleDictionary(\n baseUnmergedDictionary,\n configuration.internationalization.defaultLocale\n );\n\n appLogger(\n `${task.dictionaryPreset} Filling missing metadata for ${colorizePath(basename(baseUnmergedDictionary.filePath!))}`,\n {\n level: 'info',\n }\n );\n\n const runAudit = async () => {\n if (aiClient && aiConfig) {\n const result = await aiClient.auditDictionaryMetadata({\n fileContent: JSON.stringify(defaultLocaleDictionary),\n aiConfig,\n });\n\n return {\n data: result,\n };\n }\n\n return await intlayerAPI.ai.auditContentDeclarationMetadata({\n fileContent: JSON.stringify(defaultLocaleDictionary),\n aiOptions,\n });\n };\n\n const metadataResult = options?.onHandle\n ? await options.onHandle(runAudit)\n : await runAudit();\n\n metadata = metadataResult.data?.fileContent;\n }\n\n const translatedContentResults = await Promise.all(\n task.targetLocales.map(async (targetLocale) => {\n /**\n * In complete mode, for large dictionaries, we want to filter all content that is already translated\n *\n * targetLocale: fr\n *\n * { test1: t({ ar: 'Hello', en: 'Hello', fr: 'Bonjour' } }) -> {}\n * { test2: t({ ar: 'Hello', en: 'Hello' }) } -> { test2: t({ ar: 'Hello', en: 'Hello' }) }\n *\n */\n // Reset to base dictionary for each locale to ensure we filter from the original\n let dictionaryToProcess = structuredClone(baseUnmergedDictionary);\n\n let targetLocaleDictionary: Dictionary;\n\n if (typeof baseUnmergedDictionary.locale === 'string') {\n // For per-locale files, the content is already in simple JSON format (not translation nodes)\n // The base dictionary is already the source locale content\n\n // Load the existing target locale dictionary\n const targetLocaleFilePath =\n baseUnmergedDictionary.filePath?.replace(\n new RegExp(`/${task.sourceLocale}/`, 'g'),\n `/${targetLocale}/`\n );\n\n // Find the target locale dictionary in unmerged dictionaries\n const targetUnmergedDictionary = targetLocaleFilePath\n ? unmergedDictionariesRecord[task.dictionaryKey]?.find(\n (dict) =>\n dict.filePath === targetLocaleFilePath &&\n dict.locale === targetLocale\n )\n : undefined;\n\n targetLocaleDictionary = targetUnmergedDictionary ?? {\n key: baseUnmergedDictionary.key,\n content: {},\n filePath: targetLocaleFilePath,\n locale: targetLocale,\n };\n\n // In complete mode, filter out already translated content\n if (mode === 'complete') {\n dictionaryToProcess = getFilterMissingContentPerLocale(\n dictionaryToProcess,\n targetUnmergedDictionary\n );\n }\n } else {\n // For multilingual dictionaries\n if (mode === 'complete') {\n // Remove all nodes that don't have any content to translate\n dictionaryToProcess = getFilterMissingTranslationsDictionary(\n dictionaryToProcess,\n targetLocale\n );\n }\n\n dictionaryToProcess = getPerLocaleDictionary(\n dictionaryToProcess,\n task.sourceLocale\n );\n\n targetLocaleDictionary = getPerLocaleDictionary(\n baseUnmergedDictionary,\n targetLocale\n );\n }\n\n const localePreset = colon(\n [\n colorize('[', ANSIColors.GREY_DARK),\n formatLocale(targetLocale),\n colorize(']', ANSIColors.GREY_DARK),\n ].join(''),\n { colSize: 18 }\n );\n\n const createChunkPreset = (\n chunkIndex: number,\n totalChunks: number\n ) => {\n if (totalChunks <= 1) return '';\n return colon(\n [\n colorize('[', ANSIColors.GREY_DARK),\n colorizeNumber(chunkIndex + 1),\n colorize(`/${totalChunks}`, ANSIColors.GREY_DARK),\n colorize(']', ANSIColors.GREY_DARK),\n ].join(''),\n { colSize: 5 }\n );\n };\n\n appLogger(\n `${task.dictionaryPreset}${localePreset} Preparing ${colorizePath(basename(targetLocaleDictionary.filePath!))}`,\n {\n level: 'info',\n }\n );\n\n const isContentStructured =\n (typeof dictionaryToProcess.content === 'object' &&\n dictionaryToProcess.content !== null) ||\n Array.isArray(dictionaryToProcess.content);\n\n const rawContentToProcess = isContentStructured\n ? dictionaryToProcess.content\n : {\n __INTLAYER_ROOT_PRIMITIVE_CONTENT__:\n dictionaryToProcess.content,\n };\n\n // Strip null values before sending to AI — nulls need no translation\n // and confuse the model. They will be re-injected after merging.\n const { content: contentToProcess, nulls: strippedNullValues } =\n stripNullValues(rawContentToProcess);\n\n const chunkedJsonContent: JsonChunk[] = chunkJSON(\n contentToProcess as unknown as Record<string, any>,\n CHUNK_SIZE\n );\n\n const nbOfChunks = chunkedJsonContent.length;\n\n if (nbOfChunks > 1) {\n appLogger(\n `${task.dictionaryPreset}${localePreset} Split into ${colorizeNumber(nbOfChunks)} chunks for translation`,\n {\n level: 'info',\n }\n );\n }\n\n const chunkResult: JsonChunk[] = [];\n\n // Process chunks in parallel (globally throttled) to allow concurrent translation\n const chunkPromises = chunkedJsonContent.map((chunk) => {\n const chunkPreset = createChunkPreset(chunk.index, chunk.total);\n\n if (nbOfChunks > 1) {\n appLogger(\n `${task.dictionaryPreset}${localePreset}${chunkPreset} Translating chunk`,\n {\n level: 'info',\n }\n );\n }\n\n // Reconstruct partial JSON content from this chunk's patches\n const chunkContent = reconstructFromSingleChunk(chunk);\n const presetOutputContent = reduceObjectFormat(\n isContentStructured\n ? targetLocaleDictionary.content\n : {\n __INTLAYER_ROOT_PRIMITIVE_CONTENT__:\n targetLocaleDictionary.content,\n },\n chunkContent\n ) as unknown as JSON;\n\n const executeTranslation = async () => {\n return await retryManager(\n async () => {\n let translationResult: any;\n\n if (aiClient && aiConfig) {\n translationResult = await aiClient.translateJSON({\n entryFileContent: chunkContent as unknown as JSON,\n presetOutputContent,\n dictionaryDescription:\n dictionaryToProcess.description ??\n metadata?.description ??\n '',\n entryLocale: task.sourceLocale,\n outputLocale: targetLocale,\n mode,\n aiConfig,\n });\n } else {\n translationResult = await intlayerAPI.ai\n .translateJSON({\n entryFileContent: chunkContent as unknown as JSON,\n presetOutputContent,\n dictionaryDescription:\n dictionaryToProcess.description ??\n metadata?.description ??\n '',\n entryLocale: task.sourceLocale,\n outputLocale: targetLocale,\n mode,\n aiOptions,\n })\n .then((result) => result.data);\n }\n\n if (!translationResult?.fileContent) {\n throw new Error('No content result');\n }\n\n const { isIdentic } = verifyIdenticObjectFormat(\n translationResult.fileContent,\n chunkContent\n );\n\n if (!isIdentic) {\n throw new Error(\n 'Translation result does not match expected format'\n );\n }\n\n notifySuccess();\n return translationResult.fileContent;\n },\n {\n maxRetry: MAX_RETRY,\n delay: RETRY_DELAY,\n onError: ({ error, attempt, maxRetry }) => {\n const chunkPreset = createChunkPreset(\n chunk.index,\n chunk.total\n );\n appLogger(\n `${task.dictionaryPreset}${localePreset}${chunkPreset} ${colorize('Error filling:', ANSIColors.RED)} ${colorize(typeof error === 'string' ? error : JSON.stringify(error), ANSIColors.GREY_DARK)} - Attempt ${colorizeNumber(attempt + 1)} of ${colorizeNumber(maxRetry)}`,\n {\n level: 'error',\n }\n );\n\n followingErrors += 1;\n\n if (followingErrors >= MAX_FOLLOWING_ERRORS) {\n appLogger(`There is something wrong.`, {\n level: 'error',\n });\n process.exit(1); // 1 for error\n }\n },\n }\n )();\n };\n\n const wrapped = options?.onHandle\n ? options.onHandle(executeTranslation) // queued in global limiter\n : executeTranslation(); // no global limiter\n\n return wrapped.then((result) => ({ chunk, result }));\n });\n\n // Wait for all chunks for this locale in parallel (still capped by global limiter)\n const chunkResults = await Promise.all(chunkPromises);\n\n // Maintain order\n chunkResults\n .sort((chunkA, chunkB) => chunkA.chunk.index - chunkB.chunk.index)\n .forEach(({ result }) => {\n chunkResult.push(result);\n });\n\n // Merge partial JSON objects produced from each chunk into a single object\n let mergedContent = mergeChunks(chunkResult);\n\n // Re-inject null values that were stripped before AI translation\n if (strippedNullValues) {\n mergedContent = deepMergeContent(mergedContent, strippedNullValues);\n }\n\n const merged = {\n ...dictionaryToProcess,\n content: mergedContent,\n };\n\n // For per-locale files, merge the newly translated content with existing target content\n let finalContent = merged.content;\n\n if (!isContentStructured) {\n finalContent = (finalContent as any)\n ?.__INTLAYER_ROOT_PRIMITIVE_CONTENT__;\n }\n\n if (typeof baseUnmergedDictionary.locale === 'string') {\n // Deep merge: existing content + newly translated content\n finalContent = deepMergeContent(\n targetLocaleDictionary.content ?? {},\n finalContent\n );\n }\n\n return [targetLocale, finalContent] as const;\n })\n );\n\n const translatedContent: Partial<Record<Locale, Dictionary['content']>> =\n Object.fromEntries(translatedContentResults);\n\n const baseDictionary = baseUnmergedDictionary.locale\n ? {\n ...baseUnmergedDictionary,\n key: baseUnmergedDictionary.key!,\n content: {},\n }\n : baseUnmergedDictionary;\n\n let dictionaryOutput: Dictionary = {\n ...getMultilingualDictionary(baseDictionary),\n locale: undefined, // Ensure the dictionary is multilingual\n ...metadata,\n };\n\n for (const targetLocale of task.targetLocales) {\n if (translatedContent[targetLocale]) {\n dictionaryOutput = insertContentInDictionary(\n dictionaryOutput,\n translatedContent[targetLocale],\n targetLocale\n );\n }\n }\n\n appLogger(\n `${task.dictionaryPreset} ${colorize('Translation completed successfully', ANSIColors.GREEN)} for ${colorizePath(basename(dictionaryOutput.filePath!))}`,\n {\n level: 'info',\n }\n );\n\n if (\n baseUnmergedDictionary.locale &&\n (baseUnmergedDictionary.fill === true ||\n baseUnmergedDictionary.fill === undefined) &&\n baseUnmergedDictionary.location === 'local'\n ) {\n const dictionaryFilePath = baseUnmergedDictionary\n .filePath!.split('.')\n .slice(0, -1);\n\n const contentIndex = dictionaryFilePath[dictionaryFilePath.length - 1];\n\n return JSON.parse(\n JSON.stringify({\n ...task,\n dictionaryOutput: {\n ...dictionaryOutput,\n fill: undefined,\n filled: true,\n },\n }).replaceAll(\n new RegExp(`\\\\.${contentIndex}\\\\.[a-zA-Z0-9]+`, 'g'),\n `.filled.${contentIndex}.json`\n )\n );\n }\n\n return {\n ...task,\n dictionaryOutput,\n };\n },\n {\n maxRetry: GROUP_MAX_RETRY,\n delay: RETRY_DELAY,\n onError: ({ error, attempt, maxRetry }) =>\n appLogger(\n `${task.dictionaryPreset} ${colorize('Error:', ANSIColors.RED)} ${colorize(typeof error === 'string' ? error : JSON.stringify(error), ANSIColors.GREY_DARK)} - Attempt ${colorizeNumber(attempt + 1)} of ${colorizeNumber(maxRetry)}`,\n {\n level: 'error',\n }\n ),\n onMaxTryReached: ({ error }) =>\n appLogger(\n `${task.dictionaryPreset} ${colorize('Maximum number of retries reached:', ANSIColors.RED)} ${colorize(typeof error === 'string' ? error : JSON.stringify(error), ANSIColors.GREY_DARK)}`,\n {\n level: 'error',\n }\n ),\n }\n )();\n\n return result as TranslateDictionaryResult;\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"translateDictionary.mjs","names":[],"sources":["../../../src/fill/translateDictionary.ts"],"sourcesContent":["import { basename } from 'node:path';\nimport type { AIConfig } from '@intlayer/ai';\nimport { type AIOptions, getIntlayerAPIProxy } from '@intlayer/api';\nimport {\n chunkJSON,\n formatLocale,\n type JsonChunk,\n mergeChunks,\n reconstructFromSingleChunk,\n reduceObjectFormat,\n verifyIdenticObjectFormat,\n} from '@intlayer/chokidar/utils';\nimport * as ANSIColors from '@intlayer/config/colors';\nimport {\n colon,\n colorize,\n colorizeNumber,\n colorizePath,\n getAppLogger,\n} from '@intlayer/config/logger';\nimport { retryManager } from '@intlayer/config/utils';\nimport {\n getFilterMissingTranslationsDictionary,\n getMultilingualDictionary,\n getPerLocaleDictionary,\n insertContentInDictionary,\n} from '@intlayer/core/plugins';\nimport type { Locale } from '@intlayer/types/allLocales';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport type { Dictionary } from '@intlayer/types/dictionary';\nimport { getUnmergedDictionaries } from '@intlayer/unmerged-dictionaries-entry';\nimport type { AIClient } from '../utils/setupAI';\nimport { deepMergeContent } from './deepMergeContent';\nimport { getFilterMissingContentPerLocale } from './getFilterMissingContentPerLocale';\nimport type { TranslationTask } from './listTranslationsTasks';\n\ntype TranslateDictionaryResult = TranslationTask & {\n dictionaryOutput: Dictionary | null;\n};\n\ntype TranslateDictionaryOptions = {\n mode: 'complete' | 'review';\n aiOptions?: AIOptions;\n fillMetadata?: boolean;\n onHandle?: ReturnType<\n typeof import('@intlayer/chokidar/utils').getGlobalLimiter\n >;\n onSuccess?: () => void;\n onError?: (error: unknown) => void;\n getAbortError?: () => Error | null;\n aiClient?: AIClient;\n aiConfig?: AIConfig;\n};\n\nconst hasMissingMetadata = (dictionary: Dictionary) =>\n !dictionary.description || !dictionary.title || !dictionary.tags;\n\n/**\n * Recursively strips null values from an object, returning the cleaned content\n * and a separate object containing only the null-valued paths so they can be\n * re-injected after AI translation (nulls don't need translation).\n */\nconst stripNullValues = (\n obj: any\n): { content: any; nulls: any; hasNulls: boolean } => {\n if (typeof obj !== 'object' || obj === null || Array.isArray(obj)) {\n return { content: obj, nulls: undefined, hasNulls: false };\n }\n\n const content: any = {};\n const nulls: any = {};\n let hasNulls = false;\n\n for (const [key, value] of Object.entries(obj)) {\n if (value === null) {\n nulls[key] = null;\n hasNulls = true;\n } else {\n const child = stripNullValues(value);\n content[key] = child.content;\n if (child.hasNulls) {\n nulls[key] = child.nulls;\n hasNulls = true;\n }\n }\n }\n\n return { content, nulls: hasNulls ? nulls : undefined, hasNulls };\n};\n\nconst CHUNK_SIZE = 7000; // GPT-5 Mini safe input size\nconst GROUP_MAX_RETRY = 2;\nconst MAX_RETRY = 3;\nconst RETRY_DELAY = 1000 * 10; // 10 seconds\n\nconst MAX_FOLLOWING_ERRORS = 10; // 10 errors in a row, hard exit the process\nlet followingErrors = 0;\n\nexport const translateDictionary = async (\n task: TranslationTask,\n configuration: IntlayerConfig,\n options?: TranslateDictionaryOptions\n): Promise<TranslateDictionaryResult> => {\n const appLogger = getAppLogger(configuration);\n const intlayerAPI = getIntlayerAPIProxy(undefined, configuration);\n\n const { mode, aiOptions, fillMetadata, aiClient, aiConfig } = {\n mode: 'complete',\n fillMetadata: true,\n ...options,\n } as const;\n\n const notifySuccess = () => {\n followingErrors = 0;\n options?.onSuccess?.();\n };\n\n const result = await retryManager(\n async () => {\n const unmergedDictionariesRecord = getUnmergedDictionaries(configuration);\n\n const baseUnmergedDictionary: Dictionary | undefined =\n unmergedDictionariesRecord[task.dictionaryKey].find(\n (dict) => dict.localId === task.dictionaryLocalId\n );\n\n if (!baseUnmergedDictionary) {\n appLogger(\n `${task.dictionaryPreset}Dictionary not found in unmergedDictionariesRecord. Skipping.`,\n {\n level: 'warn',\n }\n );\n return { ...task, dictionaryOutput: null };\n }\n\n let metadata:\n | Pick<Dictionary, 'description' | 'title' | 'tags'>\n | undefined;\n\n if (\n fillMetadata &&\n (hasMissingMetadata(baseUnmergedDictionary) || mode === 'review')\n ) {\n const defaultLocaleDictionary = getPerLocaleDictionary(\n baseUnmergedDictionary,\n configuration.internationalization.defaultLocale\n );\n\n appLogger(\n `${task.dictionaryPreset} Filling missing metadata for ${colorizePath(basename(baseUnmergedDictionary.filePath!))}`,\n {\n level: 'info',\n }\n );\n\n const runAudit = async () => {\n if (aiClient && aiConfig) {\n const result = await aiClient.auditDictionaryMetadata({\n fileContent: JSON.stringify(defaultLocaleDictionary),\n aiConfig,\n });\n\n return {\n data: result,\n };\n }\n\n return await intlayerAPI.ai.auditContentDeclarationMetadata({\n fileContent: JSON.stringify(defaultLocaleDictionary),\n aiOptions,\n });\n };\n\n const metadataResult = options?.onHandle\n ? await options.onHandle(runAudit)\n : await runAudit();\n\n metadata = metadataResult.data?.fileContent;\n }\n\n const translatedContentResults = await Promise.all(\n task.targetLocales.map(async (targetLocale) => {\n /**\n * In complete mode, for large dictionaries, we want to filter all content that is already translated\n *\n * targetLocale: fr\n *\n * { test1: t({ ar: 'Hello', en: 'Hello', fr: 'Bonjour' } }) -> {}\n * { test2: t({ ar: 'Hello', en: 'Hello' }) } -> { test2: t({ ar: 'Hello', en: 'Hello' }) }\n *\n */\n // Reset to base dictionary for each locale to ensure we filter from the original\n let dictionaryToProcess = structuredClone(baseUnmergedDictionary);\n\n let targetLocaleDictionary: Dictionary;\n\n if (typeof baseUnmergedDictionary.locale === 'string') {\n // For per-locale files, the content is already in simple JSON format (not translation nodes)\n // The base dictionary is already the source locale content\n\n // Load the existing target locale dictionary\n const targetLocaleFilePath =\n baseUnmergedDictionary.filePath?.replace(\n new RegExp(`/${task.sourceLocale}/`, 'g'),\n `/${targetLocale}/`\n );\n\n // Find the target locale dictionary in unmerged dictionaries\n const targetUnmergedDictionary = targetLocaleFilePath\n ? unmergedDictionariesRecord[task.dictionaryKey]?.find(\n (dict) =>\n dict.filePath === targetLocaleFilePath &&\n dict.locale === targetLocale\n )\n : undefined;\n\n targetLocaleDictionary = targetUnmergedDictionary ?? {\n key: baseUnmergedDictionary.key,\n content: {},\n filePath: targetLocaleFilePath,\n locale: targetLocale,\n };\n\n // In complete mode, filter out already translated content\n if (mode === 'complete') {\n dictionaryToProcess = getFilterMissingContentPerLocale(\n dictionaryToProcess,\n targetUnmergedDictionary\n );\n }\n } else {\n // For multilingual dictionaries\n if (mode === 'complete') {\n // Remove all nodes that don't have any content to translate\n dictionaryToProcess = getFilterMissingTranslationsDictionary(\n dictionaryToProcess,\n targetLocale\n );\n }\n\n dictionaryToProcess = getPerLocaleDictionary(\n dictionaryToProcess,\n task.sourceLocale\n );\n\n targetLocaleDictionary = getPerLocaleDictionary(\n baseUnmergedDictionary,\n targetLocale\n );\n }\n\n const localePreset = colon(\n [\n colorize('[', ANSIColors.GREY_DARK),\n formatLocale(targetLocale),\n colorize(']', ANSIColors.GREY_DARK),\n ].join(''),\n { colSize: 18 }\n );\n\n const createChunkPreset = (\n chunkIndex: number,\n totalChunks: number\n ) => {\n if (totalChunks <= 1) return '';\n return colon(\n [\n colorize('[', ANSIColors.GREY_DARK),\n colorizeNumber(chunkIndex + 1),\n colorize(`/${totalChunks}`, ANSIColors.GREY_DARK),\n colorize(']', ANSIColors.GREY_DARK),\n ].join(''),\n { colSize: 5 }\n );\n };\n\n appLogger(\n `${task.dictionaryPreset}${localePreset} Preparing ${colorizePath(basename(targetLocaleDictionary.filePath!))}`,\n {\n level: 'info',\n }\n );\n\n const isContentStructured =\n (typeof dictionaryToProcess.content === 'object' &&\n dictionaryToProcess.content !== null) ||\n Array.isArray(dictionaryToProcess.content);\n\n const rawContentToProcess = isContentStructured\n ? dictionaryToProcess.content\n : {\n __INTLAYER_ROOT_PRIMITIVE_CONTENT__:\n dictionaryToProcess.content,\n };\n\n // Strip null values before sending to AI — nulls need no translation\n // and confuse the model. They will be re-injected after merging.\n const { content: contentToProcess, nulls: strippedNullValues } =\n stripNullValues(rawContentToProcess);\n\n const chunkedJsonContent: JsonChunk[] = chunkJSON(\n contentToProcess as unknown as Record<string, any>,\n CHUNK_SIZE\n );\n\n const nbOfChunks = chunkedJsonContent.length;\n\n if (nbOfChunks > 1) {\n appLogger(\n `${task.dictionaryPreset}${localePreset} Split into ${colorizeNumber(nbOfChunks)} chunks for translation`,\n {\n level: 'info',\n }\n );\n }\n\n const chunkResult: JsonChunk[] = [];\n\n // Process chunks in parallel (globally throttled) to allow concurrent translation\n const chunkPromises = chunkedJsonContent.map((chunk) => {\n const chunkPreset = createChunkPreset(chunk.index, chunk.total);\n\n if (nbOfChunks > 1) {\n appLogger(\n `${task.dictionaryPreset}${localePreset}${chunkPreset} Translating chunk`,\n {\n level: 'info',\n }\n );\n }\n\n // Reconstruct partial JSON content from this chunk's patches\n const chunkContent = reconstructFromSingleChunk(chunk);\n const presetOutputContent = reduceObjectFormat(\n isContentStructured\n ? targetLocaleDictionary.content\n : {\n __INTLAYER_ROOT_PRIMITIVE_CONTENT__:\n targetLocaleDictionary.content,\n },\n chunkContent\n ) as unknown as JSON;\n\n const executeTranslation = async () => {\n return await retryManager(\n async () => {\n let translationResult: any;\n\n if (aiClient && aiConfig) {\n translationResult = await aiClient.translateJSON({\n entryFileContent: chunkContent as unknown as JSON,\n presetOutputContent,\n dictionaryDescription:\n dictionaryToProcess.description ??\n metadata?.description ??\n '',\n entryLocale: task.sourceLocale,\n outputLocale: targetLocale,\n mode,\n aiConfig,\n });\n } else {\n translationResult = await intlayerAPI.ai\n .translateJSON({\n entryFileContent: chunkContent as unknown as JSON,\n presetOutputContent,\n dictionaryDescription:\n dictionaryToProcess.description ??\n metadata?.description ??\n '',\n entryLocale: task.sourceLocale,\n outputLocale: targetLocale,\n mode,\n aiOptions,\n })\n .then((result) => result.data);\n }\n\n if (!translationResult?.fileContent) {\n throw new Error('No content result');\n }\n\n const { isIdentic } = verifyIdenticObjectFormat(\n translationResult.fileContent,\n chunkContent\n );\n\n if (!isIdentic) {\n throw new Error(\n 'Translation result does not match expected format'\n );\n }\n\n notifySuccess();\n return translationResult.fileContent;\n },\n {\n maxRetry: MAX_RETRY,\n delay: RETRY_DELAY,\n onError: ({ error, attempt, maxRetry }) => {\n const chunkPreset = createChunkPreset(\n chunk.index,\n chunk.total\n );\n appLogger(\n `${task.dictionaryPreset}${localePreset}${chunkPreset} ${colorize('Error filling:', ANSIColors.RED)} ${colorize(typeof error === 'string' ? error : JSON.stringify(error), ANSIColors.GREY_DARK)} - Attempt ${colorizeNumber(attempt + 1)} of ${colorizeNumber(maxRetry)}`,\n {\n level: 'error',\n }\n );\n\n followingErrors += 1;\n\n if (followingErrors >= MAX_FOLLOWING_ERRORS) {\n appLogger(`There is something wrong.`, {\n level: 'error',\n });\n process.exit(1); // 1 for error\n }\n },\n }\n )();\n };\n\n const wrapped = options?.onHandle\n ? options.onHandle(executeTranslation) // queued in global limiter\n : executeTranslation(); // no global limiter\n\n return wrapped.then((result) => ({ chunk, result }));\n });\n\n // Wait for all chunks for this locale in parallel (still capped by global limiter)\n const chunkResults = await Promise.all(chunkPromises);\n\n // Maintain order\n chunkResults\n .sort((chunkA, chunkB) => chunkA.chunk.index - chunkB.chunk.index)\n .forEach(({ result }) => {\n chunkResult.push(result);\n });\n\n // Merge partial JSON objects produced from each chunk into a single object\n let mergedContent = mergeChunks(chunkResult);\n\n // Re-inject null values that were stripped before AI translation\n if (strippedNullValues) {\n mergedContent = deepMergeContent(mergedContent, strippedNullValues);\n }\n\n const merged = {\n ...dictionaryToProcess,\n content: mergedContent,\n };\n\n // For per-locale files, merge the newly translated content with existing target content\n let finalContent = merged.content;\n\n if (!isContentStructured) {\n finalContent = (finalContent as any)\n ?.__INTLAYER_ROOT_PRIMITIVE_CONTENT__;\n }\n\n if (typeof baseUnmergedDictionary.locale === 'string') {\n // Deep merge: existing content + newly translated content\n finalContent = deepMergeContent(\n targetLocaleDictionary.content ?? {},\n finalContent\n );\n }\n\n return [targetLocale, finalContent] as const;\n })\n );\n\n const translatedContent: Partial<Record<Locale, Dictionary['content']>> =\n Object.fromEntries(translatedContentResults);\n\n const baseDictionary = baseUnmergedDictionary.locale\n ? {\n ...baseUnmergedDictionary,\n key: baseUnmergedDictionary.key!,\n content: {},\n }\n : baseUnmergedDictionary;\n\n let dictionaryOutput: Dictionary = {\n ...getMultilingualDictionary(baseDictionary),\n locale: undefined, // Ensure the dictionary is multilingual\n ...metadata,\n };\n\n for (const targetLocale of task.targetLocales) {\n if (translatedContent[targetLocale]) {\n dictionaryOutput = insertContentInDictionary(\n dictionaryOutput,\n translatedContent[targetLocale],\n targetLocale\n );\n }\n }\n\n appLogger(\n `${task.dictionaryPreset} ${colorize('Translation completed successfully', ANSIColors.GREEN)} for ${colorizePath(basename(dictionaryOutput.filePath!))}`,\n {\n level: 'info',\n }\n );\n\n // The dict-level `fill` value may have been lost during JSON serialisation\n // (functions can't be serialised to JSON). Fall back to the config-level\n // fill so that an explicit fill in intlayer.config.ts is honoured.\n const effectiveFillForCheck =\n baseUnmergedDictionary.fill ?? configuration.dictionary?.fill;\n\n if (\n baseUnmergedDictionary.locale &&\n (effectiveFillForCheck === true ||\n effectiveFillForCheck === undefined) &&\n baseUnmergedDictionary.location === 'local'\n ) {\n const dictionaryFilePath = baseUnmergedDictionary\n .filePath!.split('.')\n .slice(0, -1);\n\n const contentIndex = dictionaryFilePath[dictionaryFilePath.length - 1];\n\n return JSON.parse(\n JSON.stringify({\n ...task,\n dictionaryOutput: {\n ...dictionaryOutput,\n fill: undefined,\n filled: true,\n },\n }).replaceAll(\n new RegExp(`\\\\.${contentIndex}\\\\.[a-zA-Z0-9]+`, 'g'),\n `.filled.${contentIndex}.json`\n )\n );\n }\n\n return {\n ...task,\n dictionaryOutput,\n };\n },\n {\n maxRetry: GROUP_MAX_RETRY,\n delay: RETRY_DELAY,\n onError: ({ error, attempt, maxRetry }) =>\n appLogger(\n `${task.dictionaryPreset} ${colorize('Error:', ANSIColors.RED)} ${colorize(typeof error === 'string' ? error : JSON.stringify(error), ANSIColors.GREY_DARK)} - Attempt ${colorizeNumber(attempt + 1)} of ${colorizeNumber(maxRetry)}`,\n {\n level: 'error',\n }\n ),\n onMaxTryReached: ({ error }) =>\n appLogger(\n `${task.dictionaryPreset} ${colorize('Maximum number of retries reached:', ANSIColors.RED)} ${colorize(typeof error === 'string' ? error : JSON.stringify(error), ANSIColors.GREY_DARK)}`,\n {\n level: 'error',\n }\n ),\n }\n )();\n\n return result as TranslateDictionaryResult;\n};\n"],"mappings":"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"}
@@ -1,2 +1,52 @@
1
- import{formatFillData as e}from"./formatFillData.mjs";import{getAvailableLocalesInDictionary as t}from"./getAvailableLocalesInDictionary.mjs";import{relative as n}from"node:path";import{writeContentDeclaration as r}from"@intlayer/chokidar/build";import{formatLocale as i,formatPath as a}from"@intlayer/chokidar/utils";import{colorizeKey as o,getAppLogger as s}from"@intlayer/config/logger";import{getDictionaries as c}from"@intlayer/dictionaries-entry";const l=async(l,u,d,f)=>{let p=s(f),m=c(f)[l.key],{filePath:h}=l;if(!h){p(`No file path found for dictionary`,{level:`error`});return}let g=l.fill??f.dictionary?.fill??!0;if(g===!1){p(`Auto fill is disabled for '${o(m.key)}'`,{level:`info`});return}let _=(u??f.internationalization.locales).filter(e=>!d?.includes(e)),v=t(l),y=_.filter(e=>v.includes(e));if(y.length===0){p(`No translations available for dictionary '${o(m.key)}'`,{level:`info`});return}let b=await e(g,y,h,m.key,f);for await(let e of b){if(!e.filePath){p(`No file path found for auto filled content declaration for '${o(m.key)}'`,{level:`error`});continue}let{fill:t,...s}=l,c=n(f.system.baseDir,e.filePath);if(await r({...s,filled:!0,locale:e.isPerLocale?e.localeList[0]:void 0,localId:`${l.key}::local::${c}`,filePath:c},f,{localeList:e.localeList}),e.isPerLocale){let t=e.localeList[0];p(`Auto filled per-locale content declaration for '${o(m.key)}' written to ${a(e.filePath)} for locale ${i(t)}`,{level:`info`})}else p(`Auto filled content declaration for '${o(m.key)}' written to ${a(e.filePath)}`,{level:`info`})}};export{l as writeFill};
1
+ import { formatFillData } from "./formatFillData.mjs";
2
+ import { getAvailableLocalesInDictionary } from "./getAvailableLocalesInDictionary.mjs";
3
+ import { relative } from "node:path";
4
+ import { writeContentDeclaration } from "@intlayer/chokidar/build";
5
+ import { formatLocale, formatPath } from "@intlayer/chokidar/utils";
6
+ import { colorizeKey, getAppLogger } from "@intlayer/config/logger";
7
+ import { getDictionaries } from "@intlayer/dictionaries-entry";
8
+
9
+ //#region src/fill/writeFill.ts
10
+ const writeFill = async (contentDeclarationFile, outputLocales, parentLocales, configuration) => {
11
+ const appLogger = getAppLogger(configuration);
12
+ const fullDictionary = getDictionaries(configuration)[contentDeclarationFile.key];
13
+ const { filePath } = contentDeclarationFile;
14
+ if (!filePath) {
15
+ appLogger("No file path found for dictionary", { level: "error" });
16
+ return;
17
+ }
18
+ const fillOptions = contentDeclarationFile.fill ?? configuration.dictionary?.fill ?? true;
19
+ if (fillOptions === false) {
20
+ appLogger(`Auto fill is disabled for '${colorizeKey(fullDictionary.key)}'`, { level: "info" });
21
+ return;
22
+ }
23
+ const requestedLocales = (outputLocales ?? configuration.internationalization.locales).filter((locale) => !parentLocales?.includes(locale));
24
+ const availableLocales = getAvailableLocalesInDictionary(contentDeclarationFile);
25
+ const localeList = requestedLocales.filter((locale) => availableLocales.includes(locale));
26
+ if (localeList.length === 0) {
27
+ appLogger(`No translations available for dictionary '${colorizeKey(fullDictionary.key)}'`, { level: "info" });
28
+ return;
29
+ }
30
+ const fillData = await formatFillData(fillOptions, localeList, filePath, fullDictionary.key, configuration);
31
+ for await (const output of fillData) {
32
+ if (!output.filePath) {
33
+ appLogger(`No file path found for auto filled content declaration for '${colorizeKey(fullDictionary.key)}'`, { level: "error" });
34
+ continue;
35
+ }
36
+ const { fill, ...rest } = contentDeclarationFile;
37
+ const relativeFilePath = relative(configuration.system.baseDir, output.filePath);
38
+ await writeContentDeclaration({
39
+ ...rest,
40
+ filled: true,
41
+ locale: output.isPerLocale ? output.localeList[0] : void 0,
42
+ localId: `${contentDeclarationFile.key}::local::${relativeFilePath}`,
43
+ filePath: relativeFilePath
44
+ }, configuration, { localeList: output.isPerLocale ? output.localeList : outputLocales ?? configuration.internationalization.locales });
45
+ if (output.isPerLocale) appLogger(`Auto filled per-locale content declaration for '${colorizeKey(fullDictionary.key)}' written to ${formatPath(output.filePath)} for locale ${formatLocale(output.localeList[0])}`, { level: "info" });
46
+ else appLogger(`Auto filled content declaration for '${colorizeKey(fullDictionary.key)}' written to ${formatPath(output.filePath)}`, { level: "info" });
47
+ }
48
+ };
49
+
50
+ //#endregion
51
+ export { writeFill };
2
52
  //# sourceMappingURL=writeFill.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"writeFill.mjs","names":[],"sources":["../../../src/fill/writeFill.ts"],"sourcesContent":["import { relative } from 'node:path';\nimport { writeContentDeclaration } from '@intlayer/chokidar/build';\nimport { formatLocale, formatPath } from '@intlayer/chokidar/utils';\nimport { colorizeKey, getAppLogger } from '@intlayer/config/logger';\nimport { getDictionaries } from '@intlayer/dictionaries-entry';\nimport type { Locale } from '@intlayer/types/allLocales';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport type { Dictionary, Fill } from '@intlayer/types/dictionary';\nimport { type FillData, formatFillData } from './formatFillData';\nimport { getAvailableLocalesInDictionary } from './getAvailableLocalesInDictionary';\n\nexport const writeFill = async (\n contentDeclarationFile: Dictionary,\n outputLocales: Locale[],\n parentLocales: Locale[],\n configuration: IntlayerConfig\n) => {\n const appLogger = getAppLogger(configuration);\n const dictionaries = getDictionaries(configuration);\n\n const fullDictionary = dictionaries[contentDeclarationFile.key];\n\n const { filePath } = contentDeclarationFile;\n\n if (!filePath) {\n appLogger('No file path found for dictionary', {\n level: 'error',\n });\n return;\n }\n\n const fillOptions: Fill | undefined =\n contentDeclarationFile.fill ?? configuration.dictionary?.fill ?? true;\n\n if ((fillOptions as boolean) === false) {\n appLogger(\n `Auto fill is disabled for '${colorizeKey(fullDictionary.key)}'`,\n {\n level: 'info',\n }\n );\n return;\n }\n\n const requestedLocales: Locale[] = (\n outputLocales ?? configuration.internationalization.locales\n ).filter((locale) => !parentLocales?.includes(locale));\n\n // Get locales that actually have translations in the content\n const availableLocales = getAvailableLocalesInDictionary(\n contentDeclarationFile\n );\n\n // Only write files for locales that have actual translations\n const localeList = requestedLocales.filter((locale) =>\n availableLocales.includes(locale)\n );\n\n if (localeList.length === 0) {\n appLogger(\n `No translations available for dictionary '${colorizeKey(fullDictionary.key)}'`,\n {\n level: 'info',\n }\n );\n return;\n }\n\n const fillData: FillData[] = await formatFillData(\n fillOptions as Fill,\n localeList,\n filePath,\n fullDictionary.key,\n configuration\n );\n\n for await (const output of fillData) {\n if (!output.filePath) {\n appLogger(\n `No file path found for auto filled content declaration for '${colorizeKey(fullDictionary.key)}'`,\n {\n level: 'error',\n }\n );\n continue;\n }\n\n const { fill, ...rest } = contentDeclarationFile;\n\n const relativeFilePath = relative(\n configuration.system.baseDir,\n output.filePath\n );\n\n // write file\n await writeContentDeclaration(\n {\n ...rest,\n filled: true,\n locale: output.isPerLocale ? output.localeList[0] : undefined,\n localId: `${contentDeclarationFile.key}::local::${relativeFilePath}`,\n filePath: relativeFilePath,\n },\n configuration,\n {\n localeList: output.localeList,\n }\n );\n\n if (output.isPerLocale) {\n const sourceLocale = output.localeList[0];\n\n appLogger(\n `Auto filled per-locale content declaration for '${colorizeKey(fullDictionary.key)}' written to ${formatPath(output.filePath)} for locale ${formatLocale(sourceLocale)}`,\n {\n level: 'info',\n }\n );\n } else {\n appLogger(\n `Auto filled content declaration for '${colorizeKey(fullDictionary.key)}' written to ${formatPath(output.filePath)}`,\n {\n level: 'info',\n }\n );\n }\n }\n};\n"],"mappings":"qcAWA,MAAa,EAAY,MACvB,EACA,EACA,EACA,IACG,CACH,IAAM,EAAY,EAAa,EAAc,CAGvC,EAFe,EAAgB,EAAc,CAEf,EAAuB,KAErD,CAAE,YAAa,EAErB,GAAI,CAAC,EAAU,CACb,EAAU,oCAAqC,CAC7C,MAAO,QACR,CAAC,CACF,OAGF,IAAM,EACJ,EAAuB,MAAQ,EAAc,YAAY,MAAQ,GAEnE,GAAK,IAA4B,GAAO,CACtC,EACE,8BAA8B,EAAY,EAAe,IAAI,CAAC,GAC9D,CACE,MAAO,OACR,CACF,CACD,OAGF,IAAM,GACJ,GAAiB,EAAc,qBAAqB,SACpD,OAAQ,GAAW,CAAC,GAAe,SAAS,EAAO,CAAC,CAGhD,EAAmB,EACvB,EACD,CAGK,EAAa,EAAiB,OAAQ,GAC1C,EAAiB,SAAS,EAAO,CAClC,CAED,GAAI,EAAW,SAAW,EAAG,CAC3B,EACE,6CAA6C,EAAY,EAAe,IAAI,CAAC,GAC7E,CACE,MAAO,OACR,CACF,CACD,OAGF,IAAM,EAAuB,MAAM,EACjC,EACA,EACA,EACA,EAAe,IACf,EACD,CAED,UAAW,IAAM,KAAU,EAAU,CACnC,GAAI,CAAC,EAAO,SAAU,CACpB,EACE,+DAA+D,EAAY,EAAe,IAAI,CAAC,GAC/F,CACE,MAAO,QACR,CACF,CACD,SAGF,GAAM,CAAE,OAAM,GAAG,GAAS,EAEpB,EAAmB,EACvB,EAAc,OAAO,QACrB,EAAO,SACR,CAiBD,GAdA,MAAM,EACJ,CACE,GAAG,EACH,OAAQ,GACR,OAAQ,EAAO,YAAc,EAAO,WAAW,GAAK,IAAA,GACpD,QAAS,GAAG,EAAuB,IAAI,WAAW,IAClD,SAAU,EACX,CACD,EACA,CACE,WAAY,EAAO,WACpB,CACF,CAEG,EAAO,YAAa,CACtB,IAAM,EAAe,EAAO,WAAW,GAEvC,EACE,mDAAmD,EAAY,EAAe,IAAI,CAAC,eAAe,EAAW,EAAO,SAAS,CAAC,cAAc,EAAa,EAAa,GACtK,CACE,MAAO,OACR,CACF,MAED,EACE,wCAAwC,EAAY,EAAe,IAAI,CAAC,eAAe,EAAW,EAAO,SAAS,GAClH,CACE,MAAO,OACR,CACF"}
1
+ {"version":3,"file":"writeFill.mjs","names":[],"sources":["../../../src/fill/writeFill.ts"],"sourcesContent":["import { relative } from 'node:path';\nimport { writeContentDeclaration } from '@intlayer/chokidar/build';\nimport { formatLocale, formatPath } from '@intlayer/chokidar/utils';\nimport { colorizeKey, getAppLogger } from '@intlayer/config/logger';\nimport { getDictionaries } from '@intlayer/dictionaries-entry';\nimport type { Locale } from '@intlayer/types/allLocales';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport type { Dictionary, Fill } from '@intlayer/types/dictionary';\nimport { type FillData, formatFillData } from './formatFillData';\nimport { getAvailableLocalesInDictionary } from './getAvailableLocalesInDictionary';\n\nexport const writeFill = async (\n contentDeclarationFile: Dictionary,\n outputLocales: Locale[],\n parentLocales: Locale[],\n configuration: IntlayerConfig\n) => {\n const appLogger = getAppLogger(configuration);\n const dictionaries = getDictionaries(configuration);\n\n const fullDictionary = dictionaries[contentDeclarationFile.key];\n\n const { filePath } = contentDeclarationFile;\n\n if (!filePath) {\n appLogger('No file path found for dictionary', {\n level: 'error',\n });\n return;\n }\n\n const fillOptions: Fill | undefined =\n contentDeclarationFile.fill ?? configuration.dictionary?.fill ?? true;\n\n if ((fillOptions as boolean) === false) {\n appLogger(\n `Auto fill is disabled for '${colorizeKey(fullDictionary.key)}'`,\n {\n level: 'info',\n }\n );\n return;\n }\n\n const requestedLocales: Locale[] = (\n outputLocales ?? configuration.internationalization.locales\n ).filter((locale) => !parentLocales?.includes(locale));\n\n // Get locales that actually have translations in the content\n const availableLocales = getAvailableLocalesInDictionary(\n contentDeclarationFile\n );\n\n // Only write files for locales that have actual translations\n const localeList = requestedLocales.filter((locale) =>\n availableLocales.includes(locale)\n );\n\n if (localeList.length === 0) {\n appLogger(\n `No translations available for dictionary '${colorizeKey(fullDictionary.key)}'`,\n {\n level: 'info',\n }\n );\n return;\n }\n\n const fillData: FillData[] = await formatFillData(\n fillOptions as Fill,\n localeList,\n filePath,\n fullDictionary.key,\n configuration\n );\n\n for await (const output of fillData) {\n if (!output.filePath) {\n appLogger(\n `No file path found for auto filled content declaration for '${colorizeKey(fullDictionary.key)}'`,\n {\n level: 'error',\n }\n );\n continue;\n }\n\n const { fill, ...rest } = contentDeclarationFile;\n\n const relativeFilePath = relative(\n configuration.system.baseDir,\n output.filePath\n );\n\n await writeContentDeclaration(\n {\n ...rest,\n filled: true,\n locale: output.isPerLocale ? output.localeList[0] : undefined,\n localId: `${contentDeclarationFile.key}::local::${relativeFilePath}`,\n filePath: relativeFilePath,\n },\n configuration,\n {\n // Per-locale files: write only the specific locale.\n // Multilingual files (string/function fill, single output): include all\n // output locales (including source) so the file is complete.\n localeList: output.isPerLocale\n ? output.localeList\n : (outputLocales ?? configuration.internationalization.locales),\n }\n );\n\n if (output.isPerLocale) {\n appLogger(\n `Auto filled per-locale content declaration for '${colorizeKey(fullDictionary.key)}' written to ${formatPath(output.filePath)} for locale ${formatLocale(output.localeList[0])}`,\n { level: 'info' }\n );\n } else {\n appLogger(\n `Auto filled content declaration for '${colorizeKey(fullDictionary.key)}' written to ${formatPath(output.filePath)}`,\n { level: 'info' }\n );\n }\n }\n};\n"],"mappings":";;;;;;;;;AAWA,MAAa,YAAY,OACvB,wBACA,eACA,eACA,kBACG;CACH,MAAM,YAAY,aAAa,cAAc;CAG7C,MAAM,iBAFe,gBAAgB,cAAc,CAEf,uBAAuB;CAE3D,MAAM,EAAE,aAAa;AAErB,KAAI,CAAC,UAAU;AACb,YAAU,qCAAqC,EAC7C,OAAO,SACR,CAAC;AACF;;CAGF,MAAM,cACJ,uBAAuB,QAAQ,cAAc,YAAY,QAAQ;AAEnE,KAAK,gBAA4B,OAAO;AACtC,YACE,8BAA8B,YAAY,eAAe,IAAI,CAAC,IAC9D,EACE,OAAO,QACR,CACF;AACD;;CAGF,MAAM,oBACJ,iBAAiB,cAAc,qBAAqB,SACpD,QAAQ,WAAW,CAAC,eAAe,SAAS,OAAO,CAAC;CAGtD,MAAM,mBAAmB,gCACvB,uBACD;CAGD,MAAM,aAAa,iBAAiB,QAAQ,WAC1C,iBAAiB,SAAS,OAAO,CAClC;AAED,KAAI,WAAW,WAAW,GAAG;AAC3B,YACE,6CAA6C,YAAY,eAAe,IAAI,CAAC,IAC7E,EACE,OAAO,QACR,CACF;AACD;;CAGF,MAAM,WAAuB,MAAM,eACjC,aACA,YACA,UACA,eAAe,KACf,cACD;AAED,YAAW,MAAM,UAAU,UAAU;AACnC,MAAI,CAAC,OAAO,UAAU;AACpB,aACE,+DAA+D,YAAY,eAAe,IAAI,CAAC,IAC/F,EACE,OAAO,SACR,CACF;AACD;;EAGF,MAAM,EAAE,MAAM,GAAG,SAAS;EAE1B,MAAM,mBAAmB,SACvB,cAAc,OAAO,SACrB,OAAO,SACR;AAED,QAAM,wBACJ;GACE,GAAG;GACH,QAAQ;GACR,QAAQ,OAAO,cAAc,OAAO,WAAW,KAAK;GACpD,SAAS,GAAG,uBAAuB,IAAI,WAAW;GAClD,UAAU;GACX,EACD,eACA,EAIE,YAAY,OAAO,cACf,OAAO,aACN,iBAAiB,cAAc,qBAAqB,SAC1D,CACF;AAED,MAAI,OAAO,YACT,WACE,mDAAmD,YAAY,eAAe,IAAI,CAAC,eAAe,WAAW,OAAO,SAAS,CAAC,cAAc,aAAa,OAAO,WAAW,GAAG,IAC9K,EAAE,OAAO,QAAQ,CAClB;MAED,WACE,wCAAwC,YAAY,eAAe,IAAI,CAAC,eAAe,WAAW,OAAO,SAAS,IAClH,EAAE,OAAO,QAAQ,CAClB"}
@@ -1,2 +1,34 @@
1
- import{join as e,relative as t}from"node:path";import{listGitFiles as n}from"@intlayer/chokidar/cli";import{getConfiguration as r}from"@intlayer/config/node";import{getUnmergedDictionaries as i}from"@intlayer/unmerged-dictionaries-entry";const a=e=>[e].flat(),o=async o=>{let s=r(o?.configOptions),{baseDir:c}=s.system,l=i(s),u=Object.values(l).flat();if(o?.file!==void 0){let n=a(o?.file).map(n=>n.startsWith(`/`)?t(c,n):e(`./`,n));u=u.filter(e=>e.filePath&&n.includes(e.filePath))}o?.keys!==void 0&&(u=u.filter(e=>a(o?.keys)?.includes(e.key))),o?.excludedKeys!==void 0&&(u=u.filter(e=>!a(o?.excludedKeys)?.includes(e.key))),o?.pathFilter!==void 0&&(u=u.filter(e=>a(o?.pathFilter)?.includes(e.filePath??``))),o?.filter!==void 0&&(u=u.filter(o?.filter));let d=o?.gitOptions;if(d){let e=await n(d);e&&(u=u.filter(t=>t.filePath?e.some(e=>t.filePath===e):!1))}return u};export{a as ensureArray,o as getTargetUnmergedDictionaries};
1
+ import { join, relative } from "node:path";
2
+ import { listGitFiles } from "@intlayer/chokidar/cli";
3
+ import { getConfiguration } from "@intlayer/config/node";
4
+ import { getUnmergedDictionaries } from "@intlayer/unmerged-dictionaries-entry";
5
+
6
+ //#region src/getTargetDictionary.ts
7
+ const ensureArray = (value) => [value].flat();
8
+ const getTargetUnmergedDictionaries = async (options) => {
9
+ const configuration = getConfiguration(options?.configOptions);
10
+ const { baseDir } = configuration.system;
11
+ const unmergedDictionariesRecord = getUnmergedDictionaries(configuration);
12
+ let result = Object.values(unmergedDictionariesRecord).flat();
13
+ if (typeof options?.file !== "undefined") {
14
+ const relativeFilePaths = ensureArray(options?.file).map((file) => file.startsWith("/") ? relative(baseDir, file) : join("./", file));
15
+ result = result.filter((dict) => dict.filePath && relativeFilePaths.includes(dict.filePath));
16
+ }
17
+ if (typeof options?.keys !== "undefined") result = result.filter((dict) => ensureArray(options?.keys)?.includes(dict.key));
18
+ if (typeof options?.excludedKeys !== "undefined") result = result.filter((dict) => !ensureArray(options?.excludedKeys)?.includes(dict.key));
19
+ if (typeof options?.pathFilter !== "undefined") result = result.filter((dict) => ensureArray(options?.pathFilter)?.includes(dict.filePath ?? ""));
20
+ if (typeof options?.filter !== "undefined") result = result.filter(options?.filter);
21
+ const gitOptions = options?.gitOptions;
22
+ if (gitOptions) {
23
+ const gitChangedFiles = await listGitFiles(gitOptions);
24
+ if (gitChangedFiles) result = result.filter((dict) => {
25
+ if (!dict.filePath) return false;
26
+ return gitChangedFiles.some((gitFile) => dict.filePath === gitFile);
27
+ });
28
+ }
29
+ return result;
30
+ };
31
+
32
+ //#endregion
33
+ export { ensureArray, getTargetUnmergedDictionaries };
2
34
  //# sourceMappingURL=getTargetDictionary.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"getTargetDictionary.mjs","names":[],"sources":["../../src/getTargetDictionary.ts"],"sourcesContent":["import { join, relative } from 'node:path';\nimport { type ListGitFilesOptions, listGitFiles } from '@intlayer/chokidar/cli';\nimport {\n type GetConfigurationOptions,\n getConfiguration,\n} from '@intlayer/config/node';\nimport type { Dictionary } from '@intlayer/types/dictionary';\nimport { getUnmergedDictionaries } from '@intlayer/unmerged-dictionaries-entry';\n\nexport const ensureArray = <T>(value: T | T[]): T[] => [value].flat() as T[];\n\n// Arguments for the fill function\nexport type GetTargetDictionaryOptions = {\n file?: string | string[];\n keys?: string | string[];\n excludedKeys?: string | string[];\n filter?: (entry: Dictionary) => boolean; // DictionaryEntry needs to be defined\n pathFilter?: string | string[];\n gitOptions?: ListGitFilesOptions;\n configOptions?: GetConfigurationOptions;\n};\n\nexport const getTargetUnmergedDictionaries = async (\n options?: GetTargetDictionaryOptions\n): Promise<Dictionary[]> => {\n const configuration = getConfiguration(options?.configOptions);\n\n const { baseDir } = configuration.system;\n\n const unmergedDictionariesRecord = getUnmergedDictionaries(configuration);\n let result = Object.values(unmergedDictionariesRecord).flat();\n\n // 1. if filePath not defined, list all content declaration files based on unmerged dictionaries list\n if (typeof options?.file !== 'undefined') {\n const fileArray = ensureArray(options?.file);\n const relativeFilePaths = fileArray.map((file) =>\n file.startsWith('/') ? relative(baseDir, file) : join('./', file)\n );\n\n result = result.filter(\n (dict) =>\n dict.filePath &&\n // Check for absolute path\n relativeFilePaths.includes(dict.filePath)\n );\n }\n\n if (typeof options?.keys !== 'undefined') {\n result = result.filter((dict) =>\n ensureArray(options?.keys)?.includes(dict.key)\n );\n }\n\n if (typeof options?.excludedKeys !== 'undefined') {\n result = result.filter(\n (dict) => !ensureArray(options?.excludedKeys)?.includes(dict.key)\n );\n }\n\n if (typeof options?.pathFilter !== 'undefined') {\n result = result.filter((dict) =>\n ensureArray(options?.pathFilter)?.includes(dict.filePath ?? '')\n );\n }\n\n if (typeof options?.filter !== 'undefined') {\n result = result.filter(options?.filter);\n }\n\n const gitOptions = options?.gitOptions;\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 result = result.filter((dict) => {\n if (!dict.filePath) return false;\n\n return gitChangedFiles.some((gitFile) => dict.filePath === gitFile);\n });\n }\n }\n\n return result;\n};\n"],"mappings":"8OASA,MAAa,EAAkB,GAAwB,CAAC,EAAM,CAAC,MAAM,CAaxD,EAAgC,KAC3C,IAC0B,CAC1B,IAAM,EAAgB,EAAiB,GAAS,cAAc,CAExD,CAAE,WAAY,EAAc,OAE5B,EAA6B,EAAwB,EAAc,CACrE,EAAS,OAAO,OAAO,EAA2B,CAAC,MAAM,CAG7D,GAAW,GAAS,OAAS,OAAa,CAExC,IAAM,EADY,EAAY,GAAS,KAAK,CACR,IAAK,GACvC,EAAK,WAAW,IAAI,CAAG,EAAS,EAAS,EAAK,CAAG,EAAK,KAAM,EAAK,CAClE,CAED,EAAS,EAAO,OACb,GACC,EAAK,UAEL,EAAkB,SAAS,EAAK,SAAS,CAC5C,CAGQ,GAAS,OAAS,SAC3B,EAAS,EAAO,OAAQ,GACtB,EAAY,GAAS,KAAK,EAAE,SAAS,EAAK,IAAI,CAC/C,EAGQ,GAAS,eAAiB,SACnC,EAAS,EAAO,OACb,GAAS,CAAC,EAAY,GAAS,aAAa,EAAE,SAAS,EAAK,IAAI,CAClE,EAGQ,GAAS,aAAe,SACjC,EAAS,EAAO,OAAQ,GACtB,EAAY,GAAS,WAAW,EAAE,SAAS,EAAK,UAAY,GAAG,CAChE,EAGQ,GAAS,SAAW,SAC7B,EAAS,EAAO,OAAO,GAAS,OAAO,EAGzC,IAAM,EAAa,GAAS,WAC5B,GAAI,EAAY,CACd,IAAM,EAAkB,MAAM,EAAa,EAAW,CAElD,IAIF,EAAS,EAAO,OAAQ,GACjB,EAAK,SAEH,EAAgB,KAAM,GAAY,EAAK,WAAa,EAAQ,CAFxC,GAG3B,EAIN,OAAO"}
1
+ {"version":3,"file":"getTargetDictionary.mjs","names":[],"sources":["../../src/getTargetDictionary.ts"],"sourcesContent":["import { join, relative } from 'node:path';\nimport { type ListGitFilesOptions, listGitFiles } from '@intlayer/chokidar/cli';\nimport {\n type GetConfigurationOptions,\n getConfiguration,\n} from '@intlayer/config/node';\nimport type { Dictionary } from '@intlayer/types/dictionary';\nimport { getUnmergedDictionaries } from '@intlayer/unmerged-dictionaries-entry';\n\nexport const ensureArray = <T>(value: T | T[]): T[] => [value].flat() as T[];\n\n// Arguments for the fill function\nexport type GetTargetDictionaryOptions = {\n file?: string | string[];\n keys?: string | string[];\n excludedKeys?: string | string[];\n filter?: (entry: Dictionary) => boolean; // DictionaryEntry needs to be defined\n pathFilter?: string | string[];\n gitOptions?: ListGitFilesOptions;\n configOptions?: GetConfigurationOptions;\n};\n\nexport const getTargetUnmergedDictionaries = async (\n options?: GetTargetDictionaryOptions\n): Promise<Dictionary[]> => {\n const configuration = getConfiguration(options?.configOptions);\n\n const { baseDir } = configuration.system;\n\n const unmergedDictionariesRecord = getUnmergedDictionaries(configuration);\n let result = Object.values(unmergedDictionariesRecord).flat();\n\n // 1. if filePath not defined, list all content declaration files based on unmerged dictionaries list\n if (typeof options?.file !== 'undefined') {\n const fileArray = ensureArray(options?.file);\n const relativeFilePaths = fileArray.map((file) =>\n file.startsWith('/') ? relative(baseDir, file) : join('./', file)\n );\n\n result = result.filter(\n (dict) =>\n dict.filePath &&\n // Check for absolute path\n relativeFilePaths.includes(dict.filePath)\n );\n }\n\n if (typeof options?.keys !== 'undefined') {\n result = result.filter((dict) =>\n ensureArray(options?.keys)?.includes(dict.key)\n );\n }\n\n if (typeof options?.excludedKeys !== 'undefined') {\n result = result.filter(\n (dict) => !ensureArray(options?.excludedKeys)?.includes(dict.key)\n );\n }\n\n if (typeof options?.pathFilter !== 'undefined') {\n result = result.filter((dict) =>\n ensureArray(options?.pathFilter)?.includes(dict.filePath ?? '')\n );\n }\n\n if (typeof options?.filter !== 'undefined') {\n result = result.filter(options?.filter);\n }\n\n const gitOptions = options?.gitOptions;\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 result = result.filter((dict) => {\n if (!dict.filePath) return false;\n\n return gitChangedFiles.some((gitFile) => dict.filePath === gitFile);\n });\n }\n }\n\n return result;\n};\n"],"mappings":";;;;;;AASA,MAAa,eAAkB,UAAwB,CAAC,MAAM,CAAC,MAAM;AAarE,MAAa,gCAAgC,OAC3C,YAC0B;CAC1B,MAAM,gBAAgB,iBAAiB,SAAS,cAAc;CAE9D,MAAM,EAAE,YAAY,cAAc;CAElC,MAAM,6BAA6B,wBAAwB,cAAc;CACzE,IAAI,SAAS,OAAO,OAAO,2BAA2B,CAAC,MAAM;AAG7D,KAAI,OAAO,SAAS,SAAS,aAAa;EAExC,MAAM,oBADY,YAAY,SAAS,KAAK,CACR,KAAK,SACvC,KAAK,WAAW,IAAI,GAAG,SAAS,SAAS,KAAK,GAAG,KAAK,MAAM,KAAK,CAClE;AAED,WAAS,OAAO,QACb,SACC,KAAK,YAEL,kBAAkB,SAAS,KAAK,SAAS,CAC5C;;AAGH,KAAI,OAAO,SAAS,SAAS,YAC3B,UAAS,OAAO,QAAQ,SACtB,YAAY,SAAS,KAAK,EAAE,SAAS,KAAK,IAAI,CAC/C;AAGH,KAAI,OAAO,SAAS,iBAAiB,YACnC,UAAS,OAAO,QACb,SAAS,CAAC,YAAY,SAAS,aAAa,EAAE,SAAS,KAAK,IAAI,CAClE;AAGH,KAAI,OAAO,SAAS,eAAe,YACjC,UAAS,OAAO,QAAQ,SACtB,YAAY,SAAS,WAAW,EAAE,SAAS,KAAK,YAAY,GAAG,CAChE;AAGH,KAAI,OAAO,SAAS,WAAW,YAC7B,UAAS,OAAO,OAAO,SAAS,OAAO;CAGzC,MAAM,aAAa,SAAS;AAC5B,KAAI,YAAY;EACd,MAAM,kBAAkB,MAAM,aAAa,WAAW;AAEtD,MAAI,gBAIF,UAAS,OAAO,QAAQ,SAAS;AAC/B,OAAI,CAAC,KAAK,SAAU,QAAO;AAE3B,UAAO,gBAAgB,MAAM,YAAY,KAAK,aAAa,QAAQ;IACnE;;AAIN,QAAO"}
@@ -1 +1,20 @@
1
- import{t as e}from"./editor-D8BGlLzF.mjs";import{extract as t}from"./extract.mjs";import{listProjectsCommand as n}from"./listProjects.mjs";import{liveSync as r}from"./liveSync.mjs";import{listContentDeclaration as i,listContentDeclarationRows as a}from"./listContentDeclaration.mjs";import{findProjectRoot as o,init as s}from"./init.mjs";import{build as c}from"./build.mjs";import{listMissingTranslations as l,listMissingTranslationsWithConfig as u}from"./test/listMissingTranslations.mjs";import{testMissingTranslations as d}from"./test/test.mjs";import{fill as f}from"./fill/fill.mjs";import{PLATFORM_OPTIONS as p,getDetectedPlatform as m,initSkills as h}from"./initSkills.mjs";import{pull as g}from"./pull.mjs";import{push as _}from"./push/push.mjs";import{pushConfig as v}from"./pushConfig.mjs";import"./_utils_asset-B187VPMw.mjs";import{reviewDoc as y}from"./reviewDoc/reviewDoc.mjs";import{searchDoc as b}from"./searchDoc.mjs";import{translateDoc as x}from"./translateDoc/translateDoc.mjs";import{dirname as S,setAPI as C}from"./cli.mjs";export{p as PLATFORM_OPTIONS,c as build,S as dirname,t as extract,f as fill,o as findProjectRoot,m as getDetectedPlatform,s as init,h as initSkills,i as listContentDeclaration,a as listContentDeclarationRows,l as listMissingTranslations,u as listMissingTranslationsWithConfig,n as listProjectsCommand,r as liveSync,g as pull,_ as push,v as pushConfig,y as reviewDoc,b as searchDoc,C as setAPI,e as startEditor,d as testMissingTranslations,x as translateDoc};
1
+ import { extract } from "./extract.mjs";
2
+ import { listProjectsCommand } from "./listProjects.mjs";
3
+ import { liveSync } from "./liveSync.mjs";
4
+ import { listContentDeclaration, listContentDeclarationRows } from "./listContentDeclaration.mjs";
5
+ import { findProjectRoot, init } from "./init.mjs";
6
+ import { startEditor } from "./editor.mjs";
7
+ import { build } from "./build.mjs";
8
+ import { listMissingTranslations, listMissingTranslationsWithConfig } from "./test/listMissingTranslations.mjs";
9
+ import { testMissingTranslations } from "./test/test.mjs";
10
+ import { fill } from "./fill/fill.mjs";
11
+ import { PLATFORM_OPTIONS, getDetectedPlatform, initSkills } from "./initSkills.mjs";
12
+ import { pull } from "./pull.mjs";
13
+ import { push } from "./push/push.mjs";
14
+ import { pushConfig } from "./pushConfig.mjs";
15
+ import { reviewDoc } from "./reviewDoc/reviewDoc.mjs";
16
+ import { searchDoc } from "./searchDoc.mjs";
17
+ import { translateDoc } from "./translateDoc/translateDoc.mjs";
18
+ import { dirname, setAPI } from "./cli.mjs";
19
+
20
+ export { PLATFORM_OPTIONS, build, dirname, extract, fill, findProjectRoot, getDetectedPlatform, init, initSkills, listContentDeclaration, listContentDeclarationRows, listMissingTranslations, listMissingTranslationsWithConfig, listProjectsCommand, liveSync, pull, push, pushConfig, reviewDoc, searchDoc, setAPI, startEditor, testMissingTranslations, translateDoc };
package/dist/esm/init.mjs CHANGED
@@ -1,2 +1,20 @@
1
- import{existsSync as e}from"node:fs";import{join as t,resolve as n}from"node:path";import{initIntlayer as r}from"@intlayer/chokidar/cli";const i=r=>{let i=r;for(;i!==n(i,`..`);){if(e(t(i,`package.json`)))return i;i=n(i,`..`)}return r},a=async e=>{await r(i(e?n(e):process.cwd()))};export{i as findProjectRoot,a as init};
1
+ import { existsSync } from "node:fs";
2
+ import { join, resolve } from "node:path";
3
+ import { initIntlayer } from "@intlayer/chokidar/cli";
4
+
5
+ //#region src/init.ts
6
+ const findProjectRoot = (startDir) => {
7
+ let currentDir = startDir;
8
+ while (currentDir !== resolve(currentDir, "..")) {
9
+ if (existsSync(join(currentDir, "package.json"))) return currentDir;
10
+ currentDir = resolve(currentDir, "..");
11
+ }
12
+ return startDir;
13
+ };
14
+ const init = async (projectRoot) => {
15
+ await initIntlayer(projectRoot ? findProjectRoot(resolve(projectRoot)) : findProjectRoot(process.cwd()));
16
+ };
17
+
18
+ //#endregion
19
+ export { findProjectRoot, init };
2
20
  //# sourceMappingURL=init.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"init.mjs","names":[],"sources":["../../src/init.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\nimport { join, resolve } from 'node:path';\nimport { initIntlayer } from '@intlayer/chokidar/cli';\n\nexport const findProjectRoot = (startDir: string) => {\n let currentDir = startDir;\n\n while (currentDir !== resolve(currentDir, '..')) {\n if (existsSync(join(currentDir, 'package.json'))) {\n return currentDir;\n }\n currentDir = resolve(currentDir, '..');\n }\n\n // If no package.json is found, return the start directory.\n // The initIntlayer function will handle the missing package.json error.\n return startDir;\n};\n\nexport const init = async (projectRoot?: string) => {\n const root = projectRoot\n ? findProjectRoot(resolve(projectRoot))\n : findProjectRoot(process.cwd());\n\n await initIntlayer(root);\n};\n"],"mappings":"yIAIA,MAAa,EAAmB,GAAqB,CACnD,IAAI,EAAa,EAEjB,KAAO,IAAe,EAAQ,EAAY,KAAK,EAAE,CAC/C,GAAI,EAAW,EAAK,EAAY,eAAe,CAAC,CAC9C,OAAO,EAET,EAAa,EAAQ,EAAY,KAAK,CAKxC,OAAO,GAGI,EAAO,KAAO,IAAyB,CAKlD,MAAM,EAHF,EADS,EACO,EAAQ,EAAY,CACpB,QAAQ,KAAK,CAAC,CAEV"}
1
+ {"version":3,"file":"init.mjs","names":[],"sources":["../../src/init.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\nimport { join, resolve } from 'node:path';\nimport { initIntlayer } from '@intlayer/chokidar/cli';\n\nexport const findProjectRoot = (startDir: string) => {\n let currentDir = startDir;\n\n while (currentDir !== resolve(currentDir, '..')) {\n if (existsSync(join(currentDir, 'package.json'))) {\n return currentDir;\n }\n currentDir = resolve(currentDir, '..');\n }\n\n // If no package.json is found, return the start directory.\n // The initIntlayer function will handle the missing package.json error.\n return startDir;\n};\n\nexport const init = async (projectRoot?: string) => {\n const root = projectRoot\n ? findProjectRoot(resolve(projectRoot))\n : findProjectRoot(process.cwd());\n\n await initIntlayer(root);\n};\n"],"mappings":";;;;;AAIA,MAAa,mBAAmB,aAAqB;CACnD,IAAI,aAAa;AAEjB,QAAO,eAAe,QAAQ,YAAY,KAAK,EAAE;AAC/C,MAAI,WAAW,KAAK,YAAY,eAAe,CAAC,CAC9C,QAAO;AAET,eAAa,QAAQ,YAAY,KAAK;;AAKxC,QAAO;;AAGT,MAAa,OAAO,OAAO,gBAAyB;AAKlD,OAAM,aAJO,cACT,gBAAgB,QAAQ,YAAY,CAAC,GACrC,gBAAgB,QAAQ,KAAK,CAAC,CAEV"}