solid-translate 1.4.2 → 1.4.3
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/chunk-P6RTZPHZ.js +141 -0
- package/dist/cli.js +38 -13
- package/dist/{translate-M737VQHG.js → translate-SWLCBQ5Y.js} +5 -1
- package/dist/vite.js +107 -29
- package/dist/vite.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-2BKJUY37.js +0 -82
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/translate.ts
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
async function loadGenerateObject() {
|
|
6
|
+
const { generateObject } = await import("ai");
|
|
7
|
+
return generateObject;
|
|
8
|
+
}
|
|
9
|
+
async function loadGenerateText() {
|
|
10
|
+
const { generateText } = await import("ai");
|
|
11
|
+
return generateText;
|
|
12
|
+
}
|
|
13
|
+
function extractJsonObject(text) {
|
|
14
|
+
const start = text.indexOf("{");
|
|
15
|
+
const end = text.lastIndexOf("}");
|
|
16
|
+
if (start === -1 || end <= start) {
|
|
17
|
+
throw new Error("model response contained no JSON object");
|
|
18
|
+
}
|
|
19
|
+
const parsed = JSON.parse(text.slice(start, end + 1));
|
|
20
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
21
|
+
throw new Error("model response was not a JSON object");
|
|
22
|
+
}
|
|
23
|
+
return parsed;
|
|
24
|
+
}
|
|
25
|
+
function collectBatchTranslations(parsed, requestedKeys) {
|
|
26
|
+
let dict = parsed;
|
|
27
|
+
const inner = parsed["translations"];
|
|
28
|
+
if (typeof inner === "object" && inner !== null && !Array.isArray(inner) && // Only unwrap when the envelope key is not itself a requested key
|
|
29
|
+
!requestedKeys.includes("translations")) {
|
|
30
|
+
dict = inner;
|
|
31
|
+
}
|
|
32
|
+
const translations = {};
|
|
33
|
+
const missing = [];
|
|
34
|
+
for (const key of requestedKeys) {
|
|
35
|
+
const value = dict[key];
|
|
36
|
+
if (typeof value === "string" && value.length > 0) {
|
|
37
|
+
translations[key] = value;
|
|
38
|
+
} else {
|
|
39
|
+
missing.push(key);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return { translations, missing };
|
|
43
|
+
}
|
|
44
|
+
async function translateBatch(model, entries, targetLocale, sourceLocale, systemPrompt, contexts) {
|
|
45
|
+
const keys = Object.keys(entries);
|
|
46
|
+
if (keys.length === 0) return {};
|
|
47
|
+
const defaultSystem = [
|
|
48
|
+
`You are a professional translator specializing in software localization.`,
|
|
49
|
+
`Translate text from "${sourceLocale}" to "${targetLocale}".`,
|
|
50
|
+
`Rules:`,
|
|
51
|
+
`- Preserve the original tone and meaning`,
|
|
52
|
+
`- Keep placeholders like {{variable}}, {variable}, {0}, {1} unchanged`,
|
|
53
|
+
`- Keep HTML tags unchanged`,
|
|
54
|
+
`- Do not add or remove content`,
|
|
55
|
+
`- Return natural, idiomatic translations`
|
|
56
|
+
].join("\n");
|
|
57
|
+
let contextSection = "";
|
|
58
|
+
if (contexts && Object.keys(contexts).length > 0) {
|
|
59
|
+
const contextLines = Object.entries(contexts).filter(([key]) => key in entries).map(([key, ctx]) => ` "${key}": ${ctx}`);
|
|
60
|
+
if (contextLines.length > 0) {
|
|
61
|
+
contextSection = [
|
|
62
|
+
``,
|
|
63
|
+
`Context hints for disambiguation:`,
|
|
64
|
+
...contextLines,
|
|
65
|
+
``
|
|
66
|
+
].join("\n");
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
const generateText = await loadGenerateText();
|
|
70
|
+
const basePrompt = [
|
|
71
|
+
`Translate each value in this JSON object from "${sourceLocale}" to "${targetLocale}".`,
|
|
72
|
+
`Respond with ONLY a JSON object \u2014 no prose, no code fences \u2014 containing the exact same keys and the translated values.`,
|
|
73
|
+
contextSection,
|
|
74
|
+
JSON.stringify(entries, null, 2)
|
|
75
|
+
].join("\n");
|
|
76
|
+
const attempt = async (prompt) => {
|
|
77
|
+
const { text } = await generateText({
|
|
78
|
+
model,
|
|
79
|
+
system: systemPrompt || defaultSystem,
|
|
80
|
+
prompt
|
|
81
|
+
});
|
|
82
|
+
return collectBatchTranslations(extractJsonObject(text), keys);
|
|
83
|
+
};
|
|
84
|
+
let { translations, missing } = await attempt(basePrompt);
|
|
85
|
+
if (missing.length > 0) {
|
|
86
|
+
const retryEntries = {};
|
|
87
|
+
for (const key of missing) retryEntries[key] = entries[key];
|
|
88
|
+
const retry = await attempt(
|
|
89
|
+
[
|
|
90
|
+
`Translate each value in this JSON object from "${sourceLocale}" to "${targetLocale}".`,
|
|
91
|
+
`Respond with ONLY a JSON object \u2014 no prose, no code fences \u2014 containing the exact same keys and the translated values.`,
|
|
92
|
+
contextSection,
|
|
93
|
+
JSON.stringify(retryEntries, null, 2)
|
|
94
|
+
].join("\n")
|
|
95
|
+
);
|
|
96
|
+
translations = { ...translations, ...retry.translations };
|
|
97
|
+
missing = retry.missing;
|
|
98
|
+
}
|
|
99
|
+
if (missing.length > 0) {
|
|
100
|
+
const sample = missing.slice(0, 3).join('", "');
|
|
101
|
+
throw new Error(
|
|
102
|
+
`model returned no translation for ${missing.length} of ${keys.length} keys (e.g. "${sample}")`
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
return translations;
|
|
106
|
+
}
|
|
107
|
+
async function translateMarkdown(model, content, targetLocale, sourceLocale, systemPrompt) {
|
|
108
|
+
const defaultSystem = [
|
|
109
|
+
`You are a professional translator specializing in documentation.`,
|
|
110
|
+
`Translate Markdown/MDX content from "${sourceLocale}" to "${targetLocale}".`,
|
|
111
|
+
`Rules:`,
|
|
112
|
+
`- Preserve all Markdown formatting (headers, lists, bold, italic, links, etc.)`,
|
|
113
|
+
`- Preserve code blocks and inline code unchanged`,
|
|
114
|
+
`- Preserve frontmatter YAML keys (only translate values)`,
|
|
115
|
+
`- Preserve MDX component syntax and JSX expressions`,
|
|
116
|
+
`- Preserve URLs and file paths unchanged`,
|
|
117
|
+
`- Return natural, idiomatic translations`
|
|
118
|
+
].join("\n");
|
|
119
|
+
const generateObject = await loadGenerateObject();
|
|
120
|
+
const { object } = await generateObject({
|
|
121
|
+
model,
|
|
122
|
+
schema: z.object({
|
|
123
|
+
translated: z.string()
|
|
124
|
+
}),
|
|
125
|
+
system: systemPrompt || defaultSystem,
|
|
126
|
+
prompt: [
|
|
127
|
+
`Translate this Markdown/MDX content from "${sourceLocale}" to "${targetLocale}".`,
|
|
128
|
+
`Return the complete translated document.`,
|
|
129
|
+
``,
|
|
130
|
+
content
|
|
131
|
+
].join("\n")
|
|
132
|
+
});
|
|
133
|
+
return object.translated;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export {
|
|
137
|
+
extractJsonObject,
|
|
138
|
+
collectBatchTranslations,
|
|
139
|
+
translateBatch,
|
|
140
|
+
translateMarkdown
|
|
141
|
+
};
|
package/dist/cli.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
translateBatch
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-P6RTZPHZ.js";
|
|
5
5
|
import {
|
|
6
6
|
__commonJS,
|
|
7
7
|
__toESM
|
|
@@ -15106,7 +15106,16 @@ async function syncLocaleFiles(options) {
|
|
|
15106
15106
|
delete lock.keys[key];
|
|
15107
15107
|
}
|
|
15108
15108
|
const changedCount = Object.keys(changedKeys).length;
|
|
15109
|
-
|
|
15109
|
+
const missingByLocale = {};
|
|
15110
|
+
for (const targetLocale of targetLocales) {
|
|
15111
|
+
const existing = readTargetFile(join(localesDir, `${targetLocale}.json`));
|
|
15112
|
+
const missing = Object.keys(sourceDict).filter(
|
|
15113
|
+
(key) => !(key in changedKeys) && !(key in existing)
|
|
15114
|
+
);
|
|
15115
|
+
if (missing.length > 0) missingByLocale[targetLocale] = missing;
|
|
15116
|
+
}
|
|
15117
|
+
const missingLocaleCount = Object.keys(missingByLocale).length;
|
|
15118
|
+
if (changedCount === 0 && deletedKeys.length === 0 && missingLocaleCount === 0) {
|
|
15110
15119
|
log("No changes detected in locale files.");
|
|
15111
15120
|
return {
|
|
15112
15121
|
status: "no-changes",
|
|
@@ -15115,7 +15124,7 @@ async function syncLocaleFiles(options) {
|
|
|
15115
15124
|
failures: []
|
|
15116
15125
|
};
|
|
15117
15126
|
}
|
|
15118
|
-
if (changedCount === 0) {
|
|
15127
|
+
if (changedCount === 0 && missingLocaleCount === 0) {
|
|
15119
15128
|
for (const targetLocale of targetLocales) {
|
|
15120
15129
|
const targetFilePath = join(localesDir, `${targetLocale}.json`);
|
|
15121
15130
|
const existing = readTargetFile(targetFilePath);
|
|
@@ -15128,27 +15137,43 @@ async function syncLocaleFiles(options) {
|
|
|
15128
15137
|
);
|
|
15129
15138
|
return { status: "synced", translatedKeys: [], deletedKeys, failures: [] };
|
|
15130
15139
|
}
|
|
15131
|
-
|
|
15132
|
-
|
|
15133
|
-
|
|
15134
|
-
|
|
15135
|
-
for (const key of Object.keys(changedKeys)) {
|
|
15136
|
-
const ctx = pendingEntries[key]?.context;
|
|
15137
|
-
if (ctx) changedContexts[key] = ctx;
|
|
15140
|
+
if (changedCount > 0) {
|
|
15141
|
+
log(
|
|
15142
|
+
`Translating ${changedCount} key${changedCount > 1 ? "s" : ""} to ${targetLocales.length} locale${targetLocales.length > 1 ? "s" : ""}...`
|
|
15143
|
+
);
|
|
15138
15144
|
}
|
|
15145
|
+
if (missingLocaleCount > 0) {
|
|
15146
|
+
const healTotal = Object.values(missingByLocale).reduce(
|
|
15147
|
+
(sum, keys) => sum + keys.length,
|
|
15148
|
+
0
|
|
15149
|
+
);
|
|
15150
|
+
log(
|
|
15151
|
+
`Healing ${healTotal} key${healTotal > 1 ? "s" : ""} missing from ${missingLocaleCount} locale file${missingLocaleCount > 1 ? "s" : ""}...`
|
|
15152
|
+
);
|
|
15153
|
+
}
|
|
15154
|
+
const contextFor = (key) => pendingEntries[key]?.context ?? lock.keys[key]?.context;
|
|
15139
15155
|
const failures = [];
|
|
15140
15156
|
const failedKeys = /* @__PURE__ */ new Set();
|
|
15141
15157
|
for (const targetLocale of targetLocales) {
|
|
15142
15158
|
const targetFilePath = join(localesDir, `${targetLocale}.json`);
|
|
15143
15159
|
const existing = readTargetFile(targetFilePath);
|
|
15144
|
-
const
|
|
15160
|
+
const localeEntries = { ...changedKeys };
|
|
15161
|
+
for (const key of missingByLocale[targetLocale] ?? []) {
|
|
15162
|
+
localeEntries[key] = sourceDict[key];
|
|
15163
|
+
}
|
|
15164
|
+
const localeContexts = {};
|
|
15165
|
+
for (const key of Object.keys(localeEntries)) {
|
|
15166
|
+
const ctx = contextFor(key);
|
|
15167
|
+
if (ctx) localeContexts[key] = ctx;
|
|
15168
|
+
}
|
|
15169
|
+
const entries = Object.entries(localeEntries);
|
|
15145
15170
|
for (let i = 0; i < entries.length; i += batchSize) {
|
|
15146
15171
|
const batch = Object.fromEntries(entries.slice(i, i + batchSize));
|
|
15147
15172
|
try {
|
|
15148
15173
|
const translated = await translate(
|
|
15149
15174
|
batch,
|
|
15150
15175
|
targetLocale,
|
|
15151
|
-
|
|
15176
|
+
localeContexts
|
|
15152
15177
|
);
|
|
15153
15178
|
Object.assign(existing, translated);
|
|
15154
15179
|
} catch (err) {
|
|
@@ -15545,7 +15570,7 @@ Run \`solid-translate translate\` to refresh ${sourceLocale} \u2192 targets.`
|
|
|
15545
15570
|
);
|
|
15546
15571
|
}
|
|
15547
15572
|
async function runTranslate() {
|
|
15548
|
-
const { translateBatch: translateBatch2, translateMarkdown: translateMarkdown2 } = await import("./translate-
|
|
15573
|
+
const { translateBatch: translateBatch2, translateMarkdown: translateMarkdown2 } = await import("./translate-SWLCBQ5Y.js");
|
|
15549
15574
|
const config = await loadConfig();
|
|
15550
15575
|
const root = process.cwd();
|
|
15551
15576
|
const sourceLocale = config.sourceLocale || "en";
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
+
collectBatchTranslations,
|
|
4
|
+
extractJsonObject,
|
|
3
5
|
translateBatch,
|
|
4
6
|
translateMarkdown
|
|
5
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-P6RTZPHZ.js";
|
|
6
8
|
import "./chunk-FYS2JH42.js";
|
|
7
9
|
export {
|
|
10
|
+
collectBatchTranslations,
|
|
11
|
+
extractJsonObject,
|
|
8
12
|
translateBatch,
|
|
9
13
|
translateMarkdown
|
|
10
14
|
};
|
package/dist/vite.js
CHANGED
|
@@ -14617,9 +14617,40 @@ function hashContent(content) {
|
|
|
14617
14617
|
|
|
14618
14618
|
// src/translate.ts
|
|
14619
14619
|
import { z } from "zod";
|
|
14620
|
-
async function
|
|
14621
|
-
const {
|
|
14622
|
-
return
|
|
14620
|
+
async function loadGenerateText() {
|
|
14621
|
+
const { generateText } = await import("ai");
|
|
14622
|
+
return generateText;
|
|
14623
|
+
}
|
|
14624
|
+
function extractJsonObject(text) {
|
|
14625
|
+
const start = text.indexOf("{");
|
|
14626
|
+
const end = text.lastIndexOf("}");
|
|
14627
|
+
if (start === -1 || end <= start) {
|
|
14628
|
+
throw new Error("model response contained no JSON object");
|
|
14629
|
+
}
|
|
14630
|
+
const parsed = JSON.parse(text.slice(start, end + 1));
|
|
14631
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
14632
|
+
throw new Error("model response was not a JSON object");
|
|
14633
|
+
}
|
|
14634
|
+
return parsed;
|
|
14635
|
+
}
|
|
14636
|
+
function collectBatchTranslations(parsed, requestedKeys) {
|
|
14637
|
+
let dict = parsed;
|
|
14638
|
+
const inner = parsed["translations"];
|
|
14639
|
+
if (typeof inner === "object" && inner !== null && !Array.isArray(inner) && // Only unwrap when the envelope key is not itself a requested key
|
|
14640
|
+
!requestedKeys.includes("translations")) {
|
|
14641
|
+
dict = inner;
|
|
14642
|
+
}
|
|
14643
|
+
const translations = {};
|
|
14644
|
+
const missing = [];
|
|
14645
|
+
for (const key of requestedKeys) {
|
|
14646
|
+
const value = dict[key];
|
|
14647
|
+
if (typeof value === "string" && value.length > 0) {
|
|
14648
|
+
translations[key] = value;
|
|
14649
|
+
} else {
|
|
14650
|
+
missing.push(key);
|
|
14651
|
+
}
|
|
14652
|
+
}
|
|
14653
|
+
return { translations, missing };
|
|
14623
14654
|
}
|
|
14624
14655
|
async function translateBatch(model, entries, targetLocale, sourceLocale, systemPrompt, contexts) {
|
|
14625
14656
|
const keys = Object.keys(entries);
|
|
@@ -14646,21 +14677,43 @@ async function translateBatch(model, entries, targetLocale, sourceLocale, system
|
|
|
14646
14677
|
].join("\n");
|
|
14647
14678
|
}
|
|
14648
14679
|
}
|
|
14649
|
-
const
|
|
14650
|
-
const
|
|
14651
|
-
|
|
14652
|
-
|
|
14653
|
-
|
|
14654
|
-
|
|
14655
|
-
|
|
14656
|
-
|
|
14657
|
-
|
|
14658
|
-
|
|
14659
|
-
|
|
14660
|
-
|
|
14661
|
-
|
|
14662
|
-
|
|
14663
|
-
|
|
14680
|
+
const generateText = await loadGenerateText();
|
|
14681
|
+
const basePrompt = [
|
|
14682
|
+
`Translate each value in this JSON object from "${sourceLocale}" to "${targetLocale}".`,
|
|
14683
|
+
`Respond with ONLY a JSON object \u2014 no prose, no code fences \u2014 containing the exact same keys and the translated values.`,
|
|
14684
|
+
contextSection,
|
|
14685
|
+
JSON.stringify(entries, null, 2)
|
|
14686
|
+
].join("\n");
|
|
14687
|
+
const attempt = async (prompt) => {
|
|
14688
|
+
const { text } = await generateText({
|
|
14689
|
+
model,
|
|
14690
|
+
system: systemPrompt || defaultSystem,
|
|
14691
|
+
prompt
|
|
14692
|
+
});
|
|
14693
|
+
return collectBatchTranslations(extractJsonObject(text), keys);
|
|
14694
|
+
};
|
|
14695
|
+
let { translations, missing } = await attempt(basePrompt);
|
|
14696
|
+
if (missing.length > 0) {
|
|
14697
|
+
const retryEntries = {};
|
|
14698
|
+
for (const key of missing) retryEntries[key] = entries[key];
|
|
14699
|
+
const retry = await attempt(
|
|
14700
|
+
[
|
|
14701
|
+
`Translate each value in this JSON object from "${sourceLocale}" to "${targetLocale}".`,
|
|
14702
|
+
`Respond with ONLY a JSON object \u2014 no prose, no code fences \u2014 containing the exact same keys and the translated values.`,
|
|
14703
|
+
contextSection,
|
|
14704
|
+
JSON.stringify(retryEntries, null, 2)
|
|
14705
|
+
].join("\n")
|
|
14706
|
+
);
|
|
14707
|
+
translations = { ...translations, ...retry.translations };
|
|
14708
|
+
missing = retry.missing;
|
|
14709
|
+
}
|
|
14710
|
+
if (missing.length > 0) {
|
|
14711
|
+
const sample = missing.slice(0, 3).join('", "');
|
|
14712
|
+
throw new Error(
|
|
14713
|
+
`model returned no translation for ${missing.length} of ${keys.length} keys (e.g. "${sample}")`
|
|
14714
|
+
);
|
|
14715
|
+
}
|
|
14716
|
+
return translations;
|
|
14664
14717
|
}
|
|
14665
14718
|
|
|
14666
14719
|
// src/extract.ts
|
|
@@ -15172,7 +15225,16 @@ async function syncLocaleFiles(options) {
|
|
|
15172
15225
|
delete lock.keys[key];
|
|
15173
15226
|
}
|
|
15174
15227
|
const changedCount = Object.keys(changedKeys).length;
|
|
15175
|
-
|
|
15228
|
+
const missingByLocale = {};
|
|
15229
|
+
for (const targetLocale of targetLocales) {
|
|
15230
|
+
const existing = readTargetFile(join(localesDir, `${targetLocale}.json`));
|
|
15231
|
+
const missing = Object.keys(sourceDict).filter(
|
|
15232
|
+
(key) => !(key in changedKeys) && !(key in existing)
|
|
15233
|
+
);
|
|
15234
|
+
if (missing.length > 0) missingByLocale[targetLocale] = missing;
|
|
15235
|
+
}
|
|
15236
|
+
const missingLocaleCount = Object.keys(missingByLocale).length;
|
|
15237
|
+
if (changedCount === 0 && deletedKeys.length === 0 && missingLocaleCount === 0) {
|
|
15176
15238
|
log("No changes detected in locale files.");
|
|
15177
15239
|
return {
|
|
15178
15240
|
status: "no-changes",
|
|
@@ -15181,7 +15243,7 @@ async function syncLocaleFiles(options) {
|
|
|
15181
15243
|
failures: []
|
|
15182
15244
|
};
|
|
15183
15245
|
}
|
|
15184
|
-
if (changedCount === 0) {
|
|
15246
|
+
if (changedCount === 0 && missingLocaleCount === 0) {
|
|
15185
15247
|
for (const targetLocale of targetLocales) {
|
|
15186
15248
|
const targetFilePath = join(localesDir, `${targetLocale}.json`);
|
|
15187
15249
|
const existing = readTargetFile(targetFilePath);
|
|
@@ -15194,27 +15256,43 @@ async function syncLocaleFiles(options) {
|
|
|
15194
15256
|
);
|
|
15195
15257
|
return { status: "synced", translatedKeys: [], deletedKeys, failures: [] };
|
|
15196
15258
|
}
|
|
15197
|
-
|
|
15198
|
-
|
|
15199
|
-
|
|
15200
|
-
|
|
15201
|
-
for (const key of Object.keys(changedKeys)) {
|
|
15202
|
-
const ctx = pendingEntries[key]?.context;
|
|
15203
|
-
if (ctx) changedContexts[key] = ctx;
|
|
15259
|
+
if (changedCount > 0) {
|
|
15260
|
+
log(
|
|
15261
|
+
`Translating ${changedCount} key${changedCount > 1 ? "s" : ""} to ${targetLocales.length} locale${targetLocales.length > 1 ? "s" : ""}...`
|
|
15262
|
+
);
|
|
15204
15263
|
}
|
|
15264
|
+
if (missingLocaleCount > 0) {
|
|
15265
|
+
const healTotal = Object.values(missingByLocale).reduce(
|
|
15266
|
+
(sum, keys) => sum + keys.length,
|
|
15267
|
+
0
|
|
15268
|
+
);
|
|
15269
|
+
log(
|
|
15270
|
+
`Healing ${healTotal} key${healTotal > 1 ? "s" : ""} missing from ${missingLocaleCount} locale file${missingLocaleCount > 1 ? "s" : ""}...`
|
|
15271
|
+
);
|
|
15272
|
+
}
|
|
15273
|
+
const contextFor = (key) => pendingEntries[key]?.context ?? lock.keys[key]?.context;
|
|
15205
15274
|
const failures = [];
|
|
15206
15275
|
const failedKeys = /* @__PURE__ */ new Set();
|
|
15207
15276
|
for (const targetLocale of targetLocales) {
|
|
15208
15277
|
const targetFilePath = join(localesDir, `${targetLocale}.json`);
|
|
15209
15278
|
const existing = readTargetFile(targetFilePath);
|
|
15210
|
-
const
|
|
15279
|
+
const localeEntries = { ...changedKeys };
|
|
15280
|
+
for (const key of missingByLocale[targetLocale] ?? []) {
|
|
15281
|
+
localeEntries[key] = sourceDict[key];
|
|
15282
|
+
}
|
|
15283
|
+
const localeContexts = {};
|
|
15284
|
+
for (const key of Object.keys(localeEntries)) {
|
|
15285
|
+
const ctx = contextFor(key);
|
|
15286
|
+
if (ctx) localeContexts[key] = ctx;
|
|
15287
|
+
}
|
|
15288
|
+
const entries = Object.entries(localeEntries);
|
|
15211
15289
|
for (let i = 0; i < entries.length; i += batchSize) {
|
|
15212
15290
|
const batch = Object.fromEntries(entries.slice(i, i + batchSize));
|
|
15213
15291
|
try {
|
|
15214
15292
|
const translated = await translate(
|
|
15215
15293
|
batch,
|
|
15216
15294
|
targetLocale,
|
|
15217
|
-
|
|
15295
|
+
localeContexts
|
|
15218
15296
|
);
|
|
15219
15297
|
Object.assign(existing, translated);
|
|
15220
15298
|
} catch (err) {
|