gt-sanity 2.0.21 → 2.1.0
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.cjs +1220 -763
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +113 -1
- package/dist/index.d.ts +113 -1
- package/dist/index.js +1211 -754
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/adapter/core.ts +27 -2
- package/src/adapter/types.ts +9 -0
- package/src/components/TranslationsProvider.tsx +92 -6
- package/src/components/page/TranslationsTable.tsx +5 -3
- package/src/components/shared/SingleDocumentView.tsx +5 -3
- package/src/components/tab/TranslationView.tsx +24 -14
- package/src/configuration/baseDocumentLevelConfig/documentLevelPatch.ts +1 -1
- package/src/configuration/baseFieldLevelConfig.ts +1 -1
- package/src/configuration/internationalizedArrayConfig/internationalizedArrayPatch.ts +59 -0
- package/src/index.ts +114 -58
- package/src/schema/InternationalizedArrayInput.tsx +278 -0
- package/src/schema/__tests__/createInternationalizedArrayTypes.test.ts +169 -0
- package/src/schema/createInternationalizedArrayTypes.ts +209 -0
- package/src/schema/types.ts +84 -0
- package/src/serialization/__tests__/BaseDocumentDeserializer/baseDeserialization.test.ts +3 -3
- package/src/serialization/__tests__/BaseDocumentMerger/baseMerge.test.ts +1 -1
- package/src/serialization/__tests__/BaseDocumentMerger/documentLevelMerge.test.ts +1 -1
- package/src/serialization/__tests__/BaseDocumentMerger/fieldLevelMerge.test.ts +1 -1
- package/src/serialization/__tests__/BaseDocumentSerializer/baseSerialization.test.ts +2 -2
- package/src/serialization/__tests__/BaseDocumentSerializer/documentInlineMarks.test.ts +4 -2
- package/src/serialization/__tests__/global.setup.ts +6 -0
- package/src/serialization/__tests__/helpers.ts +2 -1
- package/src/serialization/internationalizedArray/__tests__/internationalizedArray.test.ts +265 -0
- package/src/serialization/internationalizedArray/__tests__/serializeRoundTrip.test.ts +83 -0
- package/src/serialization/internationalizedArray/collapse.ts +52 -0
- package/src/serialization/internationalizedArray/detect.ts +81 -0
- package/src/serialization/internationalizedArray/merge.ts +149 -0
- package/src/serialization/types.ts +5 -1
- package/src/translation/__tests__/strategy.test.ts +47 -0
- package/src/translation/importDocument.ts +9 -5
- package/src/translation/strategy.ts +92 -0
- package/src/translation/uploadFiles.ts +1 -1
- package/src/types.ts +1 -1
- package/src/utils/__tests__/batchProcessor.test.ts +61 -2
- package/src/utils/batchProcessor.ts +11 -6
- package/src/utils/serialize.ts +24 -7
- package/src/serialization/index.ts +0 -16
package/dist/index.js
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { GT, getLocaleProperties } from "generaltranslation";
|
|
2
|
+
import { libraryDefaultLocale } from "generaltranslation/internal";
|
|
3
|
+
import { useClient as useClient$1, useSchema, setIfMissing, unset, ArrayOfObjectsItem, MemberItemError, definePlugin } from "sanity";
|
|
4
|
+
import { Link, route } from "sanity/router";
|
|
3
5
|
import { jsx, jsxs, Fragment } from "react/jsx-runtime";
|
|
6
|
+
import o, { useState, useEffect, createContext, useRef, useCallback, useContext, useDebugValue, createElement, useMemo } from "react";
|
|
7
|
+
import { CheckmarkCircleIcon, DownloadIcon, LinkIcon, PublishIcon, TranslateIcon, AddIcon, RemoveCircleIcon } from "@sanity/icons";
|
|
4
8
|
import { ThemeProvider, ToastProvider, Box, Flex, Spinner, Card, Text, useToast, Label, Grid, Button, Switch, Stack, Tooltip, Dialog, Container, Heading } from "@sanity/ui";
|
|
5
9
|
import { buildTheme } from "@sanity/ui/theme";
|
|
6
|
-
import
|
|
7
|
-
import {
|
|
8
|
-
import
|
|
9
|
-
import { libraryDefaultLocale } from "generaltranslation/internal";
|
|
10
|
+
import { extractWithPath, arrayToJSONMatchPath } from "@sanity/mutator";
|
|
11
|
+
import { JSONPath } from "jsonpath-plus";
|
|
12
|
+
import JSONPointer from "jsonpointer";
|
|
10
13
|
import { htmlToBlocks } from "@portabletext/block-tools";
|
|
11
14
|
import { Schema } from "@sanity/schema";
|
|
12
15
|
import { toHTML } from "@portabletext/to-html";
|
|
13
16
|
import merge from "lodash.merge";
|
|
14
|
-
import {
|
|
15
|
-
import
|
|
16
|
-
import { extractWithPath, arrayToJSONMatchPath } from "@sanity/mutator";
|
|
17
|
-
import { CheckmarkCircleIcon, DownloadIcon, LinkIcon, PublishIcon, TranslateIcon } from "@sanity/icons";
|
|
18
|
-
import { Link, route } from "sanity/router";
|
|
17
|
+
import { documentInternationalization } from "@sanity/document-internationalization";
|
|
18
|
+
import { DocumentInternationalizationMenu, documentInternationalization as documentInternationalization2, useDeleteTranslationAction, useDocumentInternationalizationContext, useDuplicateWithTranslationsAction } from "@sanity/document-internationalization";
|
|
19
19
|
const useClient = () => useClient$1({ apiVersion: "2025-09-15" });
|
|
20
20
|
function useSecrets(id) {
|
|
21
21
|
const [loading, setLoading] = useState(!0), [secrets, setSecrets] = useState(null), client = useClient();
|
|
@@ -53,9 +53,12 @@ class GTConfig {
|
|
|
53
53
|
additionalSerializers;
|
|
54
54
|
additionalDeserializers;
|
|
55
55
|
additionalBlockDeserializers;
|
|
56
|
+
translationLevel;
|
|
57
|
+
fieldLevelDocuments;
|
|
58
|
+
fieldLevelTypePrefix;
|
|
56
59
|
static instance;
|
|
57
|
-
constructor(secretsNamespace, languageField, sourceLocale, locales, singletons, singletonMapping, ignoreFields, dedupeFields, skipFields, translateDocuments, additionalStopTypes = [], additionalSerializers = {}, additionalDeserializers = { types: {} }, additionalBlockDeserializers = []) {
|
|
58
|
-
this.secretsNamespace = secretsNamespace, this.languageField = languageField, this.sourceLocale = sourceLocale, this.locales = locales, this.singletons = singletons, this.singletonMapping = singletonMapping, this.ignoreFields = ignoreFields, this.dedupeFields = dedupeFields, this.skipFields = skipFields, this.translateDocuments = translateDocuments, this.additionalStopTypes = additionalStopTypes, this.additionalSerializers = additionalSerializers, this.additionalDeserializers = additionalDeserializers, this.additionalBlockDeserializers = additionalBlockDeserializers;
|
|
60
|
+
constructor(secretsNamespace, languageField, sourceLocale, locales, singletons, singletonMapping, ignoreFields, dedupeFields, skipFields, translateDocuments, additionalStopTypes = [], additionalSerializers = {}, additionalDeserializers = { types: {} }, additionalBlockDeserializers = [], translationLevel = "document", fieldLevelDocuments = [], fieldLevelTypePrefix = "internationalizedArray") {
|
|
61
|
+
this.secretsNamespace = secretsNamespace, this.languageField = languageField, this.sourceLocale = sourceLocale, this.locales = locales, this.singletons = singletons, this.singletonMapping = singletonMapping, this.ignoreFields = ignoreFields, this.dedupeFields = dedupeFields, this.skipFields = skipFields, this.translateDocuments = translateDocuments, this.additionalStopTypes = additionalStopTypes, this.additionalSerializers = additionalSerializers, this.additionalDeserializers = additionalDeserializers, this.additionalBlockDeserializers = additionalBlockDeserializers, this.translationLevel = translationLevel, this.fieldLevelDocuments = fieldLevelDocuments, this.fieldLevelTypePrefix = fieldLevelTypePrefix;
|
|
59
62
|
}
|
|
60
63
|
static getInstance() {
|
|
61
64
|
return this.instance || (this.instance = new GTConfig(
|
|
@@ -75,8 +78,8 @@ class GTConfig {
|
|
|
75
78
|
[]
|
|
76
79
|
)), this.instance;
|
|
77
80
|
}
|
|
78
|
-
init(secretsNamespace, languageField, sourceLocale, locales, singletons, singletonMapping, ignoreFields, dedupeFields, skipFields, translateDocuments, additionalStopTypes = [], additionalSerializers = {}, additionalDeserializers = { types: {} }, additionalBlockDeserializers = []) {
|
|
79
|
-
this.secretsNamespace = secretsNamespace, this.languageField = languageField, this.sourceLocale = sourceLocale, this.locales = locales, this.singletons = singletons, this.singletonMapping = singletonMapping, this.ignoreFields = ignoreFields, this.dedupeFields = dedupeFields, this.skipFields = skipFields, this.translateDocuments = translateDocuments, this.additionalStopTypes = additionalStopTypes, this.additionalSerializers = additionalSerializers, this.additionalDeserializers = additionalDeserializers, this.additionalBlockDeserializers = additionalBlockDeserializers;
|
|
81
|
+
init(secretsNamespace, languageField, sourceLocale, locales, singletons, singletonMapping, ignoreFields, dedupeFields, skipFields, translateDocuments, additionalStopTypes = [], additionalSerializers = {}, additionalDeserializers = { types: {} }, additionalBlockDeserializers = [], translationLevel = "document", fieldLevelDocuments = [], fieldLevelTypePrefix = "internationalizedArray") {
|
|
82
|
+
this.secretsNamespace = secretsNamespace, this.languageField = languageField, this.sourceLocale = sourceLocale, this.locales = locales, this.singletons = singletons, this.singletonMapping = singletonMapping, this.ignoreFields = ignoreFields, this.dedupeFields = dedupeFields, this.skipFields = skipFields, this.translateDocuments = translateDocuments, this.additionalStopTypes = additionalStopTypes, this.additionalSerializers = additionalSerializers, this.additionalDeserializers = additionalDeserializers, this.additionalBlockDeserializers = additionalBlockDeserializers, this.translationLevel = translationLevel, this.fieldLevelDocuments = fieldLevelDocuments, this.fieldLevelTypePrefix = fieldLevelTypePrefix;
|
|
80
83
|
}
|
|
81
84
|
getSecretsNamespace() {
|
|
82
85
|
return this.secretsNamespace;
|
|
@@ -120,6 +123,15 @@ class GTConfig {
|
|
|
120
123
|
getAdditionalBlockDeserializers() {
|
|
121
124
|
return this.additionalBlockDeserializers;
|
|
122
125
|
}
|
|
126
|
+
getTranslationLevel() {
|
|
127
|
+
return this.translationLevel;
|
|
128
|
+
}
|
|
129
|
+
getFieldLevelDocuments() {
|
|
130
|
+
return this.fieldLevelDocuments;
|
|
131
|
+
}
|
|
132
|
+
getFieldLevelTypePrefix() {
|
|
133
|
+
return this.fieldLevelTypePrefix;
|
|
134
|
+
}
|
|
123
135
|
}
|
|
124
136
|
const pluginConfig = GTConfig.getInstance(), theme = buildTheme(), BaseTranslationWrapper = ({
|
|
125
137
|
children,
|
|
@@ -133,21 +145,21 @@ const pluginConfig = GTConfig.getInstance(), theme = buildTheme(), BaseTranslati
|
|
|
133
145
|
!loadingSecrets && secrets && children
|
|
134
146
|
] });
|
|
135
147
|
return /* @__PURE__ */ jsx(ThemeProvider, { theme, children: /* @__PURE__ */ jsx(ToastProvider, { paddingY: 7, children: showContainer ? /* @__PURE__ */ jsx(Box, { padding, children: content }) : content }) });
|
|
136
|
-
}, isRecord$
|
|
148
|
+
}, isRecord$5 = (value) => typeof value == "object" && value !== null && !Array.isArray(value), reconcileArray = (origArray, translatedArray) => {
|
|
137
149
|
if (translatedArray && translatedArray.some((el) => typeof el == "string"))
|
|
138
150
|
return translatedArray;
|
|
139
151
|
const combined = JSON.parse(JSON.stringify(origArray));
|
|
140
152
|
return translatedArray.forEach((translatedItem) => {
|
|
141
|
-
if (!isRecord$
|
|
153
|
+
if (!isRecord$5(translatedItem) || !translatedItem._key)
|
|
142
154
|
return;
|
|
143
155
|
const foundBlockIdx = origArray.findIndex(
|
|
144
|
-
(origBlock) => isRecord$
|
|
156
|
+
(origBlock) => isRecord$5(origBlock) && origBlock._key === translatedItem._key
|
|
145
157
|
);
|
|
146
158
|
foundBlockIdx < 0 ? console.warn(
|
|
147
159
|
`This block no longer exists on the original document. Was it removed? ${JSON.stringify(
|
|
148
160
|
translatedItem
|
|
149
161
|
)}`
|
|
150
|
-
) : isRecord$
|
|
162
|
+
) : isRecord$5(origArray[foundBlockIdx]) && (origArray[foundBlockIdx]._type === "block" || origArray[foundBlockIdx]._type === "span") ? combined[foundBlockIdx] = translatedItem : isRecord$5(origArray[foundBlockIdx]) && (combined[foundBlockIdx] = reconcileObject(
|
|
151
163
|
origArray[foundBlockIdx],
|
|
152
164
|
translatedItem
|
|
153
165
|
));
|
|
@@ -160,8 +172,8 @@ const pluginConfig = GTConfig.getInstance(), theme = buildTheme(), BaseTranslati
|
|
|
160
172
|
!value || key[0] === "_" || (typeof value == "string" ? updatedObj[key] = value : Array.isArray(value) ? updatedObj[key] = reconcileArray(
|
|
161
173
|
Array.isArray(origObject[key]) ? origObject[key] : [],
|
|
162
174
|
value
|
|
163
|
-
) : isRecord$
|
|
164
|
-
isRecord$
|
|
175
|
+
) : isRecord$5(value) && (updatedObj[key] = reconcileObject(
|
|
176
|
+
isRecord$5(origObject[key]) ? origObject[key] : {},
|
|
165
177
|
value
|
|
166
178
|
)));
|
|
167
179
|
}), updatedObj;
|
|
@@ -182,7 +194,7 @@ const pluginConfig = GTConfig.getInstance(), theme = buildTheme(), BaseTranslati
|
|
|
182
194
|
origVal ?? [],
|
|
183
195
|
translatedVal
|
|
184
196
|
) : typeof translatedVal == "object" && Object.keys(translatedVal).length && (valToPatch = reconcileObject(
|
|
185
|
-
isRecord$
|
|
197
|
+
isRecord$5(origVal) ? origVal : {},
|
|
186
198
|
translatedVal
|
|
187
199
|
));
|
|
188
200
|
const destinationPath = [
|
|
@@ -201,197 +213,683 @@ const pluginConfig = GTConfig.getInstance(), theme = buildTheme(), BaseTranslati
|
|
|
201
213
|
documentLevelMerge,
|
|
202
214
|
reconcileArray,
|
|
203
215
|
reconcileObject
|
|
204
|
-
}, defaultSchema = Schema.compile({
|
|
205
|
-
name: "default",
|
|
206
|
-
types: [
|
|
207
|
-
{
|
|
208
|
-
type: "object",
|
|
209
|
-
name: "default",
|
|
210
|
-
fields: [
|
|
211
|
-
{
|
|
212
|
-
name: "block",
|
|
213
|
-
type: "array",
|
|
214
|
-
of: [{ type: "block" }]
|
|
215
|
-
}
|
|
216
|
-
]
|
|
217
|
-
}
|
|
218
|
-
]
|
|
219
|
-
}), blockContentType = defaultSchema.get("default").fields.find((field) => field.name === "block").type, preprocess = (html) => {
|
|
220
|
-
const intermediateBlocks = htmlToBlocks(
|
|
221
|
-
`<p>${html}</p>`,
|
|
222
|
-
blockContentType
|
|
223
|
-
);
|
|
224
|
-
if (!intermediateBlocks.length)
|
|
225
|
-
throw new Error(`Error parsing string '${html}'`);
|
|
226
|
-
return intermediateBlocks[0].children[0].text;
|
|
227
216
|
};
|
|
228
|
-
function
|
|
229
|
-
|
|
230
|
-
if (!firstElement)
|
|
231
|
-
return html;
|
|
232
|
-
const encodedData = encode(JSON.stringify({ [type]: data }));
|
|
233
|
-
return firstElement.setAttribute("data-gt-internal", encodedData), firstElement.outerHTML;
|
|
217
|
+
function getPublishedId(documentId) {
|
|
218
|
+
return documentId.startsWith("drafts.") ? documentId.slice(7) : documentId;
|
|
234
219
|
}
|
|
235
|
-
function
|
|
236
|
-
|
|
237
|
-
if (!firstElement)
|
|
238
|
-
return { html };
|
|
239
|
-
const encodedData = firstElement.getAttribute("data-gt-internal");
|
|
240
|
-
let extractedData;
|
|
241
|
-
if (encodedData)
|
|
242
|
-
try {
|
|
243
|
-
const decodedData = decode(encodedData);
|
|
244
|
-
extractedData = JSON.parse(decodedData), firstElement.removeAttribute("data-gt-internal");
|
|
245
|
-
} catch (error) {
|
|
246
|
-
console.warn("Failed to decode GT internal data:", error);
|
|
247
|
-
}
|
|
248
|
-
return {
|
|
249
|
-
html: firstElement.outerHTML,
|
|
250
|
-
data: extractedData
|
|
251
|
-
};
|
|
220
|
+
function getDocumentPublishedId(document2) {
|
|
221
|
+
return getPublishedId(document2._id);
|
|
252
222
|
}
|
|
253
|
-
function
|
|
254
|
-
const
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
223
|
+
function dedupeDocumentsPreferDraft(documents) {
|
|
224
|
+
const byPublishedId = /* @__PURE__ */ new Map();
|
|
225
|
+
for (const document2 of documents) {
|
|
226
|
+
const publishedId = getDocumentPublishedId(document2);
|
|
227
|
+
(!byPublishedId.get(publishedId) || document2._id.startsWith("drafts.")) && byPublishedId.set(publishedId, document2);
|
|
228
|
+
}
|
|
229
|
+
return Array.from(byPublishedId.values());
|
|
259
230
|
}
|
|
260
|
-
function
|
|
261
|
-
const
|
|
262
|
-
|
|
263
|
-
bytes[i] = binary.charCodeAt(i);
|
|
264
|
-
return new TextDecoder().decode(bytes);
|
|
231
|
+
function createTranslationStatusKey(branchId, documentId, versionId, localeId) {
|
|
232
|
+
const publishedId = getPublishedId(documentId);
|
|
233
|
+
return branchId ? `${branchId}:${publishedId}:${versionId}:${localeId}` : `${publishedId}:${versionId}:${localeId}`;
|
|
265
234
|
}
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
"
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
list: defaultLists,
|
|
304
|
-
listItem: defaultListItem,
|
|
305
|
-
unknownBlockStyle: unknownBlockFunc
|
|
306
|
-
}, customDeserializers = { types: {} }, customBlockDeserializers = [
|
|
307
|
-
// handle marks with data-gt-internal
|
|
308
|
-
{
|
|
309
|
-
deserialize(node2, next2) {
|
|
310
|
-
if (node2.nodeType !== 1)
|
|
311
|
-
return;
|
|
312
|
-
const el = node2;
|
|
313
|
-
if (!el.hasChildNodes() || !el.getAttribute("data-gt-internal"))
|
|
314
|
-
return;
|
|
315
|
-
const { html, data } = detachGTData(el.outerHTML), block = htmlToBlocks(html, blockContentType)[0], children = next2(el.childNodes);
|
|
316
|
-
let markDefs = [];
|
|
317
|
-
return "markDefs" in block && (markDefs = block.markDefs ?? []), data?.markDef && markDefs.push(data.markDef), Array.isArray(children) && children.forEach((child) => {
|
|
318
|
-
if (!isRecord$3(child))
|
|
319
|
-
return;
|
|
320
|
-
const marks = Array.isArray(child.marks) ? child.marks : [];
|
|
321
|
-
child.marks = data?.markDef?._key ? [...marks, data.markDef._key] : marks;
|
|
322
|
-
}), {
|
|
323
|
-
...block,
|
|
324
|
-
markDefs,
|
|
325
|
-
children
|
|
326
|
-
};
|
|
327
|
-
}
|
|
328
|
-
},
|
|
329
|
-
//handle undeclared styles
|
|
330
|
-
{
|
|
331
|
-
deserialize(node2, next2) {
|
|
332
|
-
if (node2.nodeType !== 1)
|
|
333
|
-
return;
|
|
334
|
-
const el = node2;
|
|
335
|
-
if (!el.hasChildNodes() || el.getAttribute("data-type") !== "unknown-block-style")
|
|
336
|
-
return;
|
|
337
|
-
const style = el.getAttribute("data-style") ?? "";
|
|
338
|
-
return {
|
|
339
|
-
...htmlToBlocks(el.outerHTML, blockContentType)[0],
|
|
340
|
-
style,
|
|
341
|
-
children: next2(el.childNodes)
|
|
342
|
-
};
|
|
343
|
-
}
|
|
344
|
-
},
|
|
345
|
-
//handle list items
|
|
346
|
-
{
|
|
347
|
-
deserialize(node2, next2) {
|
|
348
|
-
if (node2.nodeType !== 1)
|
|
349
|
-
return;
|
|
350
|
-
const el = node2;
|
|
351
|
-
if (!el.hasChildNodes() || el.tagName.toLowerCase() !== "li")
|
|
352
|
-
return;
|
|
353
|
-
const tagsToStyle = {
|
|
354
|
-
ul: "bullet",
|
|
355
|
-
ol: "number"
|
|
356
|
-
}, parent = el.parentNode;
|
|
357
|
-
if (!parent || !parent.tagName)
|
|
358
|
-
return;
|
|
359
|
-
const listItem = tagsToStyle[parent.tagName.toLowerCase()];
|
|
360
|
-
if (!listItem)
|
|
361
|
-
return;
|
|
362
|
-
const level = el.getAttribute("data-level") && parseInt(el.getAttribute("data-level") || "0", 10), _key = el.id;
|
|
363
|
-
let block = htmlToBlocks(parent.outerHTML, blockContentType)[0];
|
|
364
|
-
const customStyle = el.children?.[0]?.getAttribute("data-style");
|
|
365
|
-
if (new RegExp(/<("[^"]*"|'[^']*'|[^'">])*>/).test(el.innerHTML)) {
|
|
366
|
-
const newBlock = htmlToBlocks(el.innerHTML, blockContentType)[0];
|
|
367
|
-
if (newBlock && (block = {
|
|
368
|
-
...block,
|
|
369
|
-
...newBlock,
|
|
370
|
-
// @ts-ignore
|
|
371
|
-
style: customStyle ?? newBlock.style
|
|
372
|
-
}, customStyle))
|
|
373
|
-
return block;
|
|
235
|
+
function createStableTranslationKey(branchId, documentId, localeId) {
|
|
236
|
+
const publishedId = getPublishedId(documentId);
|
|
237
|
+
return branchId ? `${branchId}:${publishedId}:${localeId}` : `${publishedId}:${localeId}`;
|
|
238
|
+
}
|
|
239
|
+
const findLatestDraft = (documentId, client) => {
|
|
240
|
+
const publishedId = getPublishedId(documentId), query = "*[_id == $id || _id == $draftId]", params = { id: publishedId, draftId: `drafts.${publishedId}` };
|
|
241
|
+
return client.fetch(query, params).then(
|
|
242
|
+
(docs) => docs.find((doc) => doc._id.startsWith("drafts.")) ?? docs[0]
|
|
243
|
+
);
|
|
244
|
+
}, findDocumentAtRevision = async (documentId, rev, client) => {
|
|
245
|
+
const baseUrl = `/data/history/${client.config().dataset}/documents/${documentId}?revision=${rev}`, url = client.getUrl(baseUrl);
|
|
246
|
+
return await fetch(url, { credentials: "include" }).then((req) => req.json()).then((req) => req.documents && req.documents.length ? req.documents[0] : null);
|
|
247
|
+
};
|
|
248
|
+
function forEachMatchingField(documentId, document2, fields, callback) {
|
|
249
|
+
const applicable = fields.filter(
|
|
250
|
+
(field) => field.documentId === documentId || field.documentId === void 0 || field.documentId === null
|
|
251
|
+
);
|
|
252
|
+
for (const entry of applicable)
|
|
253
|
+
if (entry.fields)
|
|
254
|
+
for (const field of entry.fields) {
|
|
255
|
+
const { property, type } = field;
|
|
256
|
+
try {
|
|
257
|
+
const results = JSONPath({
|
|
258
|
+
json: document2,
|
|
259
|
+
path: property,
|
|
260
|
+
resultType: "all",
|
|
261
|
+
flatten: !0,
|
|
262
|
+
wrap: !0
|
|
263
|
+
});
|
|
264
|
+
results && results.length > 0 && results.forEach(
|
|
265
|
+
(result) => {
|
|
266
|
+
type !== void 0 ? typeof result.value == "object" && result.value !== null && !Array.isArray(result.value) && result.value._type === type && callback(result) : callback(result);
|
|
267
|
+
}
|
|
268
|
+
);
|
|
269
|
+
} catch (error) {
|
|
270
|
+
console.warn(`Invalid JSONPath: ${property}`, error);
|
|
271
|
+
}
|
|
374
272
|
}
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
273
|
+
}
|
|
274
|
+
function deleteMatchingFields(documentId, document2, fields) {
|
|
275
|
+
const arrayRemovals = [];
|
|
276
|
+
forEachMatchingField(documentId, document2, fields, (result) => {
|
|
277
|
+
Array.isArray(result.parent) ? arrayRemovals.push({
|
|
278
|
+
parent: result.parent,
|
|
279
|
+
index: Number(result.parentProperty)
|
|
280
|
+
}) : delete result.parent[result.parentProperty];
|
|
281
|
+
}), arrayRemovals.sort((a, b) => b.index - a.index);
|
|
282
|
+
for (const { parent, index } of arrayRemovals)
|
|
283
|
+
parent.splice(index, 1);
|
|
284
|
+
}
|
|
285
|
+
function applyDocuments(documentId, sourceDocument, targetDocument, ignore, skip = [], dedupe = [], localeId) {
|
|
286
|
+
const mergedDocument = JSON.parse(JSON.stringify(sourceDocument)), clonedTarget = JSON.parse(JSON.stringify(targetDocument));
|
|
287
|
+
for (const [key, value] of Object.entries(clonedTarget))
|
|
288
|
+
mergedDocument[key] = value;
|
|
289
|
+
return forEachMatchingField(documentId, sourceDocument, ignore, (result) => {
|
|
290
|
+
JSONPointer.set(mergedDocument, result.pointer, result.value);
|
|
291
|
+
}), forEachMatchingField(documentId, sourceDocument, dedupe, (result) => {
|
|
292
|
+
JSONPointer.set(
|
|
293
|
+
mergedDocument,
|
|
294
|
+
result.pointer,
|
|
295
|
+
dedupeFieldValue(result.value, localeId)
|
|
296
|
+
);
|
|
297
|
+
}), deleteMatchingFields(documentId, mergedDocument, skip), mergedDocument;
|
|
298
|
+
}
|
|
299
|
+
function dedupeFieldValue(value, localeId) {
|
|
300
|
+
if (!localeId) return value;
|
|
301
|
+
if (typeof value == "string")
|
|
302
|
+
return appendLocaleSuffix(value, localeId);
|
|
303
|
+
if (value && typeof value == "object" && !Array.isArray(value) && typeof value.current == "string") {
|
|
304
|
+
const slug = value;
|
|
305
|
+
return {
|
|
306
|
+
...slug,
|
|
307
|
+
current: appendLocaleSuffix(slug.current, localeId)
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
return value;
|
|
311
|
+
}
|
|
312
|
+
function appendLocaleSuffix(value, localeId) {
|
|
313
|
+
if (!value) return value;
|
|
314
|
+
const suffix = createLocaleSuffix(localeId);
|
|
315
|
+
return !suffix || value.endsWith(suffix) ? value : `${value}${suffix}`;
|
|
316
|
+
}
|
|
317
|
+
function createLocaleSuffix(localeId) {
|
|
318
|
+
const normalized = localeId.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
319
|
+
return normalized ? `-${normalized}` : "";
|
|
320
|
+
}
|
|
321
|
+
function randomKey() {
|
|
322
|
+
return Math.random().toString(36).slice(2, 10);
|
|
323
|
+
}
|
|
324
|
+
async function createI18nDocAndPatchMetadata(sourceDocument, translatedDoc, localeId, client, translationMetadata, sourceDocumentId, languageField = "language") {
|
|
325
|
+
const publishedSourceDocumentId = getPublishedId(sourceDocumentId);
|
|
326
|
+
translatedDoc[languageField] = localeId;
|
|
327
|
+
const existingLocaleKey = translationMetadata.translations.find(
|
|
328
|
+
(translation) => translation.language === localeId
|
|
329
|
+
), operation = existingLocaleKey ? "replace" : "after", location = existingLocaleKey ? `translations[language == "${localeId}"]` : "translations[-1]", { _updatedAt, _createdAt, ...rest } = translatedDoc, appliedDocument = applyDocuments(
|
|
330
|
+
publishedSourceDocumentId,
|
|
331
|
+
sourceDocument,
|
|
332
|
+
rest,
|
|
333
|
+
pluginConfig.getIgnoreFields(),
|
|
334
|
+
pluginConfig.getSkipFields(),
|
|
335
|
+
pluginConfig.getDedupeFields(),
|
|
336
|
+
localeId
|
|
337
|
+
), isSingleton = pluginConfig.getSingletons().includes(publishedSourceDocumentId);
|
|
338
|
+
let createDocumentPromise;
|
|
339
|
+
if (isSingleton) {
|
|
340
|
+
const translatedDocId = pluginConfig.getSingletonMapping()(
|
|
341
|
+
publishedSourceDocumentId,
|
|
342
|
+
localeId
|
|
343
|
+
);
|
|
344
|
+
createDocumentPromise = client.create({
|
|
345
|
+
...appliedDocument,
|
|
346
|
+
_type: rest._type,
|
|
347
|
+
_id: `drafts.${translatedDocId}`
|
|
348
|
+
});
|
|
349
|
+
} else
|
|
350
|
+
createDocumentPromise = client.create({
|
|
351
|
+
...appliedDocument,
|
|
352
|
+
_type: rest._type,
|
|
353
|
+
_id: "drafts."
|
|
354
|
+
});
|
|
355
|
+
const doc = await createDocumentPromise, _ref = getPublishedId(doc._id);
|
|
356
|
+
await client.transaction().patch(
|
|
357
|
+
translationMetadata._id,
|
|
358
|
+
(p) => p.insert(operation, location, [
|
|
359
|
+
{
|
|
360
|
+
_key: randomKey(),
|
|
361
|
+
language: localeId,
|
|
362
|
+
_type: "internationalizedArrayReferenceValue",
|
|
363
|
+
value: {
|
|
364
|
+
_type: "reference",
|
|
365
|
+
_ref,
|
|
366
|
+
_weak: !0,
|
|
367
|
+
_strengthenOnPublish: {
|
|
368
|
+
type: doc._type
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
])
|
|
373
|
+
).commit();
|
|
374
|
+
}
|
|
375
|
+
const getOrCreateTranslationMetadata = async (documentId, baseDocument, client, baseLanguage) => {
|
|
376
|
+
const publishedId = getPublishedId(documentId), existingMetadata = await client.fetch(
|
|
377
|
+
`*[
|
|
378
|
+
_type == 'translation.metadata' &&
|
|
379
|
+
translations[language == $baseLanguage][0].value._ref == $id
|
|
380
|
+
][0]`,
|
|
381
|
+
{ baseLanguage, id: publishedId }
|
|
382
|
+
);
|
|
383
|
+
if (existingMetadata)
|
|
384
|
+
return existingMetadata;
|
|
385
|
+
const baseLangEntry = {
|
|
386
|
+
_key: randomKey(),
|
|
387
|
+
language: baseLanguage,
|
|
388
|
+
_type: "internationalizedArrayReferenceValue",
|
|
389
|
+
value: {
|
|
390
|
+
_type: "reference",
|
|
391
|
+
_ref: getPublishedId(baseDocument._id)
|
|
392
|
+
}
|
|
393
|
+
};
|
|
394
|
+
baseDocument._id.startsWith("drafts.") && (baseLangEntry.value = {
|
|
395
|
+
...baseLangEntry.value,
|
|
396
|
+
_weak: !0,
|
|
397
|
+
//this should reflect doc i18n config when this
|
|
398
|
+
//plugin is able to take that as a config option
|
|
399
|
+
_strengthenOnPublish: {
|
|
400
|
+
type: baseDocument._type
|
|
401
|
+
}
|
|
402
|
+
});
|
|
403
|
+
try {
|
|
404
|
+
return await client.createIfNotExists({
|
|
405
|
+
_id: `translation.metadata.${publishedId}`,
|
|
406
|
+
_type: "translation.metadata",
|
|
407
|
+
translations: [baseLangEntry]
|
|
408
|
+
});
|
|
409
|
+
} catch (error) {
|
|
410
|
+
const metadata = await client.fetch(
|
|
411
|
+
`*[
|
|
412
|
+
_type == 'translation.metadata' &&
|
|
413
|
+
translations[language == $baseLanguage][0].value._ref == $id
|
|
414
|
+
][0]`,
|
|
415
|
+
{ baseLanguage, id: publishedId }
|
|
416
|
+
);
|
|
417
|
+
if (metadata)
|
|
418
|
+
return metadata;
|
|
419
|
+
throw error;
|
|
420
|
+
}
|
|
421
|
+
}, SYSTEM_FIELDS = ["_id", "_rev", "_updatedAt", "language"], isSystemField = (field) => SYSTEM_FIELDS.includes(field);
|
|
422
|
+
async function patchI18nDoc(sourceDocumentId, i18nDocId, sourceDocument, mergedDocument, translatedFields, client, existingDocument) {
|
|
423
|
+
const cleanedMerge = {};
|
|
424
|
+
Object.entries(mergedDocument).forEach(([key, value]) => {
|
|
425
|
+
key in translatedFields && //don't overwrite any existing system values on the i18n doc
|
|
426
|
+
!isSystemField(key) && (cleanedMerge[key] = value);
|
|
427
|
+
});
|
|
428
|
+
const cleanedSourceDocument = {};
|
|
429
|
+
Object.entries(sourceDocument).forEach(([key, value]) => {
|
|
430
|
+
isSystemField(key) || (cleanedSourceDocument[key] = value);
|
|
431
|
+
});
|
|
432
|
+
const appliedDocument = applyDocuments(
|
|
433
|
+
sourceDocumentId,
|
|
434
|
+
cleanedSourceDocument,
|
|
435
|
+
cleanedMerge,
|
|
436
|
+
pluginConfig.getIgnoreFields(),
|
|
437
|
+
pluginConfig.getSkipFields()
|
|
438
|
+
), dedupeFields = pluginConfig.getDedupeFields();
|
|
439
|
+
deleteMatchingFields(sourceDocumentId, appliedDocument, dedupeFields), existingDocument && forEachMatchingField(
|
|
440
|
+
sourceDocumentId,
|
|
441
|
+
existingDocument,
|
|
442
|
+
dedupeFields,
|
|
443
|
+
(result) => {
|
|
444
|
+
JSONPointer.set(appliedDocument, result.pointer, result.value);
|
|
445
|
+
}
|
|
446
|
+
), await client.patch(i18nDocId, { set: appliedDocument }).commit();
|
|
447
|
+
}
|
|
448
|
+
const documentLevelPatch = async (docInfo, translatedFields, localeId, client, languageField = "language", mergeWithTargetLocale = !1) => {
|
|
449
|
+
const baseLanguage = pluginConfig.getSourceLocale();
|
|
450
|
+
let baseDoc = null, i18nDoc = null;
|
|
451
|
+
docInfo.documentId && docInfo.versionId && (baseDoc = await findDocumentAtRevision(
|
|
452
|
+
docInfo.documentId,
|
|
453
|
+
docInfo.versionId,
|
|
454
|
+
client
|
|
455
|
+
)), baseDoc || (baseDoc = await findLatestDraft(docInfo.documentId, client));
|
|
456
|
+
const i18nDocId = (await getOrCreateTranslationMetadata(
|
|
457
|
+
docInfo.documentId,
|
|
458
|
+
baseDoc,
|
|
459
|
+
client,
|
|
460
|
+
baseLanguage
|
|
461
|
+
)).translations.find(
|
|
462
|
+
(translation) => translation.language === localeId
|
|
463
|
+
)?.value?._ref;
|
|
464
|
+
i18nDocId && (i18nDoc = await findLatestDraft(i18nDocId, client)), mergeWithTargetLocale && i18nDoc ? baseDoc = i18nDoc : docInfo.documentId && docInfo.versionId && (baseDoc = await findDocumentAtRevision(
|
|
465
|
+
docInfo.documentId,
|
|
466
|
+
docInfo.versionId,
|
|
467
|
+
client
|
|
468
|
+
)), baseDoc || (baseDoc = await findLatestDraft(docInfo.documentId, client));
|
|
469
|
+
const merged = BaseDocumentMerger.documentLevelMerge(
|
|
470
|
+
translatedFields,
|
|
471
|
+
baseDoc
|
|
472
|
+
);
|
|
473
|
+
if (i18nDoc)
|
|
474
|
+
await patchI18nDoc(
|
|
475
|
+
docInfo.documentId,
|
|
476
|
+
i18nDoc._id,
|
|
477
|
+
baseDoc,
|
|
478
|
+
merged,
|
|
479
|
+
translatedFields,
|
|
480
|
+
client,
|
|
481
|
+
i18nDoc
|
|
482
|
+
);
|
|
483
|
+
else {
|
|
484
|
+
const freshTranslationMetadata = await getOrCreateTranslationMetadata(
|
|
485
|
+
docInfo.documentId,
|
|
486
|
+
baseDoc,
|
|
487
|
+
client,
|
|
488
|
+
baseLanguage
|
|
489
|
+
), freshI18nDocId = freshTranslationMetadata.translations.find(
|
|
490
|
+
(translation) => translation.language === localeId
|
|
491
|
+
)?.value?._ref;
|
|
492
|
+
if (freshI18nDocId) {
|
|
493
|
+
const freshI18nDoc = await findLatestDraft(freshI18nDocId, client);
|
|
494
|
+
await patchI18nDoc(
|
|
495
|
+
docInfo.documentId,
|
|
496
|
+
freshI18nDoc._id,
|
|
497
|
+
baseDoc,
|
|
498
|
+
merged,
|
|
499
|
+
translatedFields,
|
|
500
|
+
client,
|
|
501
|
+
freshI18nDoc
|
|
502
|
+
);
|
|
503
|
+
return;
|
|
504
|
+
}
|
|
505
|
+
await createI18nDocAndPatchMetadata(
|
|
506
|
+
baseDoc,
|
|
507
|
+
merged,
|
|
508
|
+
localeId,
|
|
509
|
+
client,
|
|
510
|
+
freshTranslationMetadata,
|
|
511
|
+
docInfo.documentId,
|
|
512
|
+
languageField
|
|
513
|
+
);
|
|
514
|
+
}
|
|
515
|
+
}, DEFAULT_TYPE_PREFIX$1 = "internationalizedArray";
|
|
516
|
+
function hasRecognizedTypePrefix(type) {
|
|
517
|
+
return type.startsWith(DEFAULT_TYPE_PREFIX$1) || type.startsWith(pluginConfig.getFieldLevelTypePrefix());
|
|
518
|
+
}
|
|
519
|
+
const isRecord$4 = (value) => typeof value == "object" && value !== null && !Array.isArray(value);
|
|
520
|
+
function isInternationalizedArrayItem(item) {
|
|
521
|
+
return isRecord$4(item) && typeof item._type == "string" && hasRecognizedTypePrefix(item._type) && item._type.endsWith("Value") && typeof item.language == "string" && "value" in item;
|
|
522
|
+
}
|
|
523
|
+
function isInternationalizedArrayField(value) {
|
|
524
|
+
return Array.isArray(value) && value.length > 0 && value.every(isInternationalizedArrayItem);
|
|
525
|
+
}
|
|
526
|
+
function findLocaleItem(field, locale) {
|
|
527
|
+
return field.find((item) => item.language === locale);
|
|
528
|
+
}
|
|
529
|
+
function upsertLocaleItem(baseArray, translatedValue, targetLocale, sourceLocale) {
|
|
530
|
+
const itemType = (findLocaleItem(baseArray, sourceLocale) ?? baseArray[0])._type, updated = baseArray.map(
|
|
531
|
+
(item) => item.language === targetLocale ? { ...item, value: translatedValue } : { ...item }
|
|
532
|
+
);
|
|
533
|
+
return findLocaleItem(updated, targetLocale) || updated.push({
|
|
534
|
+
_key: randomKey(),
|
|
535
|
+
_type: itemType,
|
|
536
|
+
language: targetLocale,
|
|
537
|
+
value: translatedValue
|
|
538
|
+
}), updated;
|
|
539
|
+
}
|
|
540
|
+
function mergeValue(baseValue, translatedValue, targetLocale, sourceLocale) {
|
|
541
|
+
if (isInternationalizedArrayField(baseValue))
|
|
542
|
+
return upsertLocaleItem(
|
|
543
|
+
baseValue,
|
|
544
|
+
translatedValue,
|
|
545
|
+
targetLocale,
|
|
546
|
+
sourceLocale
|
|
547
|
+
);
|
|
548
|
+
if (Array.isArray(baseValue) && Array.isArray(translatedValue)) {
|
|
549
|
+
let changed = !1;
|
|
550
|
+
const merged = baseValue.map((item) => {
|
|
551
|
+
if (!isRecord$4(item) || typeof item._key != "string")
|
|
552
|
+
return item;
|
|
553
|
+
const translatedItem = translatedValue.find(
|
|
554
|
+
(candidate) => isRecord$4(candidate) && candidate._key === item._key
|
|
555
|
+
);
|
|
556
|
+
if (!translatedItem)
|
|
557
|
+
return item;
|
|
558
|
+
const mergedItem = mergeValue(
|
|
559
|
+
item,
|
|
560
|
+
translatedItem,
|
|
561
|
+
targetLocale,
|
|
562
|
+
sourceLocale
|
|
563
|
+
);
|
|
564
|
+
return mergedItem !== void 0 ? (changed = !0, mergedItem) : item;
|
|
565
|
+
});
|
|
566
|
+
return changed ? merged : void 0;
|
|
567
|
+
}
|
|
568
|
+
if (isRecord$4(baseValue) && isRecord$4(translatedValue)) {
|
|
569
|
+
let changed = !1;
|
|
570
|
+
const merged = { ...baseValue };
|
|
571
|
+
for (const key of Object.keys(translatedValue)) {
|
|
572
|
+
if (key.startsWith("_") || !(key in baseValue))
|
|
573
|
+
continue;
|
|
574
|
+
const mergedChild = mergeValue(
|
|
575
|
+
baseValue[key],
|
|
576
|
+
translatedValue[key],
|
|
577
|
+
targetLocale,
|
|
578
|
+
sourceLocale
|
|
579
|
+
);
|
|
580
|
+
mergedChild !== void 0 && (merged[key] = mergedChild, changed = !0);
|
|
581
|
+
}
|
|
582
|
+
return changed ? merged : void 0;
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
function mergeInternationalizedArrays(baseDoc, translatedFields, targetLocale, sourceLocale) {
|
|
586
|
+
const changes = {};
|
|
587
|
+
for (const key of Object.keys(translatedFields)) {
|
|
588
|
+
if (key.startsWith("_") || !(key in baseDoc))
|
|
589
|
+
continue;
|
|
590
|
+
const merged = mergeValue(
|
|
591
|
+
baseDoc[key],
|
|
592
|
+
translatedFields[key],
|
|
593
|
+
targetLocale,
|
|
594
|
+
sourceLocale
|
|
595
|
+
);
|
|
596
|
+
merged !== void 0 && (changes[key] = merged);
|
|
597
|
+
}
|
|
598
|
+
return changes;
|
|
599
|
+
}
|
|
600
|
+
const internationalizedArrayPatch = async (docInfo, translatedFields, localeId, client) => {
|
|
601
|
+
const sourceLocale = pluginConfig.getSourceLocale(), baseDoc = await findLatestDraft(docInfo.documentId, client);
|
|
602
|
+
if (!baseDoc)
|
|
603
|
+
return;
|
|
604
|
+
const changes = mergeInternationalizedArrays(
|
|
605
|
+
baseDoc,
|
|
606
|
+
translatedFields,
|
|
607
|
+
localeId,
|
|
608
|
+
sourceLocale
|
|
609
|
+
);
|
|
610
|
+
if (Object.keys(changes).length === 0)
|
|
611
|
+
return;
|
|
612
|
+
if (baseDoc._id.startsWith("drafts.")) {
|
|
613
|
+
await client.patch(baseDoc._id).set(changes).commit();
|
|
614
|
+
return;
|
|
615
|
+
}
|
|
616
|
+
const draftId = `drafts.${getPublishedId(baseDoc._id)}`;
|
|
617
|
+
await client.transaction().createIfNotExists({ ...baseDoc, _id: draftId }).patch(draftId, (patch) => patch.set(changes)).commit();
|
|
618
|
+
}, defaultSchema = Schema.compile({
|
|
619
|
+
name: "default",
|
|
620
|
+
types: [
|
|
621
|
+
{
|
|
622
|
+
type: "object",
|
|
623
|
+
name: "default",
|
|
624
|
+
fields: [
|
|
625
|
+
{
|
|
626
|
+
name: "block",
|
|
627
|
+
type: "array",
|
|
628
|
+
of: [{ type: "block" }]
|
|
629
|
+
}
|
|
630
|
+
]
|
|
631
|
+
}
|
|
632
|
+
]
|
|
633
|
+
}), blockContentType = defaultSchema.get("default").fields.find((field) => field.name === "block").type, preprocess = (html) => {
|
|
634
|
+
const intermediateBlocks = htmlToBlocks(
|
|
635
|
+
`<p>${html}</p>`,
|
|
636
|
+
blockContentType
|
|
637
|
+
);
|
|
638
|
+
if (!intermediateBlocks.length)
|
|
639
|
+
throw new Error(`Error parsing string '${html}'`);
|
|
640
|
+
return intermediateBlocks[0].children[0].text;
|
|
641
|
+
};
|
|
642
|
+
function attachGTData(html, data, type) {
|
|
643
|
+
const firstElement = new DOMParser().parseFromString(html, "text/html").body.firstElementChild;
|
|
644
|
+
if (!firstElement)
|
|
645
|
+
return html;
|
|
646
|
+
const encodedData = encode(JSON.stringify({ [type]: data }));
|
|
647
|
+
return firstElement.setAttribute("data-gt-internal", encodedData), firstElement.outerHTML;
|
|
648
|
+
}
|
|
649
|
+
function detachGTData(html) {
|
|
650
|
+
const firstElement = new DOMParser().parseFromString(html, "text/html").body.firstElementChild;
|
|
651
|
+
if (!firstElement)
|
|
652
|
+
return { html };
|
|
653
|
+
const encodedData = firstElement.getAttribute("data-gt-internal");
|
|
654
|
+
let extractedData;
|
|
655
|
+
if (encodedData)
|
|
656
|
+
try {
|
|
657
|
+
const decodedData = decode(encodedData);
|
|
658
|
+
extractedData = JSON.parse(decodedData), firstElement.removeAttribute("data-gt-internal");
|
|
659
|
+
} catch (error) {
|
|
660
|
+
console.warn("Failed to decode GT internal data:", error);
|
|
661
|
+
}
|
|
662
|
+
return {
|
|
663
|
+
html: firstElement.outerHTML,
|
|
664
|
+
data: extractedData
|
|
665
|
+
};
|
|
666
|
+
}
|
|
667
|
+
function encode(data) {
|
|
668
|
+
const bytes = new TextEncoder().encode(data);
|
|
669
|
+
let binary = "";
|
|
670
|
+
for (let i = 0; i < bytes.length; i++)
|
|
671
|
+
binary += String.fromCharCode(bytes[i]);
|
|
672
|
+
return btoa(binary);
|
|
673
|
+
}
|
|
674
|
+
function decode(base64) {
|
|
675
|
+
const binary = atob(base64), bytes = new Uint8Array(binary.length);
|
|
676
|
+
for (let i = 0; i < binary.length; i++)
|
|
677
|
+
bytes[i] = binary.charCodeAt(i);
|
|
678
|
+
return new TextDecoder().decode(bytes);
|
|
679
|
+
}
|
|
680
|
+
const isRecord$3 = (value) => typeof value == "object" && value !== null && !Array.isArray(value), defaultStopTypes = [
|
|
681
|
+
"reference",
|
|
682
|
+
"date",
|
|
683
|
+
"datetime",
|
|
684
|
+
"file",
|
|
685
|
+
"geopoint",
|
|
686
|
+
"image",
|
|
687
|
+
"number",
|
|
688
|
+
"crop",
|
|
689
|
+
"hotspot",
|
|
690
|
+
"boolean",
|
|
691
|
+
"url",
|
|
692
|
+
"color",
|
|
693
|
+
"code"
|
|
694
|
+
], defaultMarks = {}, defaultPortableTextBlockStyles = {
|
|
695
|
+
normal: ({ value, children }) => `<p id="${value._key}">${children}</p>`,
|
|
696
|
+
blockquote: ({ value, children }) => `<blockquote id="${value._key}">${children}</blockquote>`,
|
|
697
|
+
h1: ({ value, children }) => `<h1 id="${value._key}">${children}</h1>`,
|
|
698
|
+
h2: ({ value, children }) => `<h2 id="${value._key}">${children}</h2>`,
|
|
699
|
+
h3: ({ value, children }) => `<h3 id="${value._key}">${children}</h3>`,
|
|
700
|
+
h4: ({ value, children }) => `<h4 id="${value._key}">${children}</h4>`,
|
|
701
|
+
h5: ({ value, children }) => `<h5 id="${value._key}">${children}</h5>`,
|
|
702
|
+
h6: ({ value, children }) => `<h6 id="${value._key}">${children}</h6>`
|
|
703
|
+
}, defaultLists = {
|
|
704
|
+
number: ({ value, children }) => `<ol id="${value._key.replace("-parent", "")}">${children}</ol>`,
|
|
705
|
+
bullet: ({ value, children }) => `<ul id="${value._key.replace("-parent", "")}">${children}</ul>`
|
|
706
|
+
}, defaultListItem = ({
|
|
707
|
+
value,
|
|
708
|
+
children
|
|
709
|
+
}) => {
|
|
710
|
+
const { _key, level } = value;
|
|
711
|
+
return `<li id="${(_key || "").replace("-parent", "")}" data-level="${level}">${children}</li>`;
|
|
712
|
+
}, unknownBlockFunc = ({ value, children }) => `<p id="${value._key}" data-type="unknown-block-style" data-style="${value.style}">${children}</p>`, customSerializers = {
|
|
713
|
+
unknownType: ({ value }) => `<div class="${value._type}"></div>`,
|
|
714
|
+
types: {},
|
|
715
|
+
marks: defaultMarks,
|
|
716
|
+
block: defaultPortableTextBlockStyles,
|
|
717
|
+
list: defaultLists,
|
|
718
|
+
listItem: defaultListItem,
|
|
719
|
+
unknownBlockStyle: unknownBlockFunc
|
|
720
|
+
}, customDeserializers = { types: {} }, customBlockDeserializers = [
|
|
721
|
+
// handle marks with data-gt-internal
|
|
722
|
+
{
|
|
723
|
+
deserialize(node2, next2) {
|
|
724
|
+
if (node2.nodeType !== 1)
|
|
725
|
+
return;
|
|
726
|
+
const el = node2;
|
|
727
|
+
if (!el.hasChildNodes() || !el.getAttribute("data-gt-internal"))
|
|
728
|
+
return;
|
|
729
|
+
const { html, data } = detachGTData(el.outerHTML), block = htmlToBlocks(html, blockContentType)[0], children = next2(el.childNodes);
|
|
730
|
+
let markDefs = [];
|
|
731
|
+
return "markDefs" in block && (markDefs = block.markDefs ?? []), data?.markDef && markDefs.push(data.markDef), Array.isArray(children) && children.forEach((child) => {
|
|
732
|
+
if (!isRecord$3(child))
|
|
733
|
+
return;
|
|
734
|
+
const marks = Array.isArray(child.marks) ? child.marks : [];
|
|
735
|
+
child.marks = data?.markDef?._key ? [...marks, data.markDef._key] : marks;
|
|
736
|
+
}), {
|
|
737
|
+
...block,
|
|
738
|
+
markDefs,
|
|
739
|
+
children
|
|
740
|
+
};
|
|
741
|
+
}
|
|
742
|
+
},
|
|
743
|
+
//handle undeclared styles
|
|
744
|
+
{
|
|
745
|
+
deserialize(node2, next2) {
|
|
746
|
+
if (node2.nodeType !== 1)
|
|
747
|
+
return;
|
|
748
|
+
const el = node2;
|
|
749
|
+
if (!el.hasChildNodes() || el.getAttribute("data-type") !== "unknown-block-style")
|
|
750
|
+
return;
|
|
751
|
+
const style = el.getAttribute("data-style") ?? "";
|
|
752
|
+
return {
|
|
753
|
+
...htmlToBlocks(el.outerHTML, blockContentType)[0],
|
|
754
|
+
style,
|
|
755
|
+
children: next2(el.childNodes)
|
|
756
|
+
};
|
|
757
|
+
}
|
|
758
|
+
},
|
|
759
|
+
//handle list items
|
|
760
|
+
{
|
|
761
|
+
deserialize(node2, next2) {
|
|
762
|
+
if (node2.nodeType !== 1)
|
|
763
|
+
return;
|
|
764
|
+
const el = node2;
|
|
765
|
+
if (!el.hasChildNodes() || el.tagName.toLowerCase() !== "li")
|
|
766
|
+
return;
|
|
767
|
+
const tagsToStyle = {
|
|
768
|
+
ul: "bullet",
|
|
769
|
+
ol: "number"
|
|
770
|
+
}, parent = el.parentNode;
|
|
771
|
+
if (!parent || !parent.tagName)
|
|
772
|
+
return;
|
|
773
|
+
const listItem = tagsToStyle[parent.tagName.toLowerCase()];
|
|
774
|
+
if (!listItem)
|
|
775
|
+
return;
|
|
776
|
+
const level = el.getAttribute("data-level") && parseInt(el.getAttribute("data-level") || "0", 10), _key = el.id;
|
|
777
|
+
let block = htmlToBlocks(parent.outerHTML, blockContentType)[0];
|
|
778
|
+
const customStyle = el.children?.[0]?.getAttribute("data-style");
|
|
779
|
+
if (new RegExp(/<("[^"]*"|'[^']*'|[^'">])*>/).test(el.innerHTML)) {
|
|
780
|
+
const newBlock = htmlToBlocks(el.innerHTML, blockContentType)[0];
|
|
781
|
+
if (newBlock && (block = {
|
|
782
|
+
...block,
|
|
783
|
+
...newBlock,
|
|
784
|
+
// @ts-ignore
|
|
785
|
+
style: customStyle ?? newBlock.style
|
|
786
|
+
}, customStyle))
|
|
787
|
+
return block;
|
|
788
|
+
}
|
|
789
|
+
return {
|
|
790
|
+
...block,
|
|
791
|
+
level,
|
|
792
|
+
_key,
|
|
793
|
+
listItem,
|
|
794
|
+
children: next2(el.childNodes)
|
|
795
|
+
};
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
];
|
|
799
|
+
function mergeBlocks(blocks) {
|
|
800
|
+
const mergedBlock = { ...blocks[0] };
|
|
801
|
+
mergedBlock.markDefs = mergedBlock.markDefs ?? [];
|
|
802
|
+
for (const [idx, block] of blocks.entries())
|
|
803
|
+
idx !== 0 && (mergedBlock.children.push(...block.children), mergedBlock.markDefs.push(...block.markDefs ?? []));
|
|
804
|
+
return mergedBlock._type = "block", mergedBlock;
|
|
805
|
+
}
|
|
806
|
+
const deserializeArray = (arrayHTML, deserializers = customDeserializers, blockDeserializers = customBlockDeserializers) => {
|
|
807
|
+
const output = [];
|
|
808
|
+
return Array.from(arrayHTML.children).forEach((child) => {
|
|
809
|
+
let deserializedObject;
|
|
810
|
+
try {
|
|
811
|
+
if (child.tagName?.toLowerCase() === "span")
|
|
812
|
+
deserializedObject = preprocess(child.innerHTML);
|
|
813
|
+
else if (child.className || child.getAttribute("data-type") === "object")
|
|
814
|
+
deserializedObject = deserializeObject(
|
|
815
|
+
child,
|
|
816
|
+
deserializers,
|
|
817
|
+
blockDeserializers
|
|
818
|
+
), deserializedObject && typeof deserializedObject == "object" && !Array.isArray(deserializedObject) && (deserializedObject._key = child.id);
|
|
819
|
+
else {
|
|
820
|
+
const blocks = htmlToBlocks(child.outerHTML, blockContentType, {
|
|
821
|
+
rules: blockDeserializers
|
|
822
|
+
});
|
|
823
|
+
deserializedObject = mergeBlocks(
|
|
824
|
+
blocks
|
|
825
|
+
), deserializedObject._key = child.id;
|
|
826
|
+
}
|
|
827
|
+
} catch (e) {
|
|
828
|
+
console.debug(
|
|
829
|
+
`Tried to deserialize block: ${child.outerHTML} in an array but failed to identify it! Error: ${e}`
|
|
830
|
+
);
|
|
831
|
+
}
|
|
832
|
+
output.push(deserializedObject);
|
|
833
|
+
}), output;
|
|
834
|
+
}, deserializeObject = (objectHTML, deserializers = customDeserializers, blockDeserializers = customBlockDeserializers) => {
|
|
835
|
+
const deserialize = deserializers.types?.[objectHTML.className];
|
|
836
|
+
if (deserialize)
|
|
837
|
+
return deserialize(objectHTML);
|
|
838
|
+
const output = {};
|
|
839
|
+
return objectHTML.className && (output._type = objectHTML.className), Array.from(objectHTML.children).forEach((child) => {
|
|
840
|
+
if (child.tagName?.toLowerCase() === "span")
|
|
841
|
+
output[child.className] = preprocess(child.innerHTML);
|
|
842
|
+
else if (child.getAttribute("data-level") === "field") {
|
|
843
|
+
const deserialized = deserializeHTML(
|
|
844
|
+
child.outerHTML,
|
|
845
|
+
deserializers,
|
|
846
|
+
blockDeserializers
|
|
847
|
+
);
|
|
848
|
+
deserialized && Object.keys(deserialized).length ? output[child.className] = deserialized : console.debug(
|
|
849
|
+
`Deserializer: Skipping empty or unreadable HTML: ${child.outerHTML}`
|
|
850
|
+
);
|
|
851
|
+
} else child.getAttribute("data-type") === "array" && (output[child.className] = deserializeArray(
|
|
852
|
+
child,
|
|
853
|
+
deserializers,
|
|
854
|
+
blockDeserializers
|
|
855
|
+
));
|
|
856
|
+
}), output;
|
|
857
|
+
}, deserializeHTML = (html, deserializers, blockDeserializers) => {
|
|
858
|
+
let HTMLnode = new DOMParser().parseFromString(html, "text/html").body.children[0];
|
|
859
|
+
if (HTMLnode?.getAttribute("data-level") === "field" && (HTMLnode = HTMLnode.children[0]), !HTMLnode)
|
|
860
|
+
return {};
|
|
861
|
+
let output;
|
|
862
|
+
const deserialize = deserializers.types?.[HTMLnode.className];
|
|
863
|
+
return deserialize ? output = deserialize(HTMLnode) : HTMLnode.getAttribute("data-type") === "object" ? output = deserializeObject(HTMLnode, deserializers, blockDeserializers) : HTMLnode.getAttribute("data-type") === "array" ? output = deserializeArray(HTMLnode, deserializers, blockDeserializers) : (output = {}, console.debug(
|
|
864
|
+
`Tried to deserialize block ${HTMLnode.outerHTML} but failed to identify it!`
|
|
865
|
+
)), output;
|
|
866
|
+
}, deserializeDocument$1 = (serializedDoc, deserializers = customDeserializers, blockDeserializers = customBlockDeserializers) => {
|
|
867
|
+
const metadata = {}, head = new DOMParser().parseFromString(serializedDoc, "text/html").head;
|
|
868
|
+
return Array.from(head.children).forEach((metaTag) => {
|
|
869
|
+
const validTags = ["_id", "_rev", "_type"], metaName = metaTag.getAttribute("name");
|
|
870
|
+
metaName && validTags.includes(metaName) && (metadata[metaName] = metaTag.getAttribute("content"));
|
|
871
|
+
}), {
|
|
872
|
+
...deserializeHTML(
|
|
873
|
+
serializedDoc,
|
|
874
|
+
deserializers,
|
|
875
|
+
blockDeserializers
|
|
876
|
+
),
|
|
877
|
+
...metadata
|
|
878
|
+
};
|
|
879
|
+
}, BaseDocumentDeserializer = {
|
|
880
|
+
deserializeDocument: deserializeDocument$1,
|
|
881
|
+
deserializeHTML
|
|
882
|
+
}, META_FIELDS$1 = ["_key", "_type", "_id"], isRecord$2 = (value) => typeof value == "object" && value !== null && !Array.isArray(value), languageObjectFieldFilter = (obj, baseLang) => {
|
|
883
|
+
const filterToLangField = (childObj) => {
|
|
884
|
+
const filteredObj = {};
|
|
885
|
+
return filteredObj[baseLang] = childObj[baseLang], META_FIELDS$1.forEach((field) => {
|
|
886
|
+
childObj[field] && (filteredObj[field] = childObj[field]);
|
|
887
|
+
}), filteredObj;
|
|
888
|
+
}, findBaseLang = (childObj) => {
|
|
889
|
+
const filteredObj = {};
|
|
890
|
+
META_FIELDS$1.forEach((field) => {
|
|
891
|
+
childObj[field] && (filteredObj[field] = childObj[field]);
|
|
892
|
+
});
|
|
395
893
|
for (const key in childObj)
|
|
396
894
|
if (childObj.hasOwnProperty(key)) {
|
|
397
895
|
const value = childObj[key];
|
|
@@ -518,240 +1016,78 @@ const isRecord$3 = (value) => typeof value == "object" && value !== null && !Arr
|
|
|
518
1016
|
);
|
|
519
1017
|
return schema && schema.fields ? fieldFilter(block, schema.fields, stopTypes) : block;
|
|
520
1018
|
}).map((obj) => typeof obj == "string" ? `<span>${obj}</span>` : serializeObject(obj, stopTypes, serializers));
|
|
521
|
-
return `<div class="${fieldName}" data-type="array">${output.join("")}</div>`;
|
|
522
|
-
};
|
|
523
|
-
return {
|
|
524
|
-
serializeDocument: (doc, translationLevel = "document", baseLang = "en", stopTypes = defaultStopTypes, serializers = customSerializers) => {
|
|
525
|
-
const schema = getSchema(doc._type);
|
|
526
|
-
let filteredObj = {};
|
|
527
|
-
translationLevel === "field" ? filteredObj = languageObjectFieldFilter(doc, baseLang) : filteredObj = fieldFilter(doc, schema?.fields ?? [], stopTypes);
|
|
528
|
-
const serializedFields = {};
|
|
529
|
-
for (const key in filteredObj) {
|
|
530
|
-
if (filteredObj.hasOwnProperty(key) === !1) continue;
|
|
531
|
-
const value = filteredObj[key];
|
|
532
|
-
if (typeof value == "string")
|
|
533
|
-
serializedFields[key] = value;
|
|
534
|
-
else if (Array.isArray(value))
|
|
535
|
-
serializedFields[key] = serializeArray(
|
|
536
|
-
value.filter(
|
|
537
|
-
(item) => typeof item == "string" || isRecord$1(item)
|
|
538
|
-
),
|
|
539
|
-
key,
|
|
540
|
-
stopTypes,
|
|
541
|
-
serializers
|
|
542
|
-
);
|
|
543
|
-
else if (value && isRecord$1(value) && !stopTypes.find((stopType) => stopType == value?._type)) {
|
|
544
|
-
const serialized = serializeObject(
|
|
545
|
-
value,
|
|
546
|
-
stopTypes,
|
|
547
|
-
serializers
|
|
548
|
-
);
|
|
549
|
-
serializedFields[key] = `<div class="${key}" data-level='field'>${serialized}</div>`;
|
|
550
|
-
}
|
|
551
|
-
}
|
|
552
|
-
const rawHTMLBody = document.createElement("body");
|
|
553
|
-
rawHTMLBody.innerHTML = serializeObject(
|
|
554
|
-
serializedFields,
|
|
555
|
-
stopTypes,
|
|
556
|
-
serializers
|
|
557
|
-
);
|
|
558
|
-
const rawHTMLHead = document.createElement("head");
|
|
559
|
-
["_id", "_type", "_rev"].forEach((field) => {
|
|
560
|
-
const metaEl = document.createElement("meta");
|
|
561
|
-
metaEl.setAttribute("name", field), metaEl.setAttribute("content", doc[field]), rawHTMLHead.appendChild(metaEl);
|
|
562
|
-
});
|
|
563
|
-
const versionMeta = document.createElement("meta");
|
|
564
|
-
versionMeta.setAttribute("name", "version"), versionMeta.setAttribute("content", "3"), rawHTMLHead.appendChild(versionMeta);
|
|
565
|
-
const rawHTML = document.createElement("html");
|
|
566
|
-
return rawHTML.appendChild(rawHTMLHead), rawHTML.appendChild(rawHTMLBody), {
|
|
567
|
-
name: doc._id,
|
|
568
|
-
content: rawHTML.outerHTML
|
|
569
|
-
};
|
|
570
|
-
},
|
|
571
|
-
fieldFilter,
|
|
572
|
-
languageObjectFieldFilter,
|
|
573
|
-
serializeArray,
|
|
574
|
-
serializeObject
|
|
575
|
-
};
|
|
576
|
-
};
|
|
577
|
-
function
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
idx !== 0 && (mergedBlock.children.push(...block.children), mergedBlock.markDefs.push(...block.markDefs ?? []));
|
|
582
|
-
return mergedBlock._type = "block", mergedBlock;
|
|
583
|
-
}
|
|
584
|
-
const deserializeArray = (arrayHTML, deserializers = customDeserializers, blockDeserializers = customBlockDeserializers) => {
|
|
585
|
-
const output = [];
|
|
586
|
-
return Array.from(arrayHTML.children).forEach((child) => {
|
|
587
|
-
let deserializedObject;
|
|
588
|
-
try {
|
|
589
|
-
if (child.tagName?.toLowerCase() === "span")
|
|
590
|
-
deserializedObject = preprocess(child.innerHTML);
|
|
591
|
-
else if (child.className || child.getAttribute("data-type") === "object")
|
|
592
|
-
deserializedObject = deserializeObject(
|
|
593
|
-
child,
|
|
594
|
-
deserializers,
|
|
595
|
-
blockDeserializers
|
|
596
|
-
), deserializedObject && typeof deserializedObject == "object" && !Array.isArray(deserializedObject) && (deserializedObject._key = child.id);
|
|
597
|
-
else {
|
|
598
|
-
const blocks = htmlToBlocks(child.outerHTML, blockContentType, {
|
|
599
|
-
rules: blockDeserializers
|
|
600
|
-
});
|
|
601
|
-
deserializedObject = mergeBlocks(
|
|
602
|
-
blocks
|
|
603
|
-
), deserializedObject._key = child.id;
|
|
604
|
-
}
|
|
605
|
-
} catch (e) {
|
|
606
|
-
console.debug(
|
|
607
|
-
`Tried to deserialize block: ${child.outerHTML} in an array but failed to identify it! Error: ${e}`
|
|
608
|
-
);
|
|
609
|
-
}
|
|
610
|
-
output.push(deserializedObject);
|
|
611
|
-
}), output;
|
|
612
|
-
}, deserializeObject = (objectHTML, deserializers = customDeserializers, blockDeserializers = customBlockDeserializers) => {
|
|
613
|
-
const deserialize = deserializers.types?.[objectHTML.className];
|
|
614
|
-
if (deserialize)
|
|
615
|
-
return deserialize(objectHTML);
|
|
616
|
-
const output = {};
|
|
617
|
-
return objectHTML.className && (output._type = objectHTML.className), Array.from(objectHTML.children).forEach((child) => {
|
|
618
|
-
if (child.tagName?.toLowerCase() === "span")
|
|
619
|
-
output[child.className] = preprocess(child.innerHTML);
|
|
620
|
-
else if (child.getAttribute("data-level") === "field") {
|
|
621
|
-
const deserialized = deserializeHTML(
|
|
622
|
-
child.outerHTML,
|
|
623
|
-
deserializers,
|
|
624
|
-
blockDeserializers
|
|
625
|
-
);
|
|
626
|
-
deserialized && Object.keys(deserialized).length ? output[child.className] = deserialized : console.debug(
|
|
627
|
-
`Deserializer: Skipping empty or unreadable HTML: ${child.outerHTML}`
|
|
628
|
-
);
|
|
629
|
-
} else child.getAttribute("data-type") === "array" && (output[child.className] = deserializeArray(
|
|
630
|
-
child,
|
|
631
|
-
deserializers,
|
|
632
|
-
blockDeserializers
|
|
633
|
-
));
|
|
634
|
-
}), output;
|
|
635
|
-
}, deserializeHTML = (html, deserializers, blockDeserializers) => {
|
|
636
|
-
let HTMLnode = new DOMParser().parseFromString(html, "text/html").body.children[0];
|
|
637
|
-
if (HTMLnode?.getAttribute("data-level") === "field" && (HTMLnode = HTMLnode.children[0]), !HTMLnode)
|
|
638
|
-
return {};
|
|
639
|
-
let output;
|
|
640
|
-
const deserialize = deserializers.types?.[HTMLnode.className];
|
|
641
|
-
return deserialize ? output = deserialize(HTMLnode) : HTMLnode.getAttribute("data-type") === "object" ? output = deserializeObject(HTMLnode, deserializers, blockDeserializers) : HTMLnode.getAttribute("data-type") === "array" ? output = deserializeArray(HTMLnode, deserializers, blockDeserializers) : (output = {}, console.debug(
|
|
642
|
-
`Tried to deserialize block ${HTMLnode.outerHTML} but failed to identify it!`
|
|
643
|
-
)), output;
|
|
644
|
-
}, deserializeDocument$1 = (serializedDoc, deserializers = customDeserializers, blockDeserializers = customBlockDeserializers) => {
|
|
645
|
-
const metadata = {}, head = new DOMParser().parseFromString(serializedDoc, "text/html").head;
|
|
646
|
-
return Array.from(head.children).forEach((metaTag) => {
|
|
647
|
-
const validTags = ["_id", "_rev", "_type"], metaName = metaTag.getAttribute("name");
|
|
648
|
-
metaName && validTags.includes(metaName) && (metadata[metaName] = metaTag.getAttribute("content"));
|
|
649
|
-
}), {
|
|
650
|
-
...deserializeHTML(
|
|
651
|
-
serializedDoc,
|
|
652
|
-
deserializers,
|
|
653
|
-
blockDeserializers
|
|
654
|
-
),
|
|
655
|
-
...metadata
|
|
656
|
-
};
|
|
657
|
-
}, BaseDocumentDeserializer = {
|
|
658
|
-
deserializeDocument: deserializeDocument$1,
|
|
659
|
-
deserializeHTML
|
|
660
|
-
};
|
|
661
|
-
function forEachMatchingField(documentId, document2, fields, callback) {
|
|
662
|
-
const applicable = fields.filter(
|
|
663
|
-
(field) => field.documentId === documentId || field.documentId === void 0 || field.documentId === null
|
|
664
|
-
);
|
|
665
|
-
for (const entry of applicable)
|
|
666
|
-
if (entry.fields)
|
|
667
|
-
for (const field of entry.fields) {
|
|
668
|
-
const { property, type } = field;
|
|
669
|
-
try {
|
|
670
|
-
const results = JSONPath({
|
|
671
|
-
json: document2,
|
|
672
|
-
path: property,
|
|
673
|
-
resultType: "all",
|
|
674
|
-
flatten: !0,
|
|
675
|
-
wrap: !0
|
|
676
|
-
});
|
|
677
|
-
results && results.length > 0 && results.forEach(
|
|
678
|
-
(result) => {
|
|
679
|
-
type !== void 0 ? typeof result.value == "object" && result.value !== null && !Array.isArray(result.value) && result.value._type === type && callback(result) : callback(result);
|
|
680
|
-
}
|
|
681
|
-
);
|
|
682
|
-
} catch (error) {
|
|
683
|
-
console.warn(`Invalid JSONPath: ${property}`, error);
|
|
684
|
-
}
|
|
685
|
-
}
|
|
686
|
-
}
|
|
687
|
-
function deleteMatchingFields(documentId, document2, fields) {
|
|
688
|
-
const arrayRemovals = [];
|
|
689
|
-
forEachMatchingField(documentId, document2, fields, (result) => {
|
|
690
|
-
Array.isArray(result.parent) ? arrayRemovals.push({
|
|
691
|
-
parent: result.parent,
|
|
692
|
-
index: Number(result.parentProperty)
|
|
693
|
-
}) : delete result.parent[result.parentProperty];
|
|
694
|
-
}), arrayRemovals.sort((a, b) => b.index - a.index);
|
|
695
|
-
for (const { parent, index } of arrayRemovals)
|
|
696
|
-
parent.splice(index, 1);
|
|
697
|
-
}
|
|
698
|
-
function applyDocuments(documentId, sourceDocument, targetDocument, ignore, skip = [], dedupe = [], localeId) {
|
|
699
|
-
const mergedDocument = JSON.parse(JSON.stringify(sourceDocument)), clonedTarget = JSON.parse(JSON.stringify(targetDocument));
|
|
700
|
-
for (const [key, value] of Object.entries(clonedTarget))
|
|
701
|
-
mergedDocument[key] = value;
|
|
702
|
-
return forEachMatchingField(documentId, sourceDocument, ignore, (result) => {
|
|
703
|
-
JSONPointer.set(mergedDocument, result.pointer, result.value);
|
|
704
|
-
}), forEachMatchingField(documentId, sourceDocument, dedupe, (result) => {
|
|
705
|
-
JSONPointer.set(
|
|
706
|
-
mergedDocument,
|
|
707
|
-
result.pointer,
|
|
708
|
-
dedupeFieldValue(result.value, localeId)
|
|
709
|
-
);
|
|
710
|
-
}), deleteMatchingFields(documentId, mergedDocument, skip), mergedDocument;
|
|
711
|
-
}
|
|
712
|
-
function dedupeFieldValue(value, localeId) {
|
|
713
|
-
if (!localeId) return value;
|
|
714
|
-
if (typeof value == "string")
|
|
715
|
-
return appendLocaleSuffix(value, localeId);
|
|
716
|
-
if (value && typeof value == "object" && !Array.isArray(value) && typeof value.current == "string") {
|
|
717
|
-
const slug = value;
|
|
718
|
-
return {
|
|
719
|
-
...slug,
|
|
720
|
-
current: appendLocaleSuffix(slug.current, localeId)
|
|
721
|
-
};
|
|
1019
|
+
return `<div class="${fieldName}" data-type="array">${output.join("")}</div>`;
|
|
1020
|
+
};
|
|
1021
|
+
return {
|
|
1022
|
+
serializeDocument: (doc, translationLevel = "document", baseLang = "en", stopTypes = defaultStopTypes, serializers = customSerializers) => {
|
|
1023
|
+
const schema = getSchema(doc._type);
|
|
1024
|
+
let filteredObj = {};
|
|
1025
|
+
translationLevel === "field" ? filteredObj = languageObjectFieldFilter(doc, baseLang) : filteredObj = fieldFilter(doc, schema?.fields ?? [], stopTypes);
|
|
1026
|
+
const serializedFields = {};
|
|
1027
|
+
for (const key in filteredObj) {
|
|
1028
|
+
if (filteredObj.hasOwnProperty(key) === !1) continue;
|
|
1029
|
+
const value = filteredObj[key];
|
|
1030
|
+
if (typeof value == "string")
|
|
1031
|
+
serializedFields[key] = value;
|
|
1032
|
+
else if (Array.isArray(value))
|
|
1033
|
+
serializedFields[key] = serializeArray(
|
|
1034
|
+
value.filter(
|
|
1035
|
+
(item) => typeof item == "string" || isRecord$1(item)
|
|
1036
|
+
),
|
|
1037
|
+
key,
|
|
1038
|
+
stopTypes,
|
|
1039
|
+
serializers
|
|
1040
|
+
);
|
|
1041
|
+
else if (value && isRecord$1(value) && !stopTypes.find((stopType) => stopType == value?._type)) {
|
|
1042
|
+
const serialized = serializeObject(
|
|
1043
|
+
value,
|
|
1044
|
+
stopTypes,
|
|
1045
|
+
serializers
|
|
1046
|
+
);
|
|
1047
|
+
serializedFields[key] = `<div class="${key}" data-level='field'>${serialized}</div>`;
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
const rawHTMLBody = document.createElement("body");
|
|
1051
|
+
rawHTMLBody.innerHTML = serializeObject(
|
|
1052
|
+
serializedFields,
|
|
1053
|
+
stopTypes,
|
|
1054
|
+
serializers
|
|
1055
|
+
);
|
|
1056
|
+
const rawHTMLHead = document.createElement("head");
|
|
1057
|
+
["_id", "_type", "_rev"].forEach((field) => {
|
|
1058
|
+
const metaEl = document.createElement("meta");
|
|
1059
|
+
metaEl.setAttribute("name", field), metaEl.setAttribute("content", doc[field]), rawHTMLHead.appendChild(metaEl);
|
|
1060
|
+
});
|
|
1061
|
+
const versionMeta = document.createElement("meta");
|
|
1062
|
+
versionMeta.setAttribute("name", "version"), versionMeta.setAttribute("content", "3"), rawHTMLHead.appendChild(versionMeta);
|
|
1063
|
+
const rawHTML = document.createElement("html");
|
|
1064
|
+
return rawHTML.appendChild(rawHTMLHead), rawHTML.appendChild(rawHTMLBody), {
|
|
1065
|
+
name: doc._id,
|
|
1066
|
+
content: rawHTML.outerHTML
|
|
1067
|
+
};
|
|
1068
|
+
},
|
|
1069
|
+
fieldFilter,
|
|
1070
|
+
languageObjectFieldFilter,
|
|
1071
|
+
serializeArray,
|
|
1072
|
+
serializeObject
|
|
1073
|
+
};
|
|
1074
|
+
};
|
|
1075
|
+
function collapseToSourceLocale(value, sourceLocale) {
|
|
1076
|
+
if (isInternationalizedArrayField(value)) {
|
|
1077
|
+
const sourceItem = findLocaleItem(value, sourceLocale);
|
|
1078
|
+
return sourceItem ? collapseToSourceLocale(sourceItem.value, sourceLocale) : void 0;
|
|
722
1079
|
}
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
return normalized ? `-${normalized}` : "";
|
|
733
|
-
}
|
|
734
|
-
function getPublishedId(documentId) {
|
|
735
|
-
return documentId.startsWith("drafts.") ? documentId.slice(7) : documentId;
|
|
736
|
-
}
|
|
737
|
-
function getDocumentPublishedId(document2) {
|
|
738
|
-
return getPublishedId(document2._id);
|
|
739
|
-
}
|
|
740
|
-
function dedupeDocumentsPreferDraft(documents) {
|
|
741
|
-
const byPublishedId = /* @__PURE__ */ new Map();
|
|
742
|
-
for (const document2 of documents) {
|
|
743
|
-
const publishedId = getDocumentPublishedId(document2);
|
|
744
|
-
(!byPublishedId.get(publishedId) || document2._id.startsWith("drafts.")) && byPublishedId.set(publishedId, document2);
|
|
1080
|
+
if (Array.isArray(value))
|
|
1081
|
+
return value.map((item) => collapseToSourceLocale(item, sourceLocale)).filter((item) => item !== void 0);
|
|
1082
|
+
if (isRecord$4(value)) {
|
|
1083
|
+
const collapsed = {};
|
|
1084
|
+
for (const key of Object.keys(value)) {
|
|
1085
|
+
const collapsedValue = collapseToSourceLocale(value[key], sourceLocale);
|
|
1086
|
+
collapsedValue !== void 0 && (collapsed[key] = collapsedValue);
|
|
1087
|
+
}
|
|
1088
|
+
return collapsed;
|
|
745
1089
|
}
|
|
746
|
-
return
|
|
747
|
-
}
|
|
748
|
-
function createTranslationStatusKey(branchId, documentId, versionId, localeId) {
|
|
749
|
-
const publishedId = getPublishedId(documentId);
|
|
750
|
-
return branchId ? `${branchId}:${publishedId}:${versionId}:${localeId}` : `${publishedId}:${versionId}:${localeId}`;
|
|
751
|
-
}
|
|
752
|
-
function createStableTranslationKey(branchId, documentId, localeId) {
|
|
753
|
-
const publishedId = getPublishedId(documentId);
|
|
754
|
-
return branchId ? `${branchId}:${publishedId}:${localeId}` : `${publishedId}:${localeId}`;
|
|
1090
|
+
return value;
|
|
755
1091
|
}
|
|
756
1092
|
function deserializeDocument(document2) {
|
|
757
1093
|
const deserializers = merge(
|
|
@@ -767,21 +1103,25 @@ function deserializeDocument(document2) {
|
|
|
767
1103
|
blockDeserializers
|
|
768
1104
|
);
|
|
769
1105
|
}
|
|
770
|
-
function serializeDocument(document2, schema, baseLanguage) {
|
|
1106
|
+
function serializeDocument(document2, schema, baseLanguage, level = "document") {
|
|
771
1107
|
const stopTypes = [
|
|
772
1108
|
...defaultStopTypes,
|
|
773
1109
|
...pluginConfig.getAdditionalStopTypes()
|
|
774
1110
|
], serializers = merge(
|
|
775
1111
|
customSerializers,
|
|
776
1112
|
pluginConfig.getAdditionalSerializers()
|
|
777
|
-
)
|
|
1113
|
+
);
|
|
1114
|
+
let docToSerialize = stripIgnoredFields(
|
|
778
1115
|
document2,
|
|
779
1116
|
pluginConfig.getIgnoreFields(),
|
|
780
1117
|
pluginConfig.getDedupeFields()
|
|
781
|
-
);
|
|
782
|
-
return
|
|
1118
|
+
), innerLevel = level;
|
|
1119
|
+
return level === "internationalizedArray" && (docToSerialize = collapseToSourceLocale(
|
|
1120
|
+
docToSerialize,
|
|
1121
|
+
baseLanguage
|
|
1122
|
+
), innerLevel = "document"), BaseDocumentSerializer(schema).serializeDocument(
|
|
783
1123
|
docToSerialize,
|
|
784
|
-
|
|
1124
|
+
innerLevel,
|
|
785
1125
|
baseLanguage,
|
|
786
1126
|
stopTypes,
|
|
787
1127
|
serializers
|
|
@@ -797,6 +1137,32 @@ function stripIgnoredFields(document2, ignoreFields, dedupeFields) {
|
|
|
797
1137
|
fieldsToStrip
|
|
798
1138
|
), strippedDoc;
|
|
799
1139
|
}
|
|
1140
|
+
const documentAdapter = {
|
|
1141
|
+
level: "document",
|
|
1142
|
+
serialize: (document2, schema, baseLanguage) => serializeDocument(document2, schema, baseLanguage, "document"),
|
|
1143
|
+
patch: (docInfo, deserialized, localeId, client, mergeWithTargetLocale) => documentLevelPatch(
|
|
1144
|
+
docInfo,
|
|
1145
|
+
deserialized,
|
|
1146
|
+
localeId,
|
|
1147
|
+
client,
|
|
1148
|
+
pluginConfig.getLanguageField(),
|
|
1149
|
+
mergeWithTargetLocale
|
|
1150
|
+
)
|
|
1151
|
+
}, internationalizedArrayAdapter = {
|
|
1152
|
+
level: "internationalizedArray",
|
|
1153
|
+
serialize: (document2, schema, baseLanguage) => serializeDocument(document2, schema, baseLanguage, "internationalizedArray"),
|
|
1154
|
+
patch: (docInfo, deserialized, localeId, client) => internationalizedArrayPatch(docInfo, deserialized, localeId, client)
|
|
1155
|
+
};
|
|
1156
|
+
function matchesFieldLevel(type) {
|
|
1157
|
+
return type ? pluginConfig.getFieldLevelDocuments().some((filter2) => filter2.type === type) : !1;
|
|
1158
|
+
}
|
|
1159
|
+
function getTranslationStrategyForType(type) {
|
|
1160
|
+
const level = pluginConfig.getTranslationLevel();
|
|
1161
|
+
return level === "internationalizedArray" || level === "mixed" && matchesFieldLevel(type) ? internationalizedArrayAdapter : documentAdapter;
|
|
1162
|
+
}
|
|
1163
|
+
function getTranslationStrategy(document2) {
|
|
1164
|
+
return getTranslationStrategyForType(document2._type);
|
|
1165
|
+
}
|
|
800
1166
|
async function uploadFiles(documents, secrets) {
|
|
801
1167
|
return overrideConfig(secrets), await gt.uploadSourceFiles(
|
|
802
1168
|
documents.map(({ info, serializedDocument }) => ({
|
|
@@ -831,283 +1197,79 @@ async function initProject(uploadResult, options, secrets) {
|
|
|
831
1197
|
if (status[0].status === "completed") {
|
|
832
1198
|
setupCompleted = !0;
|
|
833
1199
|
break;
|
|
834
|
-
}
|
|
835
|
-
if (status[0].status === "failed") {
|
|
836
|
-
setupFailedMessage = status[0].error?.message || "Unknown error";
|
|
837
|
-
break;
|
|
838
|
-
}
|
|
839
|
-
if (Date.now() - start > setupTimeoutMs) {
|
|
840
|
-
setupFailedMessage = "Timed out while waiting for setup generation";
|
|
841
|
-
break;
|
|
842
|
-
}
|
|
843
|
-
await new Promise((r) => setTimeout(r, pollInterval));
|
|
844
|
-
}
|
|
845
|
-
console.log(setupCompleted ? "Setup successfully completed" : `Setup ${setupFailedMessage ? "failed" : "timed out"} \u2014 proceeding without setup${setupFailedMessage ? ` (${setupFailedMessage})` : ""}`);
|
|
846
|
-
}
|
|
847
|
-
return !0;
|
|
848
|
-
}
|
|
849
|
-
async function createJobs(uploadResult, localeIds, secrets) {
|
|
850
|
-
return overrideConfig(secrets), await gt.enqueueFiles(uploadResult.uploadedFiles, {
|
|
851
|
-
sourceLocale: gt.sourceLocale || libraryDefaultLocale,
|
|
852
|
-
targetLocales: localeIds
|
|
853
|
-
});
|
|
854
|
-
}
|
|
855
|
-
async function downloadTranslations(files, secrets, maxRetries = 3, retryDelay = 1e3) {
|
|
856
|
-
overrideConfig(secrets);
|
|
857
|
-
let retries = 0;
|
|
858
|
-
for (; retries <= maxRetries; )
|
|
859
|
-
try {
|
|
860
|
-
return (await gt.downloadFileBatch(
|
|
861
|
-
files.map((file) => ({
|
|
862
|
-
fileId: file.fileId,
|
|
863
|
-
branchId: file.branchId,
|
|
864
|
-
versionId: file.versionId,
|
|
865
|
-
locale: file.locale
|
|
866
|
-
}))
|
|
867
|
-
)).files || [];
|
|
868
|
-
} catch {
|
|
869
|
-
retries++, await new Promise((resolve) => setTimeout(resolve, retryDelay));
|
|
870
|
-
}
|
|
871
|
-
return [];
|
|
872
|
-
}
|
|
873
|
-
async function checkTranslationStatus(fileQueryData, downloadStatus, secrets) {
|
|
874
|
-
overrideConfig(secrets);
|
|
875
|
-
try {
|
|
876
|
-
const currentQueryData = fileQueryData.filter((item) => {
|
|
877
|
-
const statusKey = createTranslationStatusKey(
|
|
878
|
-
item.branchId,
|
|
879
|
-
item.fileId,
|
|
880
|
-
item.versionId,
|
|
881
|
-
item.locale
|
|
882
|
-
), stableKey = createStableTranslationKey(
|
|
883
|
-
item.branchId,
|
|
884
|
-
item.fileId,
|
|
885
|
-
item.locale
|
|
886
|
-
);
|
|
887
|
-
return !downloadStatus.downloaded.has(statusKey) && !downloadStatus.downloaded.has(stableKey) && !downloadStatus.failed.has(statusKey) && !downloadStatus.failed.has(stableKey) && !downloadStatus.skipped.has(statusKey) && !downloadStatus.skipped.has(stableKey);
|
|
888
|
-
});
|
|
889
|
-
return currentQueryData.length === 0 ? !0 : ((await gt.queryFileData({
|
|
890
|
-
translatedFiles: currentQueryData
|
|
891
|
-
})).translatedFiles || []).filter(
|
|
892
|
-
(translation) => translation.completedAt
|
|
893
|
-
);
|
|
894
|
-
} catch (error) {
|
|
895
|
-
return console.error("Error checking translation status", error), [];
|
|
896
|
-
}
|
|
897
|
-
}
|
|
898
|
-
const findLatestDraft = (documentId, client) => {
|
|
899
|
-
const publishedId = getPublishedId(documentId), query = "*[_id == $id || _id == $draftId]", params = { id: publishedId, draftId: `drafts.${publishedId}` };
|
|
900
|
-
return client.fetch(query, params).then(
|
|
901
|
-
(docs) => docs.find((doc) => doc._id.startsWith("drafts.")) ?? docs[0]
|
|
902
|
-
);
|
|
903
|
-
}, findDocumentAtRevision = async (documentId, rev, client) => {
|
|
904
|
-
const baseUrl = `/data/history/${client.config().dataset}/documents/${documentId}?revision=${rev}`, url = client.getUrl(baseUrl);
|
|
905
|
-
return await fetch(url, { credentials: "include" }).then((req) => req.json()).then((req) => req.documents && req.documents.length ? req.documents[0] : null);
|
|
906
|
-
};
|
|
907
|
-
function randomKey() {
|
|
908
|
-
return Math.random().toString(36).slice(2, 10);
|
|
909
|
-
}
|
|
910
|
-
async function createI18nDocAndPatchMetadata(sourceDocument, translatedDoc, localeId, client, translationMetadata, sourceDocumentId, languageField = "language") {
|
|
911
|
-
const publishedSourceDocumentId = getPublishedId(sourceDocumentId);
|
|
912
|
-
translatedDoc[languageField] = localeId;
|
|
913
|
-
const existingLocaleKey = translationMetadata.translations.find(
|
|
914
|
-
(translation) => translation.language === localeId
|
|
915
|
-
), operation = existingLocaleKey ? "replace" : "after", location = existingLocaleKey ? `translations[language == "${localeId}"]` : "translations[-1]", { _updatedAt, _createdAt, ...rest } = translatedDoc, appliedDocument = applyDocuments(
|
|
916
|
-
publishedSourceDocumentId,
|
|
917
|
-
sourceDocument,
|
|
918
|
-
rest,
|
|
919
|
-
pluginConfig.getIgnoreFields(),
|
|
920
|
-
pluginConfig.getSkipFields(),
|
|
921
|
-
pluginConfig.getDedupeFields(),
|
|
922
|
-
localeId
|
|
923
|
-
), isSingleton = pluginConfig.getSingletons().includes(publishedSourceDocumentId);
|
|
924
|
-
let createDocumentPromise;
|
|
925
|
-
if (isSingleton) {
|
|
926
|
-
const translatedDocId = pluginConfig.getSingletonMapping()(
|
|
927
|
-
publishedSourceDocumentId,
|
|
928
|
-
localeId
|
|
929
|
-
);
|
|
930
|
-
createDocumentPromise = client.create({
|
|
931
|
-
...appliedDocument,
|
|
932
|
-
_type: rest._type,
|
|
933
|
-
_id: `drafts.${translatedDocId}`
|
|
934
|
-
});
|
|
935
|
-
} else
|
|
936
|
-
createDocumentPromise = client.create({
|
|
937
|
-
...appliedDocument,
|
|
938
|
-
_type: rest._type,
|
|
939
|
-
_id: "drafts."
|
|
940
|
-
});
|
|
941
|
-
const doc = await createDocumentPromise, _ref = getPublishedId(doc._id);
|
|
942
|
-
await client.transaction().patch(
|
|
943
|
-
translationMetadata._id,
|
|
944
|
-
(p) => p.insert(operation, location, [
|
|
945
|
-
{
|
|
946
|
-
_key: randomKey(),
|
|
947
|
-
language: localeId,
|
|
948
|
-
_type: "internationalizedArrayReferenceValue",
|
|
949
|
-
value: {
|
|
950
|
-
_type: "reference",
|
|
951
|
-
_ref,
|
|
952
|
-
_weak: !0,
|
|
953
|
-
_strengthenOnPublish: {
|
|
954
|
-
type: doc._type
|
|
955
|
-
}
|
|
956
|
-
}
|
|
957
|
-
}
|
|
958
|
-
])
|
|
959
|
-
).commit();
|
|
960
|
-
}
|
|
961
|
-
const getOrCreateTranslationMetadata = async (documentId, baseDocument, client, baseLanguage) => {
|
|
962
|
-
const publishedId = getPublishedId(documentId), existingMetadata = await client.fetch(
|
|
963
|
-
`*[
|
|
964
|
-
_type == 'translation.metadata' &&
|
|
965
|
-
translations[language == $baseLanguage][0].value._ref == $id
|
|
966
|
-
][0]`,
|
|
967
|
-
{ baseLanguage, id: publishedId }
|
|
968
|
-
);
|
|
969
|
-
if (existingMetadata)
|
|
970
|
-
return existingMetadata;
|
|
971
|
-
const baseLangEntry = {
|
|
972
|
-
_key: randomKey(),
|
|
973
|
-
language: baseLanguage,
|
|
974
|
-
_type: "internationalizedArrayReferenceValue",
|
|
975
|
-
value: {
|
|
976
|
-
_type: "reference",
|
|
977
|
-
_ref: getPublishedId(baseDocument._id)
|
|
978
|
-
}
|
|
979
|
-
};
|
|
980
|
-
baseDocument._id.startsWith("drafts.") && (baseLangEntry.value = {
|
|
981
|
-
...baseLangEntry.value,
|
|
982
|
-
_weak: !0,
|
|
983
|
-
//this should reflect doc i18n config when this
|
|
984
|
-
//plugin is able to take that as a config option
|
|
985
|
-
_strengthenOnPublish: {
|
|
986
|
-
type: baseDocument._type
|
|
1200
|
+
}
|
|
1201
|
+
if (status[0].status === "failed") {
|
|
1202
|
+
setupFailedMessage = status[0].error?.message || "Unknown error";
|
|
1203
|
+
break;
|
|
1204
|
+
}
|
|
1205
|
+
if (Date.now() - start > setupTimeoutMs) {
|
|
1206
|
+
setupFailedMessage = "Timed out while waiting for setup generation";
|
|
1207
|
+
break;
|
|
1208
|
+
}
|
|
1209
|
+
await new Promise((r) => setTimeout(r, pollInterval));
|
|
987
1210
|
}
|
|
988
|
-
|
|
989
|
-
try {
|
|
990
|
-
return await client.createIfNotExists({
|
|
991
|
-
_id: `translation.metadata.${publishedId}`,
|
|
992
|
-
_type: "translation.metadata",
|
|
993
|
-
translations: [baseLangEntry]
|
|
994
|
-
});
|
|
995
|
-
} catch (error) {
|
|
996
|
-
const metadata = await client.fetch(
|
|
997
|
-
`*[
|
|
998
|
-
_type == 'translation.metadata' &&
|
|
999
|
-
translations[language == $baseLanguage][0].value._ref == $id
|
|
1000
|
-
][0]`,
|
|
1001
|
-
{ baseLanguage, id: publishedId }
|
|
1002
|
-
);
|
|
1003
|
-
if (metadata)
|
|
1004
|
-
return metadata;
|
|
1005
|
-
throw error;
|
|
1211
|
+
console.log(setupCompleted ? "Setup successfully completed" : `Setup ${setupFailedMessage ? "failed" : "timed out"} \u2014 proceeding without setup${setupFailedMessage ? ` (${setupFailedMessage})` : ""}`);
|
|
1006
1212
|
}
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
});
|
|
1014
|
-
const cleanedSourceDocument = {};
|
|
1015
|
-
Object.entries(sourceDocument).forEach(([key, value]) => {
|
|
1016
|
-
isSystemField(key) || (cleanedSourceDocument[key] = value);
|
|
1213
|
+
return !0;
|
|
1214
|
+
}
|
|
1215
|
+
async function createJobs(uploadResult, localeIds, secrets) {
|
|
1216
|
+
return overrideConfig(secrets), await gt.enqueueFiles(uploadResult.uploadedFiles, {
|
|
1217
|
+
sourceLocale: gt.sourceLocale || libraryDefaultLocale,
|
|
1218
|
+
targetLocales: localeIds
|
|
1017
1219
|
});
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1220
|
+
}
|
|
1221
|
+
async function downloadTranslations(files, secrets, maxRetries = 3, retryDelay = 1e3) {
|
|
1222
|
+
overrideConfig(secrets);
|
|
1223
|
+
let retries = 0;
|
|
1224
|
+
for (; retries <= maxRetries; )
|
|
1225
|
+
try {
|
|
1226
|
+
return (await gt.downloadFileBatch(
|
|
1227
|
+
files.map((file) => ({
|
|
1228
|
+
fileId: file.fileId,
|
|
1229
|
+
branchId: file.branchId,
|
|
1230
|
+
versionId: file.versionId,
|
|
1231
|
+
locale: file.locale
|
|
1232
|
+
}))
|
|
1233
|
+
)).files || [];
|
|
1234
|
+
} catch {
|
|
1235
|
+
retries++, await new Promise((resolve) => setTimeout(resolve, retryDelay));
|
|
1031
1236
|
}
|
|
1032
|
-
|
|
1237
|
+
return [];
|
|
1033
1238
|
}
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
)).translations.find(
|
|
1048
|
-
(translation) => translation.language === localeId
|
|
1049
|
-
)?.value?._ref;
|
|
1050
|
-
i18nDocId && (i18nDoc = await findLatestDraft(i18nDocId, client)), mergeWithTargetLocale && i18nDoc ? baseDoc = i18nDoc : docInfo.documentId && docInfo.versionId && (baseDoc = await findDocumentAtRevision(
|
|
1051
|
-
docInfo.documentId,
|
|
1052
|
-
docInfo.versionId,
|
|
1053
|
-
client
|
|
1054
|
-
)), baseDoc || (baseDoc = await findLatestDraft(docInfo.documentId, client));
|
|
1055
|
-
const merged = BaseDocumentMerger.documentLevelMerge(
|
|
1056
|
-
translatedFields,
|
|
1057
|
-
baseDoc
|
|
1058
|
-
);
|
|
1059
|
-
if (i18nDoc)
|
|
1060
|
-
await patchI18nDoc(
|
|
1061
|
-
docInfo.documentId,
|
|
1062
|
-
i18nDoc._id,
|
|
1063
|
-
baseDoc,
|
|
1064
|
-
merged,
|
|
1065
|
-
translatedFields,
|
|
1066
|
-
client,
|
|
1067
|
-
i18nDoc
|
|
1068
|
-
);
|
|
1069
|
-
else {
|
|
1070
|
-
const freshTranslationMetadata = await getOrCreateTranslationMetadata(
|
|
1071
|
-
docInfo.documentId,
|
|
1072
|
-
baseDoc,
|
|
1073
|
-
client,
|
|
1074
|
-
baseLanguage
|
|
1075
|
-
), freshI18nDocId = freshTranslationMetadata.translations.find(
|
|
1076
|
-
(translation) => translation.language === localeId
|
|
1077
|
-
)?.value?._ref;
|
|
1078
|
-
if (freshI18nDocId) {
|
|
1079
|
-
const freshI18nDoc = await findLatestDraft(freshI18nDocId, client);
|
|
1080
|
-
await patchI18nDoc(
|
|
1081
|
-
docInfo.documentId,
|
|
1082
|
-
freshI18nDoc._id,
|
|
1083
|
-
baseDoc,
|
|
1084
|
-
merged,
|
|
1085
|
-
translatedFields,
|
|
1086
|
-
client,
|
|
1087
|
-
freshI18nDoc
|
|
1239
|
+
async function checkTranslationStatus(fileQueryData, downloadStatus, secrets) {
|
|
1240
|
+
overrideConfig(secrets);
|
|
1241
|
+
try {
|
|
1242
|
+
const currentQueryData = fileQueryData.filter((item) => {
|
|
1243
|
+
const statusKey = createTranslationStatusKey(
|
|
1244
|
+
item.branchId,
|
|
1245
|
+
item.fileId,
|
|
1246
|
+
item.versionId,
|
|
1247
|
+
item.locale
|
|
1248
|
+
), stableKey = createStableTranslationKey(
|
|
1249
|
+
item.branchId,
|
|
1250
|
+
item.fileId,
|
|
1251
|
+
item.locale
|
|
1088
1252
|
);
|
|
1089
|
-
return;
|
|
1090
|
-
}
|
|
1091
|
-
await
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
client,
|
|
1096
|
-
freshTranslationMetadata,
|
|
1097
|
-
docInfo.documentId,
|
|
1098
|
-
languageField
|
|
1253
|
+
return !downloadStatus.downloaded.has(statusKey) && !downloadStatus.downloaded.has(stableKey) && !downloadStatus.failed.has(statusKey) && !downloadStatus.failed.has(stableKey) && !downloadStatus.skipped.has(statusKey) && !downloadStatus.skipped.has(stableKey);
|
|
1254
|
+
});
|
|
1255
|
+
return currentQueryData.length === 0 ? !0 : ((await gt.queryFileData({
|
|
1256
|
+
translatedFiles: currentQueryData
|
|
1257
|
+
})).translatedFiles || []).filter(
|
|
1258
|
+
(translation) => translation.completedAt
|
|
1099
1259
|
);
|
|
1260
|
+
} catch (error) {
|
|
1261
|
+
return console.error("Error checking translation status", error), [];
|
|
1100
1262
|
}
|
|
1101
|
-
}
|
|
1263
|
+
}
|
|
1102
1264
|
async function importDocument(docInfo, localeId, document2, context, mergeWithTargetLocale = !1) {
|
|
1103
1265
|
const { client } = context, deserialized = deserializeDocument(document2);
|
|
1104
|
-
return
|
|
1266
|
+
return getTranslationStrategyForType(
|
|
1267
|
+
deserialized._type
|
|
1268
|
+
).patch(
|
|
1105
1269
|
docInfo,
|
|
1106
|
-
// versionId is not used here, since we just use the _rev id in the deserialized HTML itself
|
|
1107
1270
|
deserialized,
|
|
1108
1271
|
localeId,
|
|
1109
1272
|
client,
|
|
1110
|
-
pluginConfig.getLanguageField(),
|
|
1111
1273
|
mergeWithTargetLocale
|
|
1112
1274
|
);
|
|
1113
1275
|
}
|
|
@@ -1235,11 +1397,13 @@ async function processImportBatch(items, options = {}) {
|
|
|
1235
1397
|
), item.key),
|
|
1236
1398
|
{
|
|
1237
1399
|
...options,
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1400
|
+
// Serialize all locales of the same document when imports may patch the
|
|
1401
|
+
// source document in place: internationalized-array imports do a
|
|
1402
|
+
// read-merge-set, so concurrent locale imports would clobber each other
|
|
1403
|
+
// (last write wins). Document-level imports write to separate per-locale
|
|
1404
|
+
// documents and stay parallel. In 'mixed' mode the per-document strategy
|
|
1405
|
+
// is only known after deserialization, so serialize conservatively.
|
|
1406
|
+
getConcurrencyKey: (item) => pluginConfig.getTranslationLevel() === "document" ? void 0 : getPublishedId(item.docInfo.documentId),
|
|
1243
1407
|
onItemSuccess: (item, key) => {
|
|
1244
1408
|
successfulImports.push(key), options.onItemSuccess?.(item, key);
|
|
1245
1409
|
},
|
|
@@ -1335,7 +1499,34 @@ const getLocales = async (_secrets) => pluginConfig.getLocales().map((locale) =>
|
|
|
1335
1499
|
localeId: locale,
|
|
1336
1500
|
description: gt.getLocaleProperties(locale).name,
|
|
1337
1501
|
enabled: !0
|
|
1338
|
-
})), TranslationsContext = createContext(null),
|
|
1502
|
+
})), TranslationsContext = createContext(null), getUploadedVersionsStorageKey = (projectId, dataset) => `gt-sanity:uploadedVersions:${projectId ?? ""}:${dataset ?? ""}`;
|
|
1503
|
+
function readUploadedVersions(storageKey) {
|
|
1504
|
+
try {
|
|
1505
|
+
const raw = window.sessionStorage.getItem(storageKey);
|
|
1506
|
+
if (!raw) return /* @__PURE__ */ new Map();
|
|
1507
|
+
const parsed = JSON.parse(raw);
|
|
1508
|
+
return isPlainRecord(parsed) ? new Map(
|
|
1509
|
+
Object.entries(parsed).filter(
|
|
1510
|
+
(entry) => typeof entry[1] == "string"
|
|
1511
|
+
)
|
|
1512
|
+
) : /* @__PURE__ */ new Map();
|
|
1513
|
+
} catch {
|
|
1514
|
+
return /* @__PURE__ */ new Map();
|
|
1515
|
+
}
|
|
1516
|
+
}
|
|
1517
|
+
function writeUploadedVersions(storageKey, versions) {
|
|
1518
|
+
try {
|
|
1519
|
+
window.sessionStorage.setItem(
|
|
1520
|
+
storageKey,
|
|
1521
|
+
JSON.stringify(Object.fromEntries(versions))
|
|
1522
|
+
);
|
|
1523
|
+
} catch {
|
|
1524
|
+
}
|
|
1525
|
+
}
|
|
1526
|
+
function isPlainRecord(value) {
|
|
1527
|
+
return typeof value == "object" && value !== null && !Array.isArray(value);
|
|
1528
|
+
}
|
|
1529
|
+
const useTranslations = () => {
|
|
1339
1530
|
const context = useContext(TranslationsContext);
|
|
1340
1531
|
if (!context)
|
|
1341
1532
|
throw new Error("useTranslations must be used within TranslationsProvider");
|
|
@@ -1356,13 +1547,21 @@ const getLocales = async (_secrets) => pluginConfig.getLocales().map((locale) =>
|
|
|
1356
1547
|
downloaded: /* @__PURE__ */ new Set(),
|
|
1357
1548
|
failed: /* @__PURE__ */ new Set(),
|
|
1358
1549
|
skipped: /* @__PURE__ */ new Set()
|
|
1359
|
-
}), downloadStatusRef = useRef(downloadStatus), [translationStatuses, setTranslationStatuses] = useState(/* @__PURE__ */ new Map()), [isRefreshing, setIsRefreshing] = useState(!1), client = useClient(),
|
|
1550
|
+
}), downloadStatusRef = useRef(downloadStatus), [translationStatuses, setTranslationStatuses] = useState(/* @__PURE__ */ new Map()), [isRefreshing, setIsRefreshing] = useState(!1), client = useClient(), { projectId, dataset } = client.config(), uploadedVersionsStorageKey = getUploadedVersionsStorageKey(
|
|
1551
|
+
projectId,
|
|
1552
|
+
dataset
|
|
1553
|
+
), [uploadedVersions, setUploadedVersions] = useState(
|
|
1554
|
+
() => readUploadedVersions(uploadedVersionsStorageKey)
|
|
1555
|
+
), schema = useSchema(), translationContext = { client, schema }, toast = useToast(), { loading: loadingSecrets, secrets } = useSecrets(
|
|
1360
1556
|
pluginConfig.getSecretsNamespace()
|
|
1361
1557
|
), [branchId, setBranchId] = useState(void 0);
|
|
1362
1558
|
useEffect(() => {
|
|
1363
1559
|
downloadStatusRef.current = downloadStatus;
|
|
1364
1560
|
}, [downloadStatus]);
|
|
1365
|
-
const
|
|
1561
|
+
const getVersionId = useCallback(
|
|
1562
|
+
(document2) => uploadedVersions.get(getDocumentPublishedId(document2)) ?? document2._rev,
|
|
1563
|
+
[uploadedVersions]
|
|
1564
|
+
), fetchDocuments = useCallback(async () => {
|
|
1366
1565
|
setLoadingDocuments(!0);
|
|
1367
1566
|
try {
|
|
1368
1567
|
if (singleDocument) {
|
|
@@ -1434,7 +1633,7 @@ const getLocales = async (_secrets) => pluginConfig.getLocales().map((locale) =>
|
|
|
1434
1633
|
const availableLocaleIds = locales.filter((locale) => locale.enabled !== !1).map((locale) => locale.localeId), transformedDocuments = documents.map((doc) => {
|
|
1435
1634
|
const { [pluginConfig.getLanguageField()]: _2, ...cleanDoc } = doc, baseLanguage = pluginConfig.getSourceLocale();
|
|
1436
1635
|
try {
|
|
1437
|
-
const serialized =
|
|
1636
|
+
const serialized = getTranslationStrategy(doc).serialize(
|
|
1438
1637
|
cleanDoc,
|
|
1439
1638
|
schema,
|
|
1440
1639
|
baseLanguage
|
|
@@ -1451,7 +1650,11 @@ const getLocales = async (_secrets) => pluginConfig.getLocales().map((locale) =>
|
|
|
1451
1650
|
}
|
|
1452
1651
|
return null;
|
|
1453
1652
|
}).filter((doc) => doc !== null), uploadResult = await uploadFiles(transformedDocuments, secrets);
|
|
1454
|
-
await initProject(uploadResult, { timeout: 600 }, secrets), await createJobs(uploadResult, availableLocaleIds, secrets)
|
|
1653
|
+
await initProject(uploadResult, { timeout: 600 }, secrets), await createJobs(uploadResult, availableLocaleIds, secrets);
|
|
1654
|
+
const nextUploadedVersions = new Map(uploadedVersions);
|
|
1655
|
+
for (const { info } of transformedDocuments)
|
|
1656
|
+
info.versionId && nextUploadedVersions.set(info.documentId, info.versionId);
|
|
1657
|
+
writeUploadedVersions(uploadedVersionsStorageKey, nextUploadedVersions), setUploadedVersions(nextUploadedVersions), toast.push({
|
|
1455
1658
|
title: `Translation tasks created for ${documents.length} documents`,
|
|
1456
1659
|
status: "success",
|
|
1457
1660
|
closable: !0
|
|
@@ -1466,7 +1669,14 @@ const getLocales = async (_secrets) => pluginConfig.getLocales().map((locale) =>
|
|
|
1466
1669
|
setIsBusy(!1);
|
|
1467
1670
|
}
|
|
1468
1671
|
}
|
|
1469
|
-
}, [
|
|
1672
|
+
}, [
|
|
1673
|
+
secrets,
|
|
1674
|
+
documents,
|
|
1675
|
+
locales,
|
|
1676
|
+
schema,
|
|
1677
|
+
uploadedVersions,
|
|
1678
|
+
uploadedVersionsStorageKey
|
|
1679
|
+
]), handleImportAll = useCallback(async () => {
|
|
1470
1680
|
if (!(!secrets || documents.length === 0 || !branchId)) {
|
|
1471
1681
|
setIsBusy(!0);
|
|
1472
1682
|
try {
|
|
@@ -1652,7 +1862,7 @@ const getLocales = async (_secrets) => pluginConfig.getLocales().map((locale) =>
|
|
|
1652
1862
|
for (const localeId of availableLocaleIds) {
|
|
1653
1863
|
const documentId = getDocumentPublishedId(doc);
|
|
1654
1864
|
fileQueryData.push({
|
|
1655
|
-
versionId: doc
|
|
1865
|
+
versionId: getVersionId(doc),
|
|
1656
1866
|
fileId: documentId,
|
|
1657
1867
|
branchId,
|
|
1658
1868
|
locale: localeId
|
|
@@ -1667,7 +1877,7 @@ const getLocales = async (_secrets) => pluginConfig.getLocales().map((locale) =>
|
|
|
1667
1877
|
const newStatuses = /* @__PURE__ */ new Map();
|
|
1668
1878
|
for (const doc of documents)
|
|
1669
1879
|
for (const localeId of availableLocaleIds) {
|
|
1670
|
-
const documentId = getDocumentPublishedId(doc), versionId = doc
|
|
1880
|
+
const documentId = getDocumentPublishedId(doc), versionId = getVersionId(doc), key = createTranslationStatusKey(
|
|
1671
1881
|
branchId,
|
|
1672
1882
|
documentId,
|
|
1673
1883
|
versionId,
|
|
@@ -1710,7 +1920,7 @@ const getLocales = async (_secrets) => pluginConfig.getLocales().map((locale) =>
|
|
|
1710
1920
|
setIsRefreshing(!1);
|
|
1711
1921
|
}
|
|
1712
1922
|
}
|
|
1713
|
-
}, [secrets, documents, locales, branchId]), handleImportDocument = useCallback(
|
|
1923
|
+
}, [secrets, documents, locales, branchId, getVersionId]), handleImportDocument = useCallback(
|
|
1714
1924
|
async (documentId, versionId, localeId) => {
|
|
1715
1925
|
if (!secrets) return;
|
|
1716
1926
|
const key = createTranslationStatusKey(
|
|
@@ -1972,6 +2182,7 @@ const getLocales = async (_secrets) => pluginConfig.getLocales().map((locale) =>
|
|
|
1972
2182
|
loadingSecrets,
|
|
1973
2183
|
secrets,
|
|
1974
2184
|
branchId,
|
|
2185
|
+
getVersionId,
|
|
1975
2186
|
// Actions
|
|
1976
2187
|
setLocales,
|
|
1977
2188
|
setAutoRefresh,
|
|
@@ -3258,7 +3469,8 @@ const WrapText = dt(Box)`
|
|
|
3258
3469
|
autoPatchReferences,
|
|
3259
3470
|
setAutoPatchReferences,
|
|
3260
3471
|
autoPublish,
|
|
3261
|
-
setAutoPublish
|
|
3472
|
+
setAutoPublish,
|
|
3473
|
+
getVersionId
|
|
3262
3474
|
} = useTranslations(), [isImporting, setIsImporting] = useState(!1), [isPublishing, setIsPublishing] = useState(!1), toast = useToast(), document2 = documents[0], currentDocumentLanguage = useMemo(() => {
|
|
3263
3475
|
if (!document2) return null;
|
|
3264
3476
|
const languageField = pluginConfig.getLanguageField();
|
|
@@ -3268,15 +3480,15 @@ const WrapText = dt(Box)`
|
|
|
3268
3480
|
return locales.filter(
|
|
3269
3481
|
(locale) => locale.enabled !== !1 && locale.localeId !== sourceLocale
|
|
3270
3482
|
);
|
|
3271
|
-
}, [locales]), documentId = useMemo(() => document2 ? getDocumentPublishedId(document2) : null, [document2]), handleImportTranslations = useCallback(
|
|
3483
|
+
}, [locales]), documentId = useMemo(() => document2 ? getDocumentPublishedId(document2) : null, [document2]), versionId = useMemo(() => document2 ? getVersionId(document2) : null, [document2, getVersionId]), handleImportTranslations = useCallback(
|
|
3272
3484
|
async (options = {}) => {
|
|
3273
3485
|
const { autoOnly = !1 } = options;
|
|
3274
|
-
if (isImporting || !documentId || autoOnly && !autoImport) return;
|
|
3486
|
+
if (isImporting || !documentId || !versionId || autoOnly && !autoImport) return;
|
|
3275
3487
|
const readyTranslations = availableLocales.filter((locale) => {
|
|
3276
3488
|
const key = createTranslationStatusKey(
|
|
3277
3489
|
branchId,
|
|
3278
3490
|
documentId,
|
|
3279
|
-
|
|
3491
|
+
versionId,
|
|
3280
3492
|
locale.localeId
|
|
3281
3493
|
);
|
|
3282
3494
|
return translationStatuses.get(key)?.isReady && !importedTranslations.has(key);
|
|
@@ -3284,11 +3496,9 @@ const WrapText = dt(Box)`
|
|
|
3284
3496
|
if (readyTranslations.length !== 0) {
|
|
3285
3497
|
setIsImporting(!0);
|
|
3286
3498
|
try {
|
|
3287
|
-
|
|
3288
|
-
|
|
3289
|
-
|
|
3290
|
-
)
|
|
3291
|
-
), autoPatchReferences && await handlePatchDocumentReferences(), autoPublish && await handlePublishAllTranslations();
|
|
3499
|
+
for (const locale of readyTranslations)
|
|
3500
|
+
await handleImportDocument(documentId, versionId, locale.localeId);
|
|
3501
|
+
autoPatchReferences && await handlePatchDocumentReferences(), autoPublish && await handlePublishAllTranslations();
|
|
3292
3502
|
} finally {
|
|
3293
3503
|
setIsImporting(!1);
|
|
3294
3504
|
}
|
|
@@ -3298,6 +3508,7 @@ const WrapText = dt(Box)`
|
|
|
3298
3508
|
autoImport,
|
|
3299
3509
|
isImporting,
|
|
3300
3510
|
documentId,
|
|
3511
|
+
versionId,
|
|
3301
3512
|
availableLocales,
|
|
3302
3513
|
translationStatuses,
|
|
3303
3514
|
importedTranslations,
|
|
@@ -3372,7 +3583,7 @@ const WrapText = dt(Box)`
|
|
|
3372
3583
|
}
|
|
3373
3584
|
)
|
|
3374
3585
|
] }),
|
|
3375
|
-
documentId && availableLocales.length > 0 && /* @__PURE__ */ jsxs(Stack, { space: 4, children: [
|
|
3586
|
+
documentId && versionId && availableLocales.length > 0 && /* @__PURE__ */ jsxs(Stack, { space: 4, children: [
|
|
3376
3587
|
/* @__PURE__ */ jsxs(Flex, { align: "center", justify: "space-between", children: [
|
|
3377
3588
|
/* @__PURE__ */ jsx(Text, { as: "h2", weight: "semibold", size: 2, children: "Translation Status" }),
|
|
3378
3589
|
/* @__PURE__ */ jsxs(Flex, { gap: 3, align: "center", children: [
|
|
@@ -3402,7 +3613,7 @@ const WrapText = dt(Box)`
|
|
|
3402
3613
|
const key = createTranslationStatusKey(
|
|
3403
3614
|
branchId,
|
|
3404
3615
|
documentId,
|
|
3405
|
-
|
|
3616
|
+
versionId,
|
|
3406
3617
|
locale.localeId
|
|
3407
3618
|
), status = translationStatuses.get(key), progress = status?.progress || 0, isImported = importedTranslations.has(key);
|
|
3408
3619
|
return /* @__PURE__ */ jsx(
|
|
@@ -3414,7 +3625,7 @@ const WrapText = dt(Box)`
|
|
|
3414
3625
|
importFile: async () => {
|
|
3415
3626
|
!isImported && status?.isReady && await handleImportDocument(
|
|
3416
3627
|
documentId,
|
|
3417
|
-
|
|
3628
|
+
versionId,
|
|
3418
3629
|
locale.localeId
|
|
3419
3630
|
);
|
|
3420
3631
|
}
|
|
@@ -3437,7 +3648,7 @@ const WrapText = dt(Box)`
|
|
|
3437
3648
|
const key = createTranslationStatusKey(
|
|
3438
3649
|
branchId,
|
|
3439
3650
|
documentId,
|
|
3440
|
-
|
|
3651
|
+
versionId,
|
|
3441
3652
|
locale.localeId
|
|
3442
3653
|
);
|
|
3443
3654
|
return !translationStatuses.get(key)?.isReady || importedTranslations.has(key);
|
|
@@ -3464,7 +3675,7 @@ const WrapText = dt(Box)`
|
|
|
3464
3675
|
const key = createTranslationStatusKey(
|
|
3465
3676
|
branchId,
|
|
3466
3677
|
documentId,
|
|
3467
|
-
|
|
3678
|
+
versionId,
|
|
3468
3679
|
locale.localeId
|
|
3469
3680
|
);
|
|
3470
3681
|
return importedTranslations.has(key);
|
|
@@ -3474,7 +3685,7 @@ const WrapText = dt(Box)`
|
|
|
3474
3685
|
const key = createTranslationStatusKey(
|
|
3475
3686
|
branchId,
|
|
3476
3687
|
documentId,
|
|
3477
|
-
|
|
3688
|
+
versionId,
|
|
3478
3689
|
locale.localeId
|
|
3479
3690
|
);
|
|
3480
3691
|
return translationStatuses.get(key)?.isReady;
|
|
@@ -3562,9 +3773,6 @@ const WrapText = dt(Box)`
|
|
|
3562
3773
|
/* @__PURE__ */ jsx("code", { children: pluginConfig.getSourceLocale() }),
|
|
3563
3774
|
" documents."
|
|
3564
3775
|
] }) });
|
|
3565
|
-
}, TranslationTab = (props) => {
|
|
3566
|
-
const { displayed } = props.document;
|
|
3567
|
-
return /* @__PURE__ */ jsx(BaseTranslationWrapper, { showContainer: !1, children: /* @__PURE__ */ jsx(TranslationsProvider, { singleDocument: displayed, children: /* @__PURE__ */ jsx(TranslationView, {}) }) });
|
|
3568
3776
|
}, translateAction = (props) => {
|
|
3569
3777
|
const [dialogOpen, setDialogOpen] = useState(!1), document2 = props.draft || props.published;
|
|
3570
3778
|
return {
|
|
@@ -3579,7 +3787,228 @@ const WrapText = dt(Box)`
|
|
|
3579
3787
|
content: /* @__PURE__ */ jsx(BaseTranslationWrapper, { showContainer: !1, children: /* @__PURE__ */ jsx(TranslationsProvider, { singleDocument: document2, children: /* @__PURE__ */ jsx(TranslationView, {}) }) })
|
|
3580
3788
|
} : null
|
|
3581
3789
|
};
|
|
3582
|
-
}
|
|
3790
|
+
};
|
|
3791
|
+
function readGTOptions(schemaType) {
|
|
3792
|
+
return schemaType?.options?.gtInternationalizedArray;
|
|
3793
|
+
}
|
|
3794
|
+
function toneFromValidation(validation) {
|
|
3795
|
+
if (validation?.length) {
|
|
3796
|
+
if (validation.some((marker) => marker.level === "error")) return "critical";
|
|
3797
|
+
if (validation.some((marker) => marker.level === "warning")) return "caution";
|
|
3798
|
+
}
|
|
3799
|
+
}
|
|
3800
|
+
function RemoveLocaleButton(props) {
|
|
3801
|
+
const { isSource, readOnly, onRemove } = props, button = (
|
|
3802
|
+
// The span keeps the tooltip working when the button is disabled
|
|
3803
|
+
// (disabled buttons don't emit pointer events).
|
|
3804
|
+
/* @__PURE__ */ jsx("span", { style: { paddingBottom: "2px" }, children: /* @__PURE__ */ jsx(
|
|
3805
|
+
Button,
|
|
3806
|
+
{
|
|
3807
|
+
mode: "bleed",
|
|
3808
|
+
icon: RemoveCircleIcon,
|
|
3809
|
+
tone: "critical",
|
|
3810
|
+
disabled: readOnly || isSource,
|
|
3811
|
+
onClick: onRemove
|
|
3812
|
+
}
|
|
3813
|
+
) })
|
|
3814
|
+
);
|
|
3815
|
+
return isSource ? /* @__PURE__ */ jsx(
|
|
3816
|
+
Tooltip,
|
|
3817
|
+
{
|
|
3818
|
+
animate: !0,
|
|
3819
|
+
portal: !0,
|
|
3820
|
+
placement: "top",
|
|
3821
|
+
fallbackPlacements: ["right", "left"],
|
|
3822
|
+
content: /* @__PURE__ */ jsx(Text, { muted: !0, size: 1, children: "Can't remove the source language" }),
|
|
3823
|
+
children: button
|
|
3824
|
+
}
|
|
3825
|
+
) : button;
|
|
3826
|
+
}
|
|
3827
|
+
function InternationalizedValueItem(props) {
|
|
3828
|
+
const { inputProps, parentSchemaType, schemaType, validation } = props, value = props.value, { onChange } = inputProps, gtOptions = readGTOptions(schemaType) ?? readGTOptions(parentSchemaType), language = value?.language, languageLabel = language ? gtOptions?.titles?.[language] ?? language : "Unknown language", isSource = !!language && language === gtOptions?.sourceLocale, wrappedOnChange = useCallback(
|
|
3829
|
+
(patch) => {
|
|
3830
|
+
if (!Array.isArray(patch)) {
|
|
3831
|
+
onChange(patch);
|
|
3832
|
+
return;
|
|
3833
|
+
}
|
|
3834
|
+
const currentValue = value?.value;
|
|
3835
|
+
if (!((currentValue == null || Array.isArray(currentValue) && currentValue.length === 0) && patch.some(
|
|
3836
|
+
(p) => p.type === "insert" && Array.isArray(p.path) && p.path.length > 0 && (p.path[0] === "value" || typeof p.path[0] == "number")
|
|
3837
|
+
))) {
|
|
3838
|
+
onChange(patch);
|
|
3839
|
+
return;
|
|
3840
|
+
}
|
|
3841
|
+
const rerooted = patch.map(
|
|
3842
|
+
(p) => p.type === "insert" && Array.isArray(p.path) && p.path[0] !== "value" ? { ...p, path: ["value", ...p.path] } : p
|
|
3843
|
+
);
|
|
3844
|
+
onChange(
|
|
3845
|
+
currentValue === void 0 ? [setIfMissing([], ["value"]), ...rerooted] : rerooted
|
|
3846
|
+
);
|
|
3847
|
+
},
|
|
3848
|
+
[onChange, value?.value]
|
|
3849
|
+
), members = useMemo(
|
|
3850
|
+
() => inputProps.members.filter(
|
|
3851
|
+
(member) => member.kind === "field" && member.name === "value"
|
|
3852
|
+
).map((member) => ({
|
|
3853
|
+
...member,
|
|
3854
|
+
field: {
|
|
3855
|
+
...member.field,
|
|
3856
|
+
schemaType: {
|
|
3857
|
+
...member.field.schemaType,
|
|
3858
|
+
title: /* @__PURE__ */ jsx(Label, { muted: !0, size: 1, children: languageLabel })
|
|
3859
|
+
}
|
|
3860
|
+
}
|
|
3861
|
+
})),
|
|
3862
|
+
[inputProps.members, languageLabel]
|
|
3863
|
+
), handleRemove = useCallback(() => {
|
|
3864
|
+
onChange(unset());
|
|
3865
|
+
}, [onChange]);
|
|
3866
|
+
return /* @__PURE__ */ jsx(Card, { paddingTop: 2, tone: toneFromValidation(validation), children: /* @__PURE__ */ jsxs(Flex, { align: "flex-end", gap: 2, children: [
|
|
3867
|
+
/* @__PURE__ */ jsx(Box, { flex: 1, children: inputProps.renderInput({
|
|
3868
|
+
...inputProps,
|
|
3869
|
+
members,
|
|
3870
|
+
onChange: wrappedOnChange
|
|
3871
|
+
// renderInput's parameter is typed as the base InputProps, which
|
|
3872
|
+
// doesn't know about object members.
|
|
3873
|
+
}) }),
|
|
3874
|
+
/* @__PURE__ */ jsx(
|
|
3875
|
+
RemoveLocaleButton,
|
|
3876
|
+
{
|
|
3877
|
+
isSource,
|
|
3878
|
+
readOnly: !!inputProps.readOnly,
|
|
3879
|
+
onRemove: handleRemove
|
|
3880
|
+
}
|
|
3881
|
+
)
|
|
3882
|
+
] }) });
|
|
3883
|
+
}
|
|
3884
|
+
function InternationalizedArrayInput(props) {
|
|
3885
|
+
const { members, schemaType, value, readOnly, onItemAppend } = props, gtOptions = readGTOptions(schemaType), itemType = schemaType.of[0]?.name, handleAdd = useCallback(
|
|
3886
|
+
(language) => {
|
|
3887
|
+
const item = { _key: randomKey(), _type: itemType, language };
|
|
3888
|
+
onItemAppend(item);
|
|
3889
|
+
},
|
|
3890
|
+
[onItemAppend, itemType]
|
|
3891
|
+
);
|
|
3892
|
+
if (!gtOptions)
|
|
3893
|
+
return props.renderDefault(props);
|
|
3894
|
+
const allLocales = [gtOptions.sourceLocale, ...gtOptions.locales], existing = new Set(
|
|
3895
|
+
(value ?? []).map((item) => item.language).filter(Boolean)
|
|
3896
|
+
), missing = allLocales.filter((locale) => !existing.has(locale));
|
|
3897
|
+
return /* @__PURE__ */ jsxs(Stack, { space: 3, children: [
|
|
3898
|
+
members.length > 0 ? /* @__PURE__ */ jsx(Stack, { space: 2, children: members.map(
|
|
3899
|
+
(member) => member.kind === "item" ? /* @__PURE__ */ jsx(ArrayOfObjectsItem, { ...props, member }, member.key) : /* @__PURE__ */ jsx(MemberItemError, { member }, member.key)
|
|
3900
|
+
) }) : /* @__PURE__ */ jsx(Card, { border: !0, tone: "transparent", padding: 3, radius: 2, children: /* @__PURE__ */ jsx(Text, { size: 1, muted: !0, children: "This internationalized field currently has no translations." }) }),
|
|
3901
|
+
!readOnly && missing.length > 0 ? /* @__PURE__ */ jsx(Grid, { columns: Math.min(missing.length, 4), gap: 1, children: missing.map((locale) => /* @__PURE__ */ jsx(
|
|
3902
|
+
Button,
|
|
3903
|
+
{
|
|
3904
|
+
icon: AddIcon,
|
|
3905
|
+
mode: "ghost",
|
|
3906
|
+
text: gtOptions.titles?.[locale] ?? locale,
|
|
3907
|
+
onClick: () => handleAdd(locale)
|
|
3908
|
+
},
|
|
3909
|
+
locale
|
|
3910
|
+
)) }) : null
|
|
3911
|
+
] });
|
|
3912
|
+
}
|
|
3913
|
+
const DEFAULT_TYPE_PREFIX = "internationalizedArray";
|
|
3914
|
+
function resolveComponents(overrides) {
|
|
3915
|
+
const input = overrides?.input === !1 ? void 0 : overrides?.input ?? InternationalizedArrayInput, item = overrides?.item === !1 ? void 0 : overrides?.item ?? InternationalizedValueItem, defaultField = input === InternationalizedArrayInput ? (
|
|
3916
|
+
// Reset the field level so inline per-locale inputs don't inherit
|
|
3917
|
+
// nested-object indentation.
|
|
3918
|
+
((fieldProps) => fieldProps.renderDefault({ ...fieldProps, level: 0 }))
|
|
3919
|
+
) : void 0, field = overrides?.field === !1 ? void 0 : overrides?.field ?? defaultField;
|
|
3920
|
+
return { input, item, field };
|
|
3921
|
+
}
|
|
3922
|
+
function capitalize(value) {
|
|
3923
|
+
return value.charAt(0).toUpperCase() + value.slice(1);
|
|
3924
|
+
}
|
|
3925
|
+
function fieldTypeName(fieldType) {
|
|
3926
|
+
return typeof fieldType == "string" ? fieldType : fieldType.name;
|
|
3927
|
+
}
|
|
3928
|
+
function valueField(fieldType) {
|
|
3929
|
+
if (fieldType === "string" || fieldType === "text")
|
|
3930
|
+
return { name: "value", type: fieldType, title: "Value" };
|
|
3931
|
+
if (fieldType === "block")
|
|
3932
|
+
return {
|
|
3933
|
+
name: "value",
|
|
3934
|
+
type: "array",
|
|
3935
|
+
title: "Value",
|
|
3936
|
+
of: [{ type: "block" }]
|
|
3937
|
+
};
|
|
3938
|
+
const { name: _name, ...rest } = fieldType;
|
|
3939
|
+
return { name: "value", title: "Value", ...rest };
|
|
3940
|
+
}
|
|
3941
|
+
function makeLanguageTitle(options) {
|
|
3942
|
+
const { languageTitles, getLanguageTitle, sourceLocale } = options;
|
|
3943
|
+
return (locale) => getLanguageTitle?.(locale) ?? languageTitles?.[locale] ?? getLocaleProperties(locale, sourceLocale).name ?? locale;
|
|
3944
|
+
}
|
|
3945
|
+
function buildTypesForPrefix(prefix2, options) {
|
|
3946
|
+
const { sourceLocale, locales, fieldTypes } = options, languageTitle = makeLanguageTitle(options), components = resolveComponents(options.components);
|
|
3947
|
+
return fieldTypes.map((fieldType) => {
|
|
3948
|
+
const typeName = `${prefix2}${capitalize(fieldTypeName(fieldType))}`, valueTypeName = `${typeName}Value`, gtInternationalizedArray = {
|
|
3949
|
+
sourceLocale,
|
|
3950
|
+
locales,
|
|
3951
|
+
titles: Object.fromEntries(
|
|
3952
|
+
[sourceLocale, ...locales].map((locale) => [
|
|
3953
|
+
locale,
|
|
3954
|
+
languageTitle(locale)
|
|
3955
|
+
])
|
|
3956
|
+
)
|
|
3957
|
+
}, valueObject = {
|
|
3958
|
+
type: "object",
|
|
3959
|
+
name: valueTypeName,
|
|
3960
|
+
...components.item ? { components: { item: components.item } } : {},
|
|
3961
|
+
fields: [
|
|
3962
|
+
{
|
|
3963
|
+
name: "language",
|
|
3964
|
+
type: "string",
|
|
3965
|
+
title: "Language",
|
|
3966
|
+
readOnly: !0,
|
|
3967
|
+
// GT's inline item shows the locale as the value field's label;
|
|
3968
|
+
// with a custom or detached item, keep the field visible so the
|
|
3969
|
+
// locale is still discoverable in default/dialog rendering.
|
|
3970
|
+
hidden: components.item === InternationalizedValueItem
|
|
3971
|
+
},
|
|
3972
|
+
valueField(fieldType)
|
|
3973
|
+
],
|
|
3974
|
+
options: { gtInternationalizedArray },
|
|
3975
|
+
preview: {
|
|
3976
|
+
select: { language: "language", value: "value" },
|
|
3977
|
+
prepare(selection) {
|
|
3978
|
+
const localeLabel = selection.language ? languageTitle(selection.language) : "Unknown";
|
|
3979
|
+
return {
|
|
3980
|
+
title: typeof selection.value == "string" ? selection.value : localeLabel,
|
|
3981
|
+
subtitle: localeLabel
|
|
3982
|
+
};
|
|
3983
|
+
}
|
|
3984
|
+
}
|
|
3985
|
+
}, arrayComponents = {
|
|
3986
|
+
...components.input ? { input: components.input } : {},
|
|
3987
|
+
...components.field ? { field: components.field } : {}
|
|
3988
|
+
};
|
|
3989
|
+
return {
|
|
3990
|
+
name: typeName,
|
|
3991
|
+
type: "array",
|
|
3992
|
+
...Object.keys(arrayComponents).length ? { components: arrayComponents } : {},
|
|
3993
|
+
of: [valueObject],
|
|
3994
|
+
options: { gtInternationalizedArray }
|
|
3995
|
+
};
|
|
3996
|
+
});
|
|
3997
|
+
}
|
|
3998
|
+
function createInternationalizedArrayTypes(options) {
|
|
3999
|
+
const prefix2 = options.typePrefix ?? DEFAULT_TYPE_PREFIX, includeCompatibilityTypes = options.includeCompatibilityTypes ?? !0, types = buildTypesForPrefix(prefix2, options);
|
|
4000
|
+
return includeCompatibilityTypes && prefix2 !== DEFAULT_TYPE_PREFIX && types.push(...buildTypesForPrefix(DEFAULT_TYPE_PREFIX, options)), types;
|
|
4001
|
+
}
|
|
4002
|
+
function resolveFieldLevelConfig(config) {
|
|
4003
|
+
return {
|
|
4004
|
+
enabled: !1,
|
|
4005
|
+
fieldTypes: ["string", "text"],
|
|
4006
|
+
typePrefix: DEFAULT_TYPE_PREFIX,
|
|
4007
|
+
includeCompatibilityTypes: !0,
|
|
4008
|
+
...config
|
|
4009
|
+
};
|
|
4010
|
+
}
|
|
4011
|
+
const TranslationsTable = () => {
|
|
3583
4012
|
const {
|
|
3584
4013
|
documents,
|
|
3585
4014
|
locales,
|
|
@@ -3588,7 +4017,8 @@ const WrapText = dt(Box)`
|
|
|
3588
4017
|
downloadStatus,
|
|
3589
4018
|
importedTranslations,
|
|
3590
4019
|
handleImportDocument,
|
|
3591
|
-
branchId
|
|
4020
|
+
branchId,
|
|
4021
|
+
getVersionId
|
|
3592
4022
|
} = useTranslations();
|
|
3593
4023
|
return loadingDocuments ? /* @__PURE__ */ jsx(Flex, { align: "center", justify: "center", padding: 4, children: /* @__PURE__ */ jsx(Spinner, {}) }) : /* @__PURE__ */ jsx(Box, { style: { maxHeight: "60vh", overflowY: "auto" }, children: /* @__PURE__ */ jsx(Stack, { space: 2, children: documents.map((document2) => /* @__PURE__ */ jsx(Card, { shadow: 1, padding: 3, children: /* @__PURE__ */ jsxs(Stack, { space: 3, children: [
|
|
3594
4024
|
/* @__PURE__ */ jsx(Flex, { justify: "space-between", align: "flex-start", children: /* @__PURE__ */ jsxs(Box, { flex: 1, children: [
|
|
@@ -3596,10 +4026,10 @@ const WrapText = dt(Box)`
|
|
|
3596
4026
|
/* @__PURE__ */ jsx(Text, { size: 0, muted: !0, style: { marginTop: "2px" }, children: document2._type })
|
|
3597
4027
|
] }) }),
|
|
3598
4028
|
/* @__PURE__ */ jsx(Stack, { space: 2, children: locales.length > 0 ? locales.filter((locale) => locale.enabled !== !1).map((locale) => {
|
|
3599
|
-
const documentId = getDocumentPublishedId(document2), key = createTranslationStatusKey(
|
|
4029
|
+
const documentId = getDocumentPublishedId(document2), versionId = getVersionId(document2), key = createTranslationStatusKey(
|
|
3600
4030
|
branchId,
|
|
3601
4031
|
documentId,
|
|
3602
|
-
|
|
4032
|
+
versionId,
|
|
3603
4033
|
locale.localeId
|
|
3604
4034
|
), status = translationStatuses.get(key), isDownloaded = downloadStatus.downloaded.has(key), isImported = importedTranslations.has(key);
|
|
3605
4035
|
return /* @__PURE__ */ jsx(
|
|
@@ -3611,12 +4041,12 @@ const WrapText = dt(Box)`
|
|
|
3611
4041
|
importFile: async () => {
|
|
3612
4042
|
await handleImportDocument(
|
|
3613
4043
|
documentId,
|
|
3614
|
-
|
|
4044
|
+
versionId,
|
|
3615
4045
|
locale.localeId
|
|
3616
4046
|
);
|
|
3617
4047
|
}
|
|
3618
4048
|
},
|
|
3619
|
-
`${document2._id}-${
|
|
4049
|
+
`${document2._id}-${versionId}-${locale.localeId}`
|
|
3620
4050
|
);
|
|
3621
4051
|
}) : /* @__PURE__ */ jsx(Text, { size: 1, muted: !0, children: "No locales configured" }) })
|
|
3622
4052
|
] }) }, document2._id)) }) });
|
|
@@ -3970,7 +4400,10 @@ const WrapText = dt(Box)`
|
|
|
3970
4400
|
}
|
|
3971
4401
|
)
|
|
3972
4402
|
] });
|
|
3973
|
-
}, TranslationsTool = () => /* @__PURE__ */ jsx(BaseTranslationWrapper, { showContainer: !1, children: /* @__PURE__ */ jsx(TranslationsProvider, { children: /* @__PURE__ */ jsx(TranslationsToolContent, {}) }) }),
|
|
4403
|
+
}, TranslationsTool = () => /* @__PURE__ */ jsx(BaseTranslationWrapper, { showContainer: !1, children: /* @__PURE__ */ jsx(TranslationsProvider, { children: /* @__PURE__ */ jsx(TranslationsToolContent, {}) }) }), TranslationTab = (props) => {
|
|
4404
|
+
const { displayed } = props.document;
|
|
4405
|
+
return /* @__PURE__ */ jsx(BaseTranslationWrapper, { showContainer: !1, children: /* @__PURE__ */ jsx(TranslationsProvider, { singleDocument: displayed, children: /* @__PURE__ */ jsx(TranslationView, {}) }) });
|
|
4406
|
+
}, gtPlugin = definePlugin(
|
|
3974
4407
|
({
|
|
3975
4408
|
languageField = "language",
|
|
3976
4409
|
sourceLocale,
|
|
@@ -3990,11 +4423,16 @@ const WrapText = dt(Box)`
|
|
|
3990
4423
|
additionalSerializers = {},
|
|
3991
4424
|
additionalDeserializers = {},
|
|
3992
4425
|
additionalBlockDeserializers = [],
|
|
3993
|
-
showDocumentInternationalization = !0
|
|
4426
|
+
showDocumentInternationalization = !0,
|
|
4427
|
+
internationalizedArray,
|
|
4428
|
+
fieldLevelLocalization,
|
|
4429
|
+
translationLevel = "document",
|
|
4430
|
+
fieldLevelDocuments
|
|
3994
4431
|
}) => {
|
|
3995
|
-
const resolvedSourceLocale = sourceLocale ?? defaultLocale ?? libraryDefaultLocale
|
|
3996
|
-
|
|
3997
|
-
|
|
4432
|
+
const resolvedSourceLocale = sourceLocale ?? defaultLocale ?? libraryDefaultLocale, normalizeFilters = (entries) => entries?.map((entry) => typeof entry == "string" ? { type: entry } : entry).filter((filter2) => filter2.documentId || filter2.type), normalizedTranslateDocuments = normalizeFilters(translateDocuments), normalizedFieldLevelDocuments = normalizeFilters(fieldLevelDocuments) ?? [], fieldLevelConfig = resolveFieldLevelConfig(
|
|
4433
|
+
internationalizedArray ?? fieldLevelLocalization
|
|
4434
|
+
);
|
|
4435
|
+
pluginConfig.init(
|
|
3998
4436
|
secretsNamespace,
|
|
3999
4437
|
languageField,
|
|
4000
4438
|
resolvedSourceLocale,
|
|
@@ -4009,17 +4447,22 @@ const WrapText = dt(Box)`
|
|
|
4009
4447
|
additionalStopTypes,
|
|
4010
4448
|
additionalSerializers,
|
|
4011
4449
|
additionalDeserializers,
|
|
4012
|
-
additionalBlockDeserializers
|
|
4450
|
+
additionalBlockDeserializers,
|
|
4451
|
+
translationLevel,
|
|
4452
|
+
normalizedFieldLevelDocuments,
|
|
4453
|
+
fieldLevelConfig.typePrefix
|
|
4013
4454
|
), gt.setConfig({
|
|
4014
4455
|
sourceLocale: resolvedSourceLocale,
|
|
4015
4456
|
customMapping,
|
|
4016
4457
|
apiKey,
|
|
4017
4458
|
projectId
|
|
4018
4459
|
});
|
|
4460
|
+
const arrayLocalizedTypes = /* @__PURE__ */ new Set();
|
|
4461
|
+
translationLevel === "internationalizedArray" ? normalizedTranslateDocuments?.map((filter2) => filter2.type).filter((type) => !!type).forEach((type) => arrayLocalizedTypes.add(type)) : translationLevel === "mixed" && normalizedFieldLevelDocuments.map((filter2) => filter2.type).filter((type) => !!type).forEach((type) => arrayLocalizedTypes.add(type));
|
|
4019
4462
|
const plugins = [];
|
|
4020
4463
|
if (showDocumentInternationalization) {
|
|
4021
|
-
const
|
|
4022
|
-
if (
|
|
4464
|
+
const schemaTypes2 = normalizedTranslateDocuments?.map((filter2) => filter2.type).filter((type) => !!type).filter((type) => !arrayLocalizedTypes.has(type)) ?? [];
|
|
4465
|
+
if (schemaTypes2.length > 0) {
|
|
4023
4466
|
const supportedLanguages = [resolvedSourceLocale, ...locales].map((locale) => {
|
|
4024
4467
|
const props = getLocaleProperties(locale, resolvedSourceLocale);
|
|
4025
4468
|
return { id: locale, title: props.name };
|
|
@@ -4027,15 +4470,28 @@ const WrapText = dt(Box)`
|
|
|
4027
4470
|
plugins.push(
|
|
4028
4471
|
documentInternationalization({
|
|
4029
4472
|
supportedLanguages,
|
|
4030
|
-
schemaTypes,
|
|
4473
|
+
schemaTypes: schemaTypes2,
|
|
4031
4474
|
languageField
|
|
4032
4475
|
})
|
|
4033
4476
|
);
|
|
4034
4477
|
}
|
|
4035
4478
|
}
|
|
4479
|
+
const schemaTypes = fieldLevelConfig.enabled ? createInternationalizedArrayTypes({
|
|
4480
|
+
sourceLocale: resolvedSourceLocale,
|
|
4481
|
+
locales,
|
|
4482
|
+
fieldTypes: fieldLevelConfig.fieldTypes,
|
|
4483
|
+
languageTitles: fieldLevelConfig.languageTitles,
|
|
4484
|
+
getLanguageTitle: fieldLevelConfig.getLanguageTitle,
|
|
4485
|
+
typePrefix: fieldLevelConfig.typePrefix,
|
|
4486
|
+
includeCompatibilityTypes: fieldLevelConfig.includeCompatibilityTypes,
|
|
4487
|
+
components: fieldLevelConfig.components
|
|
4488
|
+
}) : [];
|
|
4036
4489
|
return {
|
|
4037
4490
|
name: "gt-sanity",
|
|
4038
4491
|
plugins,
|
|
4492
|
+
schema: {
|
|
4493
|
+
types: schemaTypes
|
|
4494
|
+
},
|
|
4039
4495
|
tools: [
|
|
4040
4496
|
{
|
|
4041
4497
|
name: "translations",
|
|
@@ -4057,6 +4513,7 @@ export {
|
|
|
4057
4513
|
DocumentInternationalizationMenu,
|
|
4058
4514
|
TranslationTab as TranslationsTab,
|
|
4059
4515
|
attachGTData,
|
|
4516
|
+
createInternationalizedArrayTypes,
|
|
4060
4517
|
customSerializers,
|
|
4061
4518
|
defaultStopTypes,
|
|
4062
4519
|
detachGTData,
|