@sanity/document-internationalization 2.0.2 → 2.1.0
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 +1 -1
- package/README.md +17 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.esm.js +82 -9
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +81 -8
- package/dist/index.js.map +1 -1
- package/package.json +4 -3
- package/src/components/DocumentInternationalizationMenu.tsx +3 -3
- package/src/components/LanguageOption.tsx +15 -8
- package/src/types.ts +32 -0
- package/src/utils/excludePaths.ts +123 -0
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -169,6 +169,23 @@ defineField({
|
|
|
169
169
|
})
|
|
170
170
|
```
|
|
171
171
|
|
|
172
|
+
### Excluding fields
|
|
173
|
+
|
|
174
|
+
The default behaviour of this plugin when creating a new translation is to duplicate the originating document, which is useful for then translating the fields directly in the new document - perhaps with [Sanity AI Assist](https://github.com/sanity-io/assist). However, sometimes you may want to exclude certain fields from being copied to the new document. You can do this by updating your schema to exclude certain types or fields with `options.documentInternationalization.exclude`:
|
|
175
|
+
|
|
176
|
+
```ts
|
|
177
|
+
defineField({
|
|
178
|
+
name: 'title',
|
|
179
|
+
title: 'Title',
|
|
180
|
+
type: 'string',
|
|
181
|
+
options: {
|
|
182
|
+
documentInternationalization: {
|
|
183
|
+
exclude: true,
|
|
184
|
+
},
|
|
185
|
+
},
|
|
186
|
+
}),
|
|
187
|
+
```
|
|
188
|
+
|
|
172
189
|
## Querying translations
|
|
173
190
|
|
|
174
191
|
### Querying with GROQ
|
package/dist/index.d.ts
CHANGED
|
@@ -20,6 +20,13 @@ export declare type DocumentInternationalizationMenuProps = {
|
|
|
20
20
|
documentId: string
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
export declare interface DocumentInternationalizationSchemaOpts {
|
|
24
|
+
documentInternationalization?: {
|
|
25
|
+
/** Set to true to disable duplication of this field or type */
|
|
26
|
+
exclude?: boolean
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
23
30
|
export declare type Language = {
|
|
24
31
|
id: Intl.UnicodeBCP47LocaleIdentifier
|
|
25
32
|
title: string
|
|
@@ -57,3 +64,25 @@ export declare type TranslationReference = KeyedObject & {
|
|
|
57
64
|
export declare function useDocumentInternationalizationContext(): PluginConfigContext
|
|
58
65
|
|
|
59
66
|
export {}
|
|
67
|
+
|
|
68
|
+
declare module 'sanity' {
|
|
69
|
+
interface ArrayOptions extends DocumentInternationalizationSchemaOpts {}
|
|
70
|
+
interface BlockOptions extends DocumentInternationalizationSchemaOpts {}
|
|
71
|
+
interface BooleanOptions extends DocumentInternationalizationSchemaOpts {}
|
|
72
|
+
interface CrossDatasetReferenceOptions
|
|
73
|
+
extends DocumentInternationalizationSchemaOpts {}
|
|
74
|
+
interface DateOptions extends DocumentInternationalizationSchemaOpts {}
|
|
75
|
+
interface DatetimeOptions extends DocumentInternationalizationSchemaOpts {}
|
|
76
|
+
interface FileOptions extends DocumentInternationalizationSchemaOpts {}
|
|
77
|
+
interface GeopointOptions extends DocumentInternationalizationSchemaOpts {}
|
|
78
|
+
interface ImageOptions extends DocumentInternationalizationSchemaOpts {}
|
|
79
|
+
interface NumberOptions extends DocumentInternationalizationSchemaOpts {}
|
|
80
|
+
interface ObjectOptions extends DocumentInternationalizationSchemaOpts {}
|
|
81
|
+
interface ReferenceBaseOptions
|
|
82
|
+
extends DocumentInternationalizationSchemaOpts {}
|
|
83
|
+
interface SlugOptions extends DocumentInternationalizationSchemaOpts {}
|
|
84
|
+
interface StringOptions extends DocumentInternationalizationSchemaOpts {}
|
|
85
|
+
interface TextOptions extends DocumentInternationalizationSchemaOpts {}
|
|
86
|
+
interface UrlOptions extends DocumentInternationalizationSchemaOpts {}
|
|
87
|
+
interface EmailOptions extends DocumentInternationalizationSchemaOpts {}
|
|
88
|
+
}
|
package/dist/index.esm.js
CHANGED
|
@@ -2,11 +2,12 @@ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
|
2
2
|
import { TrashIcon, CogIcon, SplitVerticalIcon, CheckmarkIcon, AddIcon, EditIcon, TranslateIcon, InfoOutlineIcon } from '@sanity/icons';
|
|
3
3
|
import { Flex, Spinner, Stack, Text, Card, Grid, Button, useToast, Tooltip, Box, Badge, useClickOutside, TextInput, Popover, Inline, Dialog } from '@sanity/ui';
|
|
4
4
|
import { useMemo, useEffect, createContext, useContext, useState, useCallback } from 'react';
|
|
5
|
-
import { useSchema, Preview, useClient, useEditState, useValidationStatus, TextWithTone, useWorkspace, PatchEvent, unset, defineType, defineField, definePlugin, isSanityDocument } from 'sanity';
|
|
5
|
+
import { useSchema, Preview, useClient, isDocumentSchemaType, pathToString, useEditState, useValidationStatus, TextWithTone, useWorkspace, PatchEvent, unset, defineType, defineField, definePlugin, isSanityDocument } from 'sanity';
|
|
6
6
|
import { Feedback, useListeningQuery } from 'sanity-plugin-utils';
|
|
7
7
|
import { uuid } from '@sanity/uuid';
|
|
8
8
|
import { usePaneRouter, useDocumentPane } from 'sanity/desk';
|
|
9
9
|
import { RouterContext } from 'sanity/router';
|
|
10
|
+
import { Mutation, extractWithPath } from '@sanity/mutator';
|
|
10
11
|
import styled from 'styled-components';
|
|
11
12
|
import { internationalizedArray } from 'sanity-plugin-internationalized-array';
|
|
12
13
|
function DocumentPreview(props) {
|
|
@@ -403,6 +404,77 @@ function createReference(key, ref, type) {
|
|
|
403
404
|
}
|
|
404
405
|
};
|
|
405
406
|
}
|
|
407
|
+
function removeExcludedPaths(doc, schemaType) {
|
|
408
|
+
if (!isDocumentSchemaType(schemaType) || !doc) {
|
|
409
|
+
return doc;
|
|
410
|
+
}
|
|
411
|
+
const pathsToExclude = extractPaths(doc, schemaType, []).filter(field => {
|
|
412
|
+
var _a, _b, _c;
|
|
413
|
+
return ((_c = (_b = (_a = field.schemaType) == null ? void 0 : _a.options) == null ? void 0 : _b.documentInternationalization) == null ? void 0 : _c.exclude) === true;
|
|
414
|
+
}).map(field => {
|
|
415
|
+
return pathToString(field.path);
|
|
416
|
+
});
|
|
417
|
+
const mut = new Mutation({
|
|
418
|
+
mutations: [{
|
|
419
|
+
patch: {
|
|
420
|
+
id: doc._id,
|
|
421
|
+
unset: pathsToExclude
|
|
422
|
+
}
|
|
423
|
+
}]
|
|
424
|
+
});
|
|
425
|
+
return mut.apply(doc);
|
|
426
|
+
}
|
|
427
|
+
function extractPaths(doc, schemaType, path) {
|
|
428
|
+
return schemaType.fields.reduce((acc, field) => {
|
|
429
|
+
var _a, _b;
|
|
430
|
+
const fieldPath = [...path, field.name];
|
|
431
|
+
const fieldSchema = field.type;
|
|
432
|
+
const {
|
|
433
|
+
value
|
|
434
|
+
} = (_a = extractWithPath(pathToString(fieldPath), doc)[0]) != null ? _a : {};
|
|
435
|
+
if (!value) {
|
|
436
|
+
return acc;
|
|
437
|
+
}
|
|
438
|
+
const thisFieldWithPath = {
|
|
439
|
+
path: fieldPath,
|
|
440
|
+
name: field.name,
|
|
441
|
+
schemaType: fieldSchema,
|
|
442
|
+
value
|
|
443
|
+
};
|
|
444
|
+
if (fieldSchema.jsonType === "object") {
|
|
445
|
+
const innerFields = extractPaths(doc, fieldSchema, fieldPath);
|
|
446
|
+
return [...acc, thisFieldWithPath, ...innerFields];
|
|
447
|
+
} else if (fieldSchema.jsonType === "array" && fieldSchema.of.length && fieldSchema.of.some(item => "fields" in item)) {
|
|
448
|
+
const {
|
|
449
|
+
value: arrayValue
|
|
450
|
+
} = (_b = extractWithPath(pathToString(fieldPath), doc)[0]) != null ? _b : {};
|
|
451
|
+
let arrayPaths = [];
|
|
452
|
+
if (arrayValue == null ? void 0 : arrayValue.length) {
|
|
453
|
+
for (const item of arrayValue) {
|
|
454
|
+
const itemPath = [...fieldPath, {
|
|
455
|
+
_key: item._key
|
|
456
|
+
}];
|
|
457
|
+
let itemSchema = fieldSchema.of.find(t => t.name === item._type);
|
|
458
|
+
if (!item._type) {
|
|
459
|
+
itemSchema = fieldSchema.of[0];
|
|
460
|
+
}
|
|
461
|
+
if (item._key && itemSchema) {
|
|
462
|
+
const innerFields = extractPaths(doc, itemSchema, itemPath);
|
|
463
|
+
const arrayMember = {
|
|
464
|
+
path: itemPath,
|
|
465
|
+
name: item._key,
|
|
466
|
+
schemaType: itemSchema,
|
|
467
|
+
value: item
|
|
468
|
+
};
|
|
469
|
+
arrayPaths = [...arrayPaths, arrayMember, ...innerFields];
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
return [...acc, thisFieldWithPath, ...arrayPaths];
|
|
474
|
+
}
|
|
475
|
+
return [...acc, thisFieldWithPath];
|
|
476
|
+
}, []);
|
|
477
|
+
}
|
|
406
478
|
function LanguageOption(props) {
|
|
407
479
|
var _a;
|
|
408
480
|
const {
|
|
@@ -426,7 +498,7 @@ function LanguageOption(props) {
|
|
|
426
498
|
apiVersion
|
|
427
499
|
});
|
|
428
500
|
const toast = useToast();
|
|
429
|
-
const open = useOpenInNewPane((_a = translation == null ? void 0 : translation.value) == null ? void 0 : _a._ref, schemaType);
|
|
501
|
+
const open = useOpenInNewPane((_a = translation == null ? void 0 : translation.value) == null ? void 0 : _a._ref, schemaType.name);
|
|
430
502
|
const handleOpen = useCallback(() => open(), [open]);
|
|
431
503
|
const handleCreate = useCallback(async () => {
|
|
432
504
|
if (!source) {
|
|
@@ -440,19 +512,20 @@ function LanguageOption(props) {
|
|
|
440
512
|
}
|
|
441
513
|
const transaction = client.transaction();
|
|
442
514
|
const newTranslationDocumentId = uuid();
|
|
443
|
-
|
|
515
|
+
let newTranslationDocument = {
|
|
444
516
|
...source,
|
|
445
517
|
_id: "drafts.".concat(newTranslationDocumentId),
|
|
446
518
|
// 2. Update language of the translation
|
|
447
519
|
[languageField]: language.id
|
|
448
520
|
};
|
|
521
|
+
newTranslationDocument = removeExcludedPaths(newTranslationDocument, schemaType);
|
|
449
522
|
transaction.create(newTranslationDocument);
|
|
450
|
-
const sourceReference = createReference(sourceLanguageId, documentId, schemaType, !weakReferences);
|
|
451
|
-
const newTranslationReference = createReference(language.id, newTranslationDocumentId, schemaType, !weakReferences);
|
|
523
|
+
const sourceReference = createReference(sourceLanguageId, documentId, schemaType.name, !weakReferences);
|
|
524
|
+
const newTranslationReference = createReference(language.id, newTranslationDocumentId, schemaType.name, !weakReferences);
|
|
452
525
|
const newMetadataDocument = {
|
|
453
526
|
_id: metadataId,
|
|
454
527
|
_type: METADATA_SCHEMA_NAME,
|
|
455
|
-
schemaTypes: [schemaType],
|
|
528
|
+
schemaTypes: [schemaType.name],
|
|
456
529
|
translations: [sourceReference]
|
|
457
530
|
};
|
|
458
531
|
transaction.createIfNotExists(newMetadataDocument);
|
|
@@ -605,7 +678,7 @@ function DocumentInternationalizationMenu(props) {
|
|
|
605
678
|
const {
|
|
606
679
|
documentId
|
|
607
680
|
} = props;
|
|
608
|
-
const schemaType = props.schemaType
|
|
681
|
+
const schemaType = props.schemaType;
|
|
609
682
|
const {
|
|
610
683
|
languageField,
|
|
611
684
|
supportedLanguages
|
|
@@ -640,7 +713,7 @@ function DocumentInternationalizationMenu(props) {
|
|
|
640
713
|
const {
|
|
641
714
|
draft,
|
|
642
715
|
published
|
|
643
|
-
} = useEditState(documentId, schemaType);
|
|
716
|
+
} = useEditState(documentId, schemaType.name);
|
|
644
717
|
const source = draft || published;
|
|
645
718
|
const documentIsInOneMetadataDocument = useMemo(() => {
|
|
646
719
|
return Array.isArray(data) && data.length <= 1;
|
|
@@ -725,7 +798,7 @@ function DocumentInternationalizationMenu(props) {
|
|
|
725
798
|
if (!documentId) {
|
|
726
799
|
return null;
|
|
727
800
|
}
|
|
728
|
-
if (!schemaType) {
|
|
801
|
+
if (!schemaType || !schemaType.name) {
|
|
729
802
|
return null;
|
|
730
803
|
}
|
|
731
804
|
return /* @__PURE__ */jsx(Popover, {
|