@sanity/document-internationalization 2.0.0-studio-v3-plugin-v2.13 → 2.0.0-studio-v3-plugin-v2.14
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/README.md +49 -162
- package/lib/index.esm.js +24 -9
- package/lib/index.esm.js.map +1 -1
- package/lib/index.js +24 -9
- package/lib/index.js.map +1 -1
- package/package.json +1 -1
- package/src/components/DocumentInternationalizationMenu.tsx +4 -4
- package/src/components/LanguageManage.tsx +25 -8
package/README.md
CHANGED
|
@@ -1,62 +1,59 @@
|
|
|
1
1
|
# @sanity/document-internationalization
|
|
2
2
|
|
|
3
|
+
All new rewrite exclusively for Sanity Studio v3!
|
|
4
|
+
|
|
3
5
|
- [@sanity/document-internationalization](#sanitydocument-internationalization)
|
|
4
|
-
- [
|
|
6
|
+
- [What this plugin solves](#what-this-plugin-solves)
|
|
7
|
+
- [Many projects use both!](#many-projects-use-both)
|
|
8
|
+
- [Upgrade](#upgrade)
|
|
5
9
|
- [Install](#install)
|
|
6
10
|
- [Usage](#usage)
|
|
7
11
|
- [Basic configuration](#basic-configuration)
|
|
8
12
|
- [Advanced configuration](#advanced-configuration)
|
|
9
13
|
- [Language field](#language-field)
|
|
10
|
-
- [
|
|
11
|
-
- [useDocumentInternationalizationContext](#usedocumentinternationalizationcontext)
|
|
12
|
-
- [DocumentInternationalizationMenu](#documentinternationalizationmenu)
|
|
13
|
-
- [Code examples](#code-examples)
|
|
14
|
+
- [Querying translations](#querying-translations)
|
|
14
15
|
- [Querying with GROQ](#querying-with-groq)
|
|
15
16
|
- [Querying with GraphQL](#querying-with-graphql)
|
|
16
|
-
- [Allowing the same slug on different language versions](#allowing-the-same-slug-on-different-language-versions)
|
|
17
|
-
- [Deleting documents](#deleting-documents)
|
|
18
|
-
- [Deleting a single translated document](#deleting-a-single-translated-document)
|
|
19
|
-
- [Deleting all translations](#deleting-all-translations)
|
|
20
17
|
- [Note on document quotas](#note-on-document-quotas)
|
|
18
|
+
- [Documentation](#documentation)
|
|
21
19
|
- [License](#license)
|
|
22
20
|
- [Develop \& test](#develop--test)
|
|
23
21
|
- [Release new version](#release-new-version)
|
|
24
22
|
|
|
25
|
-
|
|
23
|
+

|
|
26
24
|
|
|
27
|
-
|
|
25
|
+
## What this plugin solves
|
|
28
26
|
|
|
29
|
-
|
|
27
|
+
There are two popular methods of internationalization in Sanity Studio:
|
|
30
28
|
|
|
31
|
-
-
|
|
32
|
-
-
|
|
33
|
-
-
|
|
34
|
-
-
|
|
35
|
-
-
|
|
36
|
-
-
|
|
37
|
-
-
|
|
29
|
+
- **Document-level translation**
|
|
30
|
+
- A unique document version for every language
|
|
31
|
+
- Joined together by references in a `translation.metadata` document
|
|
32
|
+
- Best for documents that have unique, language-specific fields and no common content across languages
|
|
33
|
+
- Best for translating content using Portable Text
|
|
34
|
+
- **Field-level translation**
|
|
35
|
+
- A single document with many languages of content
|
|
36
|
+
- Achieved by mapping over languages on each field
|
|
37
|
+
- Best for documents that have a mix of language-specific and common fields
|
|
38
|
+
- Not recommended for Portable Text
|
|
38
39
|
|
|
39
|
-
|
|
40
|
+
This plugin adds features to the Studio to improve handling **document-level translations**.
|
|
40
41
|
|
|
41
|
-
|
|
42
|
+
- A Language Selector to create and browse language-specific versions of a Document
|
|
43
|
+
- Hooks and components to use throughout your custom components to handle translations
|
|
44
|
+
- Document Badges to highlight the language version of a document
|
|
42
45
|
|
|
43
|
-
**
|
|
46
|
+
For **field-level translations** you should use the [sanity-plugin-internationalized-array](https://github.com/sanity-io/sanity-plugin-internationalized-array).
|
|
44
47
|
|
|
45
|
-
|
|
46
|
-
- Your queries will also need to change, as translation references have moved. See the [Querying](#querying-with-groq) and [Querying with GraphQL](#querying-with-graphql) sections below. [GraphQL](#)
|
|
48
|
+
### Many projects use both!
|
|
47
49
|
|
|
48
|
-
|
|
50
|
+
An example of **document-level** translation could be a `lesson` schema, the `title`, `slug` and `content` fields would be unique in every language.
|
|
49
51
|
|
|
50
|
-
-
|
|
51
|
-
- Install from `v1.0.0` and above
|
|
52
|
-
- This version of the plugin will not be updated with new features
|
|
52
|
+
A good use of **field-level** translation could be a `person` schema. It could have the same `name` and `image` in every language, but only the `biography` would need translating.
|
|
53
53
|
|
|
54
|
-
|
|
54
|
+
## Upgrade
|
|
55
55
|
|
|
56
|
-
|
|
57
|
-
- Install from `v0.0.0` and above
|
|
58
|
-
- This version of the plugin will not be updated with new features
|
|
59
|
-
- You will not need to perform a content migration to move to Sanity Studio v3, if you install the v1 plugin.
|
|
56
|
+
See the Upgrade Guide for [v1](./docs/00-upgrade-from-v1.md) for instructions on how to upgrade from the original Document Internationalization plugin.
|
|
60
57
|
|
|
61
58
|
## Install
|
|
62
59
|
|
|
@@ -64,19 +61,16 @@ If this is your first time installing Document Internationalization, skip to the
|
|
|
64
61
|
npm install --save @sanity/document-internationalization@studio-v3-plugin-v2
|
|
65
62
|
```
|
|
66
63
|
|
|
67
|
-
or
|
|
68
|
-
|
|
69
|
-
```
|
|
70
|
-
yarn add @sanity/document-internationalization@studio-v3-plugin-v2
|
|
71
|
-
```
|
|
72
|
-
|
|
73
64
|
## Usage
|
|
74
65
|
|
|
75
66
|
Add it as a plugin in `sanity.config.ts` (or .js):
|
|
76
67
|
|
|
77
68
|
### Basic configuration
|
|
78
69
|
|
|
79
|
-
The only required configuration is
|
|
70
|
+
The only required configuration is:
|
|
71
|
+
|
|
72
|
+
- The `supportedLanguages` array and
|
|
73
|
+
- The `schemaTypes` array
|
|
80
74
|
|
|
81
75
|
```ts
|
|
82
76
|
// sanity.config.ts
|
|
@@ -135,7 +129,7 @@ export const createConfig({
|
|
|
135
129
|
languageField: `language` // defauts to "language"
|
|
136
130
|
|
|
137
131
|
// Optional
|
|
138
|
-
// Keep
|
|
132
|
+
// Keep translation.metadata references weak
|
|
139
133
|
weakReferences: true // defaults to false
|
|
140
134
|
|
|
141
135
|
// Optional
|
|
@@ -160,7 +154,7 @@ export const createConfig({
|
|
|
160
154
|
|
|
161
155
|
### Language field
|
|
162
156
|
|
|
163
|
-
The schema types that use document internationalization must also have a `string` field type with the same name configured in the `languageField` setting. Unless you want content creators to be able to change the language of a document, you may hide this field since the plugin will handle writing patches to it.
|
|
157
|
+
The schema types that use document internationalization must also have a `string` field type with the same name configured in the `languageField` setting. Unless you want content creators to be able to change the language of a document, you may hide or disable this field since the plugin will handle writing patches to it.
|
|
164
158
|
|
|
165
159
|
```ts
|
|
166
160
|
// ./schema/lesson.ts
|
|
@@ -175,44 +169,7 @@ defineField({
|
|
|
175
169
|
})
|
|
176
170
|
```
|
|
177
171
|
|
|
178
|
-
##
|
|
179
|
-
|
|
180
|
-
Some components and functions from the plugin are exported for you to use throughout the Studio. For example, a custom Tool that lists documents but also needs to pull from available languages, create new translations, show the language of an existing document and link to the metadata document.
|
|
181
|
-
|
|
182
|
-

|
|
183
|
-
|
|
184
|
-
### useDocumentInternationalizationContext
|
|
185
|
-
|
|
186
|
-
The `useDocumentInternationalizationContext` hook can be used to access all plugin configuration values, including the result of `supportedLanguages` if it is an async function.
|
|
187
|
-
|
|
188
|
-
```tsx
|
|
189
|
-
import {useDocumentInternationalizationContext} from '@sanity/document-internationalization'
|
|
190
|
-
|
|
191
|
-
export function MyComponent({doc}: {doc: SanityDocument}) {
|
|
192
|
-
const {languageField} = useDocumentInternationalizationContext()
|
|
193
|
-
|
|
194
|
-
return <Badge>{doc[languageField] ?? `No Language`}</Badge>
|
|
195
|
-
}
|
|
196
|
-
```
|
|
197
|
-
|
|
198
|
-
### DocumentInternationalizationMenu
|
|
199
|
-
|
|
200
|
-
The menu button shown at the top of documents can be imported anywhere and requires the published document ID of a document and its schema type to set the language of the document and handle creating new translations and the metadata document.
|
|
201
|
-
|
|
202
|
-
```tsx
|
|
203
|
-
import {DocumentInternationalizationMenu} from '@sanity/document-internationalization'
|
|
204
|
-
|
|
205
|
-
export function MyComponent({_id, _type}) {
|
|
206
|
-
return (
|
|
207
|
-
<DocumentInternationalizationMenu
|
|
208
|
-
documentId={_id.replace(`drafts.`, ``)}
|
|
209
|
-
schemaType={_type}
|
|
210
|
-
/>
|
|
211
|
-
)
|
|
212
|
-
}
|
|
213
|
-
```
|
|
214
|
-
|
|
215
|
-
## Code examples
|
|
172
|
+
## Querying translations
|
|
216
173
|
|
|
217
174
|
### Querying with GROQ
|
|
218
175
|
|
|
@@ -221,12 +178,11 @@ To query a single document and all its translations, we use the `references()` f
|
|
|
221
178
|
```json5
|
|
222
179
|
// All `lesson` documents of a single language
|
|
223
180
|
*[_type == "lesson" && language == $language]{
|
|
224
|
-
// Just these fields
|
|
225
181
|
title,
|
|
226
182
|
slug,
|
|
227
183
|
language,
|
|
228
184
|
// Get the translations metadata
|
|
229
|
-
// And resolve the `value` field in each array item
|
|
185
|
+
// And resolve the `value` reference field in each array item
|
|
230
186
|
"_translations": *[_type == "translation.metadata" && references(^._id)].translations[].value->{
|
|
231
187
|
title,
|
|
232
188
|
slug,
|
|
@@ -271,86 +227,6 @@ query GetTranslations($id: ID!) {
|
|
|
271
227
|
}
|
|
272
228
|
```
|
|
273
229
|
|
|
274
|
-
### Allowing the same slug on different language versions
|
|
275
|
-
|
|
276
|
-
Often your translated documents will share the same slug. You might wish to move this into the metadata document itself using the `metadataFields` option in the plugin. Alternatively, you can customize the `isUnique` function on a [slug type field](https://www.sanity.io/docs/slug-type#isUnique-**3dd89e75a768**).
|
|
277
|
-
|
|
278
|
-
```ts
|
|
279
|
-
// Add the isUnique option to your slug field
|
|
280
|
-
defineField({
|
|
281
|
-
name: 'slug',
|
|
282
|
-
type: 'slug',
|
|
283
|
-
options: {
|
|
284
|
-
isUnique: isUniqueOtherThanLanguage
|
|
285
|
-
},
|
|
286
|
-
}),
|
|
287
|
-
|
|
288
|
-
// Create the function
|
|
289
|
-
// This checks that there are no other documents
|
|
290
|
-
// With this published or draft _id
|
|
291
|
-
// Or this schema type
|
|
292
|
-
// With the same slug and language
|
|
293
|
-
export async function isUniqueOtherThanLanguage(slug: string, context: SlugValidationContext) {
|
|
294
|
-
const {document, getClient} = context
|
|
295
|
-
if (!document?.language) {
|
|
296
|
-
return true
|
|
297
|
-
}
|
|
298
|
-
const client = getClient({apiVersion: '2023-04-24'})
|
|
299
|
-
const id = document._id.replace(/^drafts\./, '')
|
|
300
|
-
const params = {
|
|
301
|
-
draft: `drafts.${id}`,
|
|
302
|
-
published: id,
|
|
303
|
-
language: document.language,
|
|
304
|
-
slug,
|
|
305
|
-
}
|
|
306
|
-
const query = `!defined(*[
|
|
307
|
-
!(_id in [$draft, $published]) &&
|
|
308
|
-
slug.current == $slug &&
|
|
309
|
-
language == $language
|
|
310
|
-
][0]._id)`
|
|
311
|
-
const result = await client.fetch(query, params)
|
|
312
|
-
return result
|
|
313
|
-
}
|
|
314
|
-
```
|
|
315
|
-
|
|
316
|
-
## Deleting documents
|
|
317
|
-
|
|
318
|
-
### Deleting a single translated document
|
|
319
|
-
|
|
320
|
-
By default, this plugin creates a strong reference between a document and its connected translation metadata document. Because reference integrity is maintained by the API, you cannot delete a document that has a strong reference to it. To offset this difficulty, the plugin exports a document action that will allow you to remove the translation reference from the action, before proceeding to delete the document. It is not added by default to your schema types.
|
|
321
|
-
|
|
322
|
-

|
|
323
|
-
|
|
324
|
-
Import into your Studio's config file
|
|
325
|
-
|
|
326
|
-
```ts
|
|
327
|
-
import {
|
|
328
|
-
documentInternationalization,
|
|
329
|
-
DeleteTranslationAction,
|
|
330
|
-
} from '@sanity/document-internationalization'
|
|
331
|
-
|
|
332
|
-
export default defineConfig({
|
|
333
|
-
// ...all other config
|
|
334
|
-
document: {
|
|
335
|
-
actions: (prev, {schemaType}) => {
|
|
336
|
-
// Add to the same schema types you use for internationalization
|
|
337
|
-
if (['page'].includes(schemaType)) {
|
|
338
|
-
// You might also like to filter out the built-in "delete" action
|
|
339
|
-
return [...prev, DeleteTranslationAction]
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
return prev
|
|
343
|
-
},
|
|
344
|
-
},
|
|
345
|
-
})
|
|
346
|
-
```
|
|
347
|
-
|
|
348
|
-
### Deleting all translations
|
|
349
|
-
|
|
350
|
-
The metadata document also contains a "Delete all translations" document action which is queued by default for only that schema type. It will delete all of the documents in the `translations` array of references, as well as the metadata document itself.
|
|
351
|
-
|
|
352
|
-

|
|
353
|
-
|
|
354
230
|
## Note on document quotas
|
|
355
231
|
|
|
356
232
|
In previous versions of this plugin, translations were stored as an array of references on the actual documents. This required a base language, lead to messy transaction histories and made deleting documents difficult.
|
|
@@ -359,6 +235,17 @@ In this version of the plugin, translations of a document are stored as an array
|
|
|
359
235
|
|
|
360
236
|
This means if you have 100 documents and they are all translated into 3 languages, you will have 400 documents. Keep this in mind for extremely high-volume datasets.
|
|
361
237
|
|
|
238
|
+
## Documentation
|
|
239
|
+
|
|
240
|
+
For more advanced topics see the documentation. For installation see [Usage](#usage).
|
|
241
|
+
|
|
242
|
+
- [Upgrade from v1](./docs/00-upgrade-from-v1.md)
|
|
243
|
+
- [Creating translations of singleton documents](./docs/01-singleton-documents.md)
|
|
244
|
+
- [Importing and creating documents](./docs/02-importing-and-creating-documents.md)
|
|
245
|
+
- [Deleting translated documents](./docs/03-deleting-translated-documents.md)
|
|
246
|
+
- [Importing plugin components](./docs/04-importing-plugin-components.md)
|
|
247
|
+
- [Allowing the same slug on different language versions](./docs/05-allowing-the-same-slug-on-different-language-versions.md)
|
|
248
|
+
|
|
362
249
|
## License
|
|
363
250
|
|
|
364
251
|
[MIT](LICENSE) © Sanity.io
|
package/lib/index.esm.js
CHANGED
|
@@ -364,12 +364,27 @@ function LanguageManage(props) {
|
|
|
364
364
|
id
|
|
365
365
|
} = props;
|
|
366
366
|
const open = useOpenInNewPane(id, METADATA_SCHEMA_NAME);
|
|
367
|
-
return /* @__PURE__ */jsx(
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
367
|
+
return /* @__PURE__ */jsx(Tooltip, {
|
|
368
|
+
content: id ? null : /* @__PURE__ */jsx(Box, {
|
|
369
|
+
padding: 2,
|
|
370
|
+
children: /* @__PURE__ */jsx(Text, {
|
|
371
|
+
muted: true,
|
|
372
|
+
size: 1,
|
|
373
|
+
children: "Document has no other translations"
|
|
374
|
+
})
|
|
375
|
+
}),
|
|
376
|
+
fallbackPlacements: ["right", "left"],
|
|
377
|
+
placement: "top",
|
|
378
|
+
portal: true,
|
|
379
|
+
children: /* @__PURE__ */jsx(Stack, {
|
|
380
|
+
children: /* @__PURE__ */jsx(Button, {
|
|
381
|
+
disabled: !id,
|
|
382
|
+
mode: "ghost",
|
|
383
|
+
text: "Manage Translations",
|
|
384
|
+
icon: CogIcon,
|
|
385
|
+
onClick: () => open()
|
|
386
|
+
})
|
|
387
|
+
})
|
|
373
388
|
});
|
|
374
389
|
}
|
|
375
390
|
function createReference(key, ref, type) {
|
|
@@ -635,15 +650,15 @@ function DocumentInternationalizationMenu(props) {
|
|
|
635
650
|
}
|
|
636
651
|
return valid;
|
|
637
652
|
}, [supportedLanguages]);
|
|
638
|
-
const content = /* @__PURE__ */jsx(
|
|
653
|
+
const content = /* @__PURE__ */jsx(Box, {
|
|
654
|
+
padding: 1,
|
|
639
655
|
children: error ? /* @__PURE__ */jsx(Card, {
|
|
640
656
|
tone: "critical",
|
|
641
|
-
padding:
|
|
657
|
+
padding: 1,
|
|
642
658
|
children: /* @__PURE__ */jsx(Text, {
|
|
643
659
|
children: "There was an error returning translations metadata"
|
|
644
660
|
})
|
|
645
661
|
}) : /* @__PURE__ */jsxs(Stack, {
|
|
646
|
-
padding: 1,
|
|
647
662
|
space: 1,
|
|
648
663
|
children: [/* @__PURE__ */jsx(LanguageManage, {
|
|
649
664
|
id: metadata == null ? void 0 : metadata._id
|