From 9fac2ef24e2ade4cf55b0d6a0e7f67e0edf57539 Mon Sep 17 00:00:00 2001 From: Dan Ribbens Date: Fri, 15 Dec 2023 23:52:12 -0500 Subject: [PATCH] feat: extend locales to have fallbackLocales --- docs/configuration/localization.mdx | 54 ++++-- .../collections/operations/local/create.ts | 16 +- .../collections/operations/local/delete.ts | 20 ++- .../src/collections/operations/local/find.ts | 26 ++- .../collections/operations/local/findByID.ts | 26 ++- .../operations/local/findVersionByID.ts | 26 ++- .../operations/local/findVersions.ts | 18 +- .../operations/local/restoreVersion.ts | 15 +- .../collections/operations/local/update.ts | 16 +- packages/payload/src/config/schema.ts | 1 + packages/payload/src/config/types.ts | 10 +- .../payload/src/localization/middleware.ts | 6 +- test/localization/config.ts | 163 +++++++++--------- test/localization/int.spec.ts | 122 ++++++++----- 14 files changed, 301 insertions(+), 218 deletions(-) diff --git a/docs/configuration/localization.mdx b/docs/configuration/localization.mdx index 86d0645315..e39a75867c 100644 --- a/docs/configuration/localization.mdx +++ b/docs/configuration/localization.mdx @@ -6,11 +6,13 @@ desc: Add and maintain as many locales as you need by adding Localization to you keywords: localization, internationalization, i18n, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, express --- -Payload features deep field-based localization support. Maintaining as many locales as you need is easy. All localization support is opt-in by default. To do so, follow the two steps below. +Payload features deep field-based localization support. Maintaining as many locales as you need is easy. All +localization support is opt-in by default. To do so, follow the two steps below. ### Enabling in the Payload config -Add the `localization` property to your Payload config to enable localization project-wide. You'll need to provide a list of all locales that you'd like to support as well as set a few other options. +Add the `localization` property to your Payload config to enable localization project-wide. You'll need to provide a +list of all locales that you'd like to support as well as set a few other options. **Example Payload config set up for localization:** @@ -57,7 +59,8 @@ export default buildConfig({ }) ``` -**Example Payload config set up for localization with full locales objects (including [internationalization](/docs/configuration/i18n) support):** +**Example Payload config set up for localization with full locales objects ( +including [internationalization](/docs/configuration/i18n) support):** ```ts import { buildConfig } from 'payload/config' @@ -93,35 +96,48 @@ export default buildConfig({ **`locales`** -Array-based list of all locales that you would like to support. These can be strings of locale codes or objects with a `label`, a locale `code`, and the `rtl` (right-to-left) property. The locale codes do not need to be in any specific format. It's up to you to define how to represent your locales. Common patterns are to use two-letter ISO 639 language codes or four-letter language and country codes (ISO 3166‑1) such as `en-US`, `en-UK`, `es-MX`, etc. +Array-based list of all locales that you would like to support. These can be strings of locale codes or objects with +a `label`, a locale `code`, `rtl` (right-to-left), and `fallbackLocale` property. The locale codes do not need to be in +any specific format. It's up to you to define how to represent your locales. Common patterns are to use two-letter ISO +639 language codes or four-letter language and country codes (ISO 3166‑1) such as `en-US`, `en-UK`, `es-MX`, etc. **`defaultLocale`** -Required string that matches one of the locale codes from the array provided. By default, if no locale is specified, documents will be returned in this locale. +Required string that matches one of the locale codes from the array provided. By default, if no locale is specified, +documents will be returned in this locale. **`fallback`** -Boolean enabling "fallback" locale functionality. If a document is requested in a locale, but a field does not have a localized value corresponding to the requested locale, then if this property is enabled, the document will automatically fall back to the fallback locale value. If this property is not enabled, the value will not be populated. +Boolean enabling "fallback" locale functionality. If a document is requested in a locale, but a field does not have a +localized value corresponding to the requested locale, then if this property is enabled, the document will automatically +fall back to the fallback locale value. If this property is not enabled, the value will not be populated. ### Field by field localization -Payload localization works on a **field** level—not a document level. In addition to configuring the base Payload config to support localization, you need to specify each field that you would like to localize. +Payload localization works on a **field** level—not a document level. In addition to configuring the base Payload config +to support localization, you need to specify each field that you would like to localize. **Here is an example of how to enable localization for a field:** ```js { name: 'title', - type: 'text', - // highlight-start - localized: true, + type +: + 'text', + // highlight-start + localized +: + true, // highlight-end } ``` -With the above configuration, the `title` field will now be saved in the database as an object of all locales instead of a single string. +With the above configuration, the `title` field will now be saved in the database as an object of all locales instead of +a single string. -All field types with a `name` property support the `localized` property—even the more complex field types like `array`s and `block`s. +All field types with a `name` property support the `localized` property—even the more complex field types like `array`s +and `block`s. Note: @@ -143,7 +159,8 @@ All field types with a `name` property support the `localized` property—even t ### Retrieving localized docs -When retrieving documents, you can specify which locale you'd like to receive as well as which fallback locale should be used. +When retrieving documents, you can specify which locale you'd like to receive as well as which fallback locale should be +used. ##### REST API @@ -155,7 +172,8 @@ Specify your desired locale by providing the `locale` query parameter directly i **`?fallback-locale=`** -Specify fallback locale to be used by providing the `fallback-locale` query parameter. This can be provided as either a valid locale as provided to your base Payload config, or `'null'`, `'false'`, or `'none'` to disable falling back. +Specify fallback locale to be used by providing the `fallback-locale` query parameter. This can be provided as either a +valid locale as provided to your base Payload config, or `'null'`, `'false'`, or `'none'` to disable falling back. **Example:** @@ -167,7 +185,9 @@ fetch('https://localhost:3000/api/pages?locale=es&fallback-locale=none'); In the GraphQL API, you can specify `locale` and `fallbackLocale` args to all relevant queries and mutations. -The `locale` arg will only accept valid locales, but locales will be formatted automatically as valid GraphQL enum values (dashes or special characters will be converted to underscores, spaces will be removed, etc.). If you are curious to see how locales are auto-formatted, you can use the [GraphQL playground](/docs/graphql/overview#graphql-playground). +The `locale` arg will only accept valid locales, but locales will be formatted automatically as valid GraphQL enum +values (dashes or special characters will be converted to underscores, spaces will be removed, etc.). If you are curious +to see how locales are auto-formatted, you can use the [GraphQL playground](/docs/graphql/overview#graphql-playground). The `fallbackLocale` arg will accept valid locales as well as `none` to disable falling back. @@ -191,7 +211,9 @@ query { ##### Local API -You can specify `locale` as well as `fallbackLocale` within the Local API as well as properties on the `options` argument. The `locale` property will accept any valid locale, and the `fallbackLocale` property will accept any valid locale as well as `'null'`, `'false'`, `false`, and `'none'`. +You can specify `locale` as well as `fallbackLocale` within the Local API as well as properties on the `options` +argument. The `locale` property will accept any valid locale, and the `fallbackLocale` property will accept any valid +locale as well as `'null'`, `'false'`, `false`, and `'none'`. **Example:** diff --git a/packages/payload/src/collections/operations/local/create.ts b/packages/payload/src/collections/operations/local/create.ts index 4c8804a841..9099e9ab6e 100644 --- a/packages/payload/src/collections/operations/local/create.ts +++ b/packages/payload/src/collections/operations/local/create.ts @@ -49,10 +49,10 @@ export default async function createLocal locale === code)?.fallbackLocale : null if (!collection) { @@ -73,8 +76,9 @@ export default async function createLocal( collection: collectionSlug, context, depth, - fallbackLocale, - locale = null, + fallbackLocale: fallbackLocaleArg, + locale: localeArg = null, overrideAccess = true, - req: incomingReq, + req: incomingReq = {} as PayloadRequest, showHiddenFields, user, where, } = options const collection = payload.collections[collectionSlug] - const defaultLocale = payload?.config?.localization - ? payload?.config?.localization?.defaultLocale + const localizationConfig = payload?.config?.localization + const defaultLocale = localizationConfig ? localizationConfig.defaultLocale : null + const locale = localeArg || incomingReq.locale || defaultLocale + const fallbackLocale = localizationConfig + ? localizationConfig.locales.find(({ code }) => locale === code)?.fallbackLocale : null if (!collection) { @@ -82,9 +85,12 @@ async function deleteLocal( } const req = { - fallbackLocale: typeof fallbackLocale !== 'undefined' ? fallbackLocale : defaultLocale, + fallbackLocale: + typeof fallbackLocaleArg !== 'undefined' + ? fallbackLocaleArg + : fallbackLocale || defaultLocale, i18n: i18nInit(payload.config.i18n), - locale: locale ?? defaultLocale, + locale: locale, payload, payloadAPI: 'local', transactionID: incomingReq?.transactionID, diff --git a/packages/payload/src/collections/operations/local/find.ts b/packages/payload/src/collections/operations/local/find.ts index db2a25fa31..f33e84f9f5 100644 --- a/packages/payload/src/collections/operations/local/find.ts +++ b/packages/payload/src/collections/operations/local/find.ts @@ -44,9 +44,9 @@ export default async function findLocal locale === code)?.fallbackLocale : null if (!collection) { @@ -69,19 +72,10 @@ export default async function findLocal locale === code)?.fallbackLocale : null if (!collection) { @@ -60,19 +63,10 @@ export default async function findByIDLocal locale === code)?.fallbackLocale : null if (!collection) { @@ -59,19 +62,10 @@ export default async function findVersionByIDLocal locale === code)?.fallbackLocale : null if (!collection) { @@ -64,9 +67,12 @@ export default async function findVersionsLocal locale === code)?.fallbackLocale + : null if (!collection) { throw new APIError( @@ -55,7 +61,10 @@ export default async function restoreVersionLocal( data, depth, draft, - fallbackLocale, + fallbackLocale: fallbackLocaleArg, file, filePath, - locale = null, + locale: localeArg = null, overrideAccess = true, overwriteExistingFiles = false, req: incomingReq, @@ -88,8 +88,11 @@ async function updateLocal( const collection = payload.collections[collectionSlug] const i18n = i18nInit(payload.config.i18n) - const defaultLocale = payload.config.localization - ? payload.config.localization?.defaultLocale + const localizationConfig = payload?.config?.localization + const defaultLocale = localizationConfig ? localizationConfig.defaultLocale : null + const locale = localeArg || incomingReq.locale || defaultLocale + const fallbackLocale = localizationConfig + ? localizationConfig.locales.find(({ code }) => locale === code)?.fallbackLocale : null if (!collection) { @@ -99,7 +102,10 @@ async function updateLocal( } const req = { - fallbackLocale: typeof fallbackLocale !== 'undefined' ? fallbackLocale : defaultLocale, + fallbackLocale: + typeof fallbackLocaleArg !== 'undefined' + ? fallbackLocaleArg + : fallbackLocale || defaultLocale, files: { file: file ?? (await getFileByPath(filePath)), }, diff --git a/packages/payload/src/config/schema.ts b/packages/payload/src/config/schema.ts index a98be21e91..5556fec27d 100644 --- a/packages/payload/src/config/schema.ts +++ b/packages/payload/src/config/schema.ts @@ -137,6 +137,7 @@ export default joi.object({ joi.array().items( joi.object().keys({ code: joi.string(), + fallbackLocale: joi.string(), label: joi .alternatives() .try( diff --git a/packages/payload/src/config/types.ts b/packages/payload/src/config/types.ts index 1ce050fa67..92fdea6fe4 100644 --- a/packages/payload/src/config/types.ts +++ b/packages/payload/src/config/types.ts @@ -11,11 +11,7 @@ import type { DeepRequired } from 'ts-essentials' import type { InlineConfig } from 'vite' import type { Configuration } from 'webpack' -import type { - DocumentTab, - DocumentTabComponent, - DocumentTabConfig, -} from '../admin/components/elements/DocumentHeader/Tabs/types' +import type { DocumentTab } from '../admin/components/elements/DocumentHeader/Tabs/types' import type { RichTextAdapter } from '../admin/components/forms/field-types/RichText/types' import type { ContextType } from '../admin/components/utilities/DocumentInfo/types' import type { User } from '../auth/types' @@ -288,6 +284,10 @@ export type Locale = { * @example "en" */ code: string + /** + * Code of another locale to use when reading documents with fallback, if not specified defaultLocale is used + */ + fallbackLocale?: string /** * label of supported locale * @example "English" diff --git a/packages/payload/src/localization/middleware.ts b/packages/payload/src/localization/middleware.ts index 8de45cb7f5..6140e07588 100644 --- a/packages/payload/src/localization/middleware.ts +++ b/packages/payload/src/localization/middleware.ts @@ -31,8 +31,12 @@ export default function localizationMiddleware(localization: SanitizedLocalizati req.locale = requestedLocale } + const fallbackLocale = + localization.locales.find(({ code }) => req.locale === code)?.fallbackLocale || + localization.defaultLocale + if (validFallbackLocales.find((locale) => locale === requestedFallbackLocale)) { - req.fallbackLocale = requestedFallbackLocale + req.fallbackLocale = requestedFallbackLocale || fallbackLocale } } diff --git a/test/localization/config.ts b/test/localization/config.ts index 5ae861a5c6..d6eb8d07d0 100644 --- a/test/localization/config.ts +++ b/test/localization/config.ts @@ -26,48 +26,26 @@ export type LocalizedPostAllLocale = LocalizedPost & { } const openAccess = { - read: () => true, create: () => true, delete: () => true, + read: () => true, update: () => true, } export default buildConfigWithDefaults({ - localization: { - locales: [ - { - label: 'English', - code: defaultLocale, - rtl: false, - }, - { - label: 'Spanish', - code: spanishLocale, - rtl: false, - }, - { - label: 'Arabic', - code: 'ar', - rtl: true, - }, - ], - defaultLocale, - fallback: true, - }, collections: [ { - slug: 'users', auth: true, fields: [ { name: 'relation', - type: 'relationship', relationTo: localizedPostsSlug, + type: 'relationship', }, ], + slug: 'users', }, { - slug: localizedPostsSlug, access: openAccess, admin: { useAsTitle: 'title', @@ -75,9 +53,9 @@ export default buildConfigWithDefaults({ fields: [ { name: 'title', - type: 'text', - localized: true, index: true, + localized: true, + type: 'text', }, { name: 'description', @@ -85,17 +63,16 @@ export default buildConfigWithDefaults({ }, { name: 'localizedCheckbox', - type: 'checkbox', localized: true, + type: 'checkbox', }, { name: 'children', - type: 'relationship', - relationTo: localizedPostsSlug, hasMany: true, + relationTo: localizedPostsSlug, + type: 'relationship', }, { - type: 'group', name: 'group', fields: [ { @@ -103,126 +80,127 @@ export default buildConfigWithDefaults({ type: 'text', }, ], + type: 'group', }, ], + slug: localizedPostsSlug, }, ArrayCollection, { - slug: withRequiredLocalizedFields, fields: [ { name: 'title', - type: 'text', - required: true, localized: true, + required: true, + type: 'text', }, { name: 'layout', - type: 'blocks', - required: true, - localized: true, blocks: [ { - slug: 'text', fields: [ { name: 'text', type: 'text', }, ], + slug: 'text', }, { - slug: 'number', fields: [ { name: 'number', type: 'number', }, ], + slug: 'number', }, ], + localized: true, + required: true, + type: 'blocks', }, ], + slug: withRequiredLocalizedFields, }, { - slug: withLocalizedRelSlug, access: openAccess, fields: [ // Relationship { name: 'localizedRelationship', - type: 'relationship', relationTo: localizedPostsSlug, + type: 'relationship', }, // Relation hasMany { name: 'localizedRelationHasManyField', - type: 'relationship', - relationTo: localizedPostsSlug, hasMany: true, + relationTo: localizedPostsSlug, + type: 'relationship', }, // Relation multiple relationTo { name: 'localizedRelationMultiRelationTo', - type: 'relationship', relationTo: [localizedPostsSlug, 'dummy'], + type: 'relationship', }, // Relation multiple relationTo hasMany { name: 'localizedRelationMultiRelationToHasMany', - type: 'relationship', - relationTo: [localizedPostsSlug, 'dummy'], hasMany: true, + relationTo: [localizedPostsSlug, 'dummy'], + type: 'relationship', }, ], + slug: withLocalizedRelSlug, }, { - slug: relationshipLocalizedSlug, fields: [ { name: 'relationship', - type: 'relationship', - relationTo: localizedPostsSlug, localized: true, + relationTo: localizedPostsSlug, + type: 'relationship', }, { name: 'relationshipHasMany', - type: 'relationship', - relationTo: localizedPostsSlug, hasMany: true, localized: true, + relationTo: localizedPostsSlug, + type: 'relationship', }, { name: 'relationMultiRelationTo', - type: 'relationship', - relationTo: [localizedPostsSlug, 'dummy'], localized: true, + relationTo: [localizedPostsSlug, 'dummy'], + type: 'relationship', }, { name: 'relationMultiRelationToHasMany', - type: 'relationship', - relationTo: [localizedPostsSlug, 'dummy'], hasMany: true, localized: true, + relationTo: [localizedPostsSlug, 'dummy'], + type: 'relationship', }, { name: 'arrayField', - label: 'Array Field', - type: 'array', - localized: true, fields: [ { - type: 'relationship', name: 'nestedRelation', label: 'Nested Relation', relationTo: localizedPostsSlug, + type: 'relationship', }, ], + label: 'Array Field', + localized: true, + type: 'array', }, ], + slug: relationshipLocalizedSlug, }, { - slug: 'dummy', access: openAccess, fields: [ { @@ -230,26 +208,49 @@ export default buildConfigWithDefaults({ type: 'text', }, ], + slug: 'dummy', }, ], globals: [ { - slug: 'global-array', fields: [ { name: 'array', - type: 'array', fields: [ { name: 'text', - type: 'text', localized: true, + type: 'text', }, ], + type: 'array', }, ], + slug: 'global-array', }, ], + localization: { + defaultLocale, + fallback: true, + locales: [ + { + code: defaultLocale, + fallbackLocale: spanishLocale, + label: 'English', + rtl: false, + }, + { + code: spanishLocale, + label: 'Spanish', + rtl: false, + }, + { + code: 'ar', + label: 'Arabic', + rtl: true, + }, + ], + }, onInit: async (payload) => { const collection = localizedPostsSlug @@ -277,12 +278,12 @@ export default buildConfigWithDefaults({ }) await payload.update({ - collection, id: localizedPost.id, - locale: spanishLocale, + collection, data: { title: spanishTitle, }, + locale: spanishLocale, }) const localizedRelation = await payload.create({ @@ -293,12 +294,12 @@ export default buildConfigWithDefaults({ }) await payload.update({ - collection, id: localizedPost.id, - locale: spanishLocale, + collection, data: { title: relationSpanishTitle, }, + locale: spanishLocale, }) const localizedRelation2 = await payload.create({ @@ -308,47 +309,46 @@ export default buildConfigWithDefaults({ }, }) await payload.update({ - collection, id: localizedPost.id, - locale: spanishLocale, + collection, data: { title: relationSpanishTitle2, }, + locale: spanishLocale, }) await payload.create({ collection: withLocalizedRelSlug, data: { - relationship: localizedRelation.id, localizedRelationHasManyField: [localizedRelation.id, localizedRelation2.id], localizedRelationMultiRelationTo: { relationTo: collection, value: localizedRelation.id }, localizedRelationMultiRelationToHasMany: [ { relationTo: localizedPostsSlug, value: localizedRelation.id }, { relationTo: localizedPostsSlug, value: localizedRelation2.id }, ], + relationship: localizedRelation.id, }, }) await payload.create({ collection: relationshipLocalizedSlug, - locale: 'en', data: { - relationship: localizedRelation.id, - relationshipHasMany: [localizedRelation.id, localizedRelation2.id], - relationMultiRelationTo: { relationTo: collection, value: localizedRelation.id }, - relationMultiRelationToHasMany: [ - { relationTo: localizedPostsSlug, value: localizedRelation.id }, - { relationTo: localizedPostsSlug, value: localizedRelation2.id }, - ], arrayField: [ { nestedRelation: localizedRelation.id, }, ], + relationMultiRelationTo: { relationTo: collection, value: localizedRelation.id }, + relationMultiRelationToHasMany: [ + { relationTo: localizedPostsSlug, value: localizedRelation.id }, + { relationTo: localizedPostsSlug, value: localizedRelation2.id }, + ], + relationship: localizedRelation.id, + relationshipHasMany: [localizedRelation.id, localizedRelation2.id], }, + locale: 'en', }) const globalArray = await payload.updateGlobal({ - slug: 'global-array', data: { array: [ { @@ -359,17 +359,18 @@ export default buildConfigWithDefaults({ }, ], }, + slug: 'global-array', }) await payload.updateGlobal({ - slug: 'global-array', - locale: 'es', data: { array: globalArray.array.map((row, i) => ({ ...row, text: `test es ${i + 1}`, })), }, + locale: 'es', + slug: 'global-array', }) }, }) diff --git a/test/localization/int.spec.ts b/test/localization/int.spec.ts index 1e4427b9ab..cfb8c23586 100644 --- a/test/localization/int.spec.ts +++ b/test/localization/int.spec.ts @@ -6,6 +6,7 @@ import type { LocalizedPost, WithLocalizedRelationship } from './payload-types' import payload from '../../packages/payload/src' import { devUser } from '../credentials' +import { englishLocale } from '../globals/config' import { initPayloadTest } from '../helpers/configHelpers' import { RESTClient } from '../helpers/rest' import { arrayCollectionSlug } from './collections/Array' @@ -37,7 +38,7 @@ describe('Localization', () => { beforeAll(async () => { ;({ serverURL } = await initPayloadTest({ __dirname, init: { local: false } })) - client = new RESTClient(config, { serverURL, defaultSlug: collection }) + client = new RESTClient(config, { defaultSlug: collection, serverURL }) await client.create({ data: { email: devUser.email, @@ -65,12 +66,12 @@ describe('Localization', () => { }) await payload.update({ - collection, id: postWithLocalizedData.id, - locale: spanishLocale, + collection, data: { title: spanishTitle, }, + locale: spanishLocale, }) }) @@ -93,19 +94,19 @@ describe('Localization', () => { it('add spanish translation', async () => { const updated = await payload.update({ - collection, id: post1.id, - locale: spanishLocale, + collection, data: { title: spanishTitle, }, + locale: spanishLocale, }) expect(updated.title).toEqual(spanishTitle) const localized: any = await payload.findByID({ - collection, id: post1.id, + collection, locale: 'all', }) @@ -115,24 +116,24 @@ describe('Localization', () => { it('should fallback to english translation when empty', async () => { await payload.update({ - collection, id: post1.id, - locale: spanishLocale, + collection, data: { title: '', }, + locale: spanishLocale, }) const retrievedInEnglish = await payload.findByID({ - collection, id: post1.id, + collection, }) expect(retrievedInEnglish.title).toEqual(englishTitle) const localizedFallback: any = await payload.findByID({ - collection, id: post1.id, + collection, locale: 'all', }) @@ -140,6 +141,47 @@ describe('Localization', () => { expect(localizedFallback.title.es).toEqual('') }) + describe('fallback locales', () => { + let englishData + let spanishData + let localizedDoc + + beforeAll(async () => { + englishData = { + description: 'english description', + localizedCheckbox: false, + } + spanishData = { + localizedCheckbox: true, + title: 'spanish title', + } + + localizedDoc = await payload.create({ + collection: localizedPostsSlug, + data: englishData, + locale: englishLocale, + }) + + await payload.update({ + id: localizedDoc.id, + collection: localizedPostsSlug, + data: spanishData, + locale: spanishLocale, + }) + }) + + it('should return localized fields using fallbackLocale specified in the requested locale config', async () => { + const englishDoc = await payload.findByID({ + id: localizedDoc.id, + collection: localizedPostsSlug, + locale: englishLocale, + }) + + expect(englishDoc.title).toStrictEqual(spanishData.title) + expect(englishDoc.localizedCheckbox).toStrictEqual(englishData.localizedCheckbox) + }) + }) + describe('querying', () => { let localizedPost: LocalizedPost beforeEach(async () => { @@ -152,19 +194,19 @@ describe('Localization', () => { // @ts-expect-error Force typing localizedPost = await payload.update({ - collection, id, - locale: spanishLocale, + collection, data: { title: spanishTitle, }, + locale: spanishLocale, }) }) it('unspecified locale returns default', async () => { const localized = await payload.findByID({ - collection, id: localizedPost.id, + collection, }) expect(localized.title).toEqual(englishTitle) @@ -172,9 +214,9 @@ describe('Localization', () => { it('specific locale - same as default', async () => { const localized = await payload.findByID({ + id: localizedPost.id, collection, locale: defaultLocale, - id: localizedPost.id, }) expect(localized.title).toEqual(englishTitle) @@ -182,9 +224,9 @@ describe('Localization', () => { it('specific locale - not default', async () => { const localized = await payload.findByID({ + id: localizedPost.id, collection, locale: spanishLocale, - id: localizedPost.id, }) expect(localized.title).toEqual(spanishTitle) @@ -192,9 +234,9 @@ describe('Localization', () => { it('all locales', async () => { const localized: any = await payload.findByID({ + id: localizedPost.id, collection, locale: 'all', - id: localizedPost.id, }) expect(localized.title.en).toEqual(englishTitle) @@ -267,7 +309,6 @@ describe('Localization', () => { withRelationship = await payload.create({ collection: withLocalizedRelSlug, data: { - localizedRelationship: localizedRelation.id, localizedRelationHasManyField: [localizedRelation.id, localizedRelation2.id], localizedRelationMultiRelationTo: { relationTo: localizedPostsSlug, @@ -277,6 +318,7 @@ describe('Localization', () => { { relationTo: localizedPostsSlug, value: localizedRelation.id }, { relationTo: localizedPostsSlug, value: localizedRelation2.id }, ], + localizedRelationship: localizedRelation.id, }, }) }) @@ -327,8 +369,8 @@ describe('Localization', () => { // the relationship fields themselves are localized on this collection const result: any = await payload.find({ collection: relationshipLocalizedSlug, - locale: 'all', depth: 1, + locale: 'all', }) expect(result.docs[0].relationship.en.id).toBeDefined() @@ -394,9 +436,9 @@ describe('Localization', () => { it('relationship population uses locale', async () => { const result = await payload.findByID({ + id: withRelationship.id, collection: withLocalizedRelSlug, depth: 1, - id: withRelationship.id, locale: spanishLocale, }) expect((result.localizedRelationship as LocalizedPost).title).toEqual(relationSpanishTitle) @@ -538,11 +580,11 @@ describe('Localization', () => { const reversedArrayRows = [...globalArray.array].reverse() const updatedGlobal = await payload.updateGlobal({ - slug: 'global-array', - locale: 'all', data: { array: reversedArrayRows, }, + locale: 'all', + slug: 'global-array', }) expect(updatedGlobal.array[0].text.en).toStrictEqual('test en 2') @@ -555,34 +597,34 @@ describe('Localization', () => { const newDoc = await payload.create({ collection: withRequiredLocalizedFields, data: { - title: 'hello', layout: [ { blockType: 'text', text: 'laiwejfilwaje', }, ], + title: 'hello', }, }) await payload.update({ - collection: withRequiredLocalizedFields, id: newDoc.id, - locale: spanishLocale, + collection: withRequiredLocalizedFields, data: { - title: 'en espanol, big bird', layout: [ { blockType: 'number', number: 12, }, ], + title: 'en espanol, big bird', }, + locale: spanishLocale, }) const updatedDoc = await payload.update({ - collection: withRequiredLocalizedFields, id: newDoc.id, + collection: withRequiredLocalizedFields, data: { title: 'hello x2', }, @@ -591,8 +633,8 @@ describe('Localization', () => { expect(updatedDoc.layout[0].blockType).toStrictEqual('text') const spanishDoc = await payload.findByID({ - collection: withRequiredLocalizedFields, id: newDoc.id, + collection: withRequiredLocalizedFields, locale: spanishLocale, }) @@ -689,8 +731,8 @@ describe('Localization', () => { }) const result = await payload.findByID({ - collection: localizedPostsSlug, id: createResult.id, + collection: localizedPostsSlug, locale: 'all', }) @@ -721,9 +763,9 @@ describe('Localization', () => { it('should use default locale as fallback', async () => { const spanishDoc = await payload.findByID({ - locale: spanishLocale, - collection: arrayCollectionSlug, id: docID, + collection: arrayCollectionSlug, + locale: spanishLocale, }) expect(spanishDoc.items[0].text).toStrictEqual(englishTitle) @@ -731,13 +773,13 @@ describe('Localization', () => { it('should use empty array as value', async () => { const updatedSpanishDoc = await payload.update({ - collection: arrayCollectionSlug, - locale: spanishLocale, - fallbackLocale: null, id: docID, + collection: arrayCollectionSlug, data: { items: [], }, + fallbackLocale: null, + locale: spanishLocale, }) expect(updatedSpanishDoc.items).toStrictEqual([]) @@ -745,21 +787,21 @@ describe('Localization', () => { it('should use fallback value if setting null', async () => { await payload.update({ - collection: arrayCollectionSlug, - locale: spanishLocale, id: docID, + collection: arrayCollectionSlug, data: { items: [], }, + locale: spanishLocale, }) const updatedSpanishDoc = await payload.update({ - collection: arrayCollectionSlug, - locale: spanishLocale, id: docID, + collection: arrayCollectionSlug, data: { items: null, }, + locale: spanishLocale, }) // should return the value of the fallback locale @@ -772,8 +814,8 @@ describe('Localization', () => { describe('Localized - Field Paths', () => { it('should allow querying by non-localized field names ending in a locale', async () => { await payload.update({ - collection, id: post1.id, + collection, data: { children: post1.id, group: { @@ -821,12 +863,12 @@ async function createLocalizedPost(data: { }) await payload.update({ - collection, id: localizedRelation.id, - locale: spanishLocale, + collection, data: { title: data.title.es, }, + locale: spanishLocale, }) return localizedRelation