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.
Files changed (48) hide show
  1. package/LICENSE.md +1 -8
  2. package/README.md +5 -5
  3. package/dist/index.d.mts +35 -30
  4. package/dist/index.d.ts +35 -30
  5. package/dist/index.js +234 -123
  6. package/dist/index.js.map +1 -1
  7. package/dist/index.mjs +237 -124
  8. package/dist/index.mjs.map +1 -1
  9. package/package.json +5 -3
  10. package/src/adapter/core.ts +72 -7
  11. package/src/adapter/createTask.ts +1 -1
  12. package/src/adapter/types.ts +9 -0
  13. package/src/components/LanguageStatus.tsx +2 -0
  14. package/src/components/NewTask.tsx +2 -0
  15. package/src/components/ProgressBar.tsx +2 -0
  16. package/src/components/TaskView.tsx +2 -0
  17. package/src/components/TranslationContext.tsx +5 -0
  18. package/src/components/TranslationView.tsx +34 -2
  19. package/src/components/TranslationsTab.tsx +4 -0
  20. package/src/components/page/TranslationsTool.tsx +876 -0
  21. package/src/configuration/baseDocumentLevelConfig/documentLevelPatch.ts +23 -10
  22. package/src/configuration/baseDocumentLevelConfig/helpers/createI18nDocAndPatchMetadata.ts +77 -24
  23. package/src/configuration/baseDocumentLevelConfig/helpers/createTranslationMetadata.ts +2 -0
  24. package/src/configuration/baseDocumentLevelConfig/helpers/getOrCreateTranslationMetadata.ts +2 -0
  25. package/src/configuration/baseDocumentLevelConfig/helpers/getTranslationMetadata.ts +2 -0
  26. package/src/configuration/baseDocumentLevelConfig/helpers/patchI18nDoc.ts +51 -8
  27. package/src/configuration/baseDocumentLevelConfig/index.ts +6 -37
  28. package/src/configuration/baseFieldLevelConfig.ts +4 -1
  29. package/src/configuration/utils/checkSerializationVersion.ts +2 -0
  30. package/src/configuration/utils/findDocumentAtRevision.ts +2 -0
  31. package/src/configuration/utils/findLatestDraft.ts +2 -0
  32. package/src/hooks/useClient.ts +3 -1
  33. package/src/hooks/useSecrets.ts +2 -0
  34. package/src/index.ts +70 -32
  35. package/src/translation/checkTranslationStatus.ts +42 -0
  36. package/src/translation/createJobs.ts +16 -0
  37. package/src/translation/downloadTranslations.ts +68 -0
  38. package/src/translation/importDocument.ts +24 -0
  39. package/src/translation/initProject.ts +61 -0
  40. package/src/translation/uploadFiles.ts +32 -0
  41. package/src/types.ts +4 -1
  42. package/src/utils/applyDocuments.ts +72 -0
  43. package/src/utils/serialize.ts +32 -0
  44. package/src/utils/shared.ts +1 -0
  45. package/src/configuration/baseDocumentLevelConfig/helpers/index.ts +0 -5
  46. package/src/configuration/baseDocumentLevelConfig/legacyDocumentLevelPatch.ts +0 -69
  47. package/src/configuration/index.ts +0 -18
  48. 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, findDocumentAtRevision } from '../utils';
5
- import {
6
- createI18nDocAndPatchMetadata,
7
- getOrCreateTranslationMetadata,
8
- patchI18nDoc,
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(docInfo.documentId, merged, translatedFields, client);
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
- languageField
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 const createI18nDocAndPatchMetadata = (
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
- languageField: string = 'language'
9
- ): void => {
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
- client.create({ ...rest, _id: 'drafts.' }).then((doc) => {
26
- const _ref = doc._id.replace('drafts.', '');
27
- client
28
- .transaction()
29
- .patch(translationMetadata._id, (p) =>
30
- p.insert(operation, location, [
31
- {
32
- _key: localeId,
33
- _type: 'internationalizedArrayReferenceValue',
34
- value: {
35
- _type: 'reference',
36
- _ref,
37
- _weak: true,
38
- _strengthenOnPublish: {
39
- type: doc._type,
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
- .commit();
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,3 +1,5 @@
1
+ // adapted from https://github.com/sanity-io/sanity-translations-tab. See LICENSE.md for more details.
2
+
1
3
  import {
2
4
  KeyedObject,
3
5
  Reference,
@@ -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 {
2
4
  KeyedObject,
3
5
  Reference,
@@ -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, SanityDocumentLike } from 'sanity';
2
4
 
3
5
  export const getTranslationMetadata = (
@@ -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 const patchI18nDoc = (
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
- ): void => {
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
- !['_id', '_rev', '_updatedAt', 'language'].includes(key)
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
- client
22
- .transaction()
23
- .patch(i18nDocId, (p) => p.set(cleanedMerge))
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, findDocumentAtRevision } from './utils';
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');
@@ -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
 
3
5
  //revision fetch
@@ -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
 
3
5
  //use perspectives in the future
@@ -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: '2022-12-07' });
6
+ return useSanityClient({ apiVersion: '2025-09-15' });
5
7
  };
@@ -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 { useState, useEffect } from 'react';
2
4
  import { SanityDocumentLike } from 'sanity';
3
5
  import { useClient } from './useClient';
package/src/index.ts CHANGED
@@ -8,15 +8,11 @@ import {
8
8
  TranslationsTabConfigOptions,
9
9
  GTFile,
10
10
  } from './types';
11
- import {
12
- baseDocumentLevelConfig,
13
- legacyDocumentLevelConfig as baseLegacyDocumentLevelConfig,
14
- legacyDocumentLevelPatch,
15
- baseFieldLevelConfig,
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 '@generaltranslation/sanity'
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
- Omit<Parameters<typeof gt.setConfig>[0], 'locales'> & { locales: string[] }
108
- >(({ sourceLocale, locales, customMapping, apiKey, projectId }) => {
109
- gtConfig.setLocales(locales);
110
- gtConfig.setSourceLocale(sourceLocale || libraryDefaultLocale);
111
- gt.setConfig({
112
- sourceLocale: sourceLocale,
113
- customMapping: customMapping,
114
- apiKey: apiKey,
115
- projectId: projectId,
116
- });
117
- return {
118
- name: '@generaltranslation/sanity',
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
+ }