gt-sanity 0.0.6 → 1.0.1

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 (88) hide show
  1. package/LICENSE.md +1 -1
  2. package/dist/index.d.mts +95 -73
  3. package/dist/index.d.ts +95 -73
  4. package/dist/index.js +6233 -1162
  5. package/dist/index.js.map +1 -1
  6. package/dist/index.mjs +6292 -1193
  7. package/dist/index.mjs.map +1 -1
  8. package/package.json +9 -4
  9. package/src/adapter/core.ts +41 -4
  10. package/src/adapter/getLocales.ts +2 -2
  11. package/src/components/TranslationsProvider.tsx +942 -0
  12. package/src/components/page/BatchProgress.tsx +27 -0
  13. package/src/components/page/ImportAllDialog.tsx +51 -0
  14. package/src/components/page/ImportMissingDialog.tsx +55 -0
  15. package/src/components/page/TranslateAllDialog.tsx +55 -0
  16. package/src/components/page/TranslationsTable.tsx +81 -0
  17. package/src/components/page/TranslationsTool.tsx +299 -837
  18. package/src/components/shared/BaseTranslationWrapper.tsx +82 -0
  19. package/src/components/shared/LocaleCheckbox.tsx +47 -0
  20. package/src/components/shared/SingleDocumentView.tsx +108 -0
  21. package/src/components/tab/TranslationView.tsx +379 -0
  22. package/src/components/tab/TranslationsTab.tsx +25 -0
  23. package/src/configuration/baseDocumentLevelConfig/documentLevelPatch.ts +6 -9
  24. package/src/configuration/baseDocumentLevelConfig/helpers/createI18nDocAndPatchMetadata.ts +5 -24
  25. package/src/configuration/baseDocumentLevelConfig/helpers/patchI18nDoc.ts +3 -23
  26. package/src/configuration/baseDocumentLevelConfig/index.ts +16 -68
  27. package/src/configuration/baseFieldLevelConfig.ts +15 -50
  28. package/src/index.ts +29 -43
  29. package/src/sanity-api/findDocuments.ts +44 -0
  30. package/src/sanity-api/publishDocuments.ts +49 -0
  31. package/src/sanity-api/resolveRefs.ts +146 -0
  32. package/src/serialization/BaseDocumentMerger.ts +138 -0
  33. package/src/serialization/BaseSerializationConfig.ts +220 -0
  34. package/src/serialization/__tests__/BaseDocumentDeserializer/__snapshots__/documentLevelDeserialization.test.ts.snap +189 -0
  35. package/src/serialization/__tests__/BaseDocumentDeserializer/__snapshots__/fieldLevelDeserialization.test.ts.snap +107 -0
  36. package/src/serialization/__tests__/BaseDocumentDeserializer/baseDeserialization.test.ts +397 -0
  37. package/src/serialization/__tests__/BaseDocumentDeserializer/documentLevelDeserialization.test.ts +107 -0
  38. package/src/serialization/__tests__/BaseDocumentDeserializer/fieldLevelDeserialization.test.ts +107 -0
  39. package/src/serialization/__tests__/BaseDocumentMerger/__snapshots__/documentLevelMerge.test.ts.snap +193 -0
  40. package/src/serialization/__tests__/BaseDocumentMerger/__snapshots__/fieldLevelMerge.test.ts.snap +97 -0
  41. package/src/serialization/__tests__/BaseDocumentMerger/baseMerge.test.ts +36 -0
  42. package/src/serialization/__tests__/BaseDocumentMerger/documentLevelMerge.test.ts +96 -0
  43. package/src/serialization/__tests__/BaseDocumentMerger/fieldLevelMerge.test.ts +142 -0
  44. package/src/serialization/__tests__/BaseDocumentMerger/utils.ts +52 -0
  45. package/src/serialization/__tests__/BaseDocumentSerializer/__snapshots__/documentInlineMarks.test.ts.snap +39 -0
  46. package/src/serialization/__tests__/BaseDocumentSerializer/__snapshots__/documentLevelSerialization.test.ts.snap +8 -0
  47. package/src/serialization/__tests__/BaseDocumentSerializer/__snapshots__/fieldLevelSerialization.test.ts.snap +8 -0
  48. package/src/serialization/__tests__/BaseDocumentSerializer/baseSerialization.test.ts +345 -0
  49. package/src/serialization/__tests__/BaseDocumentSerializer/documentInlineMarks.test.ts +53 -0
  50. package/src/serialization/__tests__/BaseDocumentSerializer/documentLevelSerialization.test.ts +120 -0
  51. package/src/serialization/__tests__/BaseDocumentSerializer/fieldLevelSerialization.test.ts +153 -0
  52. package/src/serialization/__tests__/BaseDocumentSerializer/utils.ts +27 -0
  53. package/src/serialization/__tests__/README +2 -0
  54. package/src/serialization/__tests__/__fixtures__/annotationAndInlineBlocks.json +140 -0
  55. package/src/serialization/__tests__/__fixtures__/customStyles.json +62 -0
  56. package/src/serialization/__tests__/__fixtures__/documentInlineMarks.json +70 -0
  57. package/src/serialization/__tests__/__fixtures__/documentLevelArticle.json +185 -0
  58. package/src/serialization/__tests__/__fixtures__/fieldLevelArticle.json +107 -0
  59. package/src/serialization/__tests__/__fixtures__/inlineDocumentLevelArticle.json +134 -0
  60. package/src/serialization/__tests__/__fixtures__/inlineSchema.ts +270 -0
  61. package/src/serialization/__tests__/__fixtures__/messy-html.html +26 -0
  62. package/src/serialization/__tests__/__fixtures__/nestedLanguageFields.json +54 -0
  63. package/src/serialization/__tests__/__fixtures__/schema.ts +310 -0
  64. package/src/serialization/__tests__/global.setup.ts +40 -0
  65. package/src/serialization/__tests__/helpers.ts +132 -0
  66. package/src/serialization/data.ts +82 -0
  67. package/src/serialization/deserialize/BaseDocumentDeserializer.ts +171 -0
  68. package/src/serialization/deserialize/helpers.ts +42 -0
  69. package/src/serialization/helpers.ts +18 -0
  70. package/src/serialization/index.ts +11 -0
  71. package/src/serialization/serialize/fieldFilters.ts +124 -0
  72. package/src/serialization/serialize/index.ts +284 -0
  73. package/src/serialization/types.ts +41 -0
  74. package/src/translation/importDocument.ts +4 -5
  75. package/src/translation/uploadFiles.ts +1 -1
  76. package/src/types.ts +3 -19
  77. package/src/utils/batchProcessor.ts +111 -0
  78. package/src/utils/importUtils.ts +95 -0
  79. package/src/utils/serialize.ts +25 -5
  80. package/src/utils/shared.ts +1 -1
  81. package/src/adapter/index.ts +0 -13
  82. package/src/components/NewTask.tsx +0 -251
  83. package/src/components/TaskView.tsx +0 -257
  84. package/src/components/TranslationContext.tsx +0 -24
  85. package/src/components/TranslationView.tsx +0 -114
  86. package/src/components/TranslationsTab.tsx +0 -181
  87. /package/src/components/{LanguageStatus.tsx → shared/LanguageStatus.tsx} +0 -0
  88. /package/src/components/{ProgressBar.tsx → shared/ProgressBar.tsx} +0 -0
@@ -1,7 +1,7 @@
1
1
  // adapted from https://github.com/sanity-io/sanity-translations-tab. See LICENSE.md for more details.
2
2
 
3
3
  import { SanityClient, SanityDocumentLike } from 'sanity';
4
- import { gtConfig } from '../../../adapter/core';
4
+ import { pluginConfig } from '../../../adapter/core';
5
5
  import { applyDocuments } from '../../../utils/applyDocuments';
6
6
 
7
7
  export async function createI18nDocAndPatchMetadata(
@@ -11,8 +11,7 @@ export async function createI18nDocAndPatchMetadata(
11
11
  client: SanityClient,
12
12
  translationMetadata: SanityDocumentLike,
13
13
  sourceDocumentId: string,
14
- languageField: string = 'language',
15
- publish: boolean = false
14
+ languageField: string = 'language'
16
15
  ): Promise<void> {
17
16
  translatedDoc[languageField] = localeId;
18
17
  const translations = translationMetadata.translations as Record<
@@ -34,16 +33,16 @@ export async function createI18nDocAndPatchMetadata(
34
33
  sourceDocumentId,
35
34
  sourceDocument,
36
35
  rest,
37
- gtConfig.getIgnoreFields()
36
+ pluginConfig.getIgnoreFields()
38
37
  );
39
38
 
40
39
  // Check if this is a singleton document and apply singleton mapping
41
- const singletons = gtConfig.getSingletons();
40
+ const singletons = pluginConfig.getSingletons();
42
41
  const isSingleton = singletons.includes(sourceDocumentId);
43
42
 
44
43
  let createDocumentPromise;
45
44
  if (isSingleton) {
46
- const singletonMapping = gtConfig.getSingletonMapping();
45
+ const singletonMapping = pluginConfig.getSingletonMapping();
47
46
  const translatedDocId = singletonMapping(sourceDocumentId, localeId);
48
47
  createDocumentPromise = client.create({
49
48
  ...appliedDocument,
@@ -79,22 +78,4 @@ export async function createI18nDocAndPatchMetadata(
79
78
  ])
80
79
  )
81
80
  .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
81
  }
@@ -2,7 +2,7 @@
2
2
 
3
3
  import { SanityClient, SanityDocumentLike } from 'sanity';
4
4
  import { applyDocuments } from '../../../utils/applyDocuments';
5
- import { gtConfig } from '../../../adapter/core';
5
+ import { pluginConfig } from '../../../adapter/core';
6
6
 
7
7
  const SYSTEM_FIELDS = ['_id', '_rev', '_updatedAt', 'language'];
8
8
 
@@ -14,8 +14,7 @@ export async function patchI18nDoc(
14
14
  sourceDocument: SanityDocumentLike,
15
15
  mergedDocument: SanityDocumentLike,
16
16
  translatedFields: Record<string, any>,
17
- client: SanityClient,
18
- publish: boolean = false
17
+ client: SanityClient
19
18
  ): Promise<void> {
20
19
  const cleanedMerge: Record<string, any> = {};
21
20
  Object.entries(mergedDocument).forEach(([key, value]) => {
@@ -37,32 +36,13 @@ export async function patchI18nDoc(
37
36
  cleanedSourceDocument[key] = value;
38
37
  }
39
38
  });
40
-
41
39
  const appliedDocument = applyDocuments(
42
40
  sourceDocumentId,
43
41
  cleanedSourceDocument,
44
42
  cleanedMerge,
45
- gtConfig.getIgnoreFields()
43
+ pluginConfig.getIgnoreFields()
46
44
  );
47
45
  const newDocument = await client
48
46
  .patch(i18nDocId, { set: appliedDocument })
49
47
  .commit();
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
48
  }
@@ -5,50 +5,22 @@ import {
5
5
  GTSerializedDocument,
6
6
  ImportTranslation,
7
7
  } from '../../types';
8
- import { SanityDocument } from 'sanity';
9
8
  import { findLatestDraft } from '../utils/findLatestDraft';
10
- import { documentLevelPatch } from './documentLevelPatch';
11
- import {
12
- BaseDocumentDeserializer,
13
- BaseDocumentSerializer,
14
- defaultStopTypes,
15
- customSerializers,
16
- customBlockDeserializers,
17
- } from 'sanity-naive-html-serializer';
18
- import { gtConfig } from '../../adapter/core';
9
+ import { pluginConfig } from '../../adapter/core';
10
+ import { importDocument } from '../../translation/importDocument';
11
+ import { serializeDocument } from '../../utils/serialize';
19
12
 
20
13
  export const baseDocumentLevelConfig = {
21
14
  exportForTranslation: async (
22
- ...params: Parameters<ExportForTranslation>
15
+ docInfo: Parameters<ExportForTranslation>[0],
16
+ context: Parameters<ExportForTranslation>[1]
23
17
  ): Promise<GTSerializedDocument> => {
24
- const [
25
- docInfo,
26
- context,
27
- serializationOptions = {},
28
- languageField = 'language',
29
- ] = params;
30
18
  const { client, schema } = context;
31
- const stopTypes = [
32
- ...(serializationOptions.additionalStopTypes ?? []),
33
- ...defaultStopTypes,
34
- ];
35
- const serializers = {
36
- ...customSerializers,
37
- types: {
38
- ...customSerializers.types,
39
- ...(serializationOptions.additionalSerializers ?? {}),
40
- },
41
- };
19
+ const languageField = pluginConfig.getLanguageField();
42
20
  const doc = await findLatestDraft(docInfo.documentId, client);
43
21
  delete doc[languageField];
44
- const baseLanguage = gtConfig.getSourceLocale();
45
- const serialized = BaseDocumentSerializer(schema).serializeDocument(
46
- doc,
47
- 'document',
48
- baseLanguage,
49
- stopTypes,
50
- serializers
51
- );
22
+ const baseLanguage = pluginConfig.getSourceLocale();
23
+ const serialized = serializeDocument(doc, schema, baseLanguage);
52
24
  return {
53
25
  content: serialized.content,
54
26
  documentId: docInfo.documentId,
@@ -56,43 +28,19 @@ export const baseDocumentLevelConfig = {
56
28
  };
57
29
  },
58
30
  importTranslation: (
59
- ...params: Parameters<ImportTranslation>
31
+ docInfo: Parameters<ImportTranslation>[0],
32
+ localeId: Parameters<ImportTranslation>[1],
33
+ document: Parameters<ImportTranslation>[2],
34
+ context: Parameters<ImportTranslation>[3],
35
+ mergeWithTargetLocale: boolean = false
60
36
  ): Promise<void> => {
61
- const [
37
+ return importDocument(
62
38
  docInfo,
63
39
  localeId,
64
40
  document,
65
41
  context,
66
- serializationOptions = {},
67
- languageField = 'language',
68
- mergeWithTargetLocale = false,
69
- publish = false,
70
- ] = params;
71
- const { client } = context;
72
- const deserializers = {
73
- types: {
74
- ...(serializationOptions.additionalDeserializers ?? {}),
75
- },
76
- };
77
- const blockDeserializers = [
78
- ...(serializationOptions.additionalBlockDeserializers ?? []),
79
- ...customBlockDeserializers,
80
- ];
81
-
82
- const deserialized = BaseDocumentDeserializer.deserializeDocument(
83
- document,
84
- deserializers,
85
- blockDeserializers
86
- ) as SanityDocument;
87
- return documentLevelPatch(
88
- docInfo, // versionId is not used here, since we just use the _rev id in the deserialized HTML itself
89
- deserialized,
90
- localeId,
91
- client,
92
- languageField,
93
- mergeWithTargetLocale,
94
- publish
42
+ mergeWithTargetLocale
95
43
  );
96
44
  },
97
- secretsNamespace: 'translationService',
45
+ secretsNamespace: 'generaltranslation',
98
46
  };
@@ -8,7 +8,7 @@ import {
8
8
  defaultStopTypes,
9
9
  customSerializers,
10
10
  customBlockDeserializers,
11
- } from 'sanity-naive-html-serializer';
11
+ } from '../serialization/';
12
12
 
13
13
  import type {
14
14
  ExportForTranslation,
@@ -18,7 +18,8 @@ import type {
18
18
  } from '../types';
19
19
  import { findLatestDraft } from './utils/findLatestDraft';
20
20
  import { findDocumentAtRevision } from './utils/findDocumentAtRevision';
21
- import { gtConfig } from '../adapter/core';
21
+ import { pluginConfig } from '../adapter/core';
22
+ import { deserializeDocument, serializeDocument } from '../utils/serialize';
22
23
 
23
24
  export const fieldLevelPatch = async (
24
25
  docInfo: GTFile,
@@ -28,7 +29,7 @@ export const fieldLevelPatch = async (
28
29
  mergeWithTargetLocale: boolean = false
29
30
  ): Promise<void> => {
30
31
  let baseDoc: SanityDocument;
31
- const baseLanguage = gtConfig.getSourceLocale();
32
+ const baseLanguage = pluginConfig.getSourceLocale();
32
33
  if (docInfo.documentId && docInfo.versionId) {
33
34
  baseDoc = await findDocumentAtRevision(
34
35
  docInfo.documentId,
@@ -51,30 +52,13 @@ export const fieldLevelPatch = async (
51
52
 
52
53
  export const baseFieldLevelConfig = {
53
54
  exportForTranslation: async (
54
- ...params: Parameters<ExportForTranslation>
55
+ docInfo: Parameters<ExportForTranslation>[0],
56
+ context: Parameters<ExportForTranslation>[1]
55
57
  ): Promise<GTSerializedDocument> => {
56
- const [docInfo, context, serializationOptions = {}] = params;
57
- const baseLanguage = gtConfig.getSourceLocale();
58
+ const baseLanguage = pluginConfig.getSourceLocale();
58
59
  const { client, schema } = context;
59
- const stopTypes = [
60
- ...(serializationOptions.additionalStopTypes ?? []),
61
- ...defaultStopTypes,
62
- ];
63
- const serializers = {
64
- ...customSerializers,
65
- types: {
66
- ...customSerializers.types,
67
- ...(serializationOptions.additionalSerializers ?? {}),
68
- },
69
- };
70
60
  const doc = await findLatestDraft(docInfo.documentId, client);
71
- const serialized = BaseDocumentSerializer(schema).serializeDocument(
72
- doc,
73
- 'field',
74
- baseLanguage,
75
- stopTypes,
76
- serializers
77
- );
61
+ const serialized = serializeDocument(doc, schema, baseLanguage);
78
62
  return {
79
63
  content: serialized.content,
80
64
  documentId: docInfo.documentId,
@@ -82,33 +66,14 @@ export const baseFieldLevelConfig = {
82
66
  };
83
67
  },
84
68
  importTranslation: (
85
- ...params: Parameters<ImportTranslation>
69
+ docInfo: Parameters<ImportTranslation>[0],
70
+ localeId: Parameters<ImportTranslation>[1],
71
+ document: Parameters<ImportTranslation>[2],
72
+ context: Parameters<ImportTranslation>[3],
73
+ mergeWithTargetLocale: boolean = false
86
74
  ): Promise<void> => {
87
- const [
88
- docInfo,
89
- localeId,
90
- document,
91
- context,
92
- serializationOptions = {},
93
- ,
94
- mergeWithTargetLocale,
95
- ] = params;
96
75
  const { client } = context;
97
- const deserializers = {
98
- types: {
99
- ...(serializationOptions.additionalDeserializers ?? {}),
100
- },
101
- };
102
- const blockDeserializers = [
103
- ...(serializationOptions.additionalBlockDeserializers ?? []),
104
- ...customBlockDeserializers,
105
- ];
106
-
107
- const deserialized = BaseDocumentDeserializer.deserializeDocument(
108
- document,
109
- deserializers,
110
- blockDeserializers
111
- ) as SanityDocument;
76
+ const deserialized = deserializeDocument(document);
112
77
  return fieldLevelPatch(
113
78
  docInfo,
114
79
  deserialized,
@@ -117,5 +82,5 @@ export const baseFieldLevelConfig = {
117
82
  mergeWithTargetLocale
118
83
  );
119
84
  },
120
- secretsNamespace: 'translationService',
85
+ secretsNamespace: 'generaltranslation',
121
86
  };
package/src/index.ts CHANGED
@@ -1,4 +1,4 @@
1
- import TranslationsTab from './components/TranslationsTab';
1
+ import TranslationsTab from './components/tab/TranslationsTab';
2
2
  import {
3
3
  Secrets,
4
4
  Adapter,
@@ -6,13 +6,8 @@ import {
6
6
  ImportTranslation,
7
7
  TranslationFunctionContext,
8
8
  TranslationsTabConfigOptions,
9
- GTFile,
10
9
  } from './types';
11
- import { baseDocumentLevelConfig } from './configuration/baseDocumentLevelConfig';
12
- import { baseFieldLevelConfig } from './configuration/baseFieldLevelConfig';
13
10
  import { findLatestDraft } from './configuration/utils/findLatestDraft';
14
- import { documentLevelPatch } from './configuration/baseDocumentLevelConfig/documentLevelPatch';
15
- import { fieldLevelPatch } from './configuration/baseFieldLevelConfig';
16
11
  import {
17
12
  BaseDocumentSerializer,
18
13
  BaseDocumentDeserializer,
@@ -20,7 +15,7 @@ import {
20
15
  defaultStopTypes,
21
16
  customSerializers,
22
17
  SerializedDocument,
23
- } from 'sanity-naive-html-serializer';
18
+ } from './serialization';
24
19
 
25
20
  export type {
26
21
  Secrets,
@@ -35,53 +30,24 @@ export {
35
30
  TranslationsTab,
36
31
  //helpers for end developers who may need to customize serialization
37
32
  findLatestDraft,
38
- documentLevelPatch,
39
- fieldLevelPatch,
40
33
  BaseDocumentSerializer,
41
34
  BaseDocumentDeserializer,
42
35
  BaseDocumentMerger,
43
36
  defaultStopTypes,
44
37
  customSerializers,
38
+ attachGTData,
39
+ detachGTData,
45
40
  };
46
41
 
47
- import { GTAdapter } from './adapter';
48
42
  import { definePlugin } from 'sanity';
49
43
  import { route } from 'sanity/router';
50
- import { gt, gtConfig } from './adapter/core';
51
- import { GTSerializedDocument } from './types';
44
+ import { gt, pluginConfig } from './adapter/core';
52
45
  import { libraryDefaultLocale } from 'generaltranslation/internal';
53
46
  import type { IgnoreFields, TranslateDocumentFilter } from './adapter/types';
54
47
  import TranslationsTool from './components/page/TranslationsTool';
55
48
  import { SECRETS_NAMESPACE } from './utils/shared';
56
-
57
- interface ConfigOptions {
58
- adapter: Adapter;
59
- secretsNamespace: string | null;
60
- exportForTranslation: (
61
- docInfo: GTFile,
62
- context: TranslationFunctionContext
63
- ) => Promise<GTSerializedDocument>;
64
- importTranslation: (
65
- docInfo: GTFile,
66
- localeId: string,
67
- doc: string,
68
- context: TranslationFunctionContext
69
- ) => Promise<void>;
70
- }
71
-
72
- export const defaultDocumentLevelConfig: ConfigOptions = {
73
- ...baseDocumentLevelConfig,
74
- adapter: GTAdapter,
75
- secretsNamespace: 'generaltranslation',
76
- };
77
-
78
- export const defaultFieldLevelConfig: ConfigOptions = {
79
- ...baseFieldLevelConfig,
80
- adapter: GTAdapter,
81
- secretsNamespace: 'generaltranslation',
82
- };
83
-
84
- export { GTAdapter };
49
+ import type { PortableTextHtmlComponents } from '@portabletext/to-html';
50
+ import { attachGTData, detachGTData } from './serialization/data';
85
51
 
86
52
  export type GTPluginConfig = Omit<
87
53
  Parameters<typeof gt.setConfig>[0],
@@ -96,6 +62,10 @@ export type GTPluginConfig = Omit<
96
62
  languageField?: string;
97
63
  translateDocuments?: TranslateDocumentFilter[];
98
64
  secretsNamespace?: string;
65
+ additionalStopTypes?: string[];
66
+ additionalSerializers?: Partial<PortableTextHtmlComponents>;
67
+ additionalDeserializers?: Record<string, any>;
68
+ additionalBlockDeserializers?: any[];
99
69
  };
100
70
 
101
71
  /**
@@ -124,6 +94,10 @@ export const gtPlugin = definePlugin<GTPluginConfig>(
124
94
  ignoreFields,
125
95
  translateDocuments,
126
96
  secretsNamespace = SECRETS_NAMESPACE,
97
+ additionalStopTypes = [],
98
+ additionalSerializers = {},
99
+ additionalDeserializers = {},
100
+ additionalBlockDeserializers = [],
127
101
  }) => {
128
102
  // Validate the translateDocuments
129
103
  translateDocuments = translateDocuments?.filter((filter) => {
@@ -133,7 +107,7 @@ export const gtPlugin = definePlugin<GTPluginConfig>(
133
107
  return false;
134
108
  });
135
109
 
136
- gtConfig.init(
110
+ pluginConfig.init(
137
111
  secretsNamespace,
138
112
  languageField,
139
113
  sourceLocale,
@@ -143,7 +117,11 @@ export const gtPlugin = definePlugin<GTPluginConfig>(
143
117
  singletonMapping ||
144
118
  ((sourceDocumentId, locale) => `${sourceDocumentId}-${locale}`),
145
119
  ignoreFields || [],
146
- translateDocuments || []
120
+ translateDocuments || [],
121
+ additionalStopTypes,
122
+ additionalSerializers,
123
+ additionalDeserializers,
124
+ additionalBlockDeserializers
147
125
  );
148
126
  gt.setConfig({
149
127
  sourceLocale: sourceLocale,
@@ -153,6 +131,14 @@ export const gtPlugin = definePlugin<GTPluginConfig>(
153
131
  });
154
132
  return {
155
133
  name: 'gt-sanity',
134
+ tools: [
135
+ {
136
+ name: 'translations',
137
+ title: 'Translations',
138
+ component: TranslationsTool,
139
+ router: route.create('/*'),
140
+ },
141
+ ],
156
142
  };
157
143
  }
158
144
  );
@@ -0,0 +1,44 @@
1
+ import { SanityClient } from 'sanity';
2
+ import { pluginConfig } from '../adapter/core';
3
+
4
+ export async function findTranslatedDocuments(
5
+ documentId: string,
6
+ client: SanityClient
7
+ ) {
8
+ const documents = await client.fetch(
9
+ `*[_type == "translation.metadata" && references($documentId)]`
10
+ );
11
+ return documents;
12
+ }
13
+
14
+ export async function findTranslatedDocumentForLocale(
15
+ sourceDocumentId: string,
16
+ localeId: string,
17
+ client: SanityClient
18
+ ) {
19
+ const cleanDocId = sourceDocumentId.replace('drafts.', '');
20
+
21
+ // Try both clean and original IDs to be safe, and use -> to directly fetch the translated doc
22
+ const query = `*[
23
+ _type == "translation.metadata" &&
24
+ (
25
+ translations[_key == $sourceLocale][0].value._ref == $cleanDocId
26
+ ) &&
27
+ defined(translations[_key == $localeId])
28
+ ][0].translations[_key == $localeId][0].value->`;
29
+
30
+ const translatedDoc = await client.fetch(query, {
31
+ sourceLocale: pluginConfig.getSourceLocale(),
32
+ cleanDocId,
33
+ localeId,
34
+ });
35
+
36
+ return translatedDoc || null;
37
+ }
38
+
39
+ export async function findDocument(documentId: string, client: SanityClient) {
40
+ const query = `*[_id == $id]`;
41
+ const params = { id: documentId };
42
+ const document = await client.fetch(query, params);
43
+ return document[0] || null;
44
+ }
@@ -0,0 +1,49 @@
1
+ import { SanityClient } from 'sanity';
2
+ import { processBatch } from '../utils/batchProcessor';
3
+ import { findDocument } from './findDocuments';
4
+
5
+ export async function publishDocument(
6
+ documentId: string,
7
+ client: SanityClient
8
+ ) {
9
+ try {
10
+ // only publish if the document is a draft
11
+ if (documentId.startsWith('drafts.')) {
12
+ await client.action(
13
+ {
14
+ actionType: 'sanity.action.document.publish',
15
+ draftId: documentId,
16
+ publishedId: documentId.replace('drafts.', ''),
17
+ },
18
+ {}
19
+ );
20
+ }
21
+ } catch (error) {
22
+ console.error('Error publishing document', error);
23
+ }
24
+ }
25
+
26
+ export async function publishTranslations(
27
+ documentIds: string[],
28
+ client: SanityClient
29
+ ) {
30
+ const publishedDocumentIds: string[] = [];
31
+ await processBatch(
32
+ documentIds,
33
+ async (documentId) => {
34
+ const document = await findDocument(`drafts.${documentId}`, client);
35
+ if (!document) {
36
+ return { documentId, published: false };
37
+ }
38
+ await publishDocument(document._id, client);
39
+ publishedDocumentIds.push(documentId);
40
+ return { documentId, published: true };
41
+ },
42
+ {
43
+ onItemFailure: (documentId, error) => {
44
+ console.error(`Failed to publish document ${documentId}:`, error);
45
+ },
46
+ }
47
+ );
48
+ return publishedDocumentIds;
49
+ }