sanity-plugin-internationalized-array 4.0.2 → 5.0.0-canary.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/README.md +141 -52
- package/dist/index.d.ts +45 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +257 -122
- package/dist/index.js.map +1 -1
- package/dist/migrations/index.d.ts +31 -0
- package/dist/migrations/index.d.ts.map +1 -0
- package/dist/migrations/index.js +26 -0
- package/dist/migrations/index.js.map +1 -0
- package/package.json +7 -5
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# sanity-plugin-internationalized-array
|
|
2
2
|
|
|
3
|
-
A plugin to register array fields with a custom input component to store field values in multiple languages, queryable by using
|
|
3
|
+
A plugin to register array fields with a custom input component to store field values in multiple languages, queryable by using a dedicated `language` field.
|
|
4
4
|
|
|
5
5
|

|
|
6
6
|
|
|
@@ -15,6 +15,7 @@ A plugin to register array fields with a custom input component to store field v
|
|
|
15
15
|
- [Usage with @sanity/language-filter](#usage-with-sanitylanguage-filter)
|
|
16
16
|
- [Shape of stored data](#shape-of-stored-data)
|
|
17
17
|
- [Querying data](#querying-data)
|
|
18
|
+
- [Migrate from v4 to v5](#migrate-from-v4-to-v5)
|
|
18
19
|
- [Migrate from objects to arrays](#migrate-from-objects-to-arrays)
|
|
19
20
|
- [Why store localized field data like this?](#why-store-localized-field-data-like-this)
|
|
20
21
|
- [License](#license)
|
|
@@ -42,18 +43,18 @@ Add it as a plugin in sanity.config.ts (or .js):
|
|
|
42
43
|
import {defineConfig} from 'sanity'
|
|
43
44
|
import {internationalizedArray} from 'sanity-plugin-internationalized-array'
|
|
44
45
|
|
|
45
|
-
|
|
46
|
+
export default defineConfig({
|
|
46
47
|
// ...
|
|
47
48
|
plugins: [
|
|
48
49
|
internationalizedArray({
|
|
49
50
|
languages: [
|
|
50
51
|
{id: 'en', title: 'English'},
|
|
51
|
-
{id: 'fr', title: 'French'}
|
|
52
|
+
{id: 'fr', title: 'French'},
|
|
52
53
|
],
|
|
53
54
|
defaultLanguages: ['en'],
|
|
54
55
|
fieldTypes: ['string'],
|
|
55
|
-
})
|
|
56
|
-
]
|
|
56
|
+
}),
|
|
57
|
+
],
|
|
57
58
|
})
|
|
58
59
|
```
|
|
59
60
|
|
|
@@ -143,16 +144,16 @@ The "Add all languages" button can be hidden with `buttonAddAll`.
|
|
|
143
144
|
import {defineConfig} from 'sanity'
|
|
144
145
|
import {internationalizedArray} from 'sanity-plugin-internationalized-array'
|
|
145
146
|
|
|
146
|
-
|
|
147
|
+
export default defineConfig({
|
|
147
148
|
// ...
|
|
148
149
|
plugins: [
|
|
149
150
|
internationalizedArray({
|
|
150
151
|
// ...other config
|
|
151
152
|
buttonLocations: ['field', 'unstable__fieldAction', 'document'], // default ['field']
|
|
152
153
|
buttonAddAll: false, // default true
|
|
153
|
-
languageDisplay: 'codeOnly' // codeOnly (default) | titleOnly | titleAndCode
|
|
154
|
-
})
|
|
155
|
-
]
|
|
154
|
+
languageDisplay: 'codeOnly', // codeOnly (default) | titleOnly | titleAndCode
|
|
155
|
+
}),
|
|
156
|
+
],
|
|
156
157
|
})
|
|
157
158
|
```
|
|
158
159
|
|
|
@@ -164,7 +165,7 @@ For more control over the `value` field, you can pass a schema definition into t
|
|
|
164
165
|
import {defineConfig} from 'sanity'
|
|
165
166
|
import {internationalizedArray} from 'sanity-plugin-internationalized-array'
|
|
166
167
|
|
|
167
|
-
export
|
|
168
|
+
export default defineConfig({
|
|
168
169
|
// ...
|
|
169
170
|
plugins: [
|
|
170
171
|
internationalizedArray({
|
|
@@ -280,75 +281,163 @@ If you have many languages and authors that predominately write in only a few, [
|
|
|
280
281
|
|
|
281
282
|

|
|
282
283
|
|
|
283
|
-
|
|
284
|
+
The plugin includes built-in integration with `@sanity/language-filter`.
|
|
285
|
+
To enable it, add `languageFilter.documentTypes` in the plugin config for the document types that should show the filter.
|
|
284
286
|
|
|
285
287
|
```ts
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
import {defineConfig, isKeySegment} from 'sanity'
|
|
289
|
-
import {languageFilter} from '@sanity/language-filter'
|
|
288
|
+
import {defineConfig} from 'sanity'
|
|
289
|
+
import {internationalizedArray} from 'sanity-plugin-internationalized-array'
|
|
290
290
|
|
|
291
291
|
export default defineConfig({
|
|
292
|
-
// ...
|
|
292
|
+
// ...
|
|
293
293
|
plugins: [
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
enclosingType.jsonType === 'object' &&
|
|
304
|
-
enclosingType.name.startsWith('internationalizedArray') &&
|
|
305
|
-
'kind' in member
|
|
306
|
-
) {
|
|
307
|
-
// Get last two segments of the field's path
|
|
308
|
-
const pathEnd = member.field.path.slice(-2)
|
|
309
|
-
// If the second-last segment is a _key, and the last segment is `value`,
|
|
310
|
-
// It's an internationalized array value
|
|
311
|
-
// And the array _key is the language of the field
|
|
312
|
-
const language =
|
|
313
|
-
pathEnd[1] === 'value' && isKeySegment(pathEnd[0]) ? pathEnd[0]._key : null
|
|
314
|
-
|
|
315
|
-
return language ? selectedLanguageIds.includes(language) : false
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
// Filter internationalized objects if you have them
|
|
319
|
-
// `localeString` must be registered as a custom schema type
|
|
320
|
-
if (enclosingType.jsonType === 'object' && enclosingType.name.startsWith('locale')) {
|
|
321
|
-
return selectedLanguageIds.includes(member.name)
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
return true
|
|
294
|
+
internationalizedArray({
|
|
295
|
+
languages: [
|
|
296
|
+
{id: 'en', title: 'English'},
|
|
297
|
+
{id: 'fr', title: 'French'},
|
|
298
|
+
],
|
|
299
|
+
defaultLanguages: ['en'],
|
|
300
|
+
fieldTypes: ['string'],
|
|
301
|
+
languageFilter: {
|
|
302
|
+
documentTypes: ['internationalizedPost', 'lesson'],
|
|
325
303
|
},
|
|
326
304
|
}),
|
|
327
305
|
],
|
|
328
306
|
})
|
|
329
307
|
```
|
|
330
308
|
|
|
309
|
+
If you need more control, you can continue using `@sanity/language-filter` directly and pass `internationalizedArrayLanguageFilter` from this package as your `filterField`.
|
|
310
|
+
|
|
331
311
|
## Shape of stored data
|
|
332
312
|
|
|
333
|
-
The custom input contains buttons which will add new array items with the language
|
|
313
|
+
The custom input contains buttons which will add new array items with a random `_key` and the language stored in a dedicated `language` field. Data returned from this array will look like this:
|
|
334
314
|
|
|
335
315
|
```json
|
|
336
316
|
"greeting": [
|
|
337
|
-
{ "_key": "en", "value": "hello" },
|
|
338
|
-
{ "_key": "fr", "value": "bonjour" },
|
|
317
|
+
{ "_key": "abc123", "language": "en", "value": "hello" },
|
|
318
|
+
{ "_key": "def456", "language": "fr", "value": "bonjour" },
|
|
339
319
|
]
|
|
340
320
|
```
|
|
341
321
|
|
|
342
322
|
## Querying data
|
|
343
323
|
|
|
344
|
-
Using GROQ filters you can query for a specific language
|
|
324
|
+
Using GROQ filters you can query for a specific language like so:
|
|
345
325
|
|
|
346
326
|
```js
|
|
347
327
|
*[_type == "person"] {
|
|
348
|
-
"greeting": greeting[
|
|
328
|
+
"greeting": greeting[language == "en"][0].value
|
|
349
329
|
}
|
|
350
330
|
```
|
|
351
331
|
|
|
332
|
+
## Migrate from v4 to v5
|
|
333
|
+
|
|
334
|
+
v5 stores the language identifier on a dedicated `language` field instead of `_key`.
|
|
335
|
+
|
|
336
|
+
### 1. Backup your data.
|
|
337
|
+
|
|
338
|
+
You can manually backup your data using the sanity CLI.
|
|
339
|
+
|
|
340
|
+
```
|
|
341
|
+
sanity dataset export production
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
This creates a production.tar.gz file in your current directory containing all your documents and assets.
|
|
345
|
+
|
|
346
|
+
You can also specify a custom filename and location:
|
|
347
|
+
|
|
348
|
+
```
|
|
349
|
+
sanity dataset export production ./backups/backup-2026-02-16.tar.gz
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
If you ever need to restore, use the import command:
|
|
353
|
+
|
|
354
|
+
```
|
|
355
|
+
sanity dataset import backup-2026-02-16.tar.gz production
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
Or you can use the backup service, read more at https://www.sanity.io/docs/content-lake/backups
|
|
359
|
+
|
|
360
|
+
### 2. Update your GROQ queries.
|
|
361
|
+
|
|
362
|
+
Use a backwards compatible query until your migration is ready and has been executed.
|
|
363
|
+
|
|
364
|
+
```diff
|
|
365
|
+
*[_type == "person"] {
|
|
366
|
+
- "greeting": greeting[_key == "en"][0].value
|
|
367
|
+
+ "greeting": greeting[language == "en" || _key == "en"][0].value
|
|
368
|
+
}
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
### 3. Data migration
|
|
372
|
+
|
|
373
|
+
The package exports a migration helper that you can run with the [migration CLI](https://www.sanity.io/docs/cli-reference/cli-migration).
|
|
374
|
+
|
|
375
|
+
Create a migration file in your project and export it:
|
|
376
|
+
|
|
377
|
+
```ts
|
|
378
|
+
// ./migrations/migrateToLanguageField.ts
|
|
379
|
+
import {migrateToLanguageField} from 'sanity-plugin-internationalized-array/migrations'
|
|
380
|
+
|
|
381
|
+
// Add the document types that contain internationalized arrays.
|
|
382
|
+
// Example: ['internationalizedPost', 'translation.metadata']
|
|
383
|
+
const DOCUMENT_TYPES: string[] = []
|
|
384
|
+
|
|
385
|
+
export default migrateToLanguageField(DOCUMENT_TYPES)
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
Then verify your migration with a dry run:
|
|
389
|
+
|
|
390
|
+
```bash
|
|
391
|
+
pnpm sanity migration run migrateToLanguageField
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
Once ready, run the migration:
|
|
395
|
+
|
|
396
|
+
```bash
|
|
397
|
+
pnpm sanity migration run migrateToLanguageField --no-dry-run
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
**If you use [@sanity/document-internationalization](https://github.com/sanity-io/plugins/tree/main/plugins/@sanity/document-internationalization):** Include `'translation.metadata'` in your migration's document types so that the `translations` array on metadata documents is migrated
|
|
401
|
+
And update `@sanity/document-internationalization` to `v6`
|
|
402
|
+
|
|
403
|
+
### 4. Update your GROQ queries
|
|
404
|
+
|
|
405
|
+
Previously we updated the GROQ queries to support both locations for the language field. Once migration is complete, update the GROQ queries again to only use `language` and remove the dependency on `_key`.
|
|
406
|
+
|
|
407
|
+
```diff
|
|
408
|
+
*[_type == "person"] {
|
|
409
|
+
- "greeting": greeting[language == "en" || _key == "en"][0].value
|
|
410
|
+
+ "greeting": greeting[language == "en"][0].value
|
|
411
|
+
}
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
## Usage with language filter
|
|
415
|
+
|
|
416
|
+
The plugin now includes built-in integration with `@sanity/language-filter`.
|
|
417
|
+
To enable it, add `languageFilter.documentTypes` in the plugin config for the document types that should show the filter.
|
|
418
|
+
|
|
419
|
+
```ts
|
|
420
|
+
import {defineConfig} from 'sanity'
|
|
421
|
+
import {internationalizedArray} from 'sanity-plugin-internationalized-array'
|
|
422
|
+
|
|
423
|
+
export default defineConfig({
|
|
424
|
+
// ...
|
|
425
|
+
plugins: [
|
|
426
|
+
internationalizedArray({
|
|
427
|
+
languages: [
|
|
428
|
+
{id: 'en', title: 'English'},
|
|
429
|
+
{id: 'fr', title: 'French'},
|
|
430
|
+
],
|
|
431
|
+
defaultLanguages: ['en'],
|
|
432
|
+
fieldTypes: ['string'],
|
|
433
|
+
languageFilter: {
|
|
434
|
+
documentTypes: ['internationalizedPost', 'lesson'],
|
|
435
|
+
},
|
|
436
|
+
}),
|
|
437
|
+
],
|
|
438
|
+
})
|
|
439
|
+
```
|
|
440
|
+
|
|
352
441
|
## Migrate from objects to arrays
|
|
353
442
|
|
|
354
443
|
[See the migration script](./migrations/transformObjectToArray.ts) inside `./migrations/transformObjectToArray.ts` of this plugin.
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as sanity0 from "sanity";
|
|
2
2
|
import { FieldDefinition, Rule, RuleTypeConstraint, SanityClient } from "sanity";
|
|
3
|
+
import { FilterFieldFunction } from "@sanity/language-filter";
|
|
3
4
|
type Language = {
|
|
4
5
|
id: Intl.UnicodeBCP47LocaleIdentifier;
|
|
5
6
|
title: string;
|
|
@@ -25,13 +26,15 @@ type ArrayConfig = {
|
|
|
25
26
|
* @deprecated Use InternationalizedArrayItem instead
|
|
26
27
|
*/
|
|
27
28
|
type Value = {
|
|
28
|
-
_key: string;
|
|
29
|
+
_key: string; /** Language identifier (e.g., 'en', 'fr'). Added in v5. */
|
|
30
|
+
language: string;
|
|
29
31
|
value?: unknown;
|
|
30
32
|
};
|
|
31
33
|
declare function isInternationalizedArrayItemType(type: string): type is InternationalizedArrayItem['_type'];
|
|
32
34
|
type InternationalizedArrayItem<T = unknown> = {
|
|
33
35
|
_key: string;
|
|
34
|
-
value?: T;
|
|
36
|
+
value?: T; /** Language identifier (e.g., 'en', 'fr'). Added in v5. */
|
|
37
|
+
language: string;
|
|
35
38
|
/**
|
|
36
39
|
* string that starts with "internationalizedArray" and ends with "Value"
|
|
37
40
|
*/
|
|
@@ -134,15 +137,50 @@ type PluginConfig = {
|
|
|
134
137
|
* @defaultValue 'code'
|
|
135
138
|
* */
|
|
136
139
|
languageDisplay?: LanguageDisplay;
|
|
140
|
+
/**
|
|
141
|
+
* Configure the language filter for the plugin by providing the document types that should show the filter.
|
|
142
|
+
* ```tsx
|
|
143
|
+
* {
|
|
144
|
+
* languageFilter: {
|
|
145
|
+
* documentTypes: ['internationalizedPost', 'lesson']
|
|
146
|
+
* }
|
|
147
|
+
* }
|
|
148
|
+
* ```
|
|
149
|
+
*/
|
|
150
|
+
languageFilter?: {
|
|
151
|
+
documentTypes: string[];
|
|
152
|
+
};
|
|
137
153
|
};
|
|
138
154
|
declare const clear: () => void;
|
|
139
|
-
declare const internationalizedArray: sanity0.Plugin<PluginConfig>;
|
|
140
155
|
/**
|
|
141
156
|
* The field name used to identify the language of an internationalized array item.
|
|
142
157
|
*
|
|
143
|
-
* In v4.x this was '_key', in v5+ this
|
|
144
|
-
*
|
|
158
|
+
* In v4.x this was '_key', in v5+ this is 'language'.
|
|
159
|
+
* ```ts
|
|
160
|
+
* {
|
|
161
|
+
* "description": [
|
|
162
|
+
* {
|
|
163
|
+
* "_key": "kjjNvZHK8Y2QpTEf3K5jc",
|
|
164
|
+
* "_type": "internationalizedArrayTextValue",
|
|
165
|
+
* "language": "en"
|
|
166
|
+
* "value": "This is the description in English"
|
|
167
|
+
* },
|
|
168
|
+
* {
|
|
169
|
+
* "_key": "kjjNvZHK8Y2QpTEf3K5jc",
|
|
170
|
+
* "_type": "internationalizedArrayTextValue",
|
|
171
|
+
* "language": "es"
|
|
172
|
+
* "value": "This is the description in Spanish"
|
|
173
|
+
* },
|
|
174
|
+
* ]
|
|
175
|
+
* }
|
|
176
|
+
* ```
|
|
177
|
+
*/
|
|
178
|
+
declare const LANGUAGE_FIELD_NAME: "language";
|
|
179
|
+
declare const internationalizedArray: sanity0.Plugin<PluginConfig>;
|
|
180
|
+
/**
|
|
181
|
+
* Default filter function for the internationalized array field.
|
|
182
|
+
* It filter the field base on the `language` value of the object.
|
|
145
183
|
*/
|
|
146
|
-
declare const
|
|
147
|
-
export { AllowedType, ArrayConfig, InternationalizedArrayItem, LANGUAGE_FIELD_NAME, Language, LanguageCallback, LanguageDisplay, PluginConfig, Value, clear, internationalizedArray, isInternationalizedArrayItemType };
|
|
184
|
+
declare const internationalizedArrayLanguageFilter: FilterFieldFunction;
|
|
185
|
+
export { AllowedType, ArrayConfig, InternationalizedArrayItem, LANGUAGE_FIELD_NAME, Language, LanguageCallback, LanguageDisplay, PluginConfig, Value, clear, internationalizedArray, internationalizedArrayLanguageFilter, isInternationalizedArrayItemType };
|
|
148
186
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/types.ts","../src/cache.ts","../src/plugin.tsx","../src/
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/types.ts","../src/cache.ts","../src/constants.ts","../src/plugin.tsx","../src/utils/internationalizedArrayLanguageFilter.ts"],"mappings":";;;KAEY,QAAA;EACV,EAAA,EAAI,IAAA,CAAK,4BAAA;EACT,KAAA;AAAA;AAAA,KAGU,WAAA;AAAA,KAEA,WAAA;EACV,IAAA;EACA,IAAA,EAAM,WAAA;EACN,SAAA,EAAW,QAAA;EACX,KAAA;EACA,KAAA;EACA,MAAA;EACA,QAAA;EACA,UAAA,GAAa,IAAA,GAAO,IAAA;EACpB,KAAA;IAAA,CAAU,GAAA;IAAuB,OAAA;MAAA,CAAW,GAAA;IAAA;EAAA;AAAA;;;;KAMlC,KAAA;EACV,IAAA,UARwB;EAUxB,QAAA;EACA,KAAA;AAAA;AAAA,iBAGc,gCAAA,CACd,IAAA,WACC,IAAA,IAAQ,0BAAA;AAAA,KAIC,0BAAA;EACV,IAAA;EACA,KAAA,GAAQ,CAAA,EAxBR;EA0BA,QAAA;EAxBA;;;EA4BA,KAAA;AAAA;AAAA,KAGU,gBAAA,IACV,MAAA,EAAQ,YAAA,EACR,aAAA,EAAe,MAAA,sBACZ,OAAA,CAAQ,QAAA;AAAA,KAED,eAAA;AAAA,KAEA,YAAA;EArC6C;AAMzD;;;EAoCE,UAAA;EAnCA;;;;;AAMF;;;;;;;EA0CE,MAAA,GAAS,MAAA;EAxC0B;AAIrC;;;;;;;;;;;;AAWA;;;;;;;;;;;;;;EAqDE,SAAA,EAAW,QAAA,KAAa,gBAAA;EAlDL;;AAErB;;;;;AAEA;EAuDE,gBAAA;;;;;;;;;;;;;;;;;;;;;;;;EAyBA,UAAA,YAAsB,kBAAA,GAAqB,eAAA;EA2BzC;;;;EAtBF,eAAA;EClGD;;;;EDuGC,YAAA;EExHW;;;;EF6HX,eAAA,GAAkB,eAAA;;AGvIpB;;;;;;ACPA;;;EJyJE,cAAA;IACE,aAAA;EAAA;AAAA;AAAA,cC1HS,KAAA;;;;ADtCb;;;;;;;;;;AAKA;;;;;AAEA;;;;;cEgBa,mBAAA;AAAA,cCVA,sBAAA,EAAsB,OAAA,CAAA,MAAA,CAAA,YAAA;;;;AHbnC;cIMa,oCAAA,EAAsC,mBAAA"}
|