gt-sanity 0.0.5 → 0.0.6
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/LICENSE.md +1 -8
- package/README.md +5 -5
- package/dist/index.d.mts +35 -30
- package/dist/index.d.ts +35 -30
- package/dist/index.js +234 -123
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +237 -124
- package/dist/index.mjs.map +1 -1
- package/package.json +5 -3
- package/src/adapter/core.ts +72 -7
- package/src/adapter/createTask.ts +1 -1
- package/src/adapter/types.ts +9 -0
- package/src/components/LanguageStatus.tsx +2 -0
- package/src/components/NewTask.tsx +2 -0
- package/src/components/ProgressBar.tsx +2 -0
- package/src/components/TaskView.tsx +2 -0
- package/src/components/TranslationContext.tsx +5 -0
- package/src/components/TranslationView.tsx +34 -2
- package/src/components/TranslationsTab.tsx +4 -0
- package/src/components/page/TranslationsTool.tsx +876 -0
- package/src/configuration/baseDocumentLevelConfig/documentLevelPatch.ts +23 -10
- package/src/configuration/baseDocumentLevelConfig/helpers/createI18nDocAndPatchMetadata.ts +77 -24
- package/src/configuration/baseDocumentLevelConfig/helpers/createTranslationMetadata.ts +2 -0
- package/src/configuration/baseDocumentLevelConfig/helpers/getOrCreateTranslationMetadata.ts +2 -0
- package/src/configuration/baseDocumentLevelConfig/helpers/getTranslationMetadata.ts +2 -0
- package/src/configuration/baseDocumentLevelConfig/helpers/patchI18nDoc.ts +51 -8
- package/src/configuration/baseDocumentLevelConfig/index.ts +6 -37
- package/src/configuration/baseFieldLevelConfig.ts +4 -1
- package/src/configuration/utils/checkSerializationVersion.ts +2 -0
- package/src/configuration/utils/findDocumentAtRevision.ts +2 -0
- package/src/configuration/utils/findLatestDraft.ts +2 -0
- package/src/hooks/useClient.ts +3 -1
- package/src/hooks/useSecrets.ts +2 -0
- package/src/index.ts +70 -32
- package/src/translation/checkTranslationStatus.ts +42 -0
- package/src/translation/createJobs.ts +16 -0
- package/src/translation/downloadTranslations.ts +68 -0
- package/src/translation/importDocument.ts +24 -0
- package/src/translation/initProject.ts +61 -0
- package/src/translation/uploadFiles.ts +32 -0
- package/src/types.ts +4 -1
- package/src/utils/applyDocuments.ts +72 -0
- package/src/utils/serialize.ts +32 -0
- package/src/utils/shared.ts +1 -0
- package/src/configuration/baseDocumentLevelConfig/helpers/index.ts +0 -5
- package/src/configuration/baseDocumentLevelConfig/legacyDocumentLevelPatch.ts +0 -69
- package/src/configuration/index.ts +0 -18
- package/src/configuration/utils/index.ts +0 -3
|
@@ -1,12 +1,13 @@
|
|
|
1
|
+
// adapted from https://github.com/sanity-io/sanity-translations-tab. See LICENSE.md for more details.
|
|
2
|
+
|
|
1
3
|
import { SanityClient, SanityDocument, SanityDocumentLike } from 'sanity';
|
|
2
4
|
import { BaseDocumentMerger } from 'sanity-naive-html-serializer';
|
|
3
5
|
|
|
4
|
-
import { findLatestDraft
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
} from './helpers';
|
|
6
|
+
import { findLatestDraft } from '../utils/findLatestDraft';
|
|
7
|
+
import { findDocumentAtRevision } from '../utils/findDocumentAtRevision';
|
|
8
|
+
import { createI18nDocAndPatchMetadata } from './helpers/createI18nDocAndPatchMetadata';
|
|
9
|
+
import { getOrCreateTranslationMetadata } from './helpers/getOrCreateTranslationMetadata';
|
|
10
|
+
import { patchI18nDoc } from './helpers/patchI18nDoc';
|
|
10
11
|
import type { GTFile } from '../../types';
|
|
11
12
|
import { gtConfig } from '../../adapter/core';
|
|
12
13
|
|
|
@@ -16,7 +17,8 @@ export const documentLevelPatch = async (
|
|
|
16
17
|
localeId: string,
|
|
17
18
|
client: SanityClient,
|
|
18
19
|
languageField: string = 'language',
|
|
19
|
-
mergeWithTargetLocale: boolean = false
|
|
20
|
+
mergeWithTargetLocale: boolean = false,
|
|
21
|
+
publish: boolean = false
|
|
20
22
|
): Promise<void> => {
|
|
21
23
|
const baseLanguage = gtConfig.getSourceLocale();
|
|
22
24
|
//this is the document we use to merge with the translated fields
|
|
@@ -92,17 +94,28 @@ export const documentLevelPatch = async (
|
|
|
92
94
|
) as SanityDocumentLike;
|
|
93
95
|
|
|
94
96
|
if (i18nDoc) {
|
|
95
|
-
patchI18nDoc(
|
|
97
|
+
await patchI18nDoc(
|
|
98
|
+
docInfo.documentId,
|
|
99
|
+
i18nDoc._id,
|
|
100
|
+
baseDoc,
|
|
101
|
+
merged,
|
|
102
|
+
translatedFields,
|
|
103
|
+
client,
|
|
104
|
+
publish
|
|
105
|
+
);
|
|
96
106
|
}
|
|
97
107
|
//otherwise, create a new document
|
|
98
108
|
//and add the document reference to the metadata document
|
|
99
109
|
else {
|
|
100
|
-
createI18nDocAndPatchMetadata(
|
|
110
|
+
await createI18nDocAndPatchMetadata(
|
|
111
|
+
baseDoc,
|
|
101
112
|
merged,
|
|
102
113
|
localeId,
|
|
103
114
|
client,
|
|
104
115
|
translationMetadata,
|
|
105
|
-
|
|
116
|
+
docInfo.documentId,
|
|
117
|
+
languageField,
|
|
118
|
+
publish
|
|
106
119
|
);
|
|
107
120
|
}
|
|
108
121
|
};
|
|
@@ -1,12 +1,19 @@
|
|
|
1
|
+
// adapted from https://github.com/sanity-io/sanity-translations-tab. See LICENSE.md for more details.
|
|
2
|
+
|
|
1
3
|
import { SanityClient, SanityDocumentLike } from 'sanity';
|
|
4
|
+
import { gtConfig } from '../../../adapter/core';
|
|
5
|
+
import { applyDocuments } from '../../../utils/applyDocuments';
|
|
2
6
|
|
|
3
|
-
export
|
|
7
|
+
export async function createI18nDocAndPatchMetadata(
|
|
8
|
+
sourceDocument: SanityDocumentLike,
|
|
4
9
|
translatedDoc: SanityDocumentLike,
|
|
5
10
|
localeId: string,
|
|
6
11
|
client: SanityClient,
|
|
7
12
|
translationMetadata: SanityDocumentLike,
|
|
8
|
-
|
|
9
|
-
|
|
13
|
+
sourceDocumentId: string,
|
|
14
|
+
languageField: string = 'language',
|
|
15
|
+
publish: boolean = false
|
|
16
|
+
): Promise<void> {
|
|
10
17
|
translatedDoc[languageField] = localeId;
|
|
11
18
|
const translations = translationMetadata.translations as Record<
|
|
12
19
|
string,
|
|
@@ -22,26 +29,72 @@ export const createI18nDocAndPatchMetadata = (
|
|
|
22
29
|
|
|
23
30
|
//remove system fields
|
|
24
31
|
const { _updatedAt, _createdAt, ...rest } = translatedDoc;
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
32
|
+
|
|
33
|
+
const appliedDocument = applyDocuments(
|
|
34
|
+
sourceDocumentId,
|
|
35
|
+
sourceDocument,
|
|
36
|
+
rest,
|
|
37
|
+
gtConfig.getIgnoreFields()
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
// Check if this is a singleton document and apply singleton mapping
|
|
41
|
+
const singletons = gtConfig.getSingletons();
|
|
42
|
+
const isSingleton = singletons.includes(sourceDocumentId);
|
|
43
|
+
|
|
44
|
+
let createDocumentPromise;
|
|
45
|
+
if (isSingleton) {
|
|
46
|
+
const singletonMapping = gtConfig.getSingletonMapping();
|
|
47
|
+
const translatedDocId = singletonMapping(sourceDocumentId, localeId);
|
|
48
|
+
createDocumentPromise = client.create({
|
|
49
|
+
...appliedDocument,
|
|
50
|
+
_type: rest._type,
|
|
51
|
+
_id: `drafts.${translatedDocId}`,
|
|
52
|
+
});
|
|
53
|
+
} else {
|
|
54
|
+
createDocumentPromise = client.create({
|
|
55
|
+
...appliedDocument,
|
|
56
|
+
_type: rest._type,
|
|
57
|
+
_id: 'drafts.',
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const doc = await createDocumentPromise;
|
|
62
|
+
const _ref = doc._id.replace('drafts.', '');
|
|
63
|
+
const result = await client
|
|
64
|
+
.transaction()
|
|
65
|
+
.patch(translationMetadata._id, (p) =>
|
|
66
|
+
p.insert(operation, location, [
|
|
67
|
+
{
|
|
68
|
+
_key: localeId,
|
|
69
|
+
_type: 'internationalizedArrayReferenceValue',
|
|
70
|
+
value: {
|
|
71
|
+
_type: 'reference',
|
|
72
|
+
_ref,
|
|
73
|
+
_weak: true,
|
|
74
|
+
_strengthenOnPublish: {
|
|
75
|
+
type: doc._type,
|
|
41
76
|
},
|
|
42
77
|
},
|
|
43
|
-
|
|
44
|
-
)
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
78
|
+
},
|
|
79
|
+
])
|
|
80
|
+
)
|
|
81
|
+
.commit();
|
|
82
|
+
|
|
83
|
+
if (publish) {
|
|
84
|
+
try {
|
|
85
|
+
// only publish if the document is a draft
|
|
86
|
+
if (doc._id.startsWith('drafts.')) {
|
|
87
|
+
await client.action(
|
|
88
|
+
{
|
|
89
|
+
actionType: 'sanity.action.document.publish',
|
|
90
|
+
draftId: doc._id,
|
|
91
|
+
publishedId: doc._id.replace('drafts.', ''),
|
|
92
|
+
},
|
|
93
|
+
{}
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
} catch (error) {
|
|
97
|
+
console.error('Error publishing document', error);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
@@ -1,25 +1,68 @@
|
|
|
1
|
+
// adapted from https://github.com/sanity-io/sanity-translations-tab. See LICENSE.md for more details.
|
|
2
|
+
|
|
1
3
|
import { SanityClient, SanityDocumentLike } from 'sanity';
|
|
4
|
+
import { applyDocuments } from '../../../utils/applyDocuments';
|
|
5
|
+
import { gtConfig } from '../../../adapter/core';
|
|
6
|
+
|
|
7
|
+
const SYSTEM_FIELDS = ['_id', '_rev', '_updatedAt', 'language'];
|
|
8
|
+
|
|
9
|
+
const isSystemField = (field: string) => SYSTEM_FIELDS.includes(field);
|
|
2
10
|
|
|
3
|
-
export
|
|
11
|
+
export async function patchI18nDoc(
|
|
12
|
+
sourceDocumentId: string,
|
|
4
13
|
i18nDocId: string,
|
|
14
|
+
sourceDocument: SanityDocumentLike,
|
|
5
15
|
mergedDocument: SanityDocumentLike,
|
|
6
16
|
translatedFields: Record<string, any>,
|
|
7
|
-
client: SanityClient
|
|
8
|
-
|
|
17
|
+
client: SanityClient,
|
|
18
|
+
publish: boolean = false
|
|
19
|
+
): Promise<void> {
|
|
9
20
|
const cleanedMerge: Record<string, any> = {};
|
|
10
21
|
Object.entries(mergedDocument).forEach(([key, value]) => {
|
|
11
22
|
if (
|
|
12
23
|
//only patch those fields that had translated strings
|
|
13
24
|
key in translatedFields &&
|
|
14
25
|
//don't overwrite any existing system values on the i18n doc
|
|
15
|
-
!
|
|
26
|
+
!isSystemField(key)
|
|
16
27
|
) {
|
|
17
28
|
cleanedMerge[key] = value;
|
|
18
29
|
}
|
|
19
30
|
});
|
|
31
|
+
const cleanedSourceDocument: Record<string, any> = {};
|
|
32
|
+
Object.entries(sourceDocument).forEach(([key, value]) => {
|
|
33
|
+
if (
|
|
34
|
+
// extract only the fields that are not system fields
|
|
35
|
+
!isSystemField(key)
|
|
36
|
+
) {
|
|
37
|
+
cleanedSourceDocument[key] = value;
|
|
38
|
+
}
|
|
39
|
+
});
|
|
20
40
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
41
|
+
const appliedDocument = applyDocuments(
|
|
42
|
+
sourceDocumentId,
|
|
43
|
+
cleanedSourceDocument,
|
|
44
|
+
cleanedMerge,
|
|
45
|
+
gtConfig.getIgnoreFields()
|
|
46
|
+
);
|
|
47
|
+
const newDocument = await client
|
|
48
|
+
.patch(i18nDocId, { set: appliedDocument })
|
|
24
49
|
.commit();
|
|
25
|
-
|
|
50
|
+
|
|
51
|
+
if (publish) {
|
|
52
|
+
try {
|
|
53
|
+
// only publish if the document is a draft
|
|
54
|
+
if (newDocument._id.startsWith('drafts.')) {
|
|
55
|
+
await client.action(
|
|
56
|
+
{
|
|
57
|
+
actionType: 'sanity.action.document.publish',
|
|
58
|
+
draftId: newDocument._id,
|
|
59
|
+
publishedId: newDocument._id.replace('drafts.', ''),
|
|
60
|
+
},
|
|
61
|
+
{}
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
} catch (error) {
|
|
65
|
+
console.error('Error publishing document', error);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
+
// adapted from https://github.com/sanity-io/sanity-translations-tab. See LICENSE.md for more details.
|
|
2
|
+
|
|
1
3
|
import {
|
|
2
4
|
ExportForTranslation,
|
|
3
5
|
GTSerializedDocument,
|
|
4
6
|
ImportTranslation,
|
|
5
7
|
} from '../../types';
|
|
6
8
|
import { SanityDocument } from 'sanity';
|
|
7
|
-
import { findLatestDraft } from '../utils';
|
|
9
|
+
import { findLatestDraft } from '../utils/findLatestDraft';
|
|
8
10
|
import { documentLevelPatch } from './documentLevelPatch';
|
|
9
|
-
import { legacyDocumentLevelPatch } from './legacyDocumentLevelPatch';
|
|
10
11
|
import {
|
|
11
12
|
BaseDocumentDeserializer,
|
|
12
13
|
BaseDocumentSerializer,
|
|
@@ -65,6 +66,7 @@ export const baseDocumentLevelConfig = {
|
|
|
65
66
|
serializationOptions = {},
|
|
66
67
|
languageField = 'language',
|
|
67
68
|
mergeWithTargetLocale = false,
|
|
69
|
+
publish = false,
|
|
68
70
|
] = params;
|
|
69
71
|
const { client } = context;
|
|
70
72
|
const deserializers = {
|
|
@@ -88,42 +90,9 @@ export const baseDocumentLevelConfig = {
|
|
|
88
90
|
localeId,
|
|
89
91
|
client,
|
|
90
92
|
languageField,
|
|
91
|
-
mergeWithTargetLocale
|
|
93
|
+
mergeWithTargetLocale,
|
|
94
|
+
publish
|
|
92
95
|
);
|
|
93
96
|
},
|
|
94
97
|
secretsNamespace: 'translationService',
|
|
95
98
|
};
|
|
96
|
-
|
|
97
|
-
export const legacyDocumentLevelConfig = {
|
|
98
|
-
...baseDocumentLevelConfig,
|
|
99
|
-
importTranslation: (
|
|
100
|
-
...params: Parameters<ImportTranslation>
|
|
101
|
-
): Promise<void> => {
|
|
102
|
-
const [docInfo, localeId, document, context, serializationOptions = {}] =
|
|
103
|
-
params;
|
|
104
|
-
const { client } = context;
|
|
105
|
-
const deserializers = {
|
|
106
|
-
types: {
|
|
107
|
-
...(serializationOptions.additionalDeserializers ?? {}),
|
|
108
|
-
},
|
|
109
|
-
};
|
|
110
|
-
const blockDeserializers = [
|
|
111
|
-
...(serializationOptions.additionalBlockDeserializers ?? []),
|
|
112
|
-
...customBlockDeserializers,
|
|
113
|
-
];
|
|
114
|
-
|
|
115
|
-
const deserialized = BaseDocumentDeserializer.deserializeDocument(
|
|
116
|
-
document,
|
|
117
|
-
deserializers,
|
|
118
|
-
blockDeserializers
|
|
119
|
-
) as SanityDocument;
|
|
120
|
-
return legacyDocumentLevelPatch(
|
|
121
|
-
docInfo, // versionId is not used here, since we just use the _rev id in the deserialized HTML itself
|
|
122
|
-
deserialized,
|
|
123
|
-
localeId,
|
|
124
|
-
client
|
|
125
|
-
);
|
|
126
|
-
},
|
|
127
|
-
};
|
|
128
|
-
|
|
129
|
-
export { documentLevelPatch, legacyDocumentLevelPatch };
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
// adapted from https://github.com/sanity-io/sanity-translations-tab. See LICENSE.md for more details.
|
|
2
|
+
|
|
1
3
|
import { SanityClient, SanityDocument } from 'sanity';
|
|
2
4
|
import {
|
|
3
5
|
BaseDocumentSerializer,
|
|
@@ -14,7 +16,8 @@ import type {
|
|
|
14
16
|
GTSerializedDocument,
|
|
15
17
|
ImportTranslation,
|
|
16
18
|
} from '../types';
|
|
17
|
-
import { findLatestDraft
|
|
19
|
+
import { findLatestDraft } from './utils/findLatestDraft';
|
|
20
|
+
import { findDocumentAtRevision } from './utils/findDocumentAtRevision';
|
|
18
21
|
import { gtConfig } from '../adapter/core';
|
|
19
22
|
|
|
20
23
|
export const fieldLevelPatch = async (
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
// adapted from https://github.com/sanity-io/sanity-translations-tab. See LICENSE.md for more details.
|
|
2
|
+
|
|
1
3
|
export const checkSerializationVersion = (HTMLdoc: string): string | null => {
|
|
2
4
|
const parser = new DOMParser();
|
|
3
5
|
const node = parser.parseFromString(HTMLdoc, 'text/html');
|
package/src/hooks/useClient.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
// adapted from https://github.com/sanity-io/sanity-translations-tab. See LICENSE.md for more details.
|
|
2
|
+
|
|
1
3
|
import { SanityClient, useClient as useSanityClient } from 'sanity';
|
|
2
4
|
|
|
3
5
|
export const useClient = (): SanityClient => {
|
|
4
|
-
return useSanityClient({ apiVersion: '
|
|
6
|
+
return useSanityClient({ apiVersion: '2025-09-15' });
|
|
5
7
|
};
|
package/src/hooks/useSecrets.ts
CHANGED
package/src/index.ts
CHANGED
|
@@ -8,15 +8,11 @@ import {
|
|
|
8
8
|
TranslationsTabConfigOptions,
|
|
9
9
|
GTFile,
|
|
10
10
|
} from './types';
|
|
11
|
-
import {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
findLatestDraft,
|
|
17
|
-
documentLevelPatch,
|
|
18
|
-
fieldLevelPatch,
|
|
19
|
-
} from './configuration';
|
|
11
|
+
import { baseDocumentLevelConfig } from './configuration/baseDocumentLevelConfig';
|
|
12
|
+
import { baseFieldLevelConfig } from './configuration/baseFieldLevelConfig';
|
|
13
|
+
import { findLatestDraft } from './configuration/utils/findLatestDraft';
|
|
14
|
+
import { documentLevelPatch } from './configuration/baseDocumentLevelConfig/documentLevelPatch';
|
|
15
|
+
import { fieldLevelPatch } from './configuration/baseFieldLevelConfig';
|
|
20
16
|
import {
|
|
21
17
|
BaseDocumentSerializer,
|
|
22
18
|
BaseDocumentDeserializer,
|
|
@@ -39,7 +35,6 @@ export {
|
|
|
39
35
|
TranslationsTab,
|
|
40
36
|
//helpers for end developers who may need to customize serialization
|
|
41
37
|
findLatestDraft,
|
|
42
|
-
legacyDocumentLevelPatch,
|
|
43
38
|
documentLevelPatch,
|
|
44
39
|
fieldLevelPatch,
|
|
45
40
|
BaseDocumentSerializer,
|
|
@@ -51,9 +46,13 @@ export {
|
|
|
51
46
|
|
|
52
47
|
import { GTAdapter } from './adapter';
|
|
53
48
|
import { definePlugin } from 'sanity';
|
|
49
|
+
import { route } from 'sanity/router';
|
|
54
50
|
import { gt, gtConfig } from './adapter/core';
|
|
55
51
|
import { GTSerializedDocument } from './types';
|
|
56
52
|
import { libraryDefaultLocale } from 'generaltranslation/internal';
|
|
53
|
+
import type { IgnoreFields, TranslateDocumentFilter } from './adapter/types';
|
|
54
|
+
import TranslationsTool from './components/page/TranslationsTool';
|
|
55
|
+
import { SECRETS_NAMESPACE } from './utils/shared';
|
|
57
56
|
|
|
58
57
|
interface ConfigOptions {
|
|
59
58
|
adapter: Adapter;
|
|
@@ -76,12 +75,6 @@ export const defaultDocumentLevelConfig: ConfigOptions = {
|
|
|
76
75
|
secretsNamespace: 'generaltranslation',
|
|
77
76
|
};
|
|
78
77
|
|
|
79
|
-
export const legacyDocumentLevelConfig: ConfigOptions = {
|
|
80
|
-
...baseLegacyDocumentLevelConfig,
|
|
81
|
-
adapter: GTAdapter,
|
|
82
|
-
secretsNamespace: 'generaltranslation',
|
|
83
|
-
};
|
|
84
|
-
|
|
85
78
|
export const defaultFieldLevelConfig: ConfigOptions = {
|
|
86
79
|
...baseFieldLevelConfig,
|
|
87
80
|
adapter: GTAdapter,
|
|
@@ -90,12 +83,27 @@ export const defaultFieldLevelConfig: ConfigOptions = {
|
|
|
90
83
|
|
|
91
84
|
export { GTAdapter };
|
|
92
85
|
|
|
86
|
+
export type GTPluginConfig = Omit<
|
|
87
|
+
Parameters<typeof gt.setConfig>[0],
|
|
88
|
+
'locales'
|
|
89
|
+
> & {
|
|
90
|
+
locales: string[];
|
|
91
|
+
singletons?: string[];
|
|
92
|
+
// Optional mapping function to map source document ids to translated singleton document ids
|
|
93
|
+
// By default, the translated singleton document is is `${sourceDocumentId}-${locale}`
|
|
94
|
+
singletonMapping?: (sourceDocumentId: string, locale: string) => string;
|
|
95
|
+
ignoreFields?: IgnoreFields[];
|
|
96
|
+
languageField?: string;
|
|
97
|
+
translateDocuments?: TranslateDocumentFilter[];
|
|
98
|
+
secretsNamespace?: string;
|
|
99
|
+
};
|
|
100
|
+
|
|
93
101
|
/**
|
|
94
102
|
* Usage in `sanity.config.ts` (or .js)
|
|
95
103
|
*
|
|
96
104
|
* ```ts
|
|
97
105
|
* import {defineConfig} from 'sanity'
|
|
98
|
-
* import {gtPlugin} from '
|
|
106
|
+
* import {gtPlugin} from 'gt-sanity'
|
|
99
107
|
*
|
|
100
108
|
* export default defineConfig({
|
|
101
109
|
* // ...
|
|
@@ -103,18 +111,48 @@ export { GTAdapter };
|
|
|
103
111
|
* })
|
|
104
112
|
* ```
|
|
105
113
|
*/
|
|
106
|
-
export const gtPlugin = definePlugin<
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
}
|
|
120
|
-
|
|
114
|
+
export const gtPlugin = definePlugin<GTPluginConfig>(
|
|
115
|
+
({
|
|
116
|
+
languageField = 'language',
|
|
117
|
+
sourceLocale = libraryDefaultLocale,
|
|
118
|
+
locales,
|
|
119
|
+
customMapping,
|
|
120
|
+
apiKey,
|
|
121
|
+
projectId,
|
|
122
|
+
singletons,
|
|
123
|
+
singletonMapping,
|
|
124
|
+
ignoreFields,
|
|
125
|
+
translateDocuments,
|
|
126
|
+
secretsNamespace = SECRETS_NAMESPACE,
|
|
127
|
+
}) => {
|
|
128
|
+
// Validate the translateDocuments
|
|
129
|
+
translateDocuments = translateDocuments?.filter((filter) => {
|
|
130
|
+
if (filter.documentId || filter.type) {
|
|
131
|
+
return true;
|
|
132
|
+
}
|
|
133
|
+
return false;
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
gtConfig.init(
|
|
137
|
+
secretsNamespace,
|
|
138
|
+
languageField,
|
|
139
|
+
sourceLocale,
|
|
140
|
+
locales,
|
|
141
|
+
singletons || [],
|
|
142
|
+
// singletons is a string array of singleton document ids
|
|
143
|
+
singletonMapping ||
|
|
144
|
+
((sourceDocumentId, locale) => `${sourceDocumentId}-${locale}`),
|
|
145
|
+
ignoreFields || [],
|
|
146
|
+
translateDocuments || []
|
|
147
|
+
);
|
|
148
|
+
gt.setConfig({
|
|
149
|
+
sourceLocale: sourceLocale,
|
|
150
|
+
customMapping: customMapping,
|
|
151
|
+
apiKey: apiKey,
|
|
152
|
+
projectId: projectId,
|
|
153
|
+
});
|
|
154
|
+
return {
|
|
155
|
+
name: 'gt-sanity',
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
);
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { Secrets } from '../types';
|
|
2
|
+
import { gt, overrideConfig } from '../adapter/core';
|
|
3
|
+
|
|
4
|
+
export async function checkTranslationStatus(
|
|
5
|
+
fileQueryData: { versionId: string; fileId: string; locale: string }[],
|
|
6
|
+
downloadStatus: {
|
|
7
|
+
downloaded: Set<string>;
|
|
8
|
+
failed: Set<string>;
|
|
9
|
+
skipped: Set<string>;
|
|
10
|
+
},
|
|
11
|
+
secrets: Secrets
|
|
12
|
+
) {
|
|
13
|
+
overrideConfig(secrets);
|
|
14
|
+
try {
|
|
15
|
+
// Only query for files that haven't been downloaded yet
|
|
16
|
+
const currentQueryData = fileQueryData.filter(
|
|
17
|
+
(item) =>
|
|
18
|
+
!downloadStatus.downloaded.has(`${item.fileId}:${item.locale}`) &&
|
|
19
|
+
!downloadStatus.failed.has(`${item.fileId}:${item.locale}`) &&
|
|
20
|
+
!downloadStatus.skipped.has(`${item.fileId}:${item.locale}`)
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
// If all files have been downloaded, we're done
|
|
24
|
+
if (currentQueryData.length === 0) {
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
// Check for translations
|
|
28
|
+
const responseData = await gt.checkFileTranslations(currentQueryData);
|
|
29
|
+
|
|
30
|
+
const translations = responseData.translations || [];
|
|
31
|
+
|
|
32
|
+
// Filter for ready translations
|
|
33
|
+
const readyTranslations = translations.filter(
|
|
34
|
+
(translation) => translation.isReady && translation.fileId
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
return readyTranslations;
|
|
38
|
+
} catch (error) {
|
|
39
|
+
console.error('Error checking translation status', error);
|
|
40
|
+
return [];
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { libraryDefaultLocale } from 'generaltranslation/internal';
|
|
2
|
+
import { gt, overrideConfig } from '../adapter/core';
|
|
3
|
+
import type { Secrets } from '../types';
|
|
4
|
+
|
|
5
|
+
export async function createJobs(
|
|
6
|
+
uploadResult: Awaited<ReturnType<typeof gt.uploadSourceFiles>>,
|
|
7
|
+
localeIds: string[],
|
|
8
|
+
secrets: Secrets
|
|
9
|
+
): Promise<Awaited<ReturnType<typeof gt.enqueueFiles>>> {
|
|
10
|
+
overrideConfig(secrets);
|
|
11
|
+
const enqueueResult = await gt.enqueueFiles(uploadResult.uploadedFiles, {
|
|
12
|
+
sourceLocale: gt.sourceLocale || libraryDefaultLocale,
|
|
13
|
+
targetLocales: localeIds,
|
|
14
|
+
});
|
|
15
|
+
return enqueueResult;
|
|
16
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { gt, overrideConfig } from '../adapter/core';
|
|
2
|
+
import type { Secrets } from '../types';
|
|
3
|
+
|
|
4
|
+
export type BatchedFiles = Array<{
|
|
5
|
+
documentId: string;
|
|
6
|
+
versionId: string;
|
|
7
|
+
translationId: string;
|
|
8
|
+
locale: string;
|
|
9
|
+
}>;
|
|
10
|
+
|
|
11
|
+
export type DownloadedFile = {
|
|
12
|
+
docData: {
|
|
13
|
+
documentId: string;
|
|
14
|
+
versionId: string;
|
|
15
|
+
translationId: string;
|
|
16
|
+
locale: string;
|
|
17
|
+
};
|
|
18
|
+
data: string;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Downloads multiple translation files in a single batch request
|
|
22
|
+
* @param files - Array of files to download with their output paths
|
|
23
|
+
* @param maxRetries - Maximum number of retry attempts
|
|
24
|
+
* @param retryDelay - Delay between retries in milliseconds
|
|
25
|
+
* @returns Object containing successful and failed file IDs
|
|
26
|
+
*/
|
|
27
|
+
export async function downloadTranslations(
|
|
28
|
+
files: BatchedFiles,
|
|
29
|
+
secrets: Secrets,
|
|
30
|
+
maxRetries = 3,
|
|
31
|
+
retryDelay = 1000
|
|
32
|
+
): Promise<DownloadedFile[]> {
|
|
33
|
+
overrideConfig(secrets);
|
|
34
|
+
let retries = 0;
|
|
35
|
+
const fileIds = files.map((file) => file.translationId);
|
|
36
|
+
|
|
37
|
+
const map = new Map(files.map((file) => [file.translationId, file]));
|
|
38
|
+
const result = [] as DownloadedFile[];
|
|
39
|
+
|
|
40
|
+
while (retries <= maxRetries) {
|
|
41
|
+
try {
|
|
42
|
+
// Download the files
|
|
43
|
+
const responseData = await gt.downloadFileBatch(fileIds);
|
|
44
|
+
const downloadedFiles = responseData.files || [];
|
|
45
|
+
|
|
46
|
+
// Process each file in the response
|
|
47
|
+
for (const file of downloadedFiles) {
|
|
48
|
+
const documentData = map.get(file.id);
|
|
49
|
+
if (!documentData) {
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
result.push({
|
|
54
|
+
docData: documentData,
|
|
55
|
+
data: file.data,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return result;
|
|
60
|
+
} catch (error) {
|
|
61
|
+
// Increment retry counter and wait before next attempt
|
|
62
|
+
retries++;
|
|
63
|
+
await new Promise((resolve) => setTimeout(resolve, retryDelay));
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return result;
|
|
68
|
+
}
|