@intlayer/cli 7.1.9 → 7.2.2
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/README.md +1 -0
- package/dist/cjs/_virtual/rolldown_runtime.cjs +10 -6
- package/dist/cjs/fill/translateDictionary.cjs +4 -25
- package/dist/cjs/fill/translateDictionary.cjs.map +1 -1
- package/dist/esm/fill/translateDictionary.mjs +4 -25
- package/dist/esm/fill/translateDictionary.mjs.map +1 -1
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/fill/translateDictionary.d.ts.map +1 -1
- package/dist/types/pull.d.ts.map +1 -1
- package/dist/types/push/pullLog.d.ts.map +1 -1
- package/package.json +12 -12
package/README.md
CHANGED
|
@@ -188,6 +188,7 @@ Explore our comprehensive documentation to get started with Intlayer and learn h
|
|
|
188
188
|
<li><a href="https://intlayer.org/doc/environment/react-native-and-expo" rel=''>React Native</a></li>
|
|
189
189
|
<li><a href="https://intlayer.org/doc/environment/lynx-and-react" rel=''>Lynx + React</a></li>
|
|
190
190
|
<li><a href="https://intlayer.org/doc/environment/vite-and-svelte" rel=''>Vite + Svelte</a></li>
|
|
191
|
+
<li><a href="https://intlayer.org/doc/environment/sveltekit" rel=''>SvelteKit</a></li>
|
|
191
192
|
<li><a href="https://intlayer.org/doc/environment/vite-and-preact" rel=''>Vite + Preact</a></li>
|
|
192
193
|
<li><a href="https://intlayer.org/doc/environment/vite-and-vue" rel=''>Vite + Vue</a></li>
|
|
193
194
|
<li><a href="https://intlayer.org/doc/environment/vite-and-nuxt" rel=''>Vite + Nuxt</a></li>
|
|
@@ -6,12 +6,16 @@ var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
|
6
6
|
var __getProtoOf = Object.getPrototypeOf;
|
|
7
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
8
|
var __copyProps = (to, from, except, desc) => {
|
|
9
|
-
if (from && typeof from === "object" || typeof from === "function")
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
9
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
10
|
+
for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
11
|
+
key = keys[i];
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except) {
|
|
13
|
+
__defProp(to, key, {
|
|
14
|
+
get: ((k) => from[k]).bind(null, key),
|
|
15
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
}
|
|
15
19
|
}
|
|
16
20
|
return to;
|
|
17
21
|
};
|
|
@@ -22,24 +22,11 @@ const translateDictionary = async (task, configuration, options) => {
|
|
|
22
22
|
fillMetadata: true,
|
|
23
23
|
...options
|
|
24
24
|
};
|
|
25
|
-
const ensureNotAborted = () => {
|
|
26
|
-
const abortError = options?.getAbortError?.();
|
|
27
|
-
if (abortError) throw abortError;
|
|
28
|
-
};
|
|
29
25
|
const notifySuccess = () => {
|
|
30
26
|
followingErrors = 0;
|
|
31
27
|
options?.onSuccess?.();
|
|
32
28
|
};
|
|
33
|
-
const notifyError = (error) => {
|
|
34
|
-
if (!options?.onError) return;
|
|
35
|
-
try {
|
|
36
|
-
options.onError(error);
|
|
37
|
-
} catch (abortError) {
|
|
38
|
-
throw abortError;
|
|
39
|
-
}
|
|
40
|
-
};
|
|
41
29
|
return await (0, __intlayer_config.retryManager)(async () => {
|
|
42
|
-
ensureNotAborted();
|
|
43
30
|
const baseUnmergedDictionary = (0, __intlayer_unmerged_dictionaries_entry.getUnmergedDictionaries)(configuration)[task.dictionaryKey].find((dict) => dict.localId === task.dictionaryLocalId);
|
|
44
31
|
if (!baseUnmergedDictionary) {
|
|
45
32
|
appLogger(`${task.dictionaryPreset}Dictionary not found in unmergedDictionariesRecord. Skipping.`, { level: "warn" });
|
|
@@ -53,20 +40,17 @@ const translateDictionary = async (task, configuration, options) => {
|
|
|
53
40
|
const defaultLocaleDictionary = (0, __intlayer_core.getPerLocaleDictionary)(baseUnmergedDictionary, configuration.internationalization.defaultLocale);
|
|
54
41
|
appLogger(`${task.dictionaryPreset} Filling missing metadata for ${(0, __intlayer_config.colorizePath)((0, node_path.basename)(baseUnmergedDictionary.filePath))}`, { level: "info" });
|
|
55
42
|
const runAudit = async () => {
|
|
56
|
-
ensureNotAborted();
|
|
57
43
|
try {
|
|
58
44
|
return await intlayerAPI.ai.auditContentDeclarationMetadata({
|
|
59
45
|
fileContent: JSON.stringify(defaultLocaleDictionary),
|
|
60
46
|
aiOptions
|
|
61
47
|
});
|
|
62
48
|
} catch (error) {
|
|
63
|
-
notifyError(error);
|
|
64
49
|
throw error;
|
|
65
50
|
}
|
|
66
51
|
};
|
|
67
52
|
metadata = (options?.onHandle ? await options.onHandle(runAudit) : await runAudit()).data?.fileContent;
|
|
68
53
|
}
|
|
69
|
-
let dictionaryToProcess = baseUnmergedDictionary;
|
|
70
54
|
const translatedContent = {};
|
|
71
55
|
for await (const targetLocale of task.targetLocales) {
|
|
72
56
|
/**
|
|
@@ -78,6 +62,7 @@ const translateDictionary = async (task, configuration, options) => {
|
|
|
78
62
|
* { test2: t({ ar: 'Hello', en: 'Hello' }) } -> { test2: t({ ar: 'Hello', en: 'Hello' }) }
|
|
79
63
|
*
|
|
80
64
|
*/
|
|
65
|
+
let dictionaryToProcess = baseUnmergedDictionary;
|
|
81
66
|
if (mode === "complete") dictionaryToProcess = (0, __intlayer_core.getFilterMissingTranslationsDictionary)(dictionaryToProcess, targetLocale);
|
|
82
67
|
dictionaryToProcess = (0, __intlayer_core.getPerLocaleDictionary)(dictionaryToProcess, task.sourceLocale);
|
|
83
68
|
const targetLocaleDictionary = (0, __intlayer_core.getPerLocaleDictionary)(baseUnmergedDictionary, targetLocale);
|
|
@@ -107,7 +92,6 @@ const translateDictionary = async (task, configuration, options) => {
|
|
|
107
92
|
const presetOutputContent = (0, __intlayer_chokidar.reduceObjectFormat)(targetLocaleDictionary.content, chunkContent);
|
|
108
93
|
const executeTranslation = async () => {
|
|
109
94
|
return await (0, __intlayer_config.retryManager)(async () => {
|
|
110
|
-
ensureNotAborted();
|
|
111
95
|
const translationResult = await intlayerAPI.ai.translateJSON({
|
|
112
96
|
entryFileContent: chunkContent,
|
|
113
97
|
presetOutputContent,
|
|
@@ -126,9 +110,8 @@ const translateDictionary = async (task, configuration, options) => {
|
|
|
126
110
|
maxRetry: MAX_RETRY,
|
|
127
111
|
delay: RETRY_DELAY,
|
|
128
112
|
onError: ({ error, attempt, maxRetry }) => {
|
|
129
|
-
notifyError(error);
|
|
130
113
|
const chunkPreset$1 = createChunkPreset(chunk.index, chunk.total);
|
|
131
|
-
appLogger(`${task.dictionaryPreset}${localePreset}${chunkPreset$1} ${(0, __intlayer_config.colorize)("Error filling:", __intlayer_config.ANSIColors.RED)} ${(0, __intlayer_config.colorize)(error, __intlayer_config.ANSIColors.GREY_DARK)} - Attempt ${(0, __intlayer_config.colorizeNumber)(attempt + 1)} of ${(0, __intlayer_config.colorizeNumber)(maxRetry)}`, { level: "error" });
|
|
114
|
+
appLogger(`${task.dictionaryPreset}${localePreset}${chunkPreset$1} ${(0, __intlayer_config.colorize)("Error filling:", __intlayer_config.ANSIColors.RED)} ${(0, __intlayer_config.colorize)(typeof error === "string" ? error : JSON.stringify(error), __intlayer_config.ANSIColors.GREY_DARK)} - Attempt ${(0, __intlayer_config.colorizeNumber)(attempt + 1)} of ${(0, __intlayer_config.colorizeNumber)(maxRetry)}`, { level: "error" });
|
|
132
115
|
followingErrors += 1;
|
|
133
116
|
if (followingErrors >= MAX_FOLLOWING_ERRORS) {
|
|
134
117
|
appLogger(`There is something wrong.`, { level: "error" });
|
|
@@ -163,12 +146,8 @@ const translateDictionary = async (task, configuration, options) => {
|
|
|
163
146
|
}, {
|
|
164
147
|
maxRetry: GROUP_MAX_RETRY,
|
|
165
148
|
delay: RETRY_DELAY,
|
|
166
|
-
onError: ({ error, attempt, maxRetry }) => {
|
|
167
|
-
|
|
168
|
-
},
|
|
169
|
-
onMaxTryReached: ({ error }) => {
|
|
170
|
-
appLogger(`${task.dictionaryPreset} ${(0, __intlayer_config.colorize)("Maximum number of retries reached:", __intlayer_config.ANSIColors.RED)} ${(0, __intlayer_config.colorize)(error, __intlayer_config.ANSIColors.GREY_DARK)}`, { level: "error" });
|
|
171
|
-
}
|
|
149
|
+
onError: ({ error, attempt, maxRetry }) => appLogger(`${task.dictionaryPreset} ${(0, __intlayer_config.colorize)("Error fill command:", __intlayer_config.ANSIColors.RED)} ${(0, __intlayer_config.colorize)(typeof error === "string" ? error : JSON.stringify(error), __intlayer_config.ANSIColors.GREY_DARK)} - Attempt ${(0, __intlayer_config.colorizeNumber)(attempt + 1)} of ${(0, __intlayer_config.colorizeNumber)(maxRetry)}`, { level: "error" }),
|
|
150
|
+
onMaxTryReached: ({ error }) => appLogger(`${task.dictionaryPreset} ${(0, __intlayer_config.colorize)("Maximum number of retries reached:", __intlayer_config.ANSIColors.RED)} ${(0, __intlayer_config.colorize)(typeof error === "string" ? error : JSON.stringify(error), __intlayer_config.ANSIColors.GREY_DARK)}`, { level: "error" })
|
|
172
151
|
})();
|
|
173
152
|
};
|
|
174
153
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"translateDictionary.cjs","names":["baseUnmergedDictionary: Dictionary | undefined","metadata:\n | Pick<Dictionary, 'description' | 'title' | 'tags'>\n | undefined","translatedContent: Partial<Record<Locale, Dictionary['content']>>","ANSIColors","chunkedJsonContent: JsonChunk[]","chunkResult: JsonChunk[]","chunkPreset","dictionaryOutput: Dictionary"],"sources":["../../../src/fill/translateDictionary.ts"],"sourcesContent":["import { basename } from 'node:path';\nimport { type AIOptions, getIntlayerAPIProxy } from '@intlayer/api';\nimport {\n chunkJSON,\n formatLocale,\n type JsonChunk,\n reconstructFromSingleChunk,\n reduceObjectFormat,\n verifyIdenticObjectFormat,\n} from '@intlayer/chokidar';\nimport {\n ANSIColors,\n colon,\n colorize,\n colorizeNumber,\n colorizePath,\n getAppLogger,\n retryManager,\n} from '@intlayer/config';\nimport {\n getFilterMissingTranslationsDictionary,\n getPerLocaleDictionary,\n insertContentInDictionary,\n mergeDictionaries,\n} from '@intlayer/core';\nimport type { Dictionary, IntlayerConfig, Locale } from '@intlayer/types';\nimport { getUnmergedDictionaries } from '@intlayer/unmerged-dictionaries-entry';\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<typeof import('@intlayer/chokidar').getGlobalLimiter>;\n onSuccess?: () => void;\n onError?: (error: unknown) => void;\n getAbortError?: () => Error | null;\n};\n\nconst hasMissingMetadata = (dictionary: Dictionary) =>\n !dictionary.description || !dictionary.title || !dictionary.tags;\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 } = {\n mode: 'complete',\n fillMetadata: true,\n ...options,\n } as const;\n\n const ensureNotAborted = () => {\n const abortError = options?.getAbortError?.();\n if (abortError) {\n throw abortError;\n }\n };\n\n const notifySuccess = () => {\n followingErrors = 0;\n options?.onSuccess?.();\n };\n\n const notifyError = (error: unknown) => {\n if (!options?.onError) return;\n try {\n options.onError(error);\n } catch (abortError) {\n throw abortError;\n }\n };\n\n const result = await retryManager(\n async () => {\n ensureNotAborted();\n\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 ensureNotAborted();\n try {\n return await intlayerAPI.ai.auditContentDeclarationMetadata({\n fileContent: JSON.stringify(defaultLocaleDictionary),\n aiOptions,\n });\n } catch (error) {\n notifyError(error);\n throw error;\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 let dictionaryToProcess = baseUnmergedDictionary;\n\n const translatedContent: Partial<Record<Locale, Dictionary['content']>> =\n {};\n\n for await (const targetLocale of task.targetLocales) {\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 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 const targetLocaleDictionary = getPerLocaleDictionary(\n baseUnmergedDictionary,\n targetLocale\n );\n\n const localePreset = colon(\n [\n colorize('[', ANSIColors.GREY_DARK),\n formatLocale(targetLocale),\n colorize(']', ANSIColors.GREY_DARK),\n ].join(''),\n { colSize: 10 }\n );\n\n const 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\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,\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 targetLocaleDictionary.content,\n chunkContent\n ) as unknown as JSON;\n\n const executeTranslation = async () => {\n return await retryManager(\n async () => {\n ensureNotAborted();\n\n const translationResult = await intlayerAPI.ai.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\n if (!translationResult.data?.fileContent) {\n throw new Error('No content result');\n }\n\n const { isIdentic } = verifyIdenticObjectFormat(\n translationResult.data.fileContent,\n chunkContent\n );\n if (!isIdentic) {\n throw new Error(\n 'Translation result does not match expected format'\n );\n }\n\n notifySuccess();\n return translationResult.data.fileContent;\n },\n {\n maxRetry: MAX_RETRY,\n delay: RETRY_DELAY,\n onError: ({ error, attempt, maxRetry }) => {\n notifyError(error);\n\n const chunkPreset = createChunkPreset(\n chunk.index,\n chunk.total\n );\n appLogger(\n `${task.dictionaryPreset}${localePreset}${chunkPreset} ${colorize('Error filling:', ANSIColors.RED)} ${colorize(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((a, b) => a.chunk.index - b.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 const merged = mergeDictionaries(\n chunkResult.map((chunk) => ({\n ...dictionaryToProcess,\n content: chunk,\n }))\n );\n\n translatedContent[targetLocale] =\n merged.content as Dictionary['content'];\n }\n\n let dictionaryOutput: Dictionary = {\n ...baseUnmergedDictionary,\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 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 fill command:', ANSIColors.RED)} ${colorize(error, ANSIColors.GREY_DARK)} - Attempt ${colorizeNumber(attempt + 1)} of ${colorizeNumber(maxRetry)}`,\n {\n level: 'error',\n }\n );\n },\n onMaxTryReached: ({ error }) => {\n appLogger(\n `${task.dictionaryPreset} ${colorize('Maximum number of retries reached:', ANSIColors.RED)} ${colorize(error, ANSIColors.GREY_DARK)}`,\n {\n level: 'error',\n }\n );\n },\n }\n )();\n\n return result as TranslateDictionaryResult;\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"translateDictionary.cjs","names":["baseUnmergedDictionary: Dictionary | undefined","metadata:\n | Pick<Dictionary, 'description' | 'title' | 'tags'>\n | undefined","translatedContent: Partial<Record<Locale, Dictionary['content']>>","ANSIColors","chunkedJsonContent: JsonChunk[]","chunkResult: JsonChunk[]","chunkPreset","dictionaryOutput: Dictionary"],"sources":["../../../src/fill/translateDictionary.ts"],"sourcesContent":["import { basename } from 'node:path';\nimport { type AIOptions, getIntlayerAPIProxy } from '@intlayer/api';\nimport {\n chunkJSON,\n formatLocale,\n type JsonChunk,\n reconstructFromSingleChunk,\n reduceObjectFormat,\n verifyIdenticObjectFormat,\n} from '@intlayer/chokidar';\nimport {\n ANSIColors,\n colon,\n colorize,\n colorizeNumber,\n colorizePath,\n getAppLogger,\n retryManager,\n} from '@intlayer/config';\nimport {\n getFilterMissingTranslationsDictionary,\n getPerLocaleDictionary,\n insertContentInDictionary,\n mergeDictionaries,\n} from '@intlayer/core';\nimport type { Dictionary, IntlayerConfig, Locale } from '@intlayer/types';\nimport { getUnmergedDictionaries } from '@intlayer/unmerged-dictionaries-entry';\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<typeof import('@intlayer/chokidar').getGlobalLimiter>;\n onSuccess?: () => void;\n onError?: (error: unknown) => void;\n getAbortError?: () => Error | null;\n};\n\nconst hasMissingMetadata = (dictionary: Dictionary) =>\n !dictionary.description || !dictionary.title || !dictionary.tags;\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 } = {\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 try {\n return await intlayerAPI.ai.auditContentDeclarationMetadata({\n fileContent: JSON.stringify(defaultLocaleDictionary),\n aiOptions,\n });\n } catch (error) {\n throw error;\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 translatedContent: Partial<Record<Locale, Dictionary['content']>> =\n {};\n\n for await (const targetLocale of task.targetLocales) {\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 = baseUnmergedDictionary;\n\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 const targetLocaleDictionary = getPerLocaleDictionary(\n baseUnmergedDictionary,\n targetLocale\n );\n\n const localePreset = colon(\n [\n colorize('[', ANSIColors.GREY_DARK),\n formatLocale(targetLocale),\n colorize(']', ANSIColors.GREY_DARK),\n ].join(''),\n { colSize: 10 }\n );\n\n const 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\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,\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 targetLocaleDictionary.content,\n chunkContent\n ) as unknown as JSON;\n\n const executeTranslation = async () => {\n return await retryManager(\n async () => {\n const translationResult = await intlayerAPI.ai.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\n if (!translationResult.data?.fileContent) {\n throw new Error('No content result');\n }\n\n const { isIdentic } = verifyIdenticObjectFormat(\n translationResult.data.fileContent,\n chunkContent\n );\n if (!isIdentic) {\n throw new Error(\n 'Translation result does not match expected format'\n );\n }\n\n notifySuccess();\n return translationResult.data.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((a, b) => a.chunk.index - b.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 const merged = mergeDictionaries(\n chunkResult.map((chunk) => ({\n ...dictionaryToProcess,\n content: chunk,\n }))\n );\n\n translatedContent[targetLocale] =\n merged.content as Dictionary['content'];\n }\n\n let dictionaryOutput: Dictionary = {\n ...baseUnmergedDictionary,\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 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 fill command:', 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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"}
|
|
@@ -21,24 +21,11 @@ const translateDictionary = async (task, configuration, options) => {
|
|
|
21
21
|
fillMetadata: true,
|
|
22
22
|
...options
|
|
23
23
|
};
|
|
24
|
-
const ensureNotAborted = () => {
|
|
25
|
-
const abortError = options?.getAbortError?.();
|
|
26
|
-
if (abortError) throw abortError;
|
|
27
|
-
};
|
|
28
24
|
const notifySuccess = () => {
|
|
29
25
|
followingErrors = 0;
|
|
30
26
|
options?.onSuccess?.();
|
|
31
27
|
};
|
|
32
|
-
const notifyError = (error) => {
|
|
33
|
-
if (!options?.onError) return;
|
|
34
|
-
try {
|
|
35
|
-
options.onError(error);
|
|
36
|
-
} catch (abortError) {
|
|
37
|
-
throw abortError;
|
|
38
|
-
}
|
|
39
|
-
};
|
|
40
28
|
return await retryManager(async () => {
|
|
41
|
-
ensureNotAborted();
|
|
42
29
|
const baseUnmergedDictionary = getUnmergedDictionaries(configuration)[task.dictionaryKey].find((dict) => dict.localId === task.dictionaryLocalId);
|
|
43
30
|
if (!baseUnmergedDictionary) {
|
|
44
31
|
appLogger(`${task.dictionaryPreset}Dictionary not found in unmergedDictionariesRecord. Skipping.`, { level: "warn" });
|
|
@@ -52,20 +39,17 @@ const translateDictionary = async (task, configuration, options) => {
|
|
|
52
39
|
const defaultLocaleDictionary = getPerLocaleDictionary(baseUnmergedDictionary, configuration.internationalization.defaultLocale);
|
|
53
40
|
appLogger(`${task.dictionaryPreset} Filling missing metadata for ${colorizePath(basename(baseUnmergedDictionary.filePath))}`, { level: "info" });
|
|
54
41
|
const runAudit = async () => {
|
|
55
|
-
ensureNotAborted();
|
|
56
42
|
try {
|
|
57
43
|
return await intlayerAPI.ai.auditContentDeclarationMetadata({
|
|
58
44
|
fileContent: JSON.stringify(defaultLocaleDictionary),
|
|
59
45
|
aiOptions
|
|
60
46
|
});
|
|
61
47
|
} catch (error) {
|
|
62
|
-
notifyError(error);
|
|
63
48
|
throw error;
|
|
64
49
|
}
|
|
65
50
|
};
|
|
66
51
|
metadata = (options?.onHandle ? await options.onHandle(runAudit) : await runAudit()).data?.fileContent;
|
|
67
52
|
}
|
|
68
|
-
let dictionaryToProcess = baseUnmergedDictionary;
|
|
69
53
|
const translatedContent = {};
|
|
70
54
|
for await (const targetLocale of task.targetLocales) {
|
|
71
55
|
/**
|
|
@@ -77,6 +61,7 @@ const translateDictionary = async (task, configuration, options) => {
|
|
|
77
61
|
* { test2: t({ ar: 'Hello', en: 'Hello' }) } -> { test2: t({ ar: 'Hello', en: 'Hello' }) }
|
|
78
62
|
*
|
|
79
63
|
*/
|
|
64
|
+
let dictionaryToProcess = baseUnmergedDictionary;
|
|
80
65
|
if (mode === "complete") dictionaryToProcess = getFilterMissingTranslationsDictionary(dictionaryToProcess, targetLocale);
|
|
81
66
|
dictionaryToProcess = getPerLocaleDictionary(dictionaryToProcess, task.sourceLocale);
|
|
82
67
|
const targetLocaleDictionary = getPerLocaleDictionary(baseUnmergedDictionary, targetLocale);
|
|
@@ -106,7 +91,6 @@ const translateDictionary = async (task, configuration, options) => {
|
|
|
106
91
|
const presetOutputContent = reduceObjectFormat(targetLocaleDictionary.content, chunkContent);
|
|
107
92
|
const executeTranslation = async () => {
|
|
108
93
|
return await retryManager(async () => {
|
|
109
|
-
ensureNotAborted();
|
|
110
94
|
const translationResult = await intlayerAPI.ai.translateJSON({
|
|
111
95
|
entryFileContent: chunkContent,
|
|
112
96
|
presetOutputContent,
|
|
@@ -125,9 +109,8 @@ const translateDictionary = async (task, configuration, options) => {
|
|
|
125
109
|
maxRetry: MAX_RETRY,
|
|
126
110
|
delay: RETRY_DELAY,
|
|
127
111
|
onError: ({ error, attempt, maxRetry }) => {
|
|
128
|
-
notifyError(error);
|
|
129
112
|
const chunkPreset$1 = createChunkPreset(chunk.index, chunk.total);
|
|
130
|
-
appLogger(`${task.dictionaryPreset}${localePreset}${chunkPreset$1} ${colorize("Error filling:", ANSIColors.RED)} ${colorize(error, ANSIColors.GREY_DARK)} - Attempt ${colorizeNumber(attempt + 1)} of ${colorizeNumber(maxRetry)}`, { level: "error" });
|
|
113
|
+
appLogger(`${task.dictionaryPreset}${localePreset}${chunkPreset$1} ${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" });
|
|
131
114
|
followingErrors += 1;
|
|
132
115
|
if (followingErrors >= MAX_FOLLOWING_ERRORS) {
|
|
133
116
|
appLogger(`There is something wrong.`, { level: "error" });
|
|
@@ -162,12 +145,8 @@ const translateDictionary = async (task, configuration, options) => {
|
|
|
162
145
|
}, {
|
|
163
146
|
maxRetry: GROUP_MAX_RETRY,
|
|
164
147
|
delay: RETRY_DELAY,
|
|
165
|
-
onError: ({ error, attempt, maxRetry }) => {
|
|
166
|
-
|
|
167
|
-
},
|
|
168
|
-
onMaxTryReached: ({ error }) => {
|
|
169
|
-
appLogger(`${task.dictionaryPreset} ${colorize("Maximum number of retries reached:", ANSIColors.RED)} ${colorize(error, ANSIColors.GREY_DARK)}`, { level: "error" });
|
|
170
|
-
}
|
|
148
|
+
onError: ({ error, attempt, maxRetry }) => appLogger(`${task.dictionaryPreset} ${colorize("Error fill command:", ANSIColors.RED)} ${colorize(typeof error === "string" ? error : JSON.stringify(error), ANSIColors.GREY_DARK)} - Attempt ${colorizeNumber(attempt + 1)} of ${colorizeNumber(maxRetry)}`, { level: "error" }),
|
|
149
|
+
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" })
|
|
171
150
|
})();
|
|
172
151
|
};
|
|
173
152
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"translateDictionary.mjs","names":["baseUnmergedDictionary: Dictionary | undefined","metadata:\n | Pick<Dictionary, 'description' | 'title' | 'tags'>\n | undefined","translatedContent: Partial<Record<Locale, Dictionary['content']>>","chunkedJsonContent: JsonChunk[]","chunkResult: JsonChunk[]","chunkPreset","dictionaryOutput: Dictionary"],"sources":["../../../src/fill/translateDictionary.ts"],"sourcesContent":["import { basename } from 'node:path';\nimport { type AIOptions, getIntlayerAPIProxy } from '@intlayer/api';\nimport {\n chunkJSON,\n formatLocale,\n type JsonChunk,\n reconstructFromSingleChunk,\n reduceObjectFormat,\n verifyIdenticObjectFormat,\n} from '@intlayer/chokidar';\nimport {\n ANSIColors,\n colon,\n colorize,\n colorizeNumber,\n colorizePath,\n getAppLogger,\n retryManager,\n} from '@intlayer/config';\nimport {\n getFilterMissingTranslationsDictionary,\n getPerLocaleDictionary,\n insertContentInDictionary,\n mergeDictionaries,\n} from '@intlayer/core';\nimport type { Dictionary, IntlayerConfig, Locale } from '@intlayer/types';\nimport { getUnmergedDictionaries } from '@intlayer/unmerged-dictionaries-entry';\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<typeof import('@intlayer/chokidar').getGlobalLimiter>;\n onSuccess?: () => void;\n onError?: (error: unknown) => void;\n getAbortError?: () => Error | null;\n};\n\nconst hasMissingMetadata = (dictionary: Dictionary) =>\n !dictionary.description || !dictionary.title || !dictionary.tags;\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 } = {\n mode: 'complete',\n fillMetadata: true,\n ...options,\n } as const;\n\n const ensureNotAborted = () => {\n const abortError = options?.getAbortError?.();\n if (abortError) {\n throw abortError;\n }\n };\n\n const notifySuccess = () => {\n followingErrors = 0;\n options?.onSuccess?.();\n };\n\n const notifyError = (error: unknown) => {\n if (!options?.onError) return;\n try {\n options.onError(error);\n } catch (abortError) {\n throw abortError;\n }\n };\n\n const result = await retryManager(\n async () => {\n ensureNotAborted();\n\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 ensureNotAborted();\n try {\n return await intlayerAPI.ai.auditContentDeclarationMetadata({\n fileContent: JSON.stringify(defaultLocaleDictionary),\n aiOptions,\n });\n } catch (error) {\n notifyError(error);\n throw error;\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 let dictionaryToProcess = baseUnmergedDictionary;\n\n const translatedContent: Partial<Record<Locale, Dictionary['content']>> =\n {};\n\n for await (const targetLocale of task.targetLocales) {\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 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 const targetLocaleDictionary = getPerLocaleDictionary(\n baseUnmergedDictionary,\n targetLocale\n );\n\n const localePreset = colon(\n [\n colorize('[', ANSIColors.GREY_DARK),\n formatLocale(targetLocale),\n colorize(']', ANSIColors.GREY_DARK),\n ].join(''),\n { colSize: 10 }\n );\n\n const 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\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,\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 targetLocaleDictionary.content,\n chunkContent\n ) as unknown as JSON;\n\n const executeTranslation = async () => {\n return await retryManager(\n async () => {\n ensureNotAborted();\n\n const translationResult = await intlayerAPI.ai.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\n if (!translationResult.data?.fileContent) {\n throw new Error('No content result');\n }\n\n const { isIdentic } = verifyIdenticObjectFormat(\n translationResult.data.fileContent,\n chunkContent\n );\n if (!isIdentic) {\n throw new Error(\n 'Translation result does not match expected format'\n );\n }\n\n notifySuccess();\n return translationResult.data.fileContent;\n },\n {\n maxRetry: MAX_RETRY,\n delay: RETRY_DELAY,\n onError: ({ error, attempt, maxRetry }) => {\n notifyError(error);\n\n const chunkPreset = createChunkPreset(\n chunk.index,\n chunk.total\n );\n appLogger(\n `${task.dictionaryPreset}${localePreset}${chunkPreset} ${colorize('Error filling:', ANSIColors.RED)} ${colorize(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((a, b) => a.chunk.index - b.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 const merged = mergeDictionaries(\n chunkResult.map((chunk) => ({\n ...dictionaryToProcess,\n content: chunk,\n }))\n );\n\n translatedContent[targetLocale] =\n merged.content as Dictionary['content'];\n }\n\n let dictionaryOutput: Dictionary = {\n ...baseUnmergedDictionary,\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 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 fill command:', ANSIColors.RED)} ${colorize(error, ANSIColors.GREY_DARK)} - Attempt ${colorizeNumber(attempt + 1)} of ${colorizeNumber(maxRetry)}`,\n {\n level: 'error',\n }\n );\n },\n onMaxTryReached: ({ error }) => {\n appLogger(\n `${task.dictionaryPreset} ${colorize('Maximum number of retries reached:', ANSIColors.RED)} ${colorize(error, ANSIColors.GREY_DARK)}`,\n {\n level: 'error',\n }\n );\n },\n }\n )();\n\n return result as TranslateDictionaryResult;\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"translateDictionary.mjs","names":["baseUnmergedDictionary: Dictionary | undefined","metadata:\n | Pick<Dictionary, 'description' | 'title' | 'tags'>\n | undefined","translatedContent: Partial<Record<Locale, Dictionary['content']>>","chunkedJsonContent: JsonChunk[]","chunkResult: JsonChunk[]","chunkPreset","dictionaryOutput: Dictionary"],"sources":["../../../src/fill/translateDictionary.ts"],"sourcesContent":["import { basename } from 'node:path';\nimport { type AIOptions, getIntlayerAPIProxy } from '@intlayer/api';\nimport {\n chunkJSON,\n formatLocale,\n type JsonChunk,\n reconstructFromSingleChunk,\n reduceObjectFormat,\n verifyIdenticObjectFormat,\n} from '@intlayer/chokidar';\nimport {\n ANSIColors,\n colon,\n colorize,\n colorizeNumber,\n colorizePath,\n getAppLogger,\n retryManager,\n} from '@intlayer/config';\nimport {\n getFilterMissingTranslationsDictionary,\n getPerLocaleDictionary,\n insertContentInDictionary,\n mergeDictionaries,\n} from '@intlayer/core';\nimport type { Dictionary, IntlayerConfig, Locale } from '@intlayer/types';\nimport { getUnmergedDictionaries } from '@intlayer/unmerged-dictionaries-entry';\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<typeof import('@intlayer/chokidar').getGlobalLimiter>;\n onSuccess?: () => void;\n onError?: (error: unknown) => void;\n getAbortError?: () => Error | null;\n};\n\nconst hasMissingMetadata = (dictionary: Dictionary) =>\n !dictionary.description || !dictionary.title || !dictionary.tags;\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 } = {\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 try {\n return await intlayerAPI.ai.auditContentDeclarationMetadata({\n fileContent: JSON.stringify(defaultLocaleDictionary),\n aiOptions,\n });\n } catch (error) {\n throw error;\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 translatedContent: Partial<Record<Locale, Dictionary['content']>> =\n {};\n\n for await (const targetLocale of task.targetLocales) {\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 = baseUnmergedDictionary;\n\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 const targetLocaleDictionary = getPerLocaleDictionary(\n baseUnmergedDictionary,\n targetLocale\n );\n\n const localePreset = colon(\n [\n colorize('[', ANSIColors.GREY_DARK),\n formatLocale(targetLocale),\n colorize(']', ANSIColors.GREY_DARK),\n ].join(''),\n { colSize: 10 }\n );\n\n const 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\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,\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 targetLocaleDictionary.content,\n chunkContent\n ) as unknown as JSON;\n\n const executeTranslation = async () => {\n return await retryManager(\n async () => {\n const translationResult = await intlayerAPI.ai.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\n if (!translationResult.data?.fileContent) {\n throw new Error('No content result');\n }\n\n const { isIdentic } = verifyIdenticObjectFormat(\n translationResult.data.fileContent,\n chunkContent\n );\n if (!isIdentic) {\n throw new Error(\n 'Translation result does not match expected format'\n );\n }\n\n notifySuccess();\n return translationResult.data.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((a, b) => a.chunk.index - b.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 const merged = mergeDictionaries(\n chunkResult.map((chunk) => ({\n ...dictionaryToProcess,\n content: chunk,\n }))\n );\n\n translatedContent[targetLocale] =\n merged.content as Dictionary['content'];\n }\n\n let dictionaryOutput: Dictionary = {\n ...baseUnmergedDictionary,\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 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 fill command:', 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 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","names":[],"sources":["../../src/config.ts"],"sourcesContent":[],"mappings":";;;KAMK,aAAA;kBACa;AAHQ,CAAA;AAMb,cAAA,SAAuB,EAAA,CAAA,OAAa,
|
|
1
|
+
{"version":3,"file":"config.d.ts","names":[],"sources":["../../src/config.ts"],"sourcesContent":[],"mappings":";;;KAMK,aAAA;kBACa;AAHQ,CAAA;AAMb,cAAA,SAAuB,EAAA,CAAA,OAAa,CAAb,EAAA,aAAa,EAAA,GAAA,IAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"translateDictionary.d.ts","names":[],"sources":["../../../src/fill/translateDictionary.ts"],"sourcesContent":[],"mappings":";;;;;;KA6BK,yBAAA,GAA4B;oBACb;;AAH2C,KAM1D,0BAAA,GAJyB;EAIzB,IAAA,EAAA,UAAA,GAAA,QAAA;EAES,SAAA,CAAA,EAAA,SAAA;EAAS,YAAA,CAAA,EAAA,OAEqC;EAA/C,QAAA,CAAA,EAAA,UAAA,CAAA,OAFU,mBAAA,CAEqC,gBAA/C,CAAA;EAGW,SAAA,CAAA,EAAA,GAAA,GAAA,IAAA;EAAK,OAAA,CAAA,EAAA,CAAA,KAAA,EAAA,OAAA,EAAA,GAAA,IAAA;EAchB,aAAA,CAAA,EAAA,GAAA,GAdW,
|
|
1
|
+
{"version":3,"file":"translateDictionary.d.ts","names":[],"sources":["../../../src/fill/translateDictionary.ts"],"sourcesContent":[],"mappings":";;;;;;KA6BK,yBAAA,GAA4B;oBACb;;AAH2C,KAM1D,0BAAA,GAJyB;EAIzB,IAAA,EAAA,UAAA,GAAA,QAAA;EAES,SAAA,CAAA,EAAA,SAAA;EAAS,YAAA,CAAA,EAAA,OAEqC;EAA/C,QAAA,CAAA,EAAA,UAAA,CAAA,OAFU,mBAAA,CAEqC,gBAA/C,CAAA;EAGW,SAAA,CAAA,EAAA,GAAA,GAAA,IAAA;EAAK,OAAA,CAAA,EAAA,CAAA,KAAA,EAAA,OAAA,EAAA,GAAA,IAAA;EAchB,aAAA,CAAA,EAAA,GAAA,GAdW,KAyUvB,GAAA,IAAA;CA1TO;AACS,cAFJ,mBAEI,EAAA,CAAA,IAAA,EADT,eACS,EAAA,aAAA,EAAA,cAAA,EAAA,OAAA,CAAA,EACL,0BADK,EAAA,GAEd,OAFc,CAEN,yBAFM,CAAA"}
|
package/dist/types/pull.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pull.d.ts","names":[],"sources":["../../src/pull.ts"],"sourcesContent":[],"mappings":";;;KAmBK,WAAA;;EAAA,mBAAW,CAAA,EAAA,MAGE;EAcL,aA8OZ,CAAA,EA5PiB,
|
|
1
|
+
{"version":3,"file":"pull.d.ts","names":[],"sources":["../../src/pull.ts"],"sourcesContent":[],"mappings":";;;KAmBK,WAAA;;EAAA,mBAAW,CAAA,EAAA,MAGE;EAcL,aA8OZ,CAAA,EA5PiB,uBAcwC;;;;;;cAA7C,iBAAwB,gBAAc"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pullLog.d.ts","names":[],"sources":["../../../src/push/pullLog.ts"],"sourcesContent":[],"mappings":";;;KAQY,UAAA;;EAAA,MAAA,EAEF,
|
|
1
|
+
{"version":3,"file":"pullLog.d.ts","names":[],"sources":["../../../src/push/pullLog.ts"],"sourcesContent":[],"mappings":";;;KAQY,UAAA;;EAAA,MAAA,EAEF,gBAAA,GAAA,SAAA,GAAA,UAAgB;EAIb,YAAA,CAAU,EAAA,MAAA;;cAAV,UAAA;;;;;;;;;;sBAeS"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@intlayer/cli",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.2.2",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Provides uniform command-line interface scripts for Intlayer, used in packages like intlayer-cli and intlayer.",
|
|
6
6
|
"keywords": [
|
|
@@ -66,27 +66,27 @@
|
|
|
66
66
|
"typecheck": "tsc --noEmit --project tsconfig.types.json"
|
|
67
67
|
},
|
|
68
68
|
"dependencies": {
|
|
69
|
-
"@intlayer/api": "7.
|
|
70
|
-
"@intlayer/chokidar": "7.
|
|
71
|
-
"@intlayer/config": "7.
|
|
72
|
-
"@intlayer/dictionaries-entry": "7.
|
|
73
|
-
"@intlayer/remote-dictionaries-entry": "7.
|
|
74
|
-
"@intlayer/types": "7.
|
|
75
|
-
"@intlayer/unmerged-dictionaries-entry": "7.
|
|
69
|
+
"@intlayer/api": "7.2.2",
|
|
70
|
+
"@intlayer/chokidar": "7.2.2",
|
|
71
|
+
"@intlayer/config": "7.2.2",
|
|
72
|
+
"@intlayer/dictionaries-entry": "7.2.2",
|
|
73
|
+
"@intlayer/remote-dictionaries-entry": "7.2.2",
|
|
74
|
+
"@intlayer/types": "7.2.2",
|
|
75
|
+
"@intlayer/unmerged-dictionaries-entry": "7.2.2",
|
|
76
76
|
"commander": "14.0.1",
|
|
77
77
|
"eventsource": "3.0.7",
|
|
78
78
|
"fast-glob": "3.3.3"
|
|
79
79
|
},
|
|
80
80
|
"devDependencies": {
|
|
81
|
-
"@intlayer/core": "7.1
|
|
81
|
+
"@intlayer/core": "7.2.1",
|
|
82
82
|
"@types/node": "24.10.1",
|
|
83
83
|
"@utils/ts-config": "1.0.4",
|
|
84
84
|
"@utils/ts-config-types": "1.0.4",
|
|
85
85
|
"@utils/tsdown-config": "1.0.4",
|
|
86
|
-
"rimraf": "6.1.
|
|
87
|
-
"tsdown": "0.16.
|
|
86
|
+
"rimraf": "6.1.2",
|
|
87
|
+
"tsdown": "0.16.6",
|
|
88
88
|
"typescript": "5.9.3",
|
|
89
|
-
"vitest": "4.0.
|
|
89
|
+
"vitest": "4.0.12"
|
|
90
90
|
},
|
|
91
91
|
"bug": {
|
|
92
92
|
"url": "https://github.com/aymericzip/intlayer/issues"
|