gt-sanity 3.1.2 → 3.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +87 -1
- package/dist/index.cjs +553 -168
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +43 -0
- package/dist/index.d.ts +43 -0
- package/dist/index.js +555 -170
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/adapter/core.ts +31 -7
- package/src/adapter/types.ts +23 -1
- package/src/components/TranslationsProvider.tsx +149 -65
- package/src/components/page/DebugInfoDialog.tsx +142 -0
- package/src/components/page/TranslationsTable.tsx +7 -2
- package/src/components/page/TranslationsTool.tsx +29 -8
- package/src/components/shared/LanguageStatus.tsx +34 -15
- package/src/components/shared/SingleDocumentView.tsx +7 -2
- package/src/components/tab/TranslationView.tsx +88 -41
- package/src/configuration/baseDocumentLevelConfig/documentLevelPatch.test.ts +84 -0
- package/src/index.ts +40 -3
- package/src/sanity-api/resolveRefs.ts +39 -0
- package/src/structure/__tests__/localizedStructure.test.ts +149 -0
- package/src/structure/localizedStructure.ts +156 -0
- package/src/utils/__tests__/debugInfo.test.ts +114 -0
- package/src/utils/__tests__/languageStatusState.test.ts +61 -0
- package/src/utils/__tests__/translationPreferences.test.ts +102 -0
- package/src/utils/__tests__/translationSummary.test.ts +88 -0
- package/src/utils/debugInfo.ts +116 -0
- package/src/utils/importUtils.ts +15 -6
- package/src/utils/languageStatusState.ts +25 -0
- package/src/utils/translationPreferences.ts +54 -0
- package/src/utils/translationSummary.ts +140 -0
- package/src/components/shared/ProgressBar.tsx +0 -54
package/dist/index.cjs
CHANGED
|
@@ -5,7 +5,16 @@ function _interopDefaultCompat(e) {
|
|
|
5
5
|
return e && typeof e == "object" && "default" in e ? e : { default: e };
|
|
6
6
|
}
|
|
7
7
|
var React__default = /* @__PURE__ */ _interopDefaultCompat(React), JSONPointer__default = /* @__PURE__ */ _interopDefaultCompat(JSONPointer), merge__default = /* @__PURE__ */ _interopDefaultCompat(merge);
|
|
8
|
-
const SECRETS_NAMESPACE = "generaltranslation.secrets", gt = new generaltranslation.GT()
|
|
8
|
+
const SECRETS_NAMESPACE = "generaltranslation.secrets", gt = new generaltranslation.GT(), DEFAULT_TRANSLATION_PREFERENCES = {
|
|
9
|
+
autoRefresh: !0,
|
|
10
|
+
autoImport: !0,
|
|
11
|
+
autoPatchReferences: !1,
|
|
12
|
+
autoPublish: !1,
|
|
13
|
+
// Opt-in: turning this on means Sanity overwrites whatever General
|
|
14
|
+
// Translation holds for that source version, including a completed
|
|
15
|
+
// translation that has not been imported yet.
|
|
16
|
+
preserveExistingTranslations: !1
|
|
17
|
+
};
|
|
9
18
|
function overrideConfig(secrets) {
|
|
10
19
|
gt.setConfig({
|
|
11
20
|
...secrets?.project && { projectId: secrets?.project },
|
|
@@ -29,10 +38,10 @@ class GTConfig {
|
|
|
29
38
|
additionalBlockDeserializers;
|
|
30
39
|
translationLevel;
|
|
31
40
|
fieldLevelDocuments;
|
|
32
|
-
|
|
41
|
+
preferences;
|
|
33
42
|
static instance;
|
|
34
|
-
constructor(secretsNamespace, languageField, sourceLocale, locales, singletons, singletonMapping, ignoreFields, dedupeFields, skipFields, translateDocuments, additionalStopTypes = [], additionalSerializers = {}, additionalDeserializers = { types: {} }, additionalBlockDeserializers = [], translationLevel = "document", fieldLevelDocuments = [],
|
|
35
|
-
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.
|
|
43
|
+
constructor(secretsNamespace, languageField, sourceLocale, locales, singletons, singletonMapping, ignoreFields, dedupeFields, skipFields, translateDocuments, additionalStopTypes = [], additionalSerializers = {}, additionalDeserializers = { types: {} }, additionalBlockDeserializers = [], translationLevel = "document", fieldLevelDocuments = [], preferences = DEFAULT_TRANSLATION_PREFERENCES) {
|
|
44
|
+
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.preferences = preferences;
|
|
36
45
|
}
|
|
37
46
|
static getInstance() {
|
|
38
47
|
return this.instance || (this.instance = new GTConfig(
|
|
@@ -52,8 +61,8 @@ class GTConfig {
|
|
|
52
61
|
[]
|
|
53
62
|
)), this.instance;
|
|
54
63
|
}
|
|
55
|
-
init(secretsNamespace, languageField, sourceLocale, locales, singletons, singletonMapping, ignoreFields, dedupeFields, skipFields, translateDocuments, additionalStopTypes = [], additionalSerializers = {}, additionalDeserializers = { types: {} }, additionalBlockDeserializers = [], translationLevel = "document", fieldLevelDocuments = [],
|
|
56
|
-
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.
|
|
64
|
+
init(secretsNamespace, languageField, sourceLocale, locales, singletons, singletonMapping, ignoreFields, dedupeFields, skipFields, translateDocuments, additionalStopTypes = [], additionalSerializers = {}, additionalDeserializers = { types: {} }, additionalBlockDeserializers = [], translationLevel = "document", fieldLevelDocuments = [], preferences = DEFAULT_TRANSLATION_PREFERENCES) {
|
|
65
|
+
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.preferences = preferences;
|
|
57
66
|
}
|
|
58
67
|
getSecretsNamespace() {
|
|
59
68
|
return this.secretsNamespace;
|
|
@@ -103,8 +112,8 @@ class GTConfig {
|
|
|
103
112
|
getFieldLevelDocuments() {
|
|
104
113
|
return this.fieldLevelDocuments;
|
|
105
114
|
}
|
|
106
|
-
|
|
107
|
-
return this.
|
|
115
|
+
getPreferences() {
|
|
116
|
+
return this.preferences;
|
|
108
117
|
}
|
|
109
118
|
}
|
|
110
119
|
const pluginConfig = GTConfig.getInstance();
|
|
@@ -1564,6 +1573,21 @@ async function resolveRefs(doc, locale, client) {
|
|
|
1564
1573
|
);
|
|
1565
1574
|
return updateDocumentReferences(doc, translatedRefs);
|
|
1566
1575
|
}
|
|
1576
|
+
async function commitResolvedRefs(translatedDoc, resolvedDoc, client) {
|
|
1577
|
+
const {
|
|
1578
|
+
_id: _resolvedId,
|
|
1579
|
+
_rev: _resolvedRev,
|
|
1580
|
+
_createdAt: _resolvedCreatedAt,
|
|
1581
|
+
_updatedAt: _resolvedUpdatedAt,
|
|
1582
|
+
...changes
|
|
1583
|
+
} = resolvedDoc;
|
|
1584
|
+
if (translatedDoc._id.startsWith("drafts.")) {
|
|
1585
|
+
await client.patch(translatedDoc._id).set(changes).commit();
|
|
1586
|
+
return;
|
|
1587
|
+
}
|
|
1588
|
+
const draftId = `drafts.${getPublishedId(translatedDoc._id)}`;
|
|
1589
|
+
await client.transaction().createIfNotExists({ ...translatedDoc, _id: draftId }).patch(draftId, (patch) => patch.set(changes)).commit();
|
|
1590
|
+
}
|
|
1567
1591
|
function findReferences(obj, path = []) {
|
|
1568
1592
|
if (!obj || typeof obj != "object")
|
|
1569
1593
|
return [];
|
|
@@ -1699,13 +1723,14 @@ async function processImportBatch(items, options = {}) {
|
|
|
1699
1723
|
}
|
|
1700
1724
|
async function getReadyFilesForImport(translationStatuses, options = {}) {
|
|
1701
1725
|
const { filterReadyFiles = () => !0 } = options, readyFilesByDocumentLocale = /* @__PURE__ */ new Map();
|
|
1702
|
-
for (const [key, status] of translationStatuses.entries())
|
|
1703
|
-
|
|
1726
|
+
for (const [key, status] of translationStatuses.entries()) {
|
|
1727
|
+
const readyStatus = status.isReady && status.fileData ? { ...status, fileData: status.fileData } : null;
|
|
1728
|
+
if (readyStatus && filterReadyFiles(key, readyStatus)) {
|
|
1704
1729
|
const fileData = {
|
|
1705
|
-
fileId: getPublishedId(
|
|
1706
|
-
versionId:
|
|
1707
|
-
branchId:
|
|
1708
|
-
locale:
|
|
1730
|
+
fileId: getPublishedId(readyStatus.fileData.fileId),
|
|
1731
|
+
versionId: readyStatus.fileData.versionId,
|
|
1732
|
+
branchId: readyStatus.fileData.branchId,
|
|
1733
|
+
locale: readyStatus.fileData.locale
|
|
1709
1734
|
};
|
|
1710
1735
|
readyFilesByDocumentLocale.set(
|
|
1711
1736
|
createStableTranslationKey(
|
|
@@ -1716,6 +1741,7 @@ async function getReadyFilesForImport(translationStatuses, options = {}) {
|
|
|
1716
1741
|
fileData
|
|
1717
1742
|
);
|
|
1718
1743
|
}
|
|
1744
|
+
}
|
|
1719
1745
|
return Array.from(readyFilesByDocumentLocale.values());
|
|
1720
1746
|
}
|
|
1721
1747
|
async function importTranslations(readyFiles, secrets, translationContext, options = {}) {
|
|
@@ -1778,7 +1804,30 @@ const getLocales = async (_secrets) => pluginConfig.getLocales().map((locale) =>
|
|
|
1778
1804
|
localeId: locale,
|
|
1779
1805
|
description: gt.getLocaleProperties(locale).name,
|
|
1780
1806
|
enabled: !0
|
|
1781
|
-
})),
|
|
1807
|
+
})), getPreferencesStorageKey = (projectId, dataset) => `gt-sanity:preferences:${projectId ?? ""}:${dataset ?? ""}`, isPlainRecord$1 = (value) => typeof value == "object" && value !== null && !Array.isArray(value);
|
|
1808
|
+
function readPreferences(storageKey, defaults) {
|
|
1809
|
+
try {
|
|
1810
|
+
const raw = window.localStorage.getItem(storageKey);
|
|
1811
|
+
if (!raw) return defaults;
|
|
1812
|
+
const parsed = JSON.parse(raw);
|
|
1813
|
+
if (!isPlainRecord$1(parsed)) return defaults;
|
|
1814
|
+
const merged = { ...defaults }, keys = Object.keys(defaults);
|
|
1815
|
+
for (const key of keys) {
|
|
1816
|
+
const value = parsed[key];
|
|
1817
|
+
typeof value == "boolean" && (merged[key] = value);
|
|
1818
|
+
}
|
|
1819
|
+
return merged;
|
|
1820
|
+
} catch {
|
|
1821
|
+
return defaults;
|
|
1822
|
+
}
|
|
1823
|
+
}
|
|
1824
|
+
function writePreferences(storageKey, value) {
|
|
1825
|
+
try {
|
|
1826
|
+
window.localStorage.setItem(storageKey, JSON.stringify(value));
|
|
1827
|
+
} catch {
|
|
1828
|
+
}
|
|
1829
|
+
}
|
|
1830
|
+
const TranslationsContext = React.createContext(null), getUploadedVersionsStorageKey = (projectId, dataset) => `gt-sanity:uploadedVersions:${projectId ?? ""}:${dataset ?? ""}`;
|
|
1782
1831
|
function readUploadedVersions(storageKey) {
|
|
1783
1832
|
try {
|
|
1784
1833
|
const raw = window.sessionStorage.getItem(storageKey);
|
|
@@ -1814,7 +1863,7 @@ const useTranslations = () => {
|
|
|
1814
1863
|
children,
|
|
1815
1864
|
singleDocument
|
|
1816
1865
|
}) => {
|
|
1817
|
-
const [isBusy, setIsBusy] = React.useState(!1), [documents, setDocuments] = React.useState([]), [locales, setLocales] = React.useState([]), [
|
|
1866
|
+
const [isBusy, setIsBusy] = React.useState(!1), [documents, setDocuments] = React.useState([]), [locales, setLocales] = React.useState([]), [loadingDocuments, setLoadingDocuments] = React.useState(!1), [importProgress, setImportProgress] = React.useState({
|
|
1818
1867
|
current: 0,
|
|
1819
1868
|
total: 0,
|
|
1820
1869
|
isImporting: !1
|
|
@@ -1826,11 +1875,42 @@ const useTranslations = () => {
|
|
|
1826
1875
|
downloaded: /* @__PURE__ */ new Set(),
|
|
1827
1876
|
failed: /* @__PURE__ */ new Set(),
|
|
1828
1877
|
skipped: /* @__PURE__ */ new Set()
|
|
1829
|
-
}), downloadStatusRef = React.useRef(downloadStatus), [translationStatuses, setTranslationStatuses] = React.useState(/* @__PURE__ */ new Map()), [
|
|
1878
|
+
}), downloadStatusRef = React.useRef(downloadStatus), [translationStatuses, setTranslationStatuses] = React.useState(/* @__PURE__ */ new Map()), [pendingTranslations, setPendingTranslations] = React.useState(
|
|
1879
|
+
/* @__PURE__ */ new Set()
|
|
1880
|
+
), [isRefreshing, setIsRefreshing] = React.useState(!1), client = useClient(), { projectId, dataset } = client.config(), uploadedVersionsStorageKey = getUploadedVersionsStorageKey(
|
|
1830
1881
|
projectId,
|
|
1831
1882
|
dataset
|
|
1832
1883
|
), [uploadedVersions, setUploadedVersions] = React.useState(
|
|
1833
1884
|
() => readUploadedVersions(uploadedVersionsStorageKey)
|
|
1885
|
+
), preferencesStorageKey = getPreferencesStorageKey(projectId, dataset), [preferences, setPreferences] = React.useState(
|
|
1886
|
+
() => readPreferences(preferencesStorageKey, pluginConfig.getPreferences())
|
|
1887
|
+
), {
|
|
1888
|
+
autoRefresh,
|
|
1889
|
+
autoImport,
|
|
1890
|
+
autoPatchReferences,
|
|
1891
|
+
autoPublish,
|
|
1892
|
+
preserveExistingTranslations
|
|
1893
|
+
} = preferences, setPreference = React.useCallback(
|
|
1894
|
+
(key, value) => {
|
|
1895
|
+
const next = { ...preferences, [key]: value };
|
|
1896
|
+
setPreferences(next), writePreferences(preferencesStorageKey, next);
|
|
1897
|
+
},
|
|
1898
|
+
[preferences, preferencesStorageKey]
|
|
1899
|
+
), setAutoRefresh = React.useCallback(
|
|
1900
|
+
(value) => setPreference("autoRefresh", value),
|
|
1901
|
+
[setPreference]
|
|
1902
|
+
), setAutoImport = React.useCallback(
|
|
1903
|
+
(value) => setPreference("autoImport", value),
|
|
1904
|
+
[setPreference]
|
|
1905
|
+
), setAutoPatchReferences = React.useCallback(
|
|
1906
|
+
(value) => setPreference("autoPatchReferences", value),
|
|
1907
|
+
[setPreference]
|
|
1908
|
+
), setAutoPublish = React.useCallback(
|
|
1909
|
+
(value) => setPreference("autoPublish", value),
|
|
1910
|
+
[setPreference]
|
|
1911
|
+
), setPreserveExistingTranslations = React.useCallback(
|
|
1912
|
+
(value) => setPreference("preserveExistingTranslations", value),
|
|
1913
|
+
[setPreference]
|
|
1834
1914
|
), schema2 = sanity.useSchema(), translationContext = { client, schema: schema2 }, toast = ui.useToast(), { loading: loadingSecrets, secrets } = useSecrets(
|
|
1835
1915
|
pluginConfig.getSecretsNamespace()
|
|
1836
1916
|
), [branchId, setBranchId] = React.useState(void 0);
|
|
@@ -1962,7 +2042,20 @@ const useTranslations = () => {
|
|
|
1962
2042
|
const nextUploadedVersions = new Map(uploadedVersions);
|
|
1963
2043
|
for (const { info } of transformedDocuments)
|
|
1964
2044
|
info.versionId && nextUploadedVersions.set(info.documentId, info.versionId);
|
|
1965
|
-
writeUploadedVersions(uploadedVersionsStorageKey, nextUploadedVersions), setUploadedVersions(nextUploadedVersions),
|
|
2045
|
+
writeUploadedVersions(uploadedVersionsStorageKey, nextUploadedVersions), setUploadedVersions(nextUploadedVersions), setPendingTranslations((prev) => {
|
|
2046
|
+
const next = new Set(prev);
|
|
2047
|
+
for (const { info } of transformedDocuments)
|
|
2048
|
+
for (const localeId of availableLocaleIds)
|
|
2049
|
+
next.add(
|
|
2050
|
+
createTranslationStatusKey(
|
|
2051
|
+
branchId,
|
|
2052
|
+
info.documentId,
|
|
2053
|
+
info.versionId ?? "",
|
|
2054
|
+
localeId
|
|
2055
|
+
)
|
|
2056
|
+
);
|
|
2057
|
+
return next;
|
|
2058
|
+
}), toast.push({
|
|
1966
2059
|
title: force ? `Retranslating ${documents.length} documents from scratch` : `Translation tasks created for ${documents.length} documents`,
|
|
1967
2060
|
status: "success",
|
|
1968
2061
|
closable: !0
|
|
@@ -2196,28 +2289,27 @@ const useTranslations = () => {
|
|
|
2196
2289
|
setBranchId(defaultBranch.branch.id);
|
|
2197
2290
|
},
|
|
2198
2291
|
[secrets]
|
|
2199
|
-
), handleRefreshAll = React.useCallback(
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
for (const
|
|
2206
|
-
const
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
const newStatuses = /* @__PURE__ */ new Map();
|
|
2292
|
+
), handleRefreshAll = React.useCallback(
|
|
2293
|
+
async ({ silent = !1 } = {}) => {
|
|
2294
|
+
if (!(!secrets || documents.length === 0 || !branchId)) {
|
|
2295
|
+
setIsRefreshing(!0);
|
|
2296
|
+
try {
|
|
2297
|
+
const availableLocaleIds = locales.filter((locale) => locale.enabled !== !1).map((locale) => locale.localeId), fileQueryData = [];
|
|
2298
|
+
for (const doc of documents)
|
|
2299
|
+
for (const localeId of availableLocaleIds) {
|
|
2300
|
+
const documentId = getDocumentPublishedId(doc);
|
|
2301
|
+
fileQueryData.push({
|
|
2302
|
+
versionId: getVersionId(doc),
|
|
2303
|
+
fileId: documentId,
|
|
2304
|
+
branchId,
|
|
2305
|
+
locale: localeId
|
|
2306
|
+
});
|
|
2307
|
+
}
|
|
2308
|
+
const readyTranslations = await checkTranslationStatus(
|
|
2309
|
+
fileQueryData,
|
|
2310
|
+
downloadStatusRef.current,
|
|
2311
|
+
secrets
|
|
2312
|
+
), newStatuses = /* @__PURE__ */ new Map();
|
|
2221
2313
|
for (const doc of documents)
|
|
2222
2314
|
for (const localeId of availableLocaleIds) {
|
|
2223
2315
|
const documentId = getDocumentPublishedId(doc), versionId = getVersionId(doc), key = createTranslationStatusKey(
|
|
@@ -2247,23 +2339,30 @@ const useTranslations = () => {
|
|
|
2247
2339
|
}
|
|
2248
2340
|
});
|
|
2249
2341
|
}
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
})
|
|
2262
|
-
|
|
2263
|
-
|
|
2342
|
+
setTranslationStatuses(newStatuses), setPendingTranslations((prev) => {
|
|
2343
|
+
if (prev.size === 0) return prev;
|
|
2344
|
+
const next = new Set(prev);
|
|
2345
|
+
for (const [key, status] of newStatuses)
|
|
2346
|
+
status.isReady && next.delete(key);
|
|
2347
|
+
return next.size === prev.size ? prev : next;
|
|
2348
|
+
}), silent || toast.push({
|
|
2349
|
+
title: `Refreshed status for ${documents.length} document(s)`,
|
|
2350
|
+
status: "success",
|
|
2351
|
+
closable: !0
|
|
2352
|
+
});
|
|
2353
|
+
} catch (error) {
|
|
2354
|
+
console.error("Error refreshing translation status:", error), toast.push({
|
|
2355
|
+
title: "Translation status could not be refreshed. Try again before importing.",
|
|
2356
|
+
status: "error",
|
|
2357
|
+
closable: !0
|
|
2358
|
+
});
|
|
2359
|
+
} finally {
|
|
2360
|
+
setIsRefreshing(!1);
|
|
2361
|
+
}
|
|
2264
2362
|
}
|
|
2265
|
-
}
|
|
2266
|
-
|
|
2363
|
+
},
|
|
2364
|
+
[secrets, documents, locales, branchId, getVersionId]
|
|
2365
|
+
), handleImportDocument = React.useCallback(
|
|
2267
2366
|
async (documentId, versionId, localeId) => {
|
|
2268
2367
|
if (!secrets) return;
|
|
2269
2368
|
const key = createTranslationStatusKey(
|
|
@@ -2381,16 +2480,7 @@ const useTranslations = () => {
|
|
|
2381
2480
|
localeId,
|
|
2382
2481
|
client
|
|
2383
2482
|
);
|
|
2384
|
-
|
|
2385
|
-
const mutation = {
|
|
2386
|
-
patch: {
|
|
2387
|
-
id: translatedDoc._id,
|
|
2388
|
-
set: resolvedDoc
|
|
2389
|
-
}
|
|
2390
|
-
};
|
|
2391
|
-
return await client.mutate([mutation]), { patched: !0, doc: translatedDoc, localeId };
|
|
2392
|
-
}
|
|
2393
|
-
return { patched: !1, doc: translatedDoc, localeId };
|
|
2483
|
+
return resolvedDoc !== translatedDoc ? (await commitResolvedRefs(translatedDoc, resolvedDoc, client), { patched: !0, doc: translatedDoc, localeId }) : { patched: !1, doc: translatedDoc, localeId };
|
|
2394
2484
|
},
|
|
2395
2485
|
{
|
|
2396
2486
|
onProgress: (current, total) => {
|
|
@@ -2487,7 +2577,7 @@ const useTranslations = () => {
|
|
|
2487
2577
|
}, [fetchLocales, secrets]), React.useEffect(() => {
|
|
2488
2578
|
documents.length > 0 && locales.length > 0 && fetchExistingTranslations();
|
|
2489
2579
|
}, [fetchExistingTranslations]), React.useEffect(() => {
|
|
2490
|
-
documents.length > 0 && locales.length > 0 && secrets && !loadingDocuments && branchId && handleRefreshAll();
|
|
2580
|
+
documents.length > 0 && locales.length > 0 && secrets && !loadingDocuments && branchId && handleRefreshAll({ silent: !0 });
|
|
2491
2581
|
}, [
|
|
2492
2582
|
documents,
|
|
2493
2583
|
locales,
|
|
@@ -2498,7 +2588,7 @@ const useTranslations = () => {
|
|
|
2498
2588
|
]), React.useEffect(() => {
|
|
2499
2589
|
if (!autoRefresh || documents.length === 0 || !secrets) return;
|
|
2500
2590
|
const interval = setInterval(async () => {
|
|
2501
|
-
await handleRefreshAll();
|
|
2591
|
+
await handleRefreshAll({ silent: !0 });
|
|
2502
2592
|
}, 1e4);
|
|
2503
2593
|
return () => clearInterval(interval);
|
|
2504
2594
|
}, [autoRefresh, documents.length, secrets, handleRefreshAll]), React.useEffect(() => {
|
|
@@ -2522,6 +2612,7 @@ const useTranslations = () => {
|
|
|
2522
2612
|
existingTranslations,
|
|
2523
2613
|
downloadStatus,
|
|
2524
2614
|
translationStatuses,
|
|
2615
|
+
pendingTranslations,
|
|
2525
2616
|
isRefreshing,
|
|
2526
2617
|
loadingSecrets,
|
|
2527
2618
|
secrets,
|
|
@@ -2564,58 +2655,216 @@ const useTranslations = () => {
|
|
|
2564
2655
|
] }) })
|
|
2565
2656
|
}
|
|
2566
2657
|
) : null;
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2658
|
+
var version = "3.1.4";
|
|
2659
|
+
function buildDebugInfo({
|
|
2660
|
+
secrets,
|
|
2661
|
+
preferences,
|
|
2662
|
+
translations,
|
|
2663
|
+
sanityProjectId,
|
|
2664
|
+
sanityDataset,
|
|
2665
|
+
branchId
|
|
2666
|
+
}) {
|
|
2667
|
+
const deserializerTypes = pluginConfig.getAdditionalDeserializers()?.types ?? {};
|
|
2668
|
+
return {
|
|
2669
|
+
package: { name: "gt-sanity", version },
|
|
2670
|
+
sanity: { projectId: sanityProjectId, dataset: sanityDataset },
|
|
2671
|
+
generalTranslation: {
|
|
2672
|
+
projectId: secrets?.project,
|
|
2673
|
+
apiKeyConfigured: !!secrets?.secret,
|
|
2674
|
+
secretsNamespace: pluginConfig.getSecretsNamespace(),
|
|
2675
|
+
secretsDocumentFound: secrets !== null,
|
|
2676
|
+
branchId
|
|
2677
|
+
},
|
|
2678
|
+
locales: {
|
|
2679
|
+
sourceLocale: pluginConfig.getSourceLocale(),
|
|
2680
|
+
targetLocales: pluginConfig.getLocales()
|
|
2681
|
+
},
|
|
2682
|
+
documents: {
|
|
2683
|
+
translationLevel: pluginConfig.getTranslationLevel(),
|
|
2684
|
+
translateDocuments: pluginConfig.getTranslateDocuments(),
|
|
2685
|
+
fieldLevelDocuments: pluginConfig.getFieldLevelDocuments(),
|
|
2686
|
+
languageField: pluginConfig.getLanguageField(),
|
|
2687
|
+
singletons: pluginConfig.getSingletons()
|
|
2688
|
+
},
|
|
2689
|
+
fieldRules: {
|
|
2690
|
+
ignoreFields: pluginConfig.getIgnoreFields(),
|
|
2691
|
+
dedupeFields: pluginConfig.getDedupeFields(),
|
|
2692
|
+
skipFields: pluginConfig.getSkipFields(),
|
|
2693
|
+
additionalStopTypes: pluginConfig.getAdditionalStopTypes()
|
|
2694
|
+
},
|
|
2695
|
+
translations,
|
|
2696
|
+
preferences,
|
|
2697
|
+
customization: {
|
|
2698
|
+
additionalSerializers: Object.keys(
|
|
2699
|
+
pluginConfig.getAdditionalSerializers()?.types ?? {}
|
|
2700
|
+
),
|
|
2701
|
+
additionalDeserializers: Object.keys(deserializerTypes),
|
|
2702
|
+
additionalBlockDeserializers: pluginConfig.getAdditionalBlockDeserializers()?.length ?? 0
|
|
2703
|
+
}
|
|
2704
|
+
};
|
|
2705
|
+
}
|
|
2706
|
+
function formatDebugInfo(info) {
|
|
2707
|
+
return JSON.stringify(info, null, 2);
|
|
2708
|
+
}
|
|
2709
|
+
const EMPTY_TRANSLATION_SUMMARY = {
|
|
2710
|
+
documentsChecked: 0,
|
|
2711
|
+
expected: 0,
|
|
2712
|
+
found: 0,
|
|
2713
|
+
draftOnly: 0,
|
|
2714
|
+
published: 0,
|
|
2715
|
+
missing: 0
|
|
2716
|
+
};
|
|
2717
|
+
function summarizeTranslations({
|
|
2718
|
+
documentCount,
|
|
2719
|
+
targetLocaleCount,
|
|
2720
|
+
translatedIds,
|
|
2721
|
+
publishedIds,
|
|
2722
|
+
draftIds
|
|
2723
|
+
}) {
|
|
2724
|
+
const unique = Array.from(new Set(translatedIds)), publishedSet = new Set(publishedIds), draftSet = new Set(draftIds), published = unique.filter((id) => publishedSet.has(id)).length, draftOnly = unique.filter(
|
|
2725
|
+
(id) => !publishedSet.has(id) && draftSet.has(`drafts.${id}`)
|
|
2726
|
+
).length, expected = documentCount * targetLocaleCount;
|
|
2727
|
+
return {
|
|
2728
|
+
documentsChecked: documentCount,
|
|
2729
|
+
expected,
|
|
2730
|
+
found: unique.length,
|
|
2731
|
+
draftOnly,
|
|
2732
|
+
published,
|
|
2733
|
+
missing: Math.max(0, expected - unique.length)
|
|
2734
|
+
};
|
|
2735
|
+
}
|
|
2736
|
+
async function collectTranslationSummary(documents, client) {
|
|
2737
|
+
const documentIds = documents.map(getDocumentPublishedId), targetLocales = pluginConfig.getLocales();
|
|
2738
|
+
if (documentIds.length === 0)
|
|
2739
|
+
return EMPTY_TRANSLATION_SUMMARY;
|
|
2740
|
+
const translatedIds = (await client.fetch(
|
|
2741
|
+
`*[_type == 'translation.metadata' && references($documentIds)]{
|
|
2742
|
+
'targets': translations[language in $targetLocales && defined(value._ref)]{
|
|
2743
|
+
language,
|
|
2744
|
+
'id': value._ref
|
|
2745
|
+
}
|
|
2746
|
+
}`,
|
|
2747
|
+
{ documentIds, targetLocales }
|
|
2748
|
+
)).flatMap((entry) => entry.targets ?? []).map((target) => target.id).filter((id) => !!id);
|
|
2749
|
+
if (translatedIds.length === 0)
|
|
2750
|
+
return summarizeTranslations({
|
|
2751
|
+
documentCount: documentIds.length,
|
|
2752
|
+
targetLocaleCount: targetLocales.length,
|
|
2753
|
+
translatedIds: [],
|
|
2754
|
+
publishedIds: [],
|
|
2755
|
+
draftIds: []
|
|
2756
|
+
});
|
|
2757
|
+
const unique = Array.from(new Set(translatedIds)), existence = await client.fetch(
|
|
2758
|
+
`{
|
|
2759
|
+
'publishedIds': *[_id in $ids]._id,
|
|
2760
|
+
'draftIds': *[_id in $draftIds]._id
|
|
2761
|
+
}`,
|
|
2762
|
+
{ ids: unique, draftIds: unique.map((id) => `drafts.${id}`) }
|
|
2763
|
+
);
|
|
2764
|
+
return summarizeTranslations({
|
|
2765
|
+
documentCount: documentIds.length,
|
|
2766
|
+
targetLocaleCount: targetLocales.length,
|
|
2767
|
+
translatedIds: unique,
|
|
2768
|
+
publishedIds: existence?.publishedIds ?? [],
|
|
2769
|
+
draftIds: existence?.draftIds ?? []
|
|
2770
|
+
});
|
|
2771
|
+
}
|
|
2772
|
+
const DebugInfoDialog = ({
|
|
2773
|
+
isOpen,
|
|
2774
|
+
onClose
|
|
2775
|
+
}) => {
|
|
2776
|
+
const {
|
|
2777
|
+
secrets,
|
|
2778
|
+
documents,
|
|
2779
|
+
branchId,
|
|
2780
|
+
autoRefresh,
|
|
2781
|
+
autoImport,
|
|
2782
|
+
autoPatchReferences,
|
|
2783
|
+
autoPublish,
|
|
2784
|
+
preserveExistingTranslations
|
|
2785
|
+
} = useTranslations(), client = useClient(), [copied, setCopied] = React.useState(!1), [translations, setTranslations] = React.useState(
|
|
2786
|
+
EMPTY_TRANSLATION_SUMMARY
|
|
2787
|
+
), { projectId, dataset } = client.config(), serialized = React.useMemo(
|
|
2788
|
+
() => formatDebugInfo(
|
|
2789
|
+
buildDebugInfo({
|
|
2790
|
+
secrets,
|
|
2791
|
+
preferences: {
|
|
2792
|
+
autoRefresh,
|
|
2793
|
+
autoImport,
|
|
2794
|
+
autoPatchReferences,
|
|
2795
|
+
autoPublish,
|
|
2796
|
+
preserveExistingTranslations
|
|
2797
|
+
},
|
|
2798
|
+
translations,
|
|
2799
|
+
sanityProjectId: projectId,
|
|
2800
|
+
sanityDataset: dataset,
|
|
2801
|
+
branchId
|
|
2802
|
+
})
|
|
2803
|
+
),
|
|
2804
|
+
[
|
|
2805
|
+
secrets,
|
|
2806
|
+
translations,
|
|
2807
|
+
branchId,
|
|
2808
|
+
projectId,
|
|
2809
|
+
dataset,
|
|
2810
|
+
autoRefresh,
|
|
2811
|
+
autoImport,
|
|
2812
|
+
autoPatchReferences,
|
|
2813
|
+
autoPublish,
|
|
2814
|
+
preserveExistingTranslations
|
|
2815
|
+
]
|
|
2816
|
+
);
|
|
2817
|
+
return React.useEffect(() => {
|
|
2818
|
+
if (!isOpen) return;
|
|
2819
|
+
let cancelled = !1;
|
|
2820
|
+
return collectTranslationSummary(documents, client).then((summary) => {
|
|
2821
|
+
cancelled || setTranslations(summary);
|
|
2822
|
+
}).catch(() => {
|
|
2823
|
+
cancelled || setTranslations(EMPTY_TRANSLATION_SUMMARY);
|
|
2824
|
+
}), () => {
|
|
2825
|
+
cancelled = !0;
|
|
2826
|
+
};
|
|
2827
|
+
}, [isOpen, documents, client]), isOpen ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
2828
|
+
ui.Dialog,
|
|
2570
2829
|
{
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
ui.Card,
|
|
2577
|
-
{
|
|
2578
|
-
tone: "positive",
|
|
2579
|
-
style: {
|
|
2580
|
-
position: "absolute",
|
|
2581
|
-
inset: 0,
|
|
2582
|
-
transform: `scaleX(${progress / 100})`,
|
|
2583
|
-
transformOrigin: "left",
|
|
2584
|
-
transition: "transform .2s ease"
|
|
2585
|
-
}
|
|
2586
|
-
}
|
|
2587
|
-
),
|
|
2830
|
+
header: "Debug info",
|
|
2831
|
+
id: "gt-debug-info-dialog",
|
|
2832
|
+
onClose,
|
|
2833
|
+
width: 2,
|
|
2834
|
+
footer: /* @__PURE__ */ jsxRuntime.jsx(ui.Box, { padding: 3, children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { gap: 2, justify: "space-between", align: "center", children: [
|
|
2588
2835
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2589
|
-
|
|
2836
|
+
ui.Button,
|
|
2590
2837
|
{
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2838
|
+
icon: icons.ClipboardIcon,
|
|
2839
|
+
text: copied ? "Copied" : "Copy",
|
|
2840
|
+
tone: copied ? "positive" : "default",
|
|
2841
|
+
mode: "ghost",
|
|
2842
|
+
onClick: async () => {
|
|
2843
|
+
try {
|
|
2844
|
+
await navigator.clipboard.writeText(serialized), setCopied(!0);
|
|
2845
|
+
} catch {
|
|
2846
|
+
setCopied(!1);
|
|
2847
|
+
}
|
|
2598
2848
|
}
|
|
2599
2849
|
}
|
|
2600
2850
|
),
|
|
2601
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
}
|
|
2613
|
-
|
|
2614
|
-
|
|
2851
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Button, { text: "Close", mode: "ghost", onClick: onClose })
|
|
2852
|
+
] }) }),
|
|
2853
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(ui.Box, { padding: 4, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Stack, { space: 4, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
2854
|
+
ui.Card,
|
|
2855
|
+
{
|
|
2856
|
+
padding: 3,
|
|
2857
|
+
radius: 2,
|
|
2858
|
+
tone: "transparent",
|
|
2859
|
+
border: !0,
|
|
2860
|
+
overflow: "auto",
|
|
2861
|
+
style: { maxHeight: "50vh" },
|
|
2862
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(ui.Code, { size: 0, language: "json", children: serialized })
|
|
2863
|
+
}
|
|
2864
|
+
) }) })
|
|
2615
2865
|
}
|
|
2616
|
-
);
|
|
2617
|
-
}
|
|
2618
|
-
const LocaleLabel = ({
|
|
2866
|
+
) : null;
|
|
2867
|
+
}, LocaleLabel = ({
|
|
2619
2868
|
localeId,
|
|
2620
2869
|
size = 1,
|
|
2621
2870
|
weight = "medium"
|
|
@@ -2626,24 +2875,31 @@ const LocaleLabel = ({
|
|
|
2626
2875
|
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size, weight, children: name }),
|
|
2627
2876
|
!verbatim && /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { fontSize: 0, radius: 2, children: code })
|
|
2628
2877
|
] });
|
|
2878
|
+
}, STATE_LABEL = {
|
|
2879
|
+
"not-translated": "Not translated",
|
|
2880
|
+
translating: "Translating\u2026",
|
|
2881
|
+
ready: "Ready to import",
|
|
2882
|
+
imported: "Imported"
|
|
2629
2883
|
}, LanguageStatus = ({
|
|
2630
2884
|
localeId,
|
|
2631
|
-
|
|
2632
|
-
importFile
|
|
2633
|
-
isImported = !1
|
|
2885
|
+
state,
|
|
2886
|
+
importFile
|
|
2634
2887
|
}) => {
|
|
2635
|
-
const [isBusy, setIsBusy] = React.useState(!1),
|
|
2888
|
+
const [isBusy, setIsBusy] = React.useState(!1), handleImport = React.useCallback(async () => {
|
|
2636
2889
|
setIsBusy(!0);
|
|
2637
2890
|
try {
|
|
2638
2891
|
await importFile();
|
|
2639
2892
|
} finally {
|
|
2640
2893
|
setIsBusy(!1);
|
|
2641
2894
|
}
|
|
2642
|
-
}, [importFile
|
|
2895
|
+
}, [importFile]);
|
|
2643
2896
|
return /* @__PURE__ */ jsxRuntime.jsxs(ui.Grid, { columns: 5, gap: 3, paddingX: 3, paddingY: 2, children: [
|
|
2644
2897
|
/* @__PURE__ */ jsxRuntime.jsx(ui.Flex, { columnStart: 1, columnEnd: 3, align: "center", children: /* @__PURE__ */ jsxRuntime.jsx(LocaleLabel, { localeId }) }),
|
|
2645
|
-
|
|
2646
|
-
|
|
2898
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { columnStart: 3, columnEnd: 5, align: "center", gap: 2, children: [
|
|
2899
|
+
state === "translating" && /* @__PURE__ */ jsxRuntime.jsx(ui.Spinner, { size: 1, muted: !0 }),
|
|
2900
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: 1, muted: state !== "ready", children: STATE_LABEL[state] })
|
|
2901
|
+
] }),
|
|
2902
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Flex, { columnStart: 5, columnEnd: 6, align: "center", justify: "flex-end", children: state === "imported" ? /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { tone: "positive", fontSize: 0, radius: 2, children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { align: "center", gap: 1, children: [
|
|
2647
2903
|
/* @__PURE__ */ jsxRuntime.jsx(icons.CheckmarkCircleIcon, {}),
|
|
2648
2904
|
/* @__PURE__ */ jsxRuntime.jsx(ui.Box, { children: "Imported" })
|
|
2649
2905
|
] }) }) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -2657,11 +2913,19 @@ const LocaleLabel = ({
|
|
|
2657
2913
|
text: "Import",
|
|
2658
2914
|
loading: isBusy,
|
|
2659
2915
|
icon: icons.DownloadIcon,
|
|
2660
|
-
disabled: isBusy ||
|
|
2916
|
+
disabled: isBusy || state !== "ready"
|
|
2661
2917
|
}
|
|
2662
2918
|
) })
|
|
2663
2919
|
] });
|
|
2664
|
-
}
|
|
2920
|
+
};
|
|
2921
|
+
function resolveLanguageStatusState({
|
|
2922
|
+
status,
|
|
2923
|
+
isImported,
|
|
2924
|
+
isPending
|
|
2925
|
+
}) {
|
|
2926
|
+
return isImported ? "imported" : status?.isReady ? "ready" : isPending ? "translating" : "not-translated";
|
|
2927
|
+
}
|
|
2928
|
+
const LocaleCheckbox = ({
|
|
2665
2929
|
locale,
|
|
2666
2930
|
toggle,
|
|
2667
2931
|
checked
|
|
@@ -2695,6 +2959,7 @@ const LocaleLabel = ({
|
|
|
2695
2959
|
documents,
|
|
2696
2960
|
locales,
|
|
2697
2961
|
translationStatuses,
|
|
2962
|
+
pendingTranslations,
|
|
2698
2963
|
branchId,
|
|
2699
2964
|
isBusy,
|
|
2700
2965
|
handleTranslateAll,
|
|
@@ -2717,7 +2982,7 @@ const LocaleLabel = ({
|
|
|
2717
2982
|
preserveExistingTranslations,
|
|
2718
2983
|
setPreserveExistingTranslations,
|
|
2719
2984
|
getVersionId
|
|
2720
|
-
} = useTranslations(), [isImporting, setIsImporting] = React.useState(!1), [isPublishing, setIsPublishing] = React.useState(!1), [isUploadingExisting, setIsUploadingExisting] = React.useState(!1), [isSaveLocalDialogOpen, setIsSaveLocalDialogOpen] = React.useState(!1), toast = ui.useToast(), document2 = documents[0], currentDocumentLanguage = React.useMemo(() => {
|
|
2985
|
+
} = useTranslations(), [isImporting, setIsImporting] = React.useState(!1), [isPublishing, setIsPublishing] = React.useState(!1), [isUploadingExisting, setIsUploadingExisting] = React.useState(!1), [isSaveLocalDialogOpen, setIsSaveLocalDialogOpen] = React.useState(!1), [isDebugInfoDialogOpen, setIsDebugInfoDialogOpen] = React.useState(!1), toast = ui.useToast(), document2 = documents[0], currentDocumentLanguage = React.useMemo(() => {
|
|
2721
2986
|
if (!document2) return null;
|
|
2722
2987
|
const languageField = pluginConfig.getLanguageField();
|
|
2723
2988
|
return document2[languageField] || pluginConfig.getSourceLocale();
|
|
@@ -2729,7 +2994,21 @@ const LocaleLabel = ({
|
|
|
2729
2994
|
}, [locales]), statusLocales = React.useMemo(() => {
|
|
2730
2995
|
const sourceLocale = pluginConfig.getSourceLocale();
|
|
2731
2996
|
return locales.filter((locale) => locale.localeId !== sourceLocale);
|
|
2732
|
-
}, [locales]), documentId = React.useMemo(() => document2 ? getDocumentPublishedId(document2) : null, [document2]), versionId = React.useMemo(() => document2 ? getVersionId(document2) : null, [document2, getVersionId]),
|
|
2997
|
+
}, [locales]), documentId = React.useMemo(() => document2 ? getDocumentPublishedId(document2) : null, [document2]), versionId = React.useMemo(() => document2 ? getVersionId(document2) : null, [document2, getVersionId]), alreadyCompleteRef = React.useRef(null), statusKeyFor = React.useCallback(
|
|
2998
|
+
(localeId) => createTranslationStatusKey(
|
|
2999
|
+
branchId,
|
|
3000
|
+
documentId ?? "",
|
|
3001
|
+
versionId ?? "",
|
|
3002
|
+
localeId
|
|
3003
|
+
),
|
|
3004
|
+
[branchId, documentId, versionId]
|
|
3005
|
+
);
|
|
3006
|
+
alreadyCompleteRef.current === null && documentId && versionId && translationStatuses.size > 0 && (alreadyCompleteRef.current = new Set(
|
|
3007
|
+
statusLocales.map((locale) => statusKeyFor(locale.localeId)).filter((key) => translationStatuses.get(key)?.isReady)
|
|
3008
|
+
));
|
|
3009
|
+
const isWaitingOnTranslations = statusLocales.some(
|
|
3010
|
+
(locale) => pendingTranslations.has(statusKeyFor(locale.localeId))
|
|
3011
|
+
), handleImportTranslations = React.useCallback(
|
|
2733
3012
|
async (options = {}) => {
|
|
2734
3013
|
const { autoOnly = !1 } = options;
|
|
2735
3014
|
if (isImporting || !documentId || !versionId || autoOnly && !autoImport) return;
|
|
@@ -2740,7 +3019,7 @@ const LocaleLabel = ({
|
|
|
2740
3019
|
versionId,
|
|
2741
3020
|
locale.localeId
|
|
2742
3021
|
);
|
|
2743
|
-
return translationStatuses.get(key)?.isReady
|
|
3022
|
+
return !translationStatuses.get(key)?.isReady || importedTranslations.has(key) ? !1 : !(autoOnly && alreadyCompleteRef.current?.has(key));
|
|
2744
3023
|
});
|
|
2745
3024
|
if (readyTranslations.length !== 0) {
|
|
2746
3025
|
setIsImporting(!0);
|
|
@@ -2771,9 +3050,7 @@ const LocaleLabel = ({
|
|
|
2771
3050
|
);
|
|
2772
3051
|
React.useEffect(() => {
|
|
2773
3052
|
handleImportTranslations({ autoOnly: !0 });
|
|
2774
|
-
}, [handleImportTranslations])
|
|
2775
|
-
setAutoRefresh(!0), setAutoPatchReferences(!0), setAutoPublish(!0);
|
|
2776
|
-
}, [setAutoRefresh, setAutoPatchReferences, setAutoPublish]);
|
|
3053
|
+
}, [handleImportTranslations]);
|
|
2777
3054
|
const toggleLocale = React.useCallback(
|
|
2778
3055
|
(localeId, shouldEnable) => {
|
|
2779
3056
|
const updatedLocales = locales.map(
|
|
@@ -2825,12 +3102,12 @@ const LocaleLabel = ({
|
|
|
2825
3102
|
ui.Button,
|
|
2826
3103
|
{
|
|
2827
3104
|
onClick: () => {
|
|
2828
|
-
|
|
3105
|
+
alreadyCompleteRef.current = /* @__PURE__ */ new Set(), handleTranslateAll();
|
|
2829
3106
|
},
|
|
2830
|
-
disabled: isBusy || !availableLocales.length,
|
|
3107
|
+
disabled: isBusy || isWaitingOnTranslations || !availableLocales.length,
|
|
2831
3108
|
icon: icons.TranslateIcon,
|
|
2832
|
-
text: "Translate",
|
|
2833
|
-
loading: isBusy && !isUploadingExisting
|
|
3109
|
+
text: isWaitingOnTranslations ? "Translating\u2026" : "Translate",
|
|
3110
|
+
loading: isBusy && !isUploadingExisting || isWaitingOnTranslations
|
|
2834
3111
|
}
|
|
2835
3112
|
)
|
|
2836
3113
|
] }),
|
|
@@ -2867,7 +3144,7 @@ const LocaleLabel = ({
|
|
|
2867
3144
|
icon: icons.RefreshIcon,
|
|
2868
3145
|
text: "Refresh",
|
|
2869
3146
|
loading: isRefreshing,
|
|
2870
|
-
onClick: handleRefreshAll,
|
|
3147
|
+
onClick: () => handleRefreshAll(),
|
|
2871
3148
|
disabled: isRefreshing || isBusy
|
|
2872
3149
|
}
|
|
2873
3150
|
)
|
|
@@ -2879,13 +3156,16 @@ const LocaleLabel = ({
|
|
|
2879
3156
|
documentId,
|
|
2880
3157
|
versionId,
|
|
2881
3158
|
locale.localeId
|
|
2882
|
-
), status = translationStatuses.get(key),
|
|
3159
|
+
), status = translationStatuses.get(key), isImported = importedTranslations.has(key);
|
|
2883
3160
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2884
3161
|
LanguageStatus,
|
|
2885
3162
|
{
|
|
2886
3163
|
localeId: locale.localeId,
|
|
2887
|
-
|
|
2888
|
-
|
|
3164
|
+
state: resolveLanguageStatusState({
|
|
3165
|
+
status,
|
|
3166
|
+
isImported,
|
|
3167
|
+
isPending: pendingTranslations.has(key)
|
|
3168
|
+
}),
|
|
2889
3169
|
importFile: async () => {
|
|
2890
3170
|
!isImported && status?.isReady && await handleImportDocument(
|
|
2891
3171
|
documentId,
|
|
@@ -2966,27 +3246,13 @@ const LocaleLabel = ({
|
|
|
2966
3246
|
] })
|
|
2967
3247
|
] }),
|
|
2968
3248
|
/* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { size: 1, muted: !0, children: [
|
|
2969
|
-
|
|
3249
|
+
statusLocales.filter(
|
|
3250
|
+
(locale) => importedTranslations.has(statusKeyFor(locale.localeId))
|
|
3251
|
+
).length,
|
|
2970
3252
|
" ",
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
documentId,
|
|
2975
|
-
versionId,
|
|
2976
|
-
locale.localeId
|
|
2977
|
-
);
|
|
2978
|
-
return importedTranslations.has(key);
|
|
2979
|
-
}).length,
|
|
2980
|
-
"/",
|
|
2981
|
-
statusLocales.filter((locale) => {
|
|
2982
|
-
const key = createTranslationStatusKey(
|
|
2983
|
-
branchId,
|
|
2984
|
-
documentId,
|
|
2985
|
-
versionId,
|
|
2986
|
-
locale.localeId
|
|
2987
|
-
);
|
|
2988
|
-
return translationStatuses.get(key)?.isReady;
|
|
2989
|
-
}).length
|
|
3253
|
+
"of ",
|
|
3254
|
+
statusLocales.length,
|
|
3255
|
+
" imported"
|
|
2990
3256
|
] })
|
|
2991
3257
|
] }),
|
|
2992
3258
|
/* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { gap: 2, align: "center", justify: "flex-start", children: [
|
|
@@ -3063,7 +3329,30 @@ const LocaleLabel = ({
|
|
|
3063
3329
|
] })
|
|
3064
3330
|
] })
|
|
3065
3331
|
] })
|
|
3066
|
-
] })
|
|
3332
|
+
] }),
|
|
3333
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Card, { borderTop: !0, paddingTop: 3, children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { align: "center", justify: "flex-end", gap: 2, children: [
|
|
3334
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { size: 1, muted: !0, children: [
|
|
3335
|
+
"gt-sanity v",
|
|
3336
|
+
version
|
|
3337
|
+
] }),
|
|
3338
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3339
|
+
ui.Button,
|
|
3340
|
+
{
|
|
3341
|
+
fontSize: 1,
|
|
3342
|
+
padding: 2,
|
|
3343
|
+
mode: "bleed",
|
|
3344
|
+
text: "Debug info",
|
|
3345
|
+
onClick: () => setIsDebugInfoDialogOpen(!0)
|
|
3346
|
+
}
|
|
3347
|
+
)
|
|
3348
|
+
] }) }),
|
|
3349
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3350
|
+
DebugInfoDialog,
|
|
3351
|
+
{
|
|
3352
|
+
isOpen: isDebugInfoDialogOpen,
|
|
3353
|
+
onClose: () => setIsDebugInfoDialogOpen(!1)
|
|
3354
|
+
}
|
|
3355
|
+
)
|
|
3067
3356
|
] }) : /* @__PURE__ */ jsxRuntime.jsx(ui.Card, { padding: 4, tone: "neutral", border: !0, children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { size: 1, muted: !0, children: [
|
|
3068
3357
|
"Translation tools are only available for",
|
|
3069
3358
|
" ",
|
|
@@ -3125,6 +3414,7 @@ const DocumentRow = ({ document: document2 }) => {
|
|
|
3125
3414
|
const {
|
|
3126
3415
|
locales,
|
|
3127
3416
|
translationStatuses,
|
|
3417
|
+
pendingTranslations,
|
|
3128
3418
|
downloadStatus,
|
|
3129
3419
|
importedTranslations,
|
|
3130
3420
|
handleImportDocument,
|
|
@@ -3163,8 +3453,11 @@ const DocumentRow = ({ document: document2 }) => {
|
|
|
3163
3453
|
LanguageStatus,
|
|
3164
3454
|
{
|
|
3165
3455
|
localeId: locale.localeId,
|
|
3166
|
-
|
|
3167
|
-
|
|
3456
|
+
state: resolveLanguageStatusState({
|
|
3457
|
+
status,
|
|
3458
|
+
isImported: isImported || isDownloaded,
|
|
3459
|
+
isPending: pendingTranslations.has(key)
|
|
3460
|
+
}),
|
|
3168
3461
|
importFile: async () => {
|
|
3169
3462
|
await handleImportDocument(
|
|
3170
3463
|
publishedId,
|
|
@@ -3337,7 +3630,7 @@ const DocumentRow = ({ document: document2 }) => {
|
|
|
3337
3630
|
"..."
|
|
3338
3631
|
] })
|
|
3339
3632
|
] }) : null, TranslationsToolContent = () => {
|
|
3340
|
-
const [isTranslateAllDialogOpen, setIsTranslateAllDialogOpen] = React.useState(!1), [isImportAllDialogOpen, setIsImportAllDialogOpen] = React.useState(!1), [isImportMissingDialogOpen, setIsImportMissingDialogOpen] = React.useState(!1), [isUploadExistingDialogOpen, setIsUploadExistingDialogOpen] = React.useState(!1), [isSaveLocalDialogOpen, setIsSaveLocalDialogOpen] = React.useState(!1), {
|
|
3633
|
+
const [isTranslateAllDialogOpen, setIsTranslateAllDialogOpen] = React.useState(!1), [isImportAllDialogOpen, setIsImportAllDialogOpen] = React.useState(!1), [isImportMissingDialogOpen, setIsImportMissingDialogOpen] = React.useState(!1), [isUploadExistingDialogOpen, setIsUploadExistingDialogOpen] = React.useState(!1), [isSaveLocalDialogOpen, setIsSaveLocalDialogOpen] = React.useState(!1), [isDebugInfoDialogOpen, setIsDebugInfoDialogOpen] = React.useState(!1), {
|
|
3341
3634
|
isBusy,
|
|
3342
3635
|
documents,
|
|
3343
3636
|
locales,
|
|
@@ -3427,7 +3720,7 @@ const DocumentRow = ({ document: document2 }) => {
|
|
|
3427
3720
|
icon: icons.RefreshIcon,
|
|
3428
3721
|
text: "Refresh",
|
|
3429
3722
|
loading: isRefreshing,
|
|
3430
|
-
onClick: handleRefreshAll,
|
|
3723
|
+
onClick: () => handleRefreshAll(),
|
|
3431
3724
|
disabled: isRefreshing || actionsDisabled
|
|
3432
3725
|
}
|
|
3433
3726
|
)
|
|
@@ -3565,11 +3858,29 @@ const DocumentRow = ({ document: document2 }) => {
|
|
|
3565
3858
|
}
|
|
3566
3859
|
)
|
|
3567
3860
|
] }),
|
|
3568
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Card, { borderTop: !0, paddingTop: 4, children: /* @__PURE__ */ jsxRuntime.jsxs(ui.
|
|
3569
|
-
|
|
3570
|
-
|
|
3571
|
-
|
|
3572
|
-
|
|
3861
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Card, { borderTop: !0, paddingTop: 4, children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { align: "center", justify: "space-between", gap: 3, wrap: "wrap", children: [
|
|
3862
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { size: 1, muted: !0, children: [
|
|
3863
|
+
"For more information, see the",
|
|
3864
|
+
" ",
|
|
3865
|
+
/* @__PURE__ */ jsxRuntime.jsx(router.Link, { href: "https://dash.generaltranslation.com", children: "General Translation Dashboard" }),
|
|
3866
|
+
"."
|
|
3867
|
+
] }),
|
|
3868
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { align: "center", gap: 2, children: [
|
|
3869
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { size: 1, muted: !0, children: [
|
|
3870
|
+
"gt-sanity v",
|
|
3871
|
+
version
|
|
3872
|
+
] }),
|
|
3873
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3874
|
+
ui.Button,
|
|
3875
|
+
{
|
|
3876
|
+
fontSize: 1,
|
|
3877
|
+
padding: 2,
|
|
3878
|
+
mode: "bleed",
|
|
3879
|
+
text: "Debug info",
|
|
3880
|
+
onClick: () => setIsDebugInfoDialogOpen(!0)
|
|
3881
|
+
}
|
|
3882
|
+
)
|
|
3883
|
+
] })
|
|
3573
3884
|
] }) })
|
|
3574
3885
|
] }) }),
|
|
3575
3886
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -3609,6 +3920,13 @@ const DocumentRow = ({ document: document2 }) => {
|
|
|
3609
3920
|
isOpen: isUploadExistingDialogOpen,
|
|
3610
3921
|
onClose: () => setIsUploadExistingDialogOpen(!1)
|
|
3611
3922
|
}
|
|
3923
|
+
),
|
|
3924
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3925
|
+
DebugInfoDialog,
|
|
3926
|
+
{
|
|
3927
|
+
isOpen: isDebugInfoDialogOpen,
|
|
3928
|
+
onClose: () => setIsDebugInfoDialogOpen(!1)
|
|
3929
|
+
}
|
|
3612
3930
|
)
|
|
3613
3931
|
] });
|
|
3614
3932
|
}, TranslationsTool = () => /* @__PURE__ */ jsxRuntime.jsx(BaseTranslationWrapper, { showContainer: !1, children: /* @__PURE__ */ jsxRuntime.jsx(TranslationsProvider, { children: /* @__PURE__ */ jsxRuntime.jsx(TranslationsToolContent, {}) }) });
|
|
@@ -3617,6 +3935,61 @@ function resolveTargetLocales(sourceLocale, locales) {
|
|
|
3617
3935
|
(locale) => locale !== sourceLocale
|
|
3618
3936
|
);
|
|
3619
3937
|
}
|
|
3938
|
+
function documentLevelTypes() {
|
|
3939
|
+
const level = pluginConfig.getTranslationLevel(), types = pluginConfig.getTranslateDocuments().map((filter) => filter.type).filter((type) => !!type);
|
|
3940
|
+
if (level === "internationalizedArray") return [];
|
|
3941
|
+
if (level === "mixed") {
|
|
3942
|
+
const fieldLevel = new Set(
|
|
3943
|
+
pluginConfig.getFieldLevelDocuments().map((filter) => filter.type)
|
|
3944
|
+
);
|
|
3945
|
+
return types.filter((type) => !fieldLevel.has(type));
|
|
3946
|
+
}
|
|
3947
|
+
return types;
|
|
3948
|
+
}
|
|
3949
|
+
function localeLabel(locale) {
|
|
3950
|
+
const sourceLocale = pluginConfig.getSourceLocale();
|
|
3951
|
+
return formatLocalePropertiesLabel(
|
|
3952
|
+
locale,
|
|
3953
|
+
generaltranslation.getLocaleProperties(locale, sourceLocale)
|
|
3954
|
+
);
|
|
3955
|
+
}
|
|
3956
|
+
function typeTitle(type, context) {
|
|
3957
|
+
return context?.schema?.get(type)?.title ?? type;
|
|
3958
|
+
}
|
|
3959
|
+
function gtStructureItems(S, context, options = {}) {
|
|
3960
|
+
const types = options.types ?? documentLevelTypes();
|
|
3961
|
+
if (types.length === 0)
|
|
3962
|
+
return console.warn(
|
|
3963
|
+
internal.createDiagnosticMessage({
|
|
3964
|
+
source: "gt-sanity",
|
|
3965
|
+
severity: "Warning",
|
|
3966
|
+
whatHappened: "No document types to group by locale",
|
|
3967
|
+
why: "the structure helper groups per document type, and none were resolved from the plugin configuration",
|
|
3968
|
+
fix: "List the types in `translateDocuments`, or pass them to the structure helper as `types`"
|
|
3969
|
+
})
|
|
3970
|
+
), [];
|
|
3971
|
+
const sourceLocale = pluginConfig.getSourceLocale(), languageField = pluginConfig.getLanguageField(), targetLocales = pluginConfig.getLocales(), titleFor = options.localeTitle ?? ((title, _locale, label) => `${title} in ${label}`);
|
|
3972
|
+
return types.map((type) => {
|
|
3973
|
+
const title = typeTitle(type, context), sourceFilter = `_type == $type && (!defined(${languageField}) || ${languageField} == $locale)`, localeFilter = `_type == $type && ${languageField} == $locale`;
|
|
3974
|
+
return S.listItem().id(`gt-${type}`).title(title).child(
|
|
3975
|
+
S.list().id(`gt-${type}-locales`).title(title).items([
|
|
3976
|
+
S.listItem().id(`gt-${type}-${sourceLocale}`).title(options.sourceTitle ?? localeLabel(sourceLocale)).child(
|
|
3977
|
+
S.documentList().id(`gt-${type}-${sourceLocale}-list`).title(options.sourceTitle ?? localeLabel(sourceLocale)).schemaType(type).filter(sourceFilter).params({ type, locale: sourceLocale })
|
|
3978
|
+
),
|
|
3979
|
+
S.divider(),
|
|
3980
|
+
...targetLocales.map((locale) => {
|
|
3981
|
+
const paneTitle = titleFor(title, locale, localeLabel(locale));
|
|
3982
|
+
return S.listItem().id(`gt-${type}-${locale}`).title(paneTitle).child(
|
|
3983
|
+
S.documentList().id(`gt-${type}-${locale}-list`).title(paneTitle).schemaType(type).filter(localeFilter).params({ type, locale })
|
|
3984
|
+
);
|
|
3985
|
+
})
|
|
3986
|
+
])
|
|
3987
|
+
);
|
|
3988
|
+
});
|
|
3989
|
+
}
|
|
3990
|
+
function gtStructure(options = {}) {
|
|
3991
|
+
return (S, context) => S.list().title("Content").items(gtStructureItems(S, context, options));
|
|
3992
|
+
}
|
|
3620
3993
|
const TranslationTab = (props) => {
|
|
3621
3994
|
const { displayed } = props.document;
|
|
3622
3995
|
return /* @__PURE__ */ jsxRuntime.jsx(BaseTranslationWrapper, { showContainer: !1, children: /* @__PURE__ */ jsxRuntime.jsx(TranslationsProvider, { singleDocument: displayed, children: /* @__PURE__ */ jsxRuntime.jsx(TranslationView, {}) }) });
|
|
@@ -3645,7 +4018,11 @@ const TranslationTab = (props) => {
|
|
|
3645
4018
|
fieldLevelLocalization,
|
|
3646
4019
|
translationLevel = "document",
|
|
3647
4020
|
fieldLevelDocuments,
|
|
3648
|
-
preserveExistingTranslations =
|
|
4021
|
+
preserveExistingTranslations = DEFAULT_TRANSLATION_PREFERENCES.preserveExistingTranslations,
|
|
4022
|
+
autoRefresh = DEFAULT_TRANSLATION_PREFERENCES.autoRefresh,
|
|
4023
|
+
autoImport = DEFAULT_TRANSLATION_PREFERENCES.autoImport,
|
|
4024
|
+
autoPatchReferences = DEFAULT_TRANSLATION_PREFERENCES.autoPatchReferences,
|
|
4025
|
+
autoPublish = DEFAULT_TRANSLATION_PREFERENCES.autoPublish
|
|
3649
4026
|
}) => {
|
|
3650
4027
|
const resolvedSourceLocale = sourceLocale ?? defaultLocale ?? internal.libraryDefaultLocale, targetLocales = resolveTargetLocales(resolvedSourceLocale, locales), normalizeFilters = (entries) => entries?.map((entry) => typeof entry == "string" ? { type: entry } : entry).filter((filter) => filter.documentId || filter.type), normalizedTranslateDocuments = normalizeFilters(translateDocuments), normalizedFieldLevelDocuments = normalizeFilters(fieldLevelDocuments) ?? [], rawFieldLevelConfig = internationalizedArray2 ?? fieldLevelLocalization;
|
|
3651
4028
|
warnOnRemovedFieldLevelOptions(rawFieldLevelConfig);
|
|
@@ -3668,7 +4045,13 @@ const TranslationTab = (props) => {
|
|
|
3668
4045
|
additionalBlockDeserializers,
|
|
3669
4046
|
translationLevel,
|
|
3670
4047
|
normalizedFieldLevelDocuments,
|
|
3671
|
-
|
|
4048
|
+
{
|
|
4049
|
+
autoRefresh,
|
|
4050
|
+
autoImport,
|
|
4051
|
+
autoPatchReferences,
|
|
4052
|
+
autoPublish,
|
|
4053
|
+
preserveExistingTranslations
|
|
4054
|
+
}
|
|
3672
4055
|
), gt.setConfig({
|
|
3673
4056
|
sourceLocale: resolvedSourceLocale,
|
|
3674
4057
|
customMapping,
|
|
@@ -3807,5 +4190,7 @@ exports.defaultStopTypes = defaultStopTypes;
|
|
|
3807
4190
|
exports.detachGTData = detachGTData;
|
|
3808
4191
|
exports.findLatestDraft = findLatestDraft;
|
|
3809
4192
|
exports.gtPlugin = gtPlugin;
|
|
4193
|
+
exports.gtStructure = gtStructure;
|
|
4194
|
+
exports.gtStructureItems = gtStructureItems;
|
|
3810
4195
|
exports.translateAction = translateAction;
|
|
3811
4196
|
//# sourceMappingURL=index.cjs.map
|