@braze/sanity-plugin-ai-translation 0.1.7 → 0.1.76
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/index.js +102 -40
- package/dist/index.js.map +1 -1
- package/package.json +12 -14
package/dist/index.js
CHANGED
|
@@ -1106,9 +1106,10 @@ const ICON_SIZE = 64, progressIconMap = {
|
|
|
1106
1106
|
}), fetchTranslationPrompts = async ({
|
|
1107
1107
|
client,
|
|
1108
1108
|
sourceLanguage,
|
|
1109
|
+
sourceDocument,
|
|
1109
1110
|
targetLanguages
|
|
1110
1111
|
}) => {
|
|
1111
|
-
const translationSettings2 = await client.fetch(
|
|
1112
|
+
const sourceDocumentString = JSON.stringify(sourceDocument), translationSettings2 = await client.fetch(
|
|
1112
1113
|
`*[_id == "translation-settings"][0] {
|
|
1113
1114
|
globalPrompt,
|
|
1114
1115
|
localePrompts[locale in $targetLanguages],
|
|
@@ -1118,19 +1119,23 @@ const ICON_SIZE = 64, progressIconMap = {
|
|
|
1118
1119
|
), translationGlossaryTerms = await client.fetch(
|
|
1119
1120
|
`*[
|
|
1120
1121
|
_type == "translationGlossaryTerm"
|
|
1121
|
-
&& count(translations[
|
|
1122
|
-
&& count(translations[
|
|
1122
|
+
&& count(translations[_key in $targetLanguages]) > 0
|
|
1123
|
+
&& count(translations[_key == $sourceLanguage]) > 0
|
|
1123
1124
|
].translations`,
|
|
1124
1125
|
{ targetLanguages, sourceLanguage }
|
|
1125
|
-
) || []
|
|
1126
|
+
) || [];
|
|
1127
|
+
console.log("translationGlossaryTerms", translationGlossaryTerms);
|
|
1128
|
+
const glossaryTermsByLocale = {};
|
|
1126
1129
|
for (const targetLanguage of targetLanguages) {
|
|
1127
|
-
|
|
1130
|
+
let relevantTranslations = translationGlossaryTerms.filter(
|
|
1128
1131
|
(termTranslations) => termTranslations.some((translation) => translation._key === targetLanguage)
|
|
1129
1132
|
).map((termTranslations) => {
|
|
1130
1133
|
const from = termTranslations.find((translation) => translation._key === sourceLanguage)?.value, to = termTranslations.find((translation) => translation._key === targetLanguage)?.value;
|
|
1131
1134
|
return !from || !to ? null : { from, to };
|
|
1132
1135
|
}).filter((item) => item !== null);
|
|
1133
|
-
|
|
1136
|
+
console.log("relevantTranslations", relevantTranslations), sourceDocument && (relevantTranslations = relevantTranslations.filter(
|
|
1137
|
+
({ from }) => termAppearsInDocument(from, sourceDocumentString)
|
|
1138
|
+
), console.log("relevantTranslations", relevantTranslations)), glossaryTermsByLocale[targetLanguage] = relevantTranslations;
|
|
1134
1139
|
}
|
|
1135
1140
|
return {
|
|
1136
1141
|
globalPrompt: translationSettings2?.globalPrompt,
|
|
@@ -1145,12 +1150,17 @@ const ICON_SIZE = 64, progressIconMap = {
|
|
|
1145
1150
|
])
|
|
1146
1151
|
)
|
|
1147
1152
|
};
|
|
1153
|
+
}, termAppearsInDocument = (term, documentString) => {
|
|
1154
|
+
const baseTerm = term.split(":")[0].trim();
|
|
1155
|
+
return documentString.includes(baseTerm.toLowerCase());
|
|
1148
1156
|
}, formatGlossaryTermInstruction = (terms) => {
|
|
1149
1157
|
const instructions = [
|
|
1150
|
-
"
|
|
1158
|
+
"## Glossary Terms",
|
|
1159
|
+
"Translate the following terms exactly as described below.",
|
|
1160
|
+
"When a term includes a colon, the suffix following the colon describes how the term should be translated for the female gender."
|
|
1151
1161
|
];
|
|
1152
1162
|
return terms.forEach(({ from, to }) => {
|
|
1153
|
-
instructions.push(`"${from}"
|
|
1163
|
+
instructions.push(`Always translate "${from}" to "${to}".`);
|
|
1154
1164
|
}), instructions.join(`
|
|
1155
1165
|
`);
|
|
1156
1166
|
}, findKeyValues = (obj, targetKey) => {
|
|
@@ -1383,7 +1393,11 @@ const setupTranslationJobStatuses = (dispatch, sourceDocuments, targetLanguages)
|
|
|
1383
1393
|
}
|
|
1384
1394
|
});
|
|
1385
1395
|
} catch (error) {
|
|
1386
|
-
|
|
1396
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1397
|
+
setJobStatus(dispatch, sourceDocument._id, targetLanguage, "error", {
|
|
1398
|
+
errorMessage
|
|
1399
|
+
}), console.error("Error translating document:", error);
|
|
1400
|
+
return;
|
|
1387
1401
|
}
|
|
1388
1402
|
}, assembleLanguageSpecs = ({
|
|
1389
1403
|
sourceDocument,
|
|
@@ -1479,11 +1493,15 @@ const setupTranslationJobStatuses = (dispatch, sourceDocuments, targetLanguages)
|
|
|
1479
1493
|
newDocumentRev: newDocument._rev
|
|
1480
1494
|
}), devLog.timeEnd("translateEntireDocument"), devLog.groupEnd(), newDocument;
|
|
1481
1495
|
} catch (error) {
|
|
1482
|
-
|
|
1496
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1497
|
+
devLog.error(
|
|
1483
1498
|
"translateEntireDocument failed",
|
|
1484
1499
|
error instanceof Error ? error : new Error(String(error)),
|
|
1485
1500
|
logContext
|
|
1486
|
-
),
|
|
1501
|
+
), setJobStatus(dispatch, sourceDocument._id, targetLanguage, "error", {
|
|
1502
|
+
errorMessage
|
|
1503
|
+
}), devLog.timeEnd("translateEntireDocument"), devLog.groupEnd();
|
|
1504
|
+
return;
|
|
1487
1505
|
}
|
|
1488
1506
|
}, updateTranslationMetadata = async ({
|
|
1489
1507
|
client,
|
|
@@ -1644,7 +1662,11 @@ const setupTranslationJobStatuses = (dispatch, sourceDocuments, targetLanguages)
|
|
|
1644
1662
|
newDocumentId: newDocument._id
|
|
1645
1663
|
}), devLog.timeEnd("translateDocument"), devLog.groupEnd(), newDocument._id;
|
|
1646
1664
|
} catch (error) {
|
|
1647
|
-
|
|
1665
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1666
|
+
devLog.error("Translation failed", error instanceof Error ? error : new Error(String(error)), logContext), setJobStatus(dispatch, sourceDocument._id, targetLanguage, "error", {
|
|
1667
|
+
errorMessage
|
|
1668
|
+
}), devLog.timeEnd("translateDocument"), devLog.groupEnd();
|
|
1669
|
+
return;
|
|
1648
1670
|
}
|
|
1649
1671
|
}, patchTranslationTarget = async ({
|
|
1650
1672
|
client,
|
|
@@ -1719,36 +1741,54 @@ const setupTranslationJobStatuses = (dispatch, sourceDocuments, targetLanguages)
|
|
|
1719
1741
|
const translationPrompts = await fetchTranslationPrompts({
|
|
1720
1742
|
client,
|
|
1721
1743
|
sourceLanguage,
|
|
1722
|
-
targetLanguages
|
|
1744
|
+
targetLanguages,
|
|
1745
|
+
sourceDocument
|
|
1723
1746
|
});
|
|
1724
1747
|
devLog.info("Translation prompts", translationPrompts), setupTranslationJobStatuses(dispatch, sourceDocuments, targetLanguages);
|
|
1725
1748
|
const translationTasks = sourceDocumentIds.flatMap(
|
|
1726
1749
|
(documentId) => targetLanguages.map(async (targetLanguage) => {
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
id
|
|
1733
|
-
|
|
1734
|
-
if (document) {
|
|
1735
|
-
const targetDocumentId = await translateDocument({
|
|
1736
|
-
dispatch,
|
|
1737
|
-
client,
|
|
1738
|
-
sourceDocument: document,
|
|
1739
|
-
targetLanguage,
|
|
1740
|
-
languageFieldName,
|
|
1741
|
-
shouldOverwriteFreshTranslations,
|
|
1742
|
-
schemaId,
|
|
1743
|
-
shouldTranslateDiffs,
|
|
1744
|
-
translationPrompts
|
|
1750
|
+
try {
|
|
1751
|
+
await translationSemaphore.withPermit(async () => {
|
|
1752
|
+
const document = await client.withConfig({
|
|
1753
|
+
// This enables us to detect documents that might be stale due to unpublished changes
|
|
1754
|
+
perspective: "raw"
|
|
1755
|
+
}).fetch("*[_id == $id][0]", {
|
|
1756
|
+
id: documentId
|
|
1745
1757
|
});
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1758
|
+
if (document) {
|
|
1759
|
+
const targetDocumentId = await translateDocument({
|
|
1760
|
+
dispatch,
|
|
1761
|
+
client,
|
|
1762
|
+
sourceDocument: document,
|
|
1763
|
+
targetLanguage,
|
|
1764
|
+
languageFieldName,
|
|
1765
|
+
shouldOverwriteFreshTranslations,
|
|
1766
|
+
schemaId,
|
|
1767
|
+
shouldTranslateDiffs,
|
|
1768
|
+
translationPrompts
|
|
1769
|
+
});
|
|
1770
|
+
targetDocumentId && targetDocumentIds.push(targetDocumentId);
|
|
1771
|
+
}
|
|
1772
|
+
});
|
|
1773
|
+
} catch (error) {
|
|
1774
|
+
devLog.error(
|
|
1775
|
+
"Individual translation task failed",
|
|
1776
|
+
error instanceof Error ? error : new Error(String(error)),
|
|
1777
|
+
{
|
|
1778
|
+
...logContext,
|
|
1779
|
+
failedDocumentId: documentId,
|
|
1780
|
+
failedTargetLanguage: targetLanguage
|
|
1781
|
+
}
|
|
1782
|
+
);
|
|
1783
|
+
}
|
|
1749
1784
|
})
|
|
1750
|
-
);
|
|
1751
|
-
|
|
1785
|
+
), results = await Promise.allSettled(translationTasks), succeeded = results.filter((r) => r.status === "fulfilled").length, failed = results.filter((r) => r.status === "rejected").length;
|
|
1786
|
+
devLog.info("Translation tasks summary", {
|
|
1787
|
+
...logContext,
|
|
1788
|
+
total: results.length,
|
|
1789
|
+
succeeded,
|
|
1790
|
+
failed
|
|
1791
|
+
}), devLog.info("All translation tasks completed", {
|
|
1752
1792
|
...logContext,
|
|
1753
1793
|
targetDocumentIds: targetDocumentIds.length
|
|
1754
1794
|
}), dispatch({
|
|
@@ -1765,9 +1805,19 @@ const setupTranslationJobStatuses = (dispatch, sourceDocuments, targetLanguages)
|
|
|
1765
1805
|
languageFieldName
|
|
1766
1806
|
});
|
|
1767
1807
|
})
|
|
1768
|
-
)
|
|
1808
|
+
);
|
|
1809
|
+
const hasAnyFailures = failed > 0;
|
|
1810
|
+
hasAnyFailures && succeeded === 0 ? (devLog.error("All translation tasks failed", void 0, logContext), dispatch({ type: "SET_OVERALL_STATUS", payload: "error" })) : hasAnyFailures ? (devLog.warn("Translation completed with some failures", {
|
|
1811
|
+
...logContext,
|
|
1812
|
+
succeeded,
|
|
1813
|
+
failed
|
|
1814
|
+
}), dispatch({ type: "SET_OVERALL_STATUS", payload: "completed" })) : (devLog.info("Document set translation completed successfully", logContext), dispatch({ type: "SET_OVERALL_STATUS", payload: "completed" })), devLog.timeEnd("translateDocumentSet"), devLog.groupEnd();
|
|
1769
1815
|
} catch (error) {
|
|
1770
|
-
|
|
1816
|
+
throw devLog.error(
|
|
1817
|
+
"translateDocumentSet failed with unexpected error",
|
|
1818
|
+
error instanceof Error ? error : new Error(String(error)),
|
|
1819
|
+
logContext
|
|
1820
|
+
), devLog.timeEnd("translateDocumentSet"), devLog.groupEnd(), dispatch({ type: "SET_OVERALL_STATUS", payload: "error" }), error;
|
|
1771
1821
|
}
|
|
1772
1822
|
}, ActionButtonBar = ({ setIsDialogOpen }) => {
|
|
1773
1823
|
const {
|
|
@@ -2086,7 +2136,7 @@ const setupTranslationJobStatuses = (dispatch, sourceDocuments, targetLanguages)
|
|
|
2086
2136
|
/* @__PURE__ */ jsx(Label, { children: "Translations" }),
|
|
2087
2137
|
/* @__PURE__ */ jsx(Card, { padding: 2, tone: "transparent", children: /* @__PURE__ */ jsx(Stack, { space: 2, children: /* @__PURE__ */ jsx("ul", { style: { listStyleType: "none", paddingInlineStart: "0" }, children: jobs.map((job) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(Box, { padding: 3, children: /* @__PURE__ */ jsxs(Flex, { justify: "space-between", align: "center", children: [
|
|
2088
2138
|
/* @__PURE__ */ jsxs(Inline, { space: 2, children: [
|
|
2089
|
-
/* @__PURE__ */ jsx(Flex, { align: "center", justify: "center", style: { width: "24px", height: "24px" }, children: ["completed", "skipped"].includes(job.status) ? /* @__PURE__ */ jsx(CheckmarkIcon, { fontSize: 24 }) : job.status === "queued" ? /* @__PURE__ */ jsx(CircleIcon, { fontSize: 24 }) : /* @__PURE__ */ jsx(Spinner, {}) }),
|
|
2139
|
+
/* @__PURE__ */ jsx(Flex, { align: "center", justify: "center", style: { width: "24px", height: "24px" }, children: ["completed", "skipped"].includes(job.status) ? /* @__PURE__ */ jsx(CheckmarkIcon, { fontSize: 24 }) : job.status === "error" ? /* @__PURE__ */ jsx(Box, { style: { color: "var(--card-critical-fg-color)" }, children: /* @__PURE__ */ jsx(ErrorOutlineIcon, { fontSize: 24 }) }) : job.status === "queued" ? /* @__PURE__ */ jsx(CircleIcon, { fontSize: 24 }) : /* @__PURE__ */ jsx(Spinner, {}) }),
|
|
2090
2140
|
/* @__PURE__ */ jsxs(Stack, { space: 3, children: [
|
|
2091
2141
|
/* @__PURE__ */ jsxs(Inline, { space: 2, children: [
|
|
2092
2142
|
/* @__PURE__ */ jsx(Text, { size: 1, weight: "semibold", children: job.title || "Untitled Document" }),
|
|
@@ -2097,11 +2147,16 @@ const setupTranslationJobStatuses = (dispatch, sourceDocuments, targetLanguages)
|
|
|
2097
2147
|
job._type,
|
|
2098
2148
|
" \u2014 ",
|
|
2099
2149
|
job._id.slice(-6)
|
|
2100
|
-
] })
|
|
2150
|
+
] }),
|
|
2151
|
+
job.status === "error" && job.errorMessage ? /* @__PURE__ */ jsx(Card, { tone: "critical", padding: 2, radius: 2, children: /* @__PURE__ */ jsxs(Text, { size: 1, children: [
|
|
2152
|
+
"Error: ",
|
|
2153
|
+
job.errorMessage
|
|
2154
|
+
] }) }) : null
|
|
2101
2155
|
] })
|
|
2102
2156
|
] }),
|
|
2103
2157
|
/* @__PURE__ */ jsxs(Flex, { gap: 2, align: "center", children: [
|
|
2104
2158
|
job.status === "skipped" ? /* @__PURE__ */ jsx(SkippedBadge, {}) : null,
|
|
2159
|
+
job.status === "error" ? /* @__PURE__ */ jsx(ErrorBadge, {}) : null,
|
|
2105
2160
|
job.targetId ? /* @__PURE__ */ jsx(
|
|
2106
2161
|
TranslationOpenerButton,
|
|
2107
2162
|
{
|
|
@@ -2165,6 +2220,13 @@ const setupTranslationJobStatuses = (dispatch, sourceDocuments, targetLanguages)
|
|
|
2165
2220
|
content: /* @__PURE__ */ jsx(Box, { padding: 1, children: /* @__PURE__ */ jsx(Text, { size: 1, muted: !0, children: "There was already a fresh translation for this document and language." }) }),
|
|
2166
2221
|
children: /* @__PURE__ */ jsx(Badge, { tone: "neutral", children: "Skipped" })
|
|
2167
2222
|
}
|
|
2223
|
+
), ErrorBadge = () => /* @__PURE__ */ jsx(
|
|
2224
|
+
Tooltip,
|
|
2225
|
+
{
|
|
2226
|
+
portal: !0,
|
|
2227
|
+
content: /* @__PURE__ */ jsx(Box, { padding: 1, children: /* @__PURE__ */ jsx(Text, { size: 1, muted: !0, children: "This translation failed. Check the error message for details." }) }),
|
|
2228
|
+
children: /* @__PURE__ */ jsx(Badge, { tone: "critical", children: "Failed" })
|
|
2229
|
+
}
|
|
2168
2230
|
), STATES_IN_WHICH_TO_SHOW_CONFIG = ["fetching_metadata", "idle"], STATES_IN_WHICH_TO_SHOW_TRANSLATION_PROGRESS = [
|
|
2169
2231
|
"translating",
|
|
2170
2232
|
"linking_references",
|