@intlayer/cli 9.0.0-canary.2 → 9.0.0-canary.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/IntlayerEventListener.cjs +1 -1
- package/dist/cjs/IntlayerEventListener.cjs.map +1 -1
- package/dist/cjs/_virtual/_utils_asset.cjs +4 -3
- package/dist/cjs/auth/login.cjs +44 -31
- package/dist/cjs/auth/login.cjs.map +1 -1
- package/dist/cjs/auth/sessionToken.cjs +0 -1
- package/dist/cjs/auth/sessionToken.cjs.map +1 -1
- package/dist/cjs/build.cjs +6 -7
- package/dist/cjs/build.cjs.map +1 -1
- package/dist/cjs/bundle.cjs +32 -1
- package/dist/cjs/bundle.cjs.map +1 -1
- package/dist/cjs/ci.cjs +96 -29
- package/dist/cjs/ci.cjs.map +1 -1
- package/dist/cjs/cli.cjs +163 -125
- package/dist/cjs/cli.cjs.map +1 -1
- package/dist/cjs/config.cjs +2 -3
- package/dist/cjs/config.cjs.map +1 -1
- package/dist/cjs/editor.cjs +0 -1
- package/dist/cjs/editor.cjs.map +1 -1
- package/dist/cjs/extract.cjs +7 -7
- package/dist/cjs/extract.cjs.map +1 -1
- package/dist/cjs/fill/deepMergeContent.cjs.map +1 -1
- package/dist/cjs/fill/extractTranslatableContent.cjs.map +1 -1
- package/dist/cjs/fill/fill.cjs +11 -11
- package/dist/cjs/fill/fill.cjs.map +1 -1
- package/dist/cjs/fill/formatAutoFilledFilePath.cjs +2 -3
- package/dist/cjs/fill/formatAutoFilledFilePath.cjs.map +1 -1
- package/dist/cjs/fill/formatFillData.cjs +3 -4
- package/dist/cjs/fill/formatFillData.cjs.map +1 -1
- package/dist/cjs/fill/getAvailableLocalesInDictionary.cjs.map +1 -1
- package/dist/cjs/fill/getFilterMissingContentPerLocale.cjs.map +1 -1
- package/dist/cjs/fill/listTranslationsTasks.cjs +2 -2
- package/dist/cjs/fill/listTranslationsTasks.cjs.map +1 -1
- package/dist/cjs/fill/translateDictionary.cjs +7 -7
- package/dist/cjs/fill/translateDictionary.cjs.map +1 -1
- package/dist/cjs/fill/writeFill.cjs +5 -6
- package/dist/cjs/fill/writeFill.cjs.map +1 -1
- package/dist/cjs/getTargetDictionary.cjs +2 -3
- package/dist/cjs/getTargetDictionary.cjs.map +1 -1
- package/dist/cjs/index.cjs +4 -4
- package/dist/cjs/init.cjs +369 -3
- package/dist/cjs/init.cjs.map +1 -1
- package/dist/cjs/initBuildOptimization.cjs +81 -0
- package/dist/cjs/initBuildOptimization.cjs.map +1 -0
- package/dist/cjs/initCompiler.cjs +117 -0
- package/dist/cjs/initCompiler.cjs.map +1 -0
- package/dist/cjs/initMCP.cjs +24 -21
- package/dist/cjs/initMCP.cjs.map +1 -1
- package/dist/cjs/initSkills.cjs +31 -28
- package/dist/cjs/initSkills.cjs.map +1 -1
- package/dist/cjs/listContentDeclaration.cjs +4 -5
- package/dist/cjs/listContentDeclaration.cjs.map +1 -1
- package/dist/cjs/listProjects.cjs +2 -3
- package/dist/cjs/listProjects.cjs.map +1 -1
- package/dist/cjs/liveSync.cjs +4 -4
- package/dist/cjs/liveSync.cjs.map +1 -1
- package/dist/cjs/pull.cjs +7 -7
- package/dist/cjs/pull.cjs.map +1 -1
- package/dist/cjs/push/pullLog.cjs +2 -2
- package/dist/cjs/push/pullLog.cjs.map +1 -1
- package/dist/cjs/push/push.cjs +13 -13
- package/dist/cjs/push/push.cjs.map +1 -1
- package/dist/cjs/pushConfig.cjs +2 -3
- package/dist/cjs/pushConfig.cjs.map +1 -1
- package/dist/cjs/pushLog.cjs.map +1 -1
- package/dist/cjs/reviewDoc/reviewDoc.cjs +16 -16
- package/dist/cjs/reviewDoc/reviewDoc.cjs.map +1 -1
- package/dist/cjs/reviewDoc/reviewDocBlockAware.cjs +12 -12
- package/dist/cjs/reviewDoc/reviewDocBlockAware.cjs.map +1 -1
- package/dist/cjs/reviewDoc/reviewDocLog.cjs +7 -8
- package/dist/cjs/reviewDoc/reviewDocLog.cjs.map +1 -1
- package/dist/cjs/scan.cjs +5 -5
- package/dist/cjs/scan.cjs.map +1 -1
- package/dist/cjs/searchDoc.cjs +2 -3
- package/dist/cjs/searchDoc.cjs.map +1 -1
- package/dist/cjs/test/listMissingTranslations.cjs +3 -4
- package/dist/cjs/test/listMissingTranslations.cjs.map +1 -1
- package/dist/cjs/test/test.cjs +11 -11
- package/dist/cjs/test/test.cjs.map +1 -1
- package/dist/cjs/translateDoc/translateDoc.cjs +6 -6
- package/dist/cjs/translateDoc/translateDoc.cjs.map +1 -1
- package/dist/cjs/translateDoc/translateFile.cjs +6 -6
- package/dist/cjs/translateDoc/translateFile.cjs.map +1 -1
- package/dist/cjs/translateDoc/validation.cjs.map +1 -1
- package/dist/cjs/utils/calculateChunks.cjs +4 -5
- package/dist/cjs/utils/calculateChunks.cjs.map +1 -1
- package/dist/cjs/utils/checkAccess.cjs.map +1 -1
- package/dist/cjs/utils/checkConfigConsistency.cjs.map +1 -1
- package/dist/cjs/utils/checkFileModifiedRange.cjs.map +1 -1
- package/dist/cjs/utils/checkLastUpdateTime.cjs +0 -1
- package/dist/cjs/utils/checkLastUpdateTime.cjs.map +1 -1
- package/dist/cjs/utils/chunkInference.cjs +0 -1
- package/dist/cjs/utils/chunkInference.cjs.map +1 -1
- package/dist/cjs/utils/fixChunkStartEndChars.cjs.map +1 -1
- package/dist/cjs/utils/formatLineRanges.cjs.map +1 -1
- package/dist/cjs/utils/formatTimeDiff.cjs.map +1 -1
- package/dist/cjs/utils/getIsFileUpdatedRecently.cjs +1 -2
- package/dist/cjs/utils/getIsFileUpdatedRecently.cjs.map +1 -1
- package/dist/cjs/utils/getOutputFilePath.cjs +2 -3
- package/dist/cjs/utils/getOutputFilePath.cjs.map +1 -1
- package/dist/cjs/utils/getParentPackageJSON.cjs +0 -1
- package/dist/cjs/utils/getParentPackageJSON.cjs.map +1 -1
- package/dist/cjs/utils/listSpecialChars.cjs.map +1 -1
- package/dist/cjs/utils/mapChunksBetweenFiles.cjs +4 -5
- package/dist/cjs/utils/mapChunksBetweenFiles.cjs.map +1 -1
- package/dist/cjs/utils/openBrowser.cjs +0 -1
- package/dist/cjs/utils/openBrowser.cjs.map +1 -1
- package/dist/cjs/utils/reorderParagraphs.cjs.map +1 -1
- package/dist/cjs/utils/selectCmsEnvironment.cjs.map +1 -1
- package/dist/cjs/utils/setupAI.cjs.map +1 -1
- package/dist/cjs/watch.cjs +28 -9
- package/dist/cjs/watch.cjs.map +1 -1
- package/dist/esm/IntlayerEventListener.mjs +1 -0
- package/dist/esm/IntlayerEventListener.mjs.map +1 -1
- package/dist/esm/_virtual/_utils_asset.mjs +4 -2
- package/dist/esm/auth/login.mjs +43 -30
- package/dist/esm/auth/login.mjs.map +1 -1
- package/dist/esm/auth/sessionToken.mjs.map +1 -1
- package/dist/esm/build.mjs +3 -3
- package/dist/esm/build.mjs.map +1 -1
- package/dist/esm/bundle.mjs +32 -1
- package/dist/esm/bundle.mjs.map +1 -1
- package/dist/esm/ci.mjs +95 -30
- package/dist/esm/ci.mjs.map +1 -1
- package/dist/esm/cli.mjs +141 -102
- package/dist/esm/cli.mjs.map +1 -1
- package/dist/esm/config.mjs +1 -1
- package/dist/esm/config.mjs.map +1 -1
- package/dist/esm/editor.mjs.map +1 -1
- package/dist/esm/extract.mjs +3 -3
- package/dist/esm/extract.mjs.map +1 -1
- package/dist/esm/fill/deepMergeContent.mjs.map +1 -1
- package/dist/esm/fill/extractTranslatableContent.mjs.map +1 -1
- package/dist/esm/fill/fill.mjs +3 -3
- package/dist/esm/fill/fill.mjs.map +1 -1
- package/dist/esm/fill/formatAutoFilledFilePath.mjs +1 -1
- package/dist/esm/fill/formatAutoFilledFilePath.mjs.map +1 -1
- package/dist/esm/fill/formatFillData.mjs +1 -1
- package/dist/esm/fill/formatFillData.mjs.map +1 -1
- package/dist/esm/fill/getAvailableLocalesInDictionary.mjs.map +1 -1
- package/dist/esm/fill/getFilterMissingContentPerLocale.mjs.map +1 -1
- package/dist/esm/fill/listTranslationsTasks.mjs +1 -1
- package/dist/esm/fill/listTranslationsTasks.mjs.map +1 -1
- package/dist/esm/fill/translateDictionary.mjs +1 -1
- package/dist/esm/fill/translateDictionary.mjs.map +1 -1
- package/dist/esm/fill/writeFill.mjs +2 -2
- package/dist/esm/fill/writeFill.mjs.map +1 -1
- package/dist/esm/getTargetDictionary.mjs +1 -1
- package/dist/esm/getTargetDictionary.mjs.map +1 -1
- package/dist/esm/index.mjs +4 -4
- package/dist/esm/init.mjs +368 -4
- package/dist/esm/init.mjs.map +1 -1
- package/dist/esm/initBuildOptimization.mjs +78 -0
- package/dist/esm/initBuildOptimization.mjs.map +1 -0
- package/dist/esm/initCompiler.mjs +114 -0
- package/dist/esm/initCompiler.mjs.map +1 -0
- package/dist/esm/initMCP.mjs +23 -20
- package/dist/esm/initMCP.mjs.map +1 -1
- package/dist/esm/initSkills.mjs +22 -19
- package/dist/esm/initSkills.mjs.map +1 -1
- package/dist/esm/listContentDeclaration.mjs +2 -2
- package/dist/esm/listContentDeclaration.mjs.map +1 -1
- package/dist/esm/listProjects.mjs +1 -1
- package/dist/esm/listProjects.mjs.map +1 -1
- package/dist/esm/liveSync.mjs +2 -2
- package/dist/esm/liveSync.mjs.map +1 -1
- package/dist/esm/pull.mjs +4 -4
- package/dist/esm/pull.mjs.map +1 -1
- package/dist/esm/push/pullLog.mjs +2 -2
- package/dist/esm/push/pullLog.mjs.map +1 -1
- package/dist/esm/push/push.mjs +3 -3
- package/dist/esm/push/push.mjs.map +1 -1
- package/dist/esm/pushConfig.mjs +1 -1
- package/dist/esm/pushConfig.mjs.map +1 -1
- package/dist/esm/pushLog.mjs.map +1 -1
- package/dist/esm/reviewDoc/reviewDoc.mjs +3 -3
- package/dist/esm/reviewDoc/reviewDoc.mjs.map +1 -1
- package/dist/esm/reviewDoc/reviewDocBlockAware.mjs +3 -3
- package/dist/esm/reviewDoc/reviewDocBlockAware.mjs.map +1 -1
- package/dist/esm/reviewDoc/reviewDocLog.mjs +2 -2
- package/dist/esm/reviewDoc/reviewDocLog.mjs.map +1 -1
- package/dist/esm/scan.mjs +1 -1
- package/dist/esm/scan.mjs.map +1 -1
- package/dist/esm/searchDoc.mjs +1 -1
- package/dist/esm/searchDoc.mjs.map +1 -1
- package/dist/esm/test/listMissingTranslations.mjs +2 -2
- package/dist/esm/test/listMissingTranslations.mjs.map +1 -1
- package/dist/esm/test/test.mjs +2 -2
- package/dist/esm/test/test.mjs.map +1 -1
- package/dist/esm/translateDoc/translateDoc.mjs +2 -2
- package/dist/esm/translateDoc/translateDoc.mjs.map +1 -1
- package/dist/esm/translateDoc/translateFile.mjs +2 -2
- package/dist/esm/translateDoc/translateFile.mjs.map +1 -1
- package/dist/esm/translateDoc/validation.mjs.map +1 -1
- package/dist/esm/utils/calculateChunks.mjs +1 -1
- package/dist/esm/utils/calculateChunks.mjs.map +1 -1
- package/dist/esm/utils/checkAccess.mjs.map +1 -1
- package/dist/esm/utils/checkConfigConsistency.mjs.map +1 -1
- package/dist/esm/utils/checkFileModifiedRange.mjs.map +1 -1
- package/dist/esm/utils/checkLastUpdateTime.mjs.map +1 -1
- package/dist/esm/utils/chunkInference.mjs.map +1 -1
- package/dist/esm/utils/fixChunkStartEndChars.mjs.map +1 -1
- package/dist/esm/utils/formatLineRanges.mjs.map +1 -1
- package/dist/esm/utils/formatTimeDiff.mjs.map +1 -1
- package/dist/esm/utils/getIsFileUpdatedRecently.mjs +1 -1
- package/dist/esm/utils/getIsFileUpdatedRecently.mjs.map +1 -1
- package/dist/esm/utils/getOutputFilePath.mjs +1 -1
- package/dist/esm/utils/getOutputFilePath.mjs.map +1 -1
- package/dist/esm/utils/getParentPackageJSON.mjs.map +1 -1
- package/dist/esm/utils/listSpecialChars.mjs.map +1 -1
- package/dist/esm/utils/mapChunksBetweenFiles.mjs +1 -1
- package/dist/esm/utils/mapChunksBetweenFiles.mjs.map +1 -1
- package/dist/esm/utils/openBrowser.mjs.map +1 -1
- package/dist/esm/utils/reorderParagraphs.mjs.map +1 -1
- package/dist/esm/utils/selectCmsEnvironment.mjs.map +1 -1
- package/dist/esm/utils/setupAI.mjs.map +1 -1
- package/dist/esm/watch.mjs +25 -5
- package/dist/esm/watch.mjs.map +1 -1
- package/dist/types/IntlayerEventListener.d.ts +0 -1
- package/dist/types/IntlayerEventListener.d.ts.map +1 -1
- package/dist/types/auth/login.d.ts +13 -5
- package/dist/types/auth/login.d.ts.map +1 -1
- package/dist/types/auth/sessionToken.d.ts +0 -1
- package/dist/types/auth/sessionToken.d.ts.map +1 -1
- package/dist/types/build.d.ts +0 -1
- package/dist/types/build.d.ts.map +1 -1
- package/dist/types/bundle.d.ts +0 -1
- package/dist/types/bundle.d.ts.map +1 -1
- package/dist/types/ci.d.ts +40 -1
- package/dist/types/ci.d.ts.map +1 -1
- package/dist/types/cli.d.ts +0 -1
- package/dist/types/cli.d.ts.map +1 -1
- package/dist/types/config.d.ts +0 -1
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/editor.d.ts.map +1 -1
- package/dist/types/extract.d.ts +0 -1
- package/dist/types/extract.d.ts.map +1 -1
- package/dist/types/fill/deepMergeContent.d.ts.map +1 -1
- package/dist/types/fill/extractTranslatableContent.d.ts.map +1 -1
- package/dist/types/fill/fill.d.ts +1 -2
- package/dist/types/fill/fill.d.ts.map +1 -1
- package/dist/types/fill/formatAutoFilledFilePath.d.ts +0 -1
- package/dist/types/fill/formatAutoFilledFilePath.d.ts.map +1 -1
- package/dist/types/fill/formatFillData.d.ts +0 -1
- package/dist/types/fill/formatFillData.d.ts.map +1 -1
- package/dist/types/fill/getAvailableLocalesInDictionary.d.ts +0 -1
- package/dist/types/fill/getAvailableLocalesInDictionary.d.ts.map +1 -1
- package/dist/types/fill/getFilterMissingContentPerLocale.d.ts +0 -1
- package/dist/types/fill/getFilterMissingContentPerLocale.d.ts.map +1 -1
- package/dist/types/fill/listTranslationsTasks.d.ts +1 -2
- package/dist/types/fill/listTranslationsTasks.d.ts.map +1 -1
- package/dist/types/fill/translateDictionary.d.ts +1 -3
- package/dist/types/fill/translateDictionary.d.ts.map +1 -1
- package/dist/types/fill/writeFill.d.ts +0 -1
- package/dist/types/fill/writeFill.d.ts.map +1 -1
- package/dist/types/getTargetDictionary.d.ts +1 -2
- package/dist/types/getTargetDictionary.d.ts.map +1 -1
- package/dist/types/index.d.ts +5 -4
- package/dist/types/init.d.ts +2 -3
- package/dist/types/init.d.ts.map +1 -1
- package/dist/types/initBuildOptimization.d.ts +20 -0
- package/dist/types/initBuildOptimization.d.ts.map +1 -0
- package/dist/types/initCompiler.d.ts +18 -0
- package/dist/types/initCompiler.d.ts.map +1 -0
- package/dist/types/initMCP.d.ts +2 -1
- package/dist/types/initMCP.d.ts.map +1 -1
- package/dist/types/initSkills.d.ts +2 -3
- package/dist/types/initSkills.d.ts.map +1 -1
- package/dist/types/listContentDeclaration.d.ts +0 -1
- package/dist/types/listContentDeclaration.d.ts.map +1 -1
- package/dist/types/listProjects.d.ts +1 -2
- package/dist/types/listProjects.d.ts.map +1 -1
- package/dist/types/liveSync.d.ts +0 -1
- package/dist/types/liveSync.d.ts.map +1 -1
- package/dist/types/pull.d.ts +0 -1
- package/dist/types/pull.d.ts.map +1 -1
- package/dist/types/push/pullLog.d.ts +1 -2
- package/dist/types/push/pullLog.d.ts.map +1 -1
- package/dist/types/push/push.d.ts +1 -2
- package/dist/types/push/push.d.ts.map +1 -1
- package/dist/types/pushConfig.d.ts +0 -1
- package/dist/types/pushConfig.d.ts.map +1 -1
- package/dist/types/pushLog.d.ts.map +1 -1
- package/dist/types/reviewDoc/reviewDoc.d.ts +2 -17
- package/dist/types/reviewDoc/reviewDoc.d.ts.map +1 -1
- package/dist/types/reviewDoc/reviewDocBlockAware.d.ts +0 -1
- package/dist/types/reviewDoc/reviewDocBlockAware.d.ts.map +1 -1
- package/dist/types/reviewDoc/reviewDocLog.d.ts +1 -2
- package/dist/types/reviewDoc/reviewDocLog.d.ts.map +1 -1
- package/dist/types/scan.d.ts +3 -2
- package/dist/types/scan.d.ts.map +1 -1
- package/dist/types/searchDoc.d.ts +1 -6
- package/dist/types/searchDoc.d.ts.map +1 -1
- package/dist/types/test/listMissingTranslations.d.ts +0 -1
- package/dist/types/test/listMissingTranslations.d.ts.map +1 -1
- package/dist/types/test/test.d.ts +0 -1
- package/dist/types/test/test.d.ts.map +1 -1
- package/dist/types/translateDoc/translateDoc.d.ts +1 -16
- package/dist/types/translateDoc/translateDoc.d.ts.map +1 -1
- package/dist/types/translateDoc/translateFile.d.ts +1 -16
- package/dist/types/translateDoc/translateFile.d.ts.map +1 -1
- package/dist/types/translateDoc/types.d.ts +1 -2
- package/dist/types/translateDoc/types.d.ts.map +1 -1
- package/dist/types/translateDoc/validation.d.ts +0 -1
- package/dist/types/translateDoc/validation.d.ts.map +1 -1
- package/dist/types/utils/calculateChunks.d.ts.map +1 -1
- package/dist/types/utils/checkAccess.d.ts +0 -1
- package/dist/types/utils/checkAccess.d.ts.map +1 -1
- package/dist/types/utils/checkConfigConsistency.d.ts.map +1 -1
- package/dist/types/utils/checkFileModifiedRange.d.ts.map +1 -1
- package/dist/types/utils/checkLastUpdateTime.d.ts.map +1 -1
- package/dist/types/utils/chunkInference.d.ts +0 -1
- package/dist/types/utils/chunkInference.d.ts.map +1 -1
- package/dist/types/utils/fixChunkStartEndChars.d.ts.map +1 -1
- package/dist/types/utils/formatLineRanges.d.ts.map +1 -1
- package/dist/types/utils/formatTimeDiff.d.ts.map +1 -1
- package/dist/types/utils/getIsFileUpdatedRecently.d.ts.map +1 -1
- package/dist/types/utils/getOutputFilePath.d.ts +0 -1
- package/dist/types/utils/getOutputFilePath.d.ts.map +1 -1
- package/dist/types/utils/getParentPackageJSON.d.ts.map +1 -1
- package/dist/types/utils/listSpecialChars.d.ts +4 -2
- package/dist/types/utils/listSpecialChars.d.ts.map +1 -1
- package/dist/types/utils/mapChunksBetweenFiles.d.ts +0 -1
- package/dist/types/utils/mapChunksBetweenFiles.d.ts.map +1 -1
- package/dist/types/utils/openBrowser.d.ts.map +1 -1
- package/dist/types/utils/reorderParagraphs.d.ts.map +1 -1
- package/dist/types/utils/selectCmsEnvironment.d.ts +0 -1
- package/dist/types/utils/selectCmsEnvironment.d.ts.map +1 -1
- package/dist/types/utils/setupAI.d.ts +1 -3
- package/dist/types/utils/setupAI.d.ts.map +1 -1
- package/dist/types/watch.d.ts +0 -1
- package/dist/types/watch.d.ts.map +1 -1
- package/package.json +17 -17
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getFilterMissingContentPerLocale.mjs","names":[],"sources":["../../../src/fill/getFilterMissingContentPerLocale.ts"],"sourcesContent":["import type { Dictionary } from '@intlayer/types/dictionary';\n\n/**\n * Recursively compares source content with target content and returns only the missing parts.\n * For per-locale files where content is simple JSON (not translation nodes).\n *\n * @param sourceContent - The source content to check\n * @param targetContent - The existing target content\n * @returns Only the content that's missing in the target\n */\nconst filterMissingContent = (sourceContent: any, targetContent: any): any => {\n // Source is null - only missing if target doesn't already have null\n if (sourceContent === null) {\n return targetContent === null ? undefined : null;\n }\n\n // If target doesn't exist or is null, all source content is missing\n if (targetContent === undefined || targetContent === null) {\n return sourceContent;\n }\n\n // Primitive values: if target exists (even if empty string), consider it translated\n if (typeof sourceContent !== 'object') {\n return undefined;\n }\n\n // Handle arrays\n if (Array.isArray(sourceContent)) {\n if (!Array.isArray(targetContent)) {\n return sourceContent;\n }\n\n const missingItems = sourceContent\n .map((item, index) => filterMissingContent(item, targetContent[index]))\n .filter((item) => item !== undefined);\n\n return missingItems.length > 0 ? missingItems : undefined;\n }\n\n // Handle objects\n const result: any = {};\n let hasMissingContent = false;\n\n for (const [key, value] of Object.entries(sourceContent)) {\n const targetValue = targetContent?.[key];\n const missingValue = filterMissingContent(value, targetValue);\n\n if (missingValue !== undefined) {\n result[key] = missingValue;\n hasMissingContent = true;\n }\n }\n\n return hasMissingContent ? result : undefined;\n};\n\n/**\n * Filters a dictionary to only include content that's missing in the target dictionary.\n * Used for per-locale content declarations in 'complete' mode.\n *\n * @param sourceDictionary - The source dictionary with content to translate\n * @param targetDictionary - The existing target dictionary\n * @returns A dictionary with only the missing content\n */\nexport const getFilterMissingContentPerLocale = (\n sourceDictionary: Dictionary,\n targetDictionary: Dictionary | undefined\n): Dictionary => {\n if (!targetDictionary || !targetDictionary.content) {\n // If no target exists, all source content is missing\n return sourceDictionary;\n }\n\n const missingContent = filterMissingContent(\n sourceDictionary.content,\n targetDictionary.content\n );\n\n return {\n ...sourceDictionary,\n content: missingContent ?? {},\n };\n};\n"],"mappings":";;;;;;;;;AAUA,MAAM,wBAAwB,eAAoB,kBAA4B;
|
|
1
|
+
{"version":3,"file":"getFilterMissingContentPerLocale.mjs","names":[],"sources":["../../../src/fill/getFilterMissingContentPerLocale.ts"],"sourcesContent":["import type { Dictionary } from '@intlayer/types/dictionary';\n\n/**\n * Recursively compares source content with target content and returns only the missing parts.\n * For per-locale files where content is simple JSON (not translation nodes).\n *\n * @param sourceContent - The source content to check\n * @param targetContent - The existing target content\n * @returns Only the content that's missing in the target\n */\nconst filterMissingContent = (sourceContent: any, targetContent: any): any => {\n // Source is null - only missing if target doesn't already have null\n if (sourceContent === null) {\n return targetContent === null ? undefined : null;\n }\n\n // If target doesn't exist or is null, all source content is missing\n if (targetContent === undefined || targetContent === null) {\n return sourceContent;\n }\n\n // Primitive values: if target exists (even if empty string), consider it translated\n if (typeof sourceContent !== 'object') {\n return undefined;\n }\n\n // Handle arrays\n if (Array.isArray(sourceContent)) {\n if (!Array.isArray(targetContent)) {\n return sourceContent;\n }\n\n const missingItems = sourceContent\n .map((item, index) => filterMissingContent(item, targetContent[index]))\n .filter((item) => item !== undefined);\n\n return missingItems.length > 0 ? missingItems : undefined;\n }\n\n // Handle objects\n const result: any = {};\n let hasMissingContent = false;\n\n for (const [key, value] of Object.entries(sourceContent)) {\n const targetValue = targetContent?.[key];\n const missingValue = filterMissingContent(value, targetValue);\n\n if (missingValue !== undefined) {\n result[key] = missingValue;\n hasMissingContent = true;\n }\n }\n\n return hasMissingContent ? result : undefined;\n};\n\n/**\n * Filters a dictionary to only include content that's missing in the target dictionary.\n * Used for per-locale content declarations in 'complete' mode.\n *\n * @param sourceDictionary - The source dictionary with content to translate\n * @param targetDictionary - The existing target dictionary\n * @returns A dictionary with only the missing content\n */\nexport const getFilterMissingContentPerLocale = (\n sourceDictionary: Dictionary,\n targetDictionary: Dictionary | undefined\n): Dictionary => {\n if (!targetDictionary || !targetDictionary.content) {\n // If no target exists, all source content is missing\n return sourceDictionary;\n }\n\n const missingContent = filterMissingContent(\n sourceDictionary.content,\n targetDictionary.content\n );\n\n return {\n ...sourceDictionary,\n content: missingContent ?? {},\n };\n};\n"],"mappings":";;;;;;;;;AAUA,MAAM,wBAAwB,eAAoB,kBAA4B;CAE5E,IAAI,kBAAkB,MACpB,OAAO,kBAAkB,OAAO,SAAY;CAI9C,IAAI,kBAAkB,UAAa,kBAAkB,MACnD,OAAO;CAIT,IAAI,OAAO,kBAAkB,UAC3B;CAIF,IAAI,MAAM,QAAQ,aAAa,GAAG;EAChC,IAAI,CAAC,MAAM,QAAQ,aAAa,GAC9B,OAAO;EAGT,MAAM,eAAe,cAClB,KAAK,MAAM,UAAU,qBAAqB,MAAM,cAAc,MAAM,CAAC,CAAC,CACtE,QAAQ,SAAS,SAAS,MAAS;EAEtC,OAAO,aAAa,SAAS,IAAI,eAAe;CAClD;CAGA,MAAM,SAAc,CAAC;CACrB,IAAI,oBAAoB;CAExB,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,aAAa,GAAG;EACxD,MAAM,cAAc,gBAAgB;EACpC,MAAM,eAAe,qBAAqB,OAAO,WAAW;EAE5D,IAAI,iBAAiB,QAAW;GAC9B,OAAO,OAAO;GACd,oBAAoB;EACtB;CACF;CAEA,OAAO,oBAAoB,SAAS;AACtC;;;;;;;;;AAUA,MAAa,oCACX,kBACA,qBACe;CACf,IAAI,CAAC,oBAAoB,CAAC,iBAAiB,SAEzC,OAAO;CAGT,MAAM,iBAAiB,qBACrB,iBAAiB,SACjB,iBAAiB,OACnB;CAEA,OAAO;EACL,GAAG;EACH,SAAS,kBAAkB,CAAC;CAC9B;AACF"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { listMissingTranslationsWithConfig } from "../test/listMissingTranslations.mjs";
|
|
2
2
|
import { basename } from "node:path";
|
|
3
|
-
import { formatLocale } from "@intlayer/chokidar/utils";
|
|
4
3
|
import * as ANSIColors from "@intlayer/config/colors";
|
|
5
4
|
import { colon, colorize, colorizeKey, colorizePath, getAppLogger } from "@intlayer/config/logger";
|
|
5
|
+
import { formatLocale } from "@intlayer/engine/utils";
|
|
6
6
|
import { getUnmergedDictionaries } from "@intlayer/unmerged-dictionaries-entry";
|
|
7
7
|
import { getFilterTranslationsOnlyDictionary } from "@intlayer/core/plugins";
|
|
8
8
|
import { getDictionaries } from "@intlayer/dictionaries-entry";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"listTranslationsTasks.mjs","names":[],"sources":["../../../src/fill/listTranslationsTasks.ts"],"sourcesContent":["import { basename } from 'node:path';\nimport
|
|
1
|
+
{"version":3,"file":"listTranslationsTasks.mjs","names":[],"sources":["../../../src/fill/listTranslationsTasks.ts"],"sourcesContent":["import { basename } from 'node:path';\nimport * as ANSIColors from '@intlayer/config/colors';\nimport {\n colon,\n colorize,\n colorizeKey,\n colorizePath,\n getAppLogger,\n} from '@intlayer/config/logger';\nimport { getFilterTranslationsOnlyDictionary } from '@intlayer/core/plugins';\nimport { getDictionaries } from '@intlayer/dictionaries-entry';\nimport { formatLocale } from '@intlayer/engine/utils';\nimport type { Locale } from '@intlayer/types/allLocales';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport type { Dictionary, LocalDictionaryId } from '@intlayer/types/dictionary';\nimport { getUnmergedDictionaries } from '@intlayer/unmerged-dictionaries-entry';\nimport { listMissingTranslationsWithConfig } from '../test';\n\nexport type TranslationTask = {\n dictionaryKey: string;\n dictionaryLocalId: LocalDictionaryId;\n sourceLocale: Locale;\n targetLocales: Locale[];\n dictionaryPreset: string;\n dictionaryFilePath: string;\n};\n\nexport const listTranslationsTasks = (\n localIds: LocalDictionaryId[],\n outputLocales: Locale[],\n mode: 'complete' | 'review',\n baseLocale: Locale,\n configuration: IntlayerConfig\n): TranslationTask[] => {\n const appLogger = getAppLogger(configuration);\n\n const mergedDictionariesRecord = getDictionaries(configuration);\n const unmergedDictionariesRecord = getUnmergedDictionaries(configuration);\n\n const allFlatDictionaries = Object.values(unmergedDictionariesRecord).flat();\n const dictionariesToProcess = allFlatDictionaries.filter((dictionary) =>\n localIds.includes(dictionary.localId!)\n );\n\n const { missingTranslations } =\n listMissingTranslationsWithConfig(configuration);\n\n const maxKeyLength = Math.max(\n ...dictionariesToProcess.map((dictionary) => dictionary.key.length)\n );\n\n const translationTasks: TranslationTask[] = [];\n\n for (const targetUnmergedDictionary of dictionariesToProcess) {\n const dictionaryPreset = colon(\n [\n ' - ',\n colorize('[', ANSIColors.GREY_DARK),\n colorizeKey(targetUnmergedDictionary.key),\n colorize(']', ANSIColors.GREY_DARK),\n ].join(''),\n { colSize: maxKeyLength + 6 }\n );\n\n const dictionaryKey = targetUnmergedDictionary.key;\n const dictionaryLocalId = targetUnmergedDictionary.localId!;\n const mainDictionaryToProcess: Dictionary =\n mergedDictionariesRecord[dictionaryKey];\n const dictionaryFilled = targetUnmergedDictionary.filled ?? false;\n\n if (dictionaryFilled === true) {\n continue;\n }\n\n const dictionaryFill =\n targetUnmergedDictionary.fill ?? configuration.dictionary?.fill ?? false;\n\n if (dictionaryFill === false) continue;\n\n const sourceLocale: Locale = (targetUnmergedDictionary.locale ??\n baseLocale) as Locale;\n\n if (!mainDictionaryToProcess) {\n appLogger(\n `${dictionaryPreset} Dictionary not found in dictionariesRecord. Skipping.`,\n {\n level: 'warn',\n }\n );\n continue;\n }\n\n if (!targetUnmergedDictionary.filePath) {\n appLogger(`${dictionaryPreset} Dictionary has no file path. Skipping.`, {\n level: 'warn',\n });\n continue;\n }\n\n const sourceLocaleContent = getFilterTranslationsOnlyDictionary(\n mainDictionaryToProcess,\n sourceLocale\n );\n\n if (\n Object.keys(sourceLocaleContent as Record<string, unknown>).length === 0\n ) {\n appLogger(\n `${dictionaryPreset} No content found for dictionary in source locale ${formatLocale(sourceLocale)}. Skipping translation for this dictionary.`,\n {\n level: 'warn',\n }\n );\n continue;\n }\n\n /**\n * In 'complete' mode, filter only the missing locales to translate\n *\n * Skip the dictionary if there are no missing locales to translate\n */\n let outputLocalesList: Locale[] = outputLocales as Locale[];\n\n if (mode === 'complete') {\n outputLocalesList =\n missingTranslations\n .find(\n (missingTranslation) => missingTranslation.key === dictionaryKey\n )\n ?.locales.filter((locale) => outputLocales.includes(locale)) ?? [];\n }\n\n if (outputLocalesList.length === 0) {\n appLogger(\n `${dictionaryPreset} ${colorize('No locales to fill, Skipping', ANSIColors.GREY_DARK)} ${colorizePath(basename(targetUnmergedDictionary.filePath))}`,\n {\n level: 'warn',\n }\n );\n continue;\n }\n\n translationTasks.push({\n dictionaryKey,\n dictionaryLocalId,\n sourceLocale,\n targetLocales: outputLocalesList,\n dictionaryPreset,\n dictionaryFilePath: targetUnmergedDictionary.filePath,\n });\n }\n\n // Return the list of tasks to execute\n return translationTasks;\n};\n"],"mappings":";;;;;;;;;;AA2BA,MAAa,yBACX,UACA,eACA,MACA,YACA,kBACsB;CACtB,MAAM,YAAY,aAAa,aAAa;CAE5C,MAAM,2BAA2B,gBAAgB,aAAa;CAC9D,MAAM,6BAA6B,wBAAwB,aAAa;CAGxE,MAAM,wBADsB,OAAO,OAAO,0BAA0B,CAAC,CAAC,KACtB,CAAC,CAAC,QAAQ,eACxD,SAAS,SAAS,WAAW,OAAQ,CACvC;CAEA,MAAM,EAAE,wBACN,kCAAkC,aAAa;CAEjD,MAAM,eAAe,KAAK,IACxB,GAAG,sBAAsB,KAAK,eAAe,WAAW,IAAI,MAAM,CACpE;CAEA,MAAM,mBAAsC,CAAC;CAE7C,KAAK,MAAM,4BAA4B,uBAAuB;EAC5D,MAAM,mBAAmB,MACvB;GACE;GACA,SAAS,KAAK,WAAW,SAAS;GAClC,YAAY,yBAAyB,GAAG;GACxC,SAAS,KAAK,WAAW,SAAS;EACpC,CAAC,CAAC,KAAK,EAAE,GACT,EAAE,SAAS,eAAe,EAAE,CAC9B;EAEA,MAAM,gBAAgB,yBAAyB;EAC/C,MAAM,oBAAoB,yBAAyB;EACnD,MAAM,0BACJ,yBAAyB;EAG3B,KAFyB,yBAAyB,UAAU,WAEnC,MACvB;EAMF,KAFE,yBAAyB,QAAQ,cAAc,YAAY,QAAQ,WAE9C,OAAO;EAE9B,MAAM,eAAwB,yBAAyB,UACrD;EAEF,IAAI,CAAC,yBAAyB;GAC5B,UACE,GAAG,iBAAiB,yDACpB,EACE,OAAO,OACT,CACF;GACA;EACF;EAEA,IAAI,CAAC,yBAAyB,UAAU;GACtC,UAAU,GAAG,iBAAiB,0CAA0C,EACtE,OAAO,OACT,CAAC;GACD;EACF;EAEA,MAAM,sBAAsB,oCAC1B,yBACA,YACF;EAEA,IACE,OAAO,KAAK,mBAA8C,CAAC,CAAC,WAAW,GACvE;GACA,UACE,GAAG,iBAAiB,oDAAoD,aAAa,YAAY,EAAE,8CACnG,EACE,OAAO,OACT,CACF;GACA;EACF;;;;;;EAOA,IAAI,oBAA8B;EAElC,IAAI,SAAS,YACX,oBACE,oBACG,MACE,uBAAuB,mBAAmB,QAAQ,aACrD,CAAC,EACC,QAAQ,QAAQ,WAAW,cAAc,SAAS,MAAM,CAAC,KAAK,CAAC;EAGvE,IAAI,kBAAkB,WAAW,GAAG;GAClC,UACE,GAAG,iBAAiB,GAAG,SAAS,gCAAgC,WAAW,SAAS,EAAE,GAAG,aAAa,SAAS,yBAAyB,QAAQ,CAAC,KACjJ,EACE,OAAO,OACT,CACF;GACA;EACF;EAEA,iBAAiB,KAAK;GACpB;GACA;GACA;GACA,eAAe;GACf;GACA,oBAAoB,yBAAyB;EAC/C,CAAC;CACH;CAGA,OAAO;AACT"}
|
|
@@ -2,9 +2,9 @@ import { getAuthenticatedAPI } from "../utils/checkAccess.mjs";
|
|
|
2
2
|
import { deepMergeContent } from "./deepMergeContent.mjs";
|
|
3
3
|
import { extractTranslatableContent, reinsertTranslatedContent } from "./extractTranslatableContent.mjs";
|
|
4
4
|
import { basename } from "node:path";
|
|
5
|
-
import { chunkJSON, excludeObjectFormat, formatLocale, mergeChunks, reconstructFromSingleChunk, verifyIdenticObjectFormat } from "@intlayer/chokidar/utils";
|
|
6
5
|
import * as ANSIColors from "@intlayer/config/colors";
|
|
7
6
|
import { colon, colorize, colorizeNumber, colorizePath, getAppLogger } from "@intlayer/config/logger";
|
|
7
|
+
import { chunkJSON, excludeObjectFormat, formatLocale, mergeChunks, reconstructFromSingleChunk, verifyIdenticObjectFormat } from "@intlayer/engine/utils";
|
|
8
8
|
import { getUnmergedDictionaries } from "@intlayer/unmerged-dictionaries-entry";
|
|
9
9
|
import { getFilterMissingTranslationsDictionary, getMultilingualDictionary, getPerLocaleDictionary, insertContentInDictionary } from "@intlayer/core/plugins";
|
|
10
10
|
import { retryManager } from "@intlayer/config/utils";
|
|
@@ -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 } from '@intlayer/api';\nimport {\n chunkJSON,\n excludeObjectFormat,\n formatLocale,\n type JsonChunk,\n mergeChunks,\n reconstructFromSingleChunk,\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 { getAuthenticatedAPI } from '../utils/checkAccess';\nimport type { AIClient } from '../utils/setupAI';\nimport { deepMergeContent } from './deepMergeContent';\nimport {\n extractTranslatableContent,\n reinsertTranslatedContent,\n} from './extractTranslatableContent';\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 createChunkPreset = (chunkIndex: number, totalChunks: number) => {\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\nconst hasMissingMetadata = (dictionary: Dictionary) =>\n !dictionary.description || !dictionary.title || !dictionary.tags;\n\nconst serializeError = (error: unknown): string => {\n if (error instanceof Error) {\n return error.cause\n ? `${error.message} (cause: ${String(error.cause)})`\n : error.message;\n }\n if (typeof error === 'string') return error;\n try {\n return JSON.stringify(error);\n } catch {\n return String(error);\n }\n};\n\nconst CHUNK_SIZE = 4000;\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 = await getAuthenticatedAPI(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 // Look up the target locale dictionary by its locale property.\n // This handles both directory-based paths (/en/file.json → /es/file.json)\n // AND filename-based paths (messages_ICU/en.json → messages_ICU/es.json).\n // Plugins like syncJSON set `dict.locale` correctly, so matching by locale\n // is the only reliable strategy — path string-replacement fails when the\n // locale appears in the filename rather than a directory segment.\n const targetUnmergedDictionary = unmergedDictionariesRecord[\n task.dictionaryKey\n ]?.find(\n (dict) =>\n dict.locale === targetLocale &&\n dict.filePath !== baseUnmergedDictionary.filePath\n );\n\n // Derive the target file path for the fallback (when the file doesn't exist yet).\n // Try directory-style first (/en/ → /es/), then filename-style (en.json → es.json).\n const sourceFilePath = baseUnmergedDictionary.filePath ?? '';\n const derivedTargetFilePath = sourceFilePath.includes(\n `/${task.sourceLocale}/`\n )\n ? sourceFilePath.replace(\n new RegExp(`/${task.sourceLocale}/`, 'g'),\n `/${targetLocale}/`\n )\n : sourceFilePath.replace(\n new RegExp(`(^|/)${task.sourceLocale}(\\\\.[^/]+)$`),\n `$1${targetLocale}$2`\n );\n\n targetLocaleDictionary = targetUnmergedDictionary ?? {\n key: baseUnmergedDictionary.key,\n content: {},\n filePath: derivedTargetFilePath || undefined,\n locale: targetLocale,\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 // Filter to only untranslated fields, preserving explicit null values as\n // default-locale fallback markers. Applied after both paths converge so\n // the same logic covers per-locale and multilingual dictionaries.\n if (mode === 'complete') {\n dictionaryToProcess = {\n ...dictionaryToProcess,\n content:\n excludeObjectFormat(\n dictionaryToProcess.content,\n targetLocaleDictionary.content\n ) ?? {},\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 appLogger(\n `${task.dictionaryPreset}${localePreset} Preparing ${colorizePath(basename(targetLocaleDictionary.filePath!))}`,\n {\n level: 'info',\n }\n );\n\n const chunkedJsonContent: JsonChunk[] = chunkJSON(\n dictionaryToProcess.content 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(async (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 const reconstructedChunk = reconstructFromSingleChunk(chunk);\n const {\n extractedContent: chunkExtractedContent,\n translatableDictionary: chunkTranslatableDictionary,\n } = extractTranslatableContent(reconstructedChunk);\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:\n chunkTranslatableDictionary as unknown as JSON,\n presetOutputContent: chunkTranslatableDictionary,\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:\n chunkTranslatableDictionary as unknown as JSON,\n presetOutputContent: chunkTranslatableDictionary,\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, error } = verifyIdenticObjectFormat(\n translationResult.fileContent,\n chunkTranslatableDictionary\n );\n\n if (!isIdentic) {\n throw new Error(\n `Translation result does not match expected format: ${error}`\n );\n }\n\n notifySuccess();\n return reinsertTranslatedContent(\n reconstructedChunk,\n chunkExtractedContent,\n translationResult.fileContent as Record<number, string>\n );\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(serializeError(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 translated chunk contents back into a single content object\n const reinsertedContent = mergeChunks(chunkResult);\n\n const merged = {\n ...dictionaryToProcess,\n content: reinsertedContent,\n };\n\n // Merge newly translated content (including explicit null fallbacks) back\n // into the existing target locale content. Applies to both per-locale and\n // multilingual paths so the target always retains previously translated\n // fields and receives null markers where the source has no translation.\n const finalContent = deepMergeContent(\n targetLocaleDictionary.content ?? {},\n merged.content\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(serializeError(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(serializeError(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 } from '@intlayer/api';\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 {\n chunkJSON,\n excludeObjectFormat,\n formatLocale,\n type JsonChunk,\n mergeChunks,\n reconstructFromSingleChunk,\n verifyIdenticObjectFormat,\n} from '@intlayer/engine/utils';\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 { getAuthenticatedAPI } from '../utils/checkAccess';\nimport type { AIClient } from '../utils/setupAI';\nimport { deepMergeContent } from './deepMergeContent';\nimport {\n extractTranslatableContent,\n reinsertTranslatedContent,\n} from './extractTranslatableContent';\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/engine/utils').getGlobalLimiter\n >;\n onSuccess?: () => void;\n onError?: (error: unknown) => void;\n getAbortError?: () => Error | null;\n aiClient?: AIClient;\n aiConfig?: AIConfig;\n};\n\nconst createChunkPreset = (chunkIndex: number, totalChunks: number) => {\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\nconst hasMissingMetadata = (dictionary: Dictionary) =>\n !dictionary.description || !dictionary.title || !dictionary.tags;\n\nconst serializeError = (error: unknown): string => {\n if (error instanceof Error) {\n return error.cause\n ? `${error.message} (cause: ${String(error.cause)})`\n : error.message;\n }\n if (typeof error === 'string') return error;\n try {\n return JSON.stringify(error);\n } catch {\n return String(error);\n }\n};\n\nconst CHUNK_SIZE = 4000;\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 = await getAuthenticatedAPI(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 // Look up the target locale dictionary by its locale property.\n // This handles both directory-based paths (/en/file.json → /es/file.json)\n // AND filename-based paths (messages_ICU/en.json → messages_ICU/es.json).\n // Plugins like syncJSON set `dict.locale` correctly, so matching by locale\n // is the only reliable strategy — path string-replacement fails when the\n // locale appears in the filename rather than a directory segment.\n const targetUnmergedDictionary = unmergedDictionariesRecord[\n task.dictionaryKey\n ]?.find(\n (dict) =>\n dict.locale === targetLocale &&\n dict.filePath !== baseUnmergedDictionary.filePath\n );\n\n // Derive the target file path for the fallback (when the file doesn't exist yet).\n // Try directory-style first (/en/ → /es/), then filename-style (en.json → es.json).\n const sourceFilePath = baseUnmergedDictionary.filePath ?? '';\n const derivedTargetFilePath = sourceFilePath.includes(\n `/${task.sourceLocale}/`\n )\n ? sourceFilePath.replace(\n new RegExp(`/${task.sourceLocale}/`, 'g'),\n `/${targetLocale}/`\n )\n : sourceFilePath.replace(\n new RegExp(`(^|/)${task.sourceLocale}(\\\\.[^/]+)$`),\n `$1${targetLocale}$2`\n );\n\n targetLocaleDictionary = targetUnmergedDictionary ?? {\n key: baseUnmergedDictionary.key,\n content: {},\n filePath: derivedTargetFilePath || undefined,\n locale: targetLocale,\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 // Filter to only untranslated fields, preserving explicit null values as\n // default-locale fallback markers. Applied after both paths converge so\n // the same logic covers per-locale and multilingual dictionaries.\n if (mode === 'complete') {\n dictionaryToProcess = {\n ...dictionaryToProcess,\n content:\n excludeObjectFormat(\n dictionaryToProcess.content,\n targetLocaleDictionary.content\n ) ?? {},\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 appLogger(\n `${task.dictionaryPreset}${localePreset} Preparing ${colorizePath(basename(targetLocaleDictionary.filePath!))}`,\n {\n level: 'info',\n }\n );\n\n const chunkedJsonContent: JsonChunk[] = chunkJSON(\n dictionaryToProcess.content 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(async (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 const reconstructedChunk = reconstructFromSingleChunk(chunk);\n const {\n extractedContent: chunkExtractedContent,\n translatableDictionary: chunkTranslatableDictionary,\n } = extractTranslatableContent(reconstructedChunk);\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:\n chunkTranslatableDictionary as unknown as JSON,\n presetOutputContent: chunkTranslatableDictionary,\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:\n chunkTranslatableDictionary as unknown as JSON,\n presetOutputContent: chunkTranslatableDictionary,\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, error } = verifyIdenticObjectFormat(\n translationResult.fileContent,\n chunkTranslatableDictionary\n );\n\n if (!isIdentic) {\n throw new Error(\n `Translation result does not match expected format: ${error}`\n );\n }\n\n notifySuccess();\n return reinsertTranslatedContent(\n reconstructedChunk,\n chunkExtractedContent,\n translationResult.fileContent as Record<number, string>\n );\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(serializeError(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 translated chunk contents back into a single content object\n const reinsertedContent = mergeChunks(chunkResult);\n\n const merged = {\n ...dictionaryToProcess,\n content: reinsertedContent,\n };\n\n // Merge newly translated content (including explicit null fallbacks) back\n // into the existing target locale content. Applies to both per-locale and\n // multilingual paths so the target always retains previously translated\n // fields and receives null markers where the source has no translation.\n const finalContent = deepMergeContent(\n targetLocaleDictionary.content ?? {},\n merged.content\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(serializeError(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(serializeError(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,9 +1,9 @@
|
|
|
1
1
|
import { formatFillData } from "./formatFillData.mjs";
|
|
2
2
|
import { getAvailableLocalesInDictionary } from "./getAvailableLocalesInDictionary.mjs";
|
|
3
3
|
import { resolve } from "node:path";
|
|
4
|
-
import { writeContentDeclaration } from "@intlayer/chokidar/build";
|
|
5
|
-
import { formatLocale, formatPath } from "@intlayer/chokidar/utils";
|
|
6
4
|
import { colorizeKey, getAppLogger } from "@intlayer/config/logger";
|
|
5
|
+
import { writeContentDeclaration } from "@intlayer/engine/build";
|
|
6
|
+
import { formatLocale, formatPath } from "@intlayer/engine/utils";
|
|
7
7
|
import { getDictionaries } from "@intlayer/dictionaries-entry";
|
|
8
8
|
|
|
9
9
|
//#region src/fill/writeFill.ts
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"writeFill.mjs","names":[],"sources":["../../../src/fill/writeFill.ts"],"sourcesContent":["import { resolve } from 'node:path';\nimport {
|
|
1
|
+
{"version":3,"file":"writeFill.mjs","names":[],"sources":["../../../src/fill/writeFill.ts"],"sourcesContent":["import { resolve } from 'node:path';\nimport { colorizeKey, getAppLogger } from '@intlayer/config/logger';\nimport { getDictionaries } from '@intlayer/dictionaries-entry';\nimport { writeContentDeclaration } from '@intlayer/engine/build';\nimport { formatLocale, formatPath } from '@intlayer/engine/utils';\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 await writeContentDeclaration(\n {\n ...rest,\n filled: true,\n locale: output.isPerLocale ? output.localeList[0] : undefined,\n localId: `${contentDeclarationFile.key}::local::${output.filePath}`,\n filePath: resolve(configuration.system.baseDir, output.filePath), // Use absolute path for vscode extension\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,aAAa;CAG5C,MAAM,iBAFe,gBAAgB,aAEH,CAAC,CAAC,uBAAuB;CAE3D,MAAM,EAAE,aAAa;CAErB,IAAI,CAAC,UAAU;EACb,UAAU,qCAAqC,EAC7C,OAAO,QACT,CAAC;EACD;CACF;CAEA,MAAM,cACJ,uBAAuB,QAAQ,cAAc,YAAY,QAAQ;CAEnE,IAAK,gBAA4B,OAAO;EACtC,UACE,8BAA8B,YAAY,eAAe,GAAG,EAAE,IAC9D,EACE,OAAO,OACT,CACF;EACA;CACF;CAEA,MAAM,oBACJ,iBAAiB,cAAc,qBAAqB,QAAO,CAC3D,QAAQ,WAAW,CAAC,eAAe,SAAS,MAAM,CAAC;CAGrD,MAAM,mBAAmB,gCACvB,sBACF;CAGA,MAAM,aAAa,iBAAiB,QAAQ,WAC1C,iBAAiB,SAAS,MAAM,CAClC;CAEA,IAAI,WAAW,WAAW,GAAG;EAC3B,UACE,6CAA6C,YAAY,eAAe,GAAG,EAAE,IAC7E,EACE,OAAO,OACT,CACF;EACA;CACF;CAEA,MAAM,WAAuB,MAAM,eACjC,aACA,YACA,UACA,eAAe,KACf,aACF;CAEA,WAAW,MAAM,UAAU,UAAU;EACnC,IAAI,CAAC,OAAO,UAAU;GACpB,UACE,+DAA+D,YAAY,eAAe,GAAG,EAAE,IAC/F,EACE,OAAO,QACT,CACF;GACA;EACF;EAEA,MAAM,EAAE,MAAM,GAAG,SAAS;EAE1B,MAAM,wBACJ;GACE,GAAG;GACH,QAAQ;GACR,QAAQ,OAAO,cAAc,OAAO,WAAW,KAAK;GACpD,SAAS,GAAG,uBAAuB,IAAI,WAAW,OAAO;GACzD,UAAU,QAAQ,cAAc,OAAO,SAAS,OAAO,QAAQ;EACjE,GACA,eACA,EAIE,YAAY,OAAO,cACf,OAAO,aACN,iBAAiB,cAAc,qBAAqB,QAC3D,CACF;EAEA,IAAI,OAAO,aACT,UACE,mDAAmD,YAAY,eAAe,GAAG,EAAE,eAAe,WAAW,OAAO,QAAQ,EAAE,cAAc,aAAa,OAAO,WAAW,EAAE,KAC7K,EAAE,OAAO,OAAO,CAClB;OAEA,UACE,wCAAwC,YAAY,eAAe,GAAG,EAAE,eAAe,WAAW,OAAO,QAAQ,KACjH,EAAE,OAAO,OAAO,CAClB;CAEJ;AACF"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { join, relative } from "node:path";
|
|
2
|
-
import { listGitFiles } from "@intlayer/chokidar/cli";
|
|
3
2
|
import { getConfiguration } from "@intlayer/config/node";
|
|
3
|
+
import { listGitFiles } from "@intlayer/engine/cli";
|
|
4
4
|
import { getUnmergedDictionaries } from "@intlayer/unmerged-dictionaries-entry";
|
|
5
5
|
|
|
6
6
|
//#region src/getTargetDictionary.ts
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getTargetDictionary.mjs","names":[],"sources":["../../src/getTargetDictionary.ts"],"sourcesContent":["import { join, relative } from 'node:path';\nimport {
|
|
1
|
+
{"version":3,"file":"getTargetDictionary.mjs","names":[],"sources":["../../src/getTargetDictionary.ts"],"sourcesContent":["import { join, relative } from 'node:path';\nimport {\n type GetConfigurationOptions,\n getConfiguration,\n} from '@intlayer/config/node';\nimport { type ListGitFilesOptions, listGitFiles } from '@intlayer/engine/cli';\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,KAAK,CAAC,CAAC,KAAK;AAapE,MAAa,gCAAgC,OAC3C,YAC0B;CAC1B,MAAM,gBAAgB,iBAAiB,SAAS,aAAa;CAE7D,MAAM,EAAE,YAAY,cAAc;CAElC,MAAM,6BAA6B,wBAAwB,aAAa;CACxE,IAAI,SAAS,OAAO,OAAO,0BAA0B,CAAC,CAAC,KAAK;CAG5D,IAAI,OAAO,SAAS,SAAS,aAAa;EAExC,MAAM,oBADY,YAAY,SAAS,IACL,CAAC,CAAC,KAAK,SACvC,KAAK,WAAW,GAAG,IAAI,SAAS,SAAS,IAAI,IAAI,KAAK,MAAM,IAAI,CAClE;EAEA,SAAS,OAAO,QACb,SACC,KAAK,YAEL,kBAAkB,SAAS,KAAK,QAAQ,CAC5C;CACF;CAEA,IAAI,OAAO,SAAS,SAAS,aAC3B,SAAS,OAAO,QAAQ,SACtB,YAAY,SAAS,IAAI,CAAC,EAAE,SAAS,KAAK,GAAG,CAC/C;CAGF,IAAI,OAAO,SAAS,iBAAiB,aACnC,SAAS,OAAO,QACb,SAAS,CAAC,YAAY,SAAS,YAAY,CAAC,EAAE,SAAS,KAAK,GAAG,CAClE;CAGF,IAAI,OAAO,SAAS,eAAe,aACjC,SAAS,OAAO,QAAQ,SACtB,YAAY,SAAS,UAAU,CAAC,EAAE,SAAS,KAAK,YAAY,EAAE,CAChE;CAGF,IAAI,OAAO,SAAS,WAAW,aAC7B,SAAS,OAAO,OAAO,SAAS,MAAM;CAGxC,MAAM,aAAa,SAAS;CAC5B,IAAI,YAAY;EACd,MAAM,kBAAkB,MAAM,aAAa,UAAU;EAErD,IAAI,iBAIF,SAAS,OAAO,QAAQ,SAAS;GAC/B,IAAI,CAAC,KAAK,UAAU,OAAO;GAE3B,OAAO,gBAAgB,MAAM,YAAY,KAAK,aAAa,OAAO;EACpE,CAAC;CAEL;CAEA,OAAO;AACT"}
|
package/dist/esm/index.mjs
CHANGED
|
@@ -2,21 +2,21 @@ import { extract } from "./extract.mjs";
|
|
|
2
2
|
import { listProjectsCommand } from "./listProjects.mjs";
|
|
3
3
|
import { liveSync } from "./liveSync.mjs";
|
|
4
4
|
import { listContentDeclaration, listContentDeclarationRows } from "./listContentDeclaration.mjs";
|
|
5
|
+
import { PLATFORM_OPTIONS, getDetectedPlatform, initSkills } from "./initSkills.mjs";
|
|
5
6
|
import { findProjectRoot, init } from "./init.mjs";
|
|
6
7
|
import { startEditor } from "./editor.mjs";
|
|
7
8
|
import { scan } from "./scan.mjs";
|
|
9
|
+
import { dirname, setAPI } from "./cli.mjs";
|
|
10
|
+
import { searchDoc } from "./searchDoc.mjs";
|
|
11
|
+
import { pushConfig } from "./pushConfig.mjs";
|
|
8
12
|
import { build } from "./build.mjs";
|
|
9
13
|
import { bundle } from "./bundle.mjs";
|
|
10
14
|
import { listMissingTranslations, listMissingTranslationsWithConfig } from "./test/listMissingTranslations.mjs";
|
|
11
15
|
import { testMissingTranslations } from "./test/test.mjs";
|
|
12
16
|
import { fill } from "./fill/fill.mjs";
|
|
13
|
-
import { PLATFORM_OPTIONS, getDetectedPlatform, initSkills } from "./initSkills.mjs";
|
|
14
17
|
import { pull } from "./pull.mjs";
|
|
15
18
|
import { push } from "./push/push.mjs";
|
|
16
|
-
import { pushConfig } from "./pushConfig.mjs";
|
|
17
19
|
import { reviewDoc } from "./reviewDoc/reviewDoc.mjs";
|
|
18
|
-
import { searchDoc } from "./searchDoc.mjs";
|
|
19
20
|
import { translateDoc } from "./translateDoc/translateDoc.mjs";
|
|
20
|
-
import { dirname, setAPI } from "./cli.mjs";
|
|
21
21
|
|
|
22
22
|
export { PLATFORM_OPTIONS, build, bundle, dirname, extract, fill, findProjectRoot, getDetectedPlatform, init, initSkills, listContentDeclaration, listContentDeclarationRows, listMissingTranslations, listMissingTranslationsWithConfig, listProjectsCommand, liveSync, pull, push, pushConfig, reviewDoc, scan, searchDoc, setAPI, startEditor, testMissingTranslations, translateDoc };
|