From 1d0141fcc8aa855b91448daad468b83dcaba4c01 Mon Sep 17 00:00:00 2001 From: Daniel Lautzenheiser Date: Fri, 26 Apr 2024 12:32:10 -0400 Subject: [PATCH] feat: make non default locales optional (#1109) --- packages/core/dev-test/config.yml | 4 ++++ .../core/src/actions/__tests__/config.spec.ts | 14 ++++++++++++-- packages/core/src/actions/config.ts | 15 ++++++++++++--- .../editor-control-pane/EditorControl.tsx | 3 ++- .../editor-control-pane/EditorControlPane.tsx | 3 ++- packages/core/src/constants/configSchema.tsx | 1 + packages/core/src/interface.ts | 2 ++ packages/core/src/lib/i18n.ts | 1 + .../src/reducers/__tests__/entryDraft.spec.ts | 2 ++ packages/docs/content/docs/i18n-support.mdx | 12 +++++++++++- 10 files changed, 49 insertions(+), 8 deletions(-) diff --git a/packages/core/dev-test/config.yml b/packages/core/dev-test/config.yml index 4129b427..bc894da8 100644 --- a/packages/core/dev-test/config.yml +++ b/packages/core/dev-test/config.yml @@ -19,6 +19,10 @@ i18n: # Optional, defaults to the first item in locales. # The locale to be used for fields validation and as a baseline for the entry. default_locale: en + + # Optional, defaults to true. + # Enforce required fields in non-default locales + enforce_required_non_default: true collections: - name: posts label: Posts diff --git a/packages/core/src/actions/__tests__/config.spec.ts b/packages/core/src/actions/__tests__/config.spec.ts index 3b8958b0..626746af 100644 --- a/packages/core/src/actions/__tests__/config.spec.ts +++ b/packages/core/src/actions/__tests__/config.spec.ts @@ -697,7 +697,12 @@ describe('config', () => { }, ], }).collections[0].i18n, - ).toEqual({ structure: 'multiple_folders', locales: ['en', 'de'], default_locale: 'en' }); + ).toEqual({ + structure: 'multiple_folders', + locales: ['en', 'de'], + default_locale: 'en', + enforce_required_non_default: true, + }); }); it('should not set root i18n on collection when collection i18n is not set', () => { @@ -757,7 +762,12 @@ describe('config', () => { }, ], }).collections[0].i18n, - ).toEqual({ structure: 'multiple_folders', locales: ['en', 'fr'], default_locale: 'fr' }); + ).toEqual({ + structure: 'multiple_folders', + locales: ['en', 'fr'], + default_locale: 'fr', + enforce_required_non_default: true, + }); }); it('should throw when i18n structure is not single_file on files collection', () => { diff --git a/packages/core/src/actions/config.ts b/packages/core/src/actions/config.ts index 39e48a9d..1d87c91c 100644 --- a/packages/core/src/actions/config.ts +++ b/packages/core/src/actions/config.ts @@ -92,17 +92,24 @@ function setI18nField(field: T) { function getI18nDefaults( collectionOrFileI18n: boolean | Partial, - { default_locale, locales = ['en'], structure = I18N_STRUCTURE_SINGLE_FILE }: Partial, + { + default_locale, + locales = ['en'], + structure = I18N_STRUCTURE_SINGLE_FILE, + enforce_required_non_default = true, + }: Partial, ): I18nInfo { if (typeof collectionOrFileI18n === 'boolean') { - return { default_locale, locales, structure }; + return { default_locale, locales, structure, enforce_required_non_default }; } else { const mergedI18n: I18nInfo = deepmerge( - { default_locale, locales, structure }, + { default_locale, locales, structure, enforce_required_non_default }, collectionOrFileI18n, ); mergedI18n.locales = collectionOrFileI18n.locales ?? locales; mergedI18n.default_locale = collectionOrFileI18n.default_locale || locales?.[0]; + mergedI18n.enforce_required_non_default = + collectionOrFileI18n.enforce_required_non_default || true; throwOnMissingDefaultLocale(mergedI18n); return mergedI18n; } @@ -202,6 +209,7 @@ function applyCollectionFileDefaults( locales: collectionI18n.locales, default_locale: collectionI18n.default_locale, structure: collectionI18n.structure, + enforce_required_non_default: collectionI18n.enforce_required_non_default, }); file.i18n = fileI18n; } else { @@ -315,6 +323,7 @@ export function applyDefaults( if (i18n) { i18n.default_locale = i18n.default_locale ?? i18n.locales[0]; + i18n.enforce_required_non_default = i18n.enforce_required_non_default ?? true; } throwOnMissingDefaultLocale(i18n); diff --git a/packages/core/src/components/entry-editor/editor-control-pane/EditorControl.tsx b/packages/core/src/components/entry-editor/editor-control-pane/EditorControl.tsx index b602133a..dbe0934e 100644 --- a/packages/core/src/components/entry-editor/editor-control-pane/EditorControl.tsx +++ b/packages/core/src/components/entry-editor/editor-control-pane/EditorControl.tsx @@ -124,7 +124,8 @@ const EditorControl: FC = ({ (!dirty && !submitted) || disabled || i18nDisabled || - (forList && field.widget === 'object' && field.fields.length === 1) + (forList && field.widget === 'object' && field.fields.length === 1) || + (i18n?.enforceRequiredNonDefault === false && i18n?.currentLocale !== i18n?.defaultLocale) ) { return; } diff --git a/packages/core/src/components/entry-editor/editor-control-pane/EditorControlPane.tsx b/packages/core/src/components/entry-editor/editor-control-pane/EditorControlPane.tsx index 0a6d34c5..e55281bb 100644 --- a/packages/core/src/components/entry-editor/editor-control-pane/EditorControlPane.tsx +++ b/packages/core/src/components/entry-editor/editor-control-pane/EditorControlPane.tsx @@ -88,11 +88,12 @@ const EditorControlPane: FC = ({ const i18n = useMemo(() => { if (hasI18n(collection)) { - const { locales, default_locale } = getI18nInfo(collection); + const { locales, default_locale, enforce_required_non_default } = getI18nInfo(collection); return { currentLocale: locale ?? locales?.[0], locales, defaultLocale: default_locale, + enforceRequiredNonDefault: enforce_required_non_default, } as I18nSettings; } diff --git a/packages/core/src/constants/configSchema.tsx b/packages/core/src/constants/configSchema.tsx index 314827f4..971f02d1 100644 --- a/packages/core/src/constants/configSchema.tsx +++ b/packages/core/src/constants/configSchema.tsx @@ -40,6 +40,7 @@ const i18n = { uniqueItems: true, }, default_locale: localeType, + enforce_required_non_default: { type: 'boolean' }, }, }; diff --git a/packages/core/src/interface.ts b/packages/core/src/interface.ts index 47fa3f91..59ada4a5 100644 --- a/packages/core/src/interface.ts +++ b/packages/core/src/interface.ts @@ -267,6 +267,7 @@ export interface I18nSettings { currentLocale: string; defaultLocale: string; locales: string[]; + enforceRequiredNonDefault?: boolean; } export type Format = keyof typeof formatExtensions; @@ -1306,6 +1307,7 @@ export interface I18nInfo { locales: string[]; default_locale?: string; structure: I18nStructure; + enforce_required_non_default?: boolean; } export interface ProcessedCodeLanguage { diff --git a/packages/core/src/lib/i18n.ts b/packages/core/src/lib/i18n.ts index f5279edb..08eeffe3 100644 --- a/packages/core/src/lib/i18n.ts +++ b/packages/core/src/lib/i18n.ts @@ -343,6 +343,7 @@ export async function getI18nEntry( i18nInfo = { structure: I18N_STRUCTURE_SINGLE_FILE, locales: [], + enforce_required_non_default: true, }; } diff --git a/packages/core/src/reducers/__tests__/entryDraft.spec.ts b/packages/core/src/reducers/__tests__/entryDraft.spec.ts index 5f24b591..ca7891e8 100644 --- a/packages/core/src/reducers/__tests__/entryDraft.spec.ts +++ b/packages/core/src/reducers/__tests__/entryDraft.spec.ts @@ -115,6 +115,7 @@ describe('entryDraft', () => { locales: ['en', 'fr', 'es'], defaultLocale: 'en', currentLocale: 'en', + enforceRequiredNonDefault: true, }, isMeta: false, }, @@ -155,6 +156,7 @@ describe('entryDraft', () => { locales: ['en', 'fr', 'es'], defaultLocale: 'en', currentLocale: 'en', + enforceRequiredNonDefault: true, }; let state = entryDraftReducer(startState, { diff --git a/packages/docs/content/docs/i18n-support.mdx b/packages/docs/content/docs/i18n-support.mdx index 249db80a..7645b461 100644 --- a/packages/docs/content/docs/i18n-support.mdx +++ b/packages/docs/content/docs/i18n-support.mdx @@ -25,6 +25,10 @@ i18n: # Optional, defaults to the first item in locales. # The locale to be used for fields validation and as a baseline for the entry. default_locale: en + + # Optional, defaults to true. + # Enforce required fields in non-default locales + enforce_required_non_default: false ``` ```js @@ -44,7 +48,13 @@ i18n: { * Optional, defaults to the first item in locales. * The locale to be used for fields validation and as a baseline for the entry. */ - default_locale: 'en' + default_locale: 'en', + + /** + * Optional, defaults to true. + * Enforce required fields in non-default locales + */ + enforce_required_non_default: false }, ```