From e4df1e9265b4d782cacb81923db58fc9239ebf5f Mon Sep 17 00:00:00 2001 From: David Annez Date: Thu, 8 Jan 2026 15:16:22 -0300 Subject: [PATCH] =?UTF-8?q?Revert=20"feat(SAPP-2780):=20Use=20dedicated=20?= =?UTF-8?q?language=20field=20instead=20of=20=5Fkey=20for=20lan=E2=80=A6"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 2a19fa01469eb6f1003923afadd8a09267eb0b67. --- README.md | 98 ++------ migrations/keyToLanguage.ts | 292 ---------------------- package-lock.json | 124 ++------- package.json | 1 - src/components/DocumentAddButtons.tsx | 6 +- src/components/InternationalizedArray.tsx | 10 +- src/components/InternationalizedInput.tsx | 18 +- src/fieldActions/index.ts | 2 +- src/schema/array.ts | 48 ++-- src/schema/object.ts | 9 +- src/types.ts | 2 - src/utils/checkAllLanguagesArePresent.ts | 2 +- src/utils/createAddLanguagePatches.ts | 13 +- src/utils/getDocumentsToTranslate.ts | 1 - 14 files changed, 74 insertions(+), 552 deletions(-) delete mode 100644 migrations/keyToLanguage.ts diff --git a/README.md b/README.md index f09e9bd..d553cda 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ # sanity-plugin-internationalized-array -A plugin to register array fields with a custom input component to store field values in multiple languages, queryable by the `language` field. +A plugin to register array fields with a custom input component to store field values in multiple languages, queryable by using the language ID as an array `_key`. ![Screenshot of an internationalized input](./img/internationalized-array.png) @@ -19,7 +19,6 @@ A plugin to register array fields with a custom input component to store field v - [Usage with @sanity/language-filter](#usage-with-sanitylanguage-filter) - [Shape of stored data](#shape-of-stored-data) - [Querying data](#querying-data) - - [Migrate from v3 to v4](#migrate-from-v3-to-v4) - [Migrate from objects to arrays](#migrate-from-objects-to-arrays) - [Why store localized field data like this?](#why-store-localized-field-data-like-this) - [License](#license) @@ -309,14 +308,15 @@ export default defineConfig({ enclosingType.name.startsWith('internationalizedArray') && 'kind' in member ) { - // Get the language from the member's parent value - // In v4+, language is stored in a dedicated `language` field - const parentValue = member.field.path.length >= 2 - ? member.field.document?.[member.field.path[0]]?.find( - (item: any) => item._key === member.field.path[1]?._key - ) - : null - const language = parentValue?.language + // Get last two segments of the field's path + const pathEnd = member.field.path.slice(-2) + // If the second-last segment is a _key, and the last segment is `value`, + // It's an internationalized array value + // And the array _key is the language of the field + const language = + pathEnd[1] === 'value' && isKeySegment(pathEnd[0]) + ? pathEnd[0]._key + : null return language ? selectedLanguageIds.includes(language) : false } @@ -339,93 +339,25 @@ export default defineConfig({ ## Shape of stored data -The custom input contains buttons which will add new array items with a `language` field identifying the language. Data returned from this array will look like this: +The custom input contains buttons which will add new array items with the language as the `_key` value. Data returned from this array will look like this: ```json "greeting": [ - { "_key": "abc123", "language": "en", "value": "hello" }, - { "_key": "def456", "language": "fr", "value": "bonjour" } + { "_key": "en", "value": "hello" }, + { "_key": "fr", "value": "bonjour" }, ] ``` -> **Note:** In versions prior to v4, the language ID was stored in the `_key` field. See [Migrate from v3 to v4](#migrate-from-v3-to-v4) if you're upgrading from an earlier version. - ## Querying data -Using GROQ filters you can query for a specific language like so: +Using GROQ filters you can query for a specific language key like so: ```js *[_type == "person"] { - "greeting": greeting[language == "en"][0].value + "greeting": greeting[_key == "en"][0].value } ``` -> **Migrating queries from v3:** If upgrading from v3, replace `_key == "en"` with `language == "en"` in your GROQ queries. - -## Migrate from v3 to v4 - -Version 4 changes how language identification is stored. Previously, the language ID was stored in the array item's `_key` field. Now, a dedicated `language` field is used, and `_key` contains a random identifier. - -**Before (v3):** -```json -{ "_key": "en", "value": "hello" } -``` - -**After (v4):** -```json -{ "_key": "abc123", "language": "en", "value": "hello" } -``` - -### Why this change? - -The `_key` field in Sanity arrays is meant for tracking item identity across edits, not for storing semantic data. Using it for language IDs caused issues with: -- Array reordering and diffing in the Studio -- Portable Text operations that rely on stable keys -- Edge cases when copying/pasting between documents - -### Migration steps - -1. **Take a backup first!** - ```bash - npx sanity@latest dataset export - ``` - -2. **Update the plugin** to v4 - -3. **Update your GROQ queries** to use `language` instead of `_key`: - ```js - // Before - greeting[_key == "en"][0].value - - // After - greeting[language == "en"][0].value - ``` - -4. **Run the migration script** to update existing documents: - - Edit `migrations/keyToLanguage.ts` to configure your document types and field names: - ```ts - const DOCUMENT_TYPES = ['post', 'page'] // Your document types - const FIELD_NAMES = ['title', 'description'] // Your internationalized fields - ``` - - First, run in dry-run mode to preview changes: - ```bash - npx sanity@latest exec ./migrations/keyToLanguage.ts --with-user-token - ``` - - Then set `DRY_RUN = false` and run again to apply changes. - -5. **Handle drafts and published documents** - The migration script processes all documents. Run it twice if needed: once for production, once after publishing any pending drafts. - -### Migration script details - -The migration script (`migrations/keyToLanguage.ts`): -- Processes documents in batches of 100 -- Uses optimistic locking (`ifRevisionID`) for safe concurrent execution -- Is idempotent - safe to run multiple times -- Skips items that already have a `language` field - ## Migrate from objects to arrays [See the migration script](https://github.com/sanity-io/sanity-plugin-internationalized-array/blob/main/migrations/transformObjectToArray.ts) inside `./migrations/transformObjectToArray.ts` of this Repo. diff --git a/migrations/keyToLanguage.ts b/migrations/keyToLanguage.ts deleted file mode 100644 index b61b860..0000000 --- a/migrations/keyToLanguage.ts +++ /dev/null @@ -1,292 +0,0 @@ -/* eslint-disable no-console, @typescript-eslint/no-explicit-any, consistent-return */ - -import {nanoid} from 'nanoid' -import {getCliClient} from 'sanity/cli' - -// Migration script: Convert _key-based language identification to dedicated language field -// -// BEFORE (v3.x): -// "greeting": [ -// { "_key": "en", "value": "hello" }, -// { "_key": "fr", "value": "bonjour" } -// ] -// -// AFTER (v4.x): -// "greeting": [ -// { "_key": "abc123", "language": "en", "value": "hello" }, -// { "_key": "def456", "language": "fr", "value": "bonjour" } -// ] -// -// This migration: -// 1. Finds documents with internationalized array fields that lack the `language` property -// 2. Copies the `_key` value to a new `language` field -// 3. Generates a new random `_key` using nanoid -// 4. Uses optimistic locking (ifRevisionID) for safe concurrent execution -// -// The script is idempotent - it can be safely re-run multiple times. - -// ============================================================================= -// CONFIGURATION - Modify these values for your project -// ============================================================================= - -/** - * Document type(s) to migrate. Can be a single type or array of types. - * Example: 'post' or ['post', 'page', 'product'] - */ -const DOCUMENT_TYPES: string | string[] = 'post' - -/** - * Field name(s) containing internationalized arrays. - * These should match the field names in your schema that use the plugin. - * Example: 'title' or ['title', 'description', 'body'] - */ -const FIELD_NAMES: string | string[] = 'title' - -/** - * Batch size for processing documents. Lower values are safer but slower. - * Default: 100 - */ -const BATCH_SIZE = 100 - -/** - * Set to true to preview changes without applying them. - * Highly recommended to run with DRY_RUN=true first! - */ -const DRY_RUN = true - -/** - * API version for Sanity client - */ -const API_VERSION = '2024-01-01' - -// ============================================================================= -// MIGRATION LOGIC - Generally no need to modify below this line -// ============================================================================= - -const client = getCliClient({apiVersion: API_VERSION}) - -// Normalize config to arrays -const documentTypes = Array.isArray(DOCUMENT_TYPES) - ? DOCUMENT_TYPES - : [DOCUMENT_TYPES] -const fieldNames = Array.isArray(FIELD_NAMES) ? FIELD_NAMES : [FIELD_NAMES] - -/** - * Build the GROQ query to find documents needing migration. - * - * A document needs migration if: - * - It matches one of the configured document types - * - It has at least one of the configured fields defined - * - At least one array item in those fields lacks a `language` property - */ -function buildFetchQuery(): string { - // Build field existence checks - const fieldChecks = fieldNames - .map((field) => `defined(${field})`) - .join(' || ') - - // Build migration status checks - find docs where any field has items without language - const migrationChecks = fieldNames - .map((field) => `count(${field}[!defined(language)]) > 0`) - .join(' || ') - - // Build projection to fetch only the fields we need - const projection = ['_id', '_rev', ...fieldNames].join(', ') - - return `*[ - _type in $types - && (${fieldChecks}) - && (${migrationChecks}) - ][0...${BATCH_SIZE}] {${projection}}` -} - -/** - * Fetch the next batch of documents that need migration - */ -async function fetchDocuments(): Promise { - const query = buildFetchQuery() - - if (DRY_RUN) { - console.log('Query:', query) - console.log('Params:', {types: documentTypes}) - } - - return client.fetch(query, {types: documentTypes}) -} - -/** - * Transform a single array item from old format to new format - */ -function transformArrayItem(item: {_key: string; value?: unknown}): { - _key: string - language: string - value?: unknown -} { - // Copy _key to language, generate new random _key - return { - ...item, - _key: nanoid(), - language: item._key, - } -} - -/** - * Build patch operations for a single document - */ -function buildPatch(doc: any): {id: string; patch: any} | null { - const setOperations: Record = {} - - for (const fieldName of fieldNames) { - const fieldValue = doc[fieldName] - - // Skip if field doesn't exist or is empty - if (!fieldValue || !Array.isArray(fieldValue) || fieldValue.length === 0) { - continue - } - - // Check if any items need migration (lack language field) - const needsMigration = fieldValue.some((item: any) => !item.language) - - if (needsMigration) { - // Transform all items in the array - setOperations[fieldName] = fieldValue.map((item: any) => { - // Only transform items that don't already have language - if (item.language) { - return item - } - return transformArrayItem(item) - }) - } - } - - // If no fields need migration, skip this document - if (Object.keys(setOperations).length === 0) { - return null - } - - return { - id: doc._id, - patch: { - set: setOperations, - ifRevisionID: doc._rev, - }, - } -} - -/** - * Build patches for a batch of documents - */ -function buildPatches(docs: any[]): Array<{id: string; patch: any}> { - return docs - .map(buildPatch) - .filter((patch): patch is {id: string; patch: any} => patch !== null) -} - -/** - * Create a transaction from patches - */ -function createTransaction(patches: Array<{id: string; patch: any}>) { - return patches.reduce( - (tx, {id, patch}) => tx.patch(id, patch), - client.transaction() - ) -} - -/** - * Commit a transaction - */ -async function commitTransaction(tx: any): Promise { - await tx.commit() -} - -/** - * Log patch details for review - */ -function logPatches(patches: Array<{id: string; patch: any}>): void { - for (const {id, patch} of patches) { - console.log(`\n${id}:`) - for (const [field, value] of Object.entries(patch.set)) { - if (Array.isArray(value)) { - console.log(` ${field}: ${value.length} items`) - for (const item of value as any[]) { - console.log(` - _key: ${item._key}, language: ${item.language}`) - } - } - } - } -} - -/** - * Main migration loop - process batches until no more documents need migration - */ -async function migrateNextBatch(): Promise { - const documents = await fetchDocuments() - - if (documents.length === 0) { - console.log('\nāœ… No more documents to migrate!') - return - } - - console.log(`\nFound ${documents.length} documents to migrate`) - - const patches = buildPatches(documents) - - if (patches.length === 0) { - console.log( - 'No patches to apply (documents may have been migrated concurrently)' - ) - return migrateNextBatch() - } - - console.log(`Built ${patches.length} patches`) - logPatches(patches) - - if (DRY_RUN) { - console.log('\nšŸ” DRY RUN - No changes applied') - console.log('Set DRY_RUN = false to apply these changes') - return - } - - console.log('\nApplying patches...') - const transaction = createTransaction(patches) - await commitTransaction(transaction) - console.log('āœ… Batch committed successfully') - - // Continue with next batch - return migrateNextBatch() -} - -/** - * Migration entry point - */ -async function runMigration(): Promise { - console.log('='.repeat(60)) - console.log('Internationalized Array Migration: _key → language') - console.log('='.repeat(60)) - console.log('\nConfiguration:') - console.log(` Document types: ${documentTypes.join(', ')}`) - console.log(` Field names: ${fieldNames.join(', ')}`) - console.log(` Batch size: ${BATCH_SIZE}`) - console.log(` Dry run: ${DRY_RUN}`) - console.log('') - - if (DRY_RUN) { - console.log('āš ļø DRY RUN MODE - No changes will be applied') - console.log( - ' Review the output and set DRY_RUN = false to apply changes\n' - ) - } - - await migrateNextBatch() - - console.log(`\n${'='.repeat(60)}`) - console.log('Migration complete') - console.log('='.repeat(60)) -} - -// Run the migration -runMigration().catch((err) => { - console.error('\nāŒ Migration failed:', err.message) - console.error(err) - process.exit(1) -}) diff --git a/package-lock.json b/package-lock.json index d34ec0a..c670dd3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,7 +15,6 @@ "@sanity/ui": "^3.1.11", "fast-deep-equal": "^3.1.3", "lodash": "^4.17.21", - "nanoid": "^5.0.7", "suspend-react": "0.1.3" }, "devDependencies": { @@ -5849,25 +5848,6 @@ "rxjs": "^7.0.0" } }, - "node_modules/@sanity/bifur-client/node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, "node_modules/@sanity/browserslist-config": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/@sanity/browserslist-config/-/browserslist-config-1.0.5.tgz", @@ -6498,24 +6478,6 @@ "node": ">=14.0.0" } }, - "node_modules/@sanity/client/node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, "node_modules/@sanity/client/node_modules/through2": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz", @@ -7753,6 +7715,25 @@ "node": ">=18" } }, + "node_modules/@sanity/mutate/node_modules/nanoid": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.6.tgz", + "integrity": "sha512-c7+7RQ+dMB5dPwwCp4ee1/iV/q2P6aK1mTZcfr1BTuVlyW9hJYiMPybJCcnBlQtuSmTIWNeazm/zqNoZSSElBg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.js" + }, + "engines": { + "node": "^18 || >=20" + } + }, "node_modules/@sanity/mutator": { "version": "3.99.0", "resolved": "https://registry.npmjs.org/@sanity/mutator/-/mutator-3.99.0.tgz", @@ -21172,9 +21153,9 @@ } }, "node_modules/nanoid": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.6.tgz", - "integrity": "sha512-c7+7RQ+dMB5dPwwCp4ee1/iV/q2P6aK1mTZcfr1BTuVlyW9hJYiMPybJCcnBlQtuSmTIWNeazm/zqNoZSSElBg==", + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", "funding": [ { "type": "github", @@ -21183,10 +21164,10 @@ ], "license": "MIT", "bin": { - "nanoid": "bin/nanoid.js" + "nanoid": "bin/nanoid.cjs" }, "engines": { - "node": "^18 || >=20" + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, "node_modules/natural-compare": { @@ -25581,25 +25562,6 @@ "node": "^10 || ^12 || >=14" } }, - "node_modules/postcss/node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, "node_modules/preferred-pm": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/preferred-pm/-/preferred-pm-4.1.1.tgz", @@ -28262,25 +28224,6 @@ "node": ">=16 || 14 >=14.17" } }, - "node_modules/sanity/node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, "node_modules/sanity/node_modules/npm-run-path": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-3.1.0.tgz", @@ -32451,25 +32394,6 @@ "@esbuild/win32-x64": "0.25.12" } }, - "node_modules/vite/node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, "node_modules/vite/node_modules/postcss": { "version": "8.5.6", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", diff --git a/package.json b/package.json index f79a5de..be7d4fb 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,6 @@ "@sanity/ui": "^3.1.11", "fast-deep-equal": "^3.1.3", "lodash": "^4.17.21", - "nanoid": "^5.0.7", "suspend-react": "0.1.3" }, "devDependencies": { diff --git a/src/components/DocumentAddButtons.tsx b/src/components/DocumentAddButtons.tsx index f7ad2d1..4ac98f7 100644 --- a/src/components/DocumentAddButtons.tsx +++ b/src/components/DocumentAddButtons.tsx @@ -1,5 +1,4 @@ import {Box, Stack, Text, useToast} from '@sanity/ui' -import {nanoid} from 'nanoid' import React, {useCallback} from 'react' import { FormInsertPatch, @@ -107,7 +106,7 @@ export default function DocumentAddButtons( return } const alreadyTranslated = documentsToTranslation.filter( - (translation) => translation?.language === languageId + (translation) => translation?._key === languageId ) const removeDuplicates = documentsToTranslation.reduce< DocumentsToTranslate[] @@ -150,8 +149,7 @@ export default function DocumentAddButtons( const insertValue = insert( [ { - _key: nanoid(), - language: languageId, + _key: languageId, _type: toTranslate._type, value: initialValue, // Use the determined initial value instead of undefined }, diff --git a/src/components/InternationalizedArray.tsx b/src/components/InternationalizedArray.tsx index 5f48abd..3b0d2e7 100644 --- a/src/components/InternationalizedArray.tsx +++ b/src/components/InternationalizedArray.tsx @@ -150,7 +150,7 @@ export default function InternationalizedArray( // This would also strip out values that don't have a language as the key const updatedValue = value .reduce((acc, v) => { - const newIndex = languages.findIndex((l) => l.id === v?.language) + const newIndex = languages.findIndex((l) => l.id === v?._key) if (newIndex > -1) { acc[newIndex] = v @@ -175,14 +175,14 @@ export default function InternationalizedArray( return true } - return value?.every((v) => languages.find((l) => l?.id === v?.language)) + return value?.every((v) => languages.find((l) => l?.id === v?._key)) }, [value, languages]) // Check languages are in the correct order const languagesInUse = useMemo( () => languages && languages.length > 1 - ? languages.filter((l) => value?.find((v) => v.language === l.id)) + ? languages.filter((l) => value?.find((v) => v._key === l.id)) : [], [languages, value] ) @@ -194,9 +194,7 @@ export default function InternationalizedArray( return value .map((v, vIndex) => - vIndex === languagesInUse.findIndex((l) => l.id === v.language) - ? null - : v + vIndex === languagesInUse.findIndex((l) => l.id === v._key) ? null : v ) .filter(Boolean) }, [value, languagesInUse]) diff --git a/src/components/InternationalizedInput.tsx b/src/components/InternationalizedInput.tsx index 4620fdb..0f89e46 100644 --- a/src/components/InternationalizedInput.tsx +++ b/src/components/InternationalizedInput.tsx @@ -24,8 +24,6 @@ import {useInternationalizedArrayContext} from './InternationalizedArrayContext' export type InternationalizedValue = { _type: string _key: string - /** The language identifier (e.g., 'en', 'fr'). This is the semantic identifier. */ - language: string value: string } @@ -146,11 +144,11 @@ export default function InternationalizedInput( useInternationalizedArrayContext() const languageKeysInUse = useMemo( - () => parentValue?.map((v) => v.language) ?? [], + () => parentValue?.map((v) => v._key) ?? [], [parentValue] ) const keyIsValid = languages?.length - ? languages.find((l) => l.id === value.language) + ? languages.find((l) => l.id === value._key) : false // Changes the key of this item, ideally to a valid language @@ -166,7 +164,7 @@ export default function InternationalizedInput( return } - onChange([set(languageId, ['language'])]) + onChange([set(languageId, ['_key'])]) }, [onChange, value, languages] ) @@ -180,13 +178,13 @@ export default function InternationalizedInput( return } - const language = languages.find((l) => l.id === value.language) + const language = languages.find((l) => l.id === value._key) const languageTitle: string = keyIsValid && language ? getLanguageDisplay(languageDisplay, language.title, language.id) : '' - const isDefault = defaultLanguages.includes(value.language) + const isDefault = defaultLanguages.includes(value._key) const removeButton = (