gt-sanity 1.0.2 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +3 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +337 -268
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +340 -271
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/actions/translateAction.tsx +24 -0
- package/src/components/TranslateButton.tsx +29 -0
- package/src/components/TranslationsProvider.tsx +35 -29
- package/src/components/tab/TranslationView.tsx +135 -87
- package/src/index.ts +15 -0
package/package.json
CHANGED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { DocumentActionComponent } from 'sanity';
|
|
2
|
+
import { useRouter } from 'sanity/router';
|
|
3
|
+
import { TranslateIcon } from '@sanity/icons';
|
|
4
|
+
|
|
5
|
+
export const translateAction: DocumentActionComponent = (props) => {
|
|
6
|
+
const router = useRouter();
|
|
7
|
+
|
|
8
|
+
return {
|
|
9
|
+
label: 'Translate',
|
|
10
|
+
icon: TranslateIcon,
|
|
11
|
+
tone: 'primary',
|
|
12
|
+
onHandle: () => {
|
|
13
|
+
// Switch to the translation tab using the document ID and type
|
|
14
|
+
const { id, type } = props;
|
|
15
|
+
|
|
16
|
+
// Navigate to the translation view for this document
|
|
17
|
+
router.navigateIntent('edit', {
|
|
18
|
+
id,
|
|
19
|
+
type,
|
|
20
|
+
view: 'general-translation',
|
|
21
|
+
});
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { Button } from '@sanity/ui';
|
|
2
|
+
import { TranslateIcon } from '@sanity/icons';
|
|
3
|
+
import { useRouter } from 'sanity/router';
|
|
4
|
+
|
|
5
|
+
export function TranslateButton() {
|
|
6
|
+
const router = useRouter();
|
|
7
|
+
|
|
8
|
+
const handleClick = () => {
|
|
9
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
10
|
+
const panes = router.state.panes as any[];
|
|
11
|
+
if (panes && panes.length > 0) {
|
|
12
|
+
const currentPane = panes[0];
|
|
13
|
+
router.navigateUrl({
|
|
14
|
+
...currentPane,
|
|
15
|
+
view: 'general-translation',
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<Button
|
|
22
|
+
icon={TranslateIcon}
|
|
23
|
+
text='Translate'
|
|
24
|
+
onClick={handleClick}
|
|
25
|
+
mode='ghost'
|
|
26
|
+
tone='primary'
|
|
27
|
+
/>
|
|
28
|
+
);
|
|
29
|
+
}
|
|
@@ -77,8 +77,8 @@ interface TranslationsContextType {
|
|
|
77
77
|
handleImportMissing: () => Promise<void>;
|
|
78
78
|
handleRefreshAll: () => Promise<void>;
|
|
79
79
|
handleImportDocument: (documentId: string, localeId: string) => Promise<void>;
|
|
80
|
-
handlePatchDocumentReferences: () => Promise<
|
|
81
|
-
handlePublishAllTranslations: () => Promise<
|
|
80
|
+
handlePatchDocumentReferences: () => Promise<number>;
|
|
81
|
+
handlePublishAllTranslations: () => Promise<number>;
|
|
82
82
|
}
|
|
83
83
|
|
|
84
84
|
const TranslationsContext = createContext<TranslationsContextType | null>(null);
|
|
@@ -594,32 +594,32 @@ export const TranslationsProvider: React.FC<TranslationsProviderProps> = ({
|
|
|
594
594
|
async (documentId: string, localeId: string) => {
|
|
595
595
|
if (!secrets) return;
|
|
596
596
|
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
const status = translationStatuses.get(key);
|
|
597
|
+
const key = `${documentId}:${localeId}`;
|
|
598
|
+
const status = translationStatuses.get(key);
|
|
600
599
|
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
600
|
+
if (!status?.isReady || !status.translationId) {
|
|
601
|
+
toast.push({
|
|
602
|
+
title: `Translation not ready for ${documentId} (${localeId})`,
|
|
603
|
+
status: 'warning',
|
|
604
|
+
closable: true,
|
|
605
|
+
});
|
|
606
|
+
return;
|
|
607
|
+
}
|
|
609
608
|
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
609
|
+
const document = documents.find(
|
|
610
|
+
(doc) => (doc._id?.replace('drafts.', '') || doc._id) === documentId
|
|
611
|
+
);
|
|
613
612
|
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
613
|
+
if (!document) {
|
|
614
|
+
toast.push({
|
|
615
|
+
title: `Document ${documentId} not found`,
|
|
616
|
+
status: 'error',
|
|
617
|
+
closable: true,
|
|
618
|
+
});
|
|
619
|
+
return;
|
|
620
|
+
}
|
|
622
621
|
|
|
622
|
+
try {
|
|
623
623
|
const downloadedFiles = await downloadTranslations(
|
|
624
624
|
[
|
|
625
625
|
{
|
|
@@ -682,11 +682,11 @@ export const TranslationsProvider: React.FC<TranslationsProviderProps> = ({
|
|
|
682
682
|
});
|
|
683
683
|
}
|
|
684
684
|
},
|
|
685
|
-
[secrets, documents, translationContext]
|
|
685
|
+
[secrets, documents, translationContext, translationStatuses]
|
|
686
686
|
);
|
|
687
687
|
|
|
688
688
|
const handlePatchDocumentReferences = useCallback(async () => {
|
|
689
|
-
if (!secrets || documents.length === 0) return;
|
|
689
|
+
if (!secrets || documents.length === 0) return 0;
|
|
690
690
|
|
|
691
691
|
setIsBusy(true);
|
|
692
692
|
|
|
@@ -775,6 +775,8 @@ export const TranslationsProvider: React.FC<TranslationsProviderProps> = ({
|
|
|
775
775
|
patchedCount > 0 || result.failureCount === 0 ? 'success' : 'error',
|
|
776
776
|
closable: true,
|
|
777
777
|
});
|
|
778
|
+
|
|
779
|
+
return patchedCount;
|
|
778
780
|
} catch (error) {
|
|
779
781
|
console.error('Error patching document references:', error);
|
|
780
782
|
toast.push({
|
|
@@ -782,6 +784,7 @@ export const TranslationsProvider: React.FC<TranslationsProviderProps> = ({
|
|
|
782
784
|
status: 'error',
|
|
783
785
|
closable: true,
|
|
784
786
|
});
|
|
787
|
+
return 0;
|
|
785
788
|
} finally {
|
|
786
789
|
setIsBusy(false);
|
|
787
790
|
setImportProgress({ current: 0, total: 0, isImporting: false });
|
|
@@ -789,7 +792,7 @@ export const TranslationsProvider: React.FC<TranslationsProviderProps> = ({
|
|
|
789
792
|
}, [secrets, documents, locales, client]);
|
|
790
793
|
|
|
791
794
|
const handlePublishAllTranslations = useCallback(async () => {
|
|
792
|
-
if (!secrets || documents.length === 0) return;
|
|
795
|
+
if (!secrets || documents.length === 0) return 0;
|
|
793
796
|
|
|
794
797
|
setIsBusy(true);
|
|
795
798
|
|
|
@@ -806,7 +809,7 @@ export const TranslationsProvider: React.FC<TranslationsProviderProps> = ({
|
|
|
806
809
|
status: 'warning',
|
|
807
810
|
closable: true,
|
|
808
811
|
});
|
|
809
|
-
return;
|
|
812
|
+
return 0;
|
|
810
813
|
}
|
|
811
814
|
|
|
812
815
|
const query = `*[
|
|
@@ -840,7 +843,7 @@ export const TranslationsProvider: React.FC<TranslationsProviderProps> = ({
|
|
|
840
843
|
status: 'warning',
|
|
841
844
|
closable: true,
|
|
842
845
|
});
|
|
843
|
-
return;
|
|
846
|
+
return 0;
|
|
844
847
|
}
|
|
845
848
|
|
|
846
849
|
const translatedDocumentIds = await publishTranslations(
|
|
@@ -853,6 +856,8 @@ export const TranslationsProvider: React.FC<TranslationsProviderProps> = ({
|
|
|
853
856
|
status: 'success',
|
|
854
857
|
closable: true,
|
|
855
858
|
});
|
|
859
|
+
|
|
860
|
+
return translatedDocumentIds.length;
|
|
856
861
|
} catch (error) {
|
|
857
862
|
console.error('Error publishing translations:', error);
|
|
858
863
|
toast.push({
|
|
@@ -860,6 +865,7 @@ export const TranslationsProvider: React.FC<TranslationsProviderProps> = ({
|
|
|
860
865
|
status: 'error',
|
|
861
866
|
closable: true,
|
|
862
867
|
});
|
|
868
|
+
return 0;
|
|
863
869
|
} finally {
|
|
864
870
|
setIsBusy(false);
|
|
865
871
|
}
|
|
@@ -15,12 +15,13 @@ import {
|
|
|
15
15
|
Flex,
|
|
16
16
|
Switch,
|
|
17
17
|
Tooltip,
|
|
18
|
+
useToast,
|
|
18
19
|
} from '@sanity/ui';
|
|
19
20
|
import { pluginConfig } from '../../adapter/core';
|
|
20
21
|
import { useTranslations } from '../TranslationsProvider';
|
|
21
22
|
import { LanguageStatus } from '../shared/LanguageStatus';
|
|
22
23
|
import { LocaleCheckbox } from '../shared/LocaleCheckbox';
|
|
23
|
-
import { DownloadIcon, LinkIcon } from '@sanity/icons';
|
|
24
|
+
import { DownloadIcon, LinkIcon, PublishIcon } from '@sanity/icons';
|
|
24
25
|
|
|
25
26
|
export const TranslationView = () => {
|
|
26
27
|
const {
|
|
@@ -35,11 +36,17 @@ export const TranslationView = () => {
|
|
|
35
36
|
importedTranslations,
|
|
36
37
|
setLocales,
|
|
37
38
|
handlePatchDocumentReferences,
|
|
39
|
+
handlePublishAllTranslations,
|
|
38
40
|
} = useTranslations();
|
|
39
41
|
|
|
40
42
|
const [autoImport, setAutoImport] = useState(false);
|
|
41
43
|
const [isImporting, setIsImporting] = useState(false);
|
|
42
44
|
const [autoRefresh, setAutoRefresh] = useState(true);
|
|
45
|
+
const [autoPatchReferences, setAutoPatchReferences] = useState(true);
|
|
46
|
+
const [autoPublish, setAutoPublish] = useState(true);
|
|
47
|
+
const [isPublishing, setIsPublishing] = useState(false);
|
|
48
|
+
|
|
49
|
+
const toast = useToast();
|
|
43
50
|
|
|
44
51
|
// Get the single document (first document in single document mode)
|
|
45
52
|
const document = documents[0];
|
|
@@ -76,70 +83,66 @@ export const TranslationView = () => {
|
|
|
76
83
|
return document._id?.replace('drafts.', '') || document._id;
|
|
77
84
|
}, [document]);
|
|
78
85
|
|
|
79
|
-
//
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
const completedTranslations = availableLocales.filter((locale) => {
|
|
84
|
-
const key = `${documentId}:${locale.localeId}`;
|
|
85
|
-
const status = translationStatuses.get(key);
|
|
86
|
-
return (
|
|
87
|
-
(status?.progress || 0) >= 100 &&
|
|
88
|
-
status?.isReady &&
|
|
89
|
-
!importedTranslations.has(key)
|
|
90
|
-
);
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
if (completedTranslations.length === 0) return;
|
|
94
|
-
|
|
95
|
-
setIsImporting(true);
|
|
96
|
-
try {
|
|
97
|
-
for (const locale of completedTranslations) {
|
|
98
|
-
await handleImportDocument(documentId, locale.localeId);
|
|
99
|
-
}
|
|
100
|
-
} finally {
|
|
101
|
-
setIsImporting(false);
|
|
102
|
-
}
|
|
103
|
-
}, [
|
|
104
|
-
autoImport,
|
|
105
|
-
isImporting,
|
|
106
|
-
documentId,
|
|
107
|
-
availableLocales,
|
|
108
|
-
translationStatuses,
|
|
109
|
-
importedTranslations,
|
|
110
|
-
handleImportDocument,
|
|
111
|
-
]);
|
|
86
|
+
// Unified import functionality
|
|
87
|
+
const handleImportTranslations = useCallback(
|
|
88
|
+
async (options: { autoOnly?: boolean } = {}) => {
|
|
89
|
+
const { autoOnly = false } = options;
|
|
112
90
|
|
|
113
|
-
|
|
114
|
-
|
|
91
|
+
// Check preconditions
|
|
92
|
+
if (isImporting || !documentId) return;
|
|
93
|
+
if (autoOnly && !autoImport) return;
|
|
115
94
|
|
|
116
|
-
|
|
117
|
-
try {
|
|
95
|
+
// Find translations ready to import
|
|
118
96
|
const readyTranslations = availableLocales.filter((locale) => {
|
|
119
97
|
const key = `${documentId}:${locale.localeId}`;
|
|
120
98
|
const status = translationStatuses.get(key);
|
|
121
99
|
return status?.isReady && !importedTranslations.has(key);
|
|
122
100
|
});
|
|
123
101
|
|
|
124
|
-
|
|
125
|
-
|
|
102
|
+
if (readyTranslations.length === 0) return;
|
|
103
|
+
|
|
104
|
+
setIsImporting(true);
|
|
105
|
+
try {
|
|
106
|
+
// Import all ready translations
|
|
107
|
+
await Promise.all(
|
|
108
|
+
readyTranslations.map((locale) =>
|
|
109
|
+
handleImportDocument(documentId, locale.localeId)
|
|
110
|
+
)
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
// Auto patch document references if enabled
|
|
114
|
+
if (autoPatchReferences) {
|
|
115
|
+
await handlePatchDocumentReferences();
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Auto publish translations if enabled
|
|
119
|
+
if (autoPublish) {
|
|
120
|
+
await handlePublishAllTranslations();
|
|
121
|
+
}
|
|
122
|
+
} finally {
|
|
123
|
+
setIsImporting(false);
|
|
126
124
|
}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
125
|
+
},
|
|
126
|
+
[
|
|
127
|
+
autoImport,
|
|
128
|
+
isImporting,
|
|
129
|
+
documentId,
|
|
130
|
+
availableLocales,
|
|
131
|
+
translationStatuses,
|
|
132
|
+
importedTranslations,
|
|
133
|
+
handleImportDocument,
|
|
134
|
+
autoPatchReferences,
|
|
135
|
+
handlePatchDocumentReferences,
|
|
136
|
+
autoPublish,
|
|
137
|
+
handlePublishAllTranslations,
|
|
138
|
+
toast,
|
|
139
|
+
]
|
|
140
|
+
);
|
|
138
141
|
|
|
139
|
-
// Check for completed translations on status updates
|
|
142
|
+
// Check for completed translations on status updates (auto-import)
|
|
140
143
|
useEffect(() => {
|
|
141
|
-
|
|
142
|
-
}, [
|
|
144
|
+
handleImportTranslations({ autoOnly: true });
|
|
145
|
+
}, [handleImportTranslations]);
|
|
143
146
|
|
|
144
147
|
// Auto refresh functionality
|
|
145
148
|
useEffect(() => {
|
|
@@ -147,7 +150,7 @@ export const TranslationView = () => {
|
|
|
147
150
|
|
|
148
151
|
const interval = setInterval(async () => {
|
|
149
152
|
await handleRefreshAll();
|
|
150
|
-
await
|
|
153
|
+
await handleImportTranslations({ autoOnly: true });
|
|
151
154
|
}, 10000);
|
|
152
155
|
|
|
153
156
|
return () => clearInterval(interval);
|
|
@@ -156,13 +159,13 @@ export const TranslationView = () => {
|
|
|
156
159
|
documentId,
|
|
157
160
|
availableLocales.length,
|
|
158
161
|
handleRefreshAll,
|
|
159
|
-
|
|
162
|
+
handleImportTranslations,
|
|
160
163
|
]);
|
|
161
164
|
|
|
162
165
|
useEffect(() => {
|
|
163
166
|
const initialRefresh = async () => {
|
|
164
167
|
await handleRefreshAll();
|
|
165
|
-
await
|
|
168
|
+
await handleImportTranslations({ autoOnly: true });
|
|
166
169
|
};
|
|
167
170
|
initialRefresh();
|
|
168
171
|
}, []);
|
|
@@ -316,7 +319,8 @@ export const TranslationView = () => {
|
|
|
316
319
|
<Flex gap={2} align='center'>
|
|
317
320
|
<Button
|
|
318
321
|
mode='ghost'
|
|
319
|
-
|
|
322
|
+
tone='primary'
|
|
323
|
+
onClick={() => handleImportTranslations()}
|
|
320
324
|
text={isImporting ? 'Importing...' : 'Import All'}
|
|
321
325
|
icon={DownloadIcon}
|
|
322
326
|
disabled={
|
|
@@ -327,36 +331,37 @@ export const TranslationView = () => {
|
|
|
327
331
|
return !status?.isReady || importedTranslations.has(key);
|
|
328
332
|
})
|
|
329
333
|
}
|
|
334
|
+
style={{ minWidth: '180px' }}
|
|
330
335
|
/>
|
|
331
|
-
<
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
/
|
|
340
|
-
{
|
|
341
|
-
availableLocales.filter((locale) => {
|
|
342
|
-
const key = `${documentId}:${locale.localeId}`;
|
|
343
|
-
const status = translationStatuses.get(key);
|
|
344
|
-
return status?.isReady;
|
|
345
|
-
}).length
|
|
346
|
-
}
|
|
347
|
-
</Text>
|
|
348
|
-
</Flex>
|
|
349
|
-
<Flex gap={2} align='center' style={{ whiteSpace: 'nowrap' }}>
|
|
350
|
-
<Text size={1}>Auto-import when complete</Text>
|
|
351
|
-
<Switch
|
|
352
|
-
checked={autoImport}
|
|
353
|
-
onChange={() => setAutoImport(!autoImport)}
|
|
354
|
-
disabled={isImporting}
|
|
355
|
-
/>
|
|
336
|
+
<Flex gap={2} align='center'>
|
|
337
|
+
<Switch
|
|
338
|
+
checked={autoImport}
|
|
339
|
+
onChange={() => setAutoImport(!autoImport)}
|
|
340
|
+
disabled={isImporting}
|
|
341
|
+
/>
|
|
342
|
+
<Text size={1}>Auto-import when complete</Text>
|
|
343
|
+
</Flex>
|
|
356
344
|
</Flex>
|
|
345
|
+
<Text size={1} muted>
|
|
346
|
+
Imported{' '}
|
|
347
|
+
{
|
|
348
|
+
availableLocales.filter((locale) => {
|
|
349
|
+
const key = `${documentId}:${locale.localeId}`;
|
|
350
|
+
return importedTranslations.has(key);
|
|
351
|
+
}).length
|
|
352
|
+
}
|
|
353
|
+
/
|
|
354
|
+
{
|
|
355
|
+
availableLocales.filter((locale) => {
|
|
356
|
+
const key = `${documentId}:${locale.localeId}`;
|
|
357
|
+
const status = translationStatuses.get(key);
|
|
358
|
+
return status?.isReady;
|
|
359
|
+
}).length
|
|
360
|
+
}
|
|
361
|
+
</Text>
|
|
357
362
|
</Flex>
|
|
358
363
|
|
|
359
|
-
<Flex justify='flex-start'>
|
|
364
|
+
<Flex gap={2} align='center' justify='flex-start'>
|
|
360
365
|
<Tooltip
|
|
361
366
|
placement='top'
|
|
362
367
|
content={`Replaces references to ${pluginConfig.getSourceLocale()} documents in this document with the corresponding translated document reference`}
|
|
@@ -364,12 +369,55 @@ export const TranslationView = () => {
|
|
|
364
369
|
<Button
|
|
365
370
|
mode='ghost'
|
|
366
371
|
tone='caution'
|
|
367
|
-
onClick={
|
|
368
|
-
|
|
372
|
+
onClick={async () => {
|
|
373
|
+
await handlePatchDocumentReferences();
|
|
374
|
+
}}
|
|
375
|
+
text={isBusy ? 'Patching...' : 'Patch References'}
|
|
369
376
|
icon={isBusy ? null : LinkIcon}
|
|
370
|
-
disabled={isBusy}
|
|
377
|
+
disabled={isBusy || isImporting}
|
|
378
|
+
style={{ minWidth: '180px' }}
|
|
371
379
|
/>
|
|
372
380
|
</Tooltip>
|
|
381
|
+
<Flex gap={2} align='center'>
|
|
382
|
+
<Switch
|
|
383
|
+
checked={autoPatchReferences}
|
|
384
|
+
onChange={() => setAutoPatchReferences(!autoPatchReferences)}
|
|
385
|
+
disabled={isImporting || isBusy}
|
|
386
|
+
/>
|
|
387
|
+
<Text size={1}>Auto-patch after import</Text>
|
|
388
|
+
</Flex>
|
|
389
|
+
</Flex>
|
|
390
|
+
|
|
391
|
+
<Flex gap={2} align='center' justify='flex-start'>
|
|
392
|
+
<Tooltip
|
|
393
|
+
placement='top'
|
|
394
|
+
content='Publishes all translations (if the source document is published)'
|
|
395
|
+
>
|
|
396
|
+
<Button
|
|
397
|
+
mode='ghost'
|
|
398
|
+
tone='positive'
|
|
399
|
+
onClick={async () => {
|
|
400
|
+
setIsPublishing(true);
|
|
401
|
+
try {
|
|
402
|
+
await handlePublishAllTranslations();
|
|
403
|
+
} finally {
|
|
404
|
+
setIsPublishing(false);
|
|
405
|
+
}
|
|
406
|
+
}}
|
|
407
|
+
text={isPublishing ? 'Publishing...' : 'Publish Translations'}
|
|
408
|
+
icon={isPublishing ? null : PublishIcon}
|
|
409
|
+
disabled={isBusy || isPublishing || isImporting}
|
|
410
|
+
style={{ minWidth: '180px' }}
|
|
411
|
+
/>
|
|
412
|
+
</Tooltip>
|
|
413
|
+
<Flex gap={2} align='center'>
|
|
414
|
+
<Switch
|
|
415
|
+
checked={autoPublish}
|
|
416
|
+
onChange={() => setAutoPublish(!autoPublish)}
|
|
417
|
+
disabled={isPublishing || isImporting || isBusy}
|
|
418
|
+
/>
|
|
419
|
+
<Text size={1}>Auto-publish after import</Text>
|
|
420
|
+
</Flex>
|
|
373
421
|
</Flex>
|
|
374
422
|
</Stack>
|
|
375
423
|
</Stack>
|
package/src/index.ts
CHANGED
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
customSerializers,
|
|
17
17
|
SerializedDocument,
|
|
18
18
|
} from './serialization';
|
|
19
|
+
import { translateAction } from './actions/translateAction';
|
|
19
20
|
|
|
20
21
|
export type {
|
|
21
22
|
Secrets,
|
|
@@ -28,6 +29,7 @@ export type {
|
|
|
28
29
|
};
|
|
29
30
|
export {
|
|
30
31
|
TranslationsTab,
|
|
32
|
+
translateAction,
|
|
31
33
|
//helpers for end developers who may need to customize serialization
|
|
32
34
|
findLatestDraft,
|
|
33
35
|
BaseDocumentSerializer,
|
|
@@ -139,6 +141,19 @@ export const gtPlugin = definePlugin<GTPluginConfig>(
|
|
|
139
141
|
router: route.create('/*'),
|
|
140
142
|
},
|
|
141
143
|
],
|
|
144
|
+
document: {
|
|
145
|
+
views: [
|
|
146
|
+
{
|
|
147
|
+
id: 'general-translation',
|
|
148
|
+
title: 'Translations',
|
|
149
|
+
component: TranslationsTab,
|
|
150
|
+
},
|
|
151
|
+
],
|
|
152
|
+
actions: (prev) => {
|
|
153
|
+
// Move translateAction to the beginning so it appears as a prominent button
|
|
154
|
+
return [...prev, translateAction];
|
|
155
|
+
},
|
|
156
|
+
},
|
|
142
157
|
};
|
|
143
158
|
}
|
|
144
159
|
);
|