diff --git a/docs/configuration/localization.mdx b/docs/configuration/localization.mdx index 64e3097e808..32bc1c529cc 100644 --- a/docs/configuration/localization.mdx +++ b/docs/configuration/localization.mdx @@ -77,11 +77,12 @@ export default buildConfig({ The following options are available: -| Option | Description | -| -------------- | ------------------------------------------------------------------------------------------------------------------------------ | -| **`locales`** | Array of all the languages that you would like to support. [More details](#locales) | -| **`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. | -| **`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 unless a fallback is explicitly provided in the request. True by default. | +| Option | Description | +|------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| **`locales`** | Array of all the languages that you would like to support. [More details](#locales) | +| **`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. | +| **`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 unless a fallback is explicitly provided in the request. True by default. | +| **`filterAvailableLocales`** | A function that is called with the array of `locales` and the `req`, it should return locales to show in admin UI selector. [See more](#filter-available-options). | ### Locales @@ -100,6 +101,35 @@ The locale codes do not need to be in any specific format. It's up to you to def _* An asterisk denotes that a property is required._ +#### Filter Available Options +In some projects you may want to filter the available locales shown in the admin UI selector. You can do this by providing a `filterAvailableLocales` function in your Payload Config. This is called on the server side and is passed the array of locales. This means that you can determine what locales are visible in the localizer selection menu at the top of the admin panel. You could do this per user, or implement a function that scopes these to tenants and more. Here is an example using request headers in a multi-tenant application: + +```ts +// ... rest of payload config +localization: { + defaultLocale: 'en', + locales: ['en', 'es'], + filterAvailableLocales: async ({ req, locales }) => { + if (getTenantFromCookie(req.headers, 'text')) { + const fullTenant = await req.payload.findByID({ + id: getTenantFromCookie(req.headers, 'text') as string, + collection: 'tenants', + req, + }) + if (fullTenant && fullTenant.supportedLocales?.length) { + return locales.filter((locale) => { + return fullTenant.supportedLocales?.includes(locale.code as 'en' | 'es') + }) + } + } + return locales + }, +} +``` + +Since the filtering happens at the root level of the application and its result is not calculated every time you navigate to a new page, you may want to call `router.refresh` in a custom component that watches when values that affect the result change. In the example above, you would want to do this when `supportedLocales` changes on the tenant document. + + ## 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. diff --git a/test/localization/config.ts b/test/localization/config.ts index e5aa60068c2..bed2f489484 100644 --- a/test/localization/config.ts +++ b/test/localization/config.ts @@ -414,9 +414,16 @@ export default buildConfigWithDefaults({ }, ], localization: { + filterAvailableLocales: ({ locales }) => { + return locales.filter((locale) => locale.code !== 'xx') + }, defaultLocale, fallback: true, locales: [ + { + code: 'xx', + label: 'FILTERED', + }, { code: defaultLocale, label: 'English', diff --git a/test/localization/e2e.spec.ts b/test/localization/e2e.spec.ts index a0f9b4b280b..c40693c6ec3 100644 --- a/test/localization/e2e.spec.ts +++ b/test/localization/e2e.spec.ts @@ -97,7 +97,7 @@ describe('Localization', () => { // }) }) - describe('localizer', async () => { + describe('localizer', () => { test('should show localizer controls', async () => { await page.goto(url.create) await expect(page.locator('.localizer.app-header__localizer')).toBeVisible() @@ -105,6 +105,13 @@ describe('Localization', () => { await expect(page.locator('.localizer .popup.popup--active')).toBeVisible() }) + test('should filter locale with filterAvailableLocales', async () => { + await page.goto(url.create) + await expect(page.locator('.localizer.app-header__localizer')).toBeVisible() + await page.locator('.localizer >> button').first().click() + await expect(page.locator('.localizer .popup.popup--active')).not.toContainText('FILTERED') + }) + test('should disable control for active locale', async () => { await page.goto(url.create) @@ -472,7 +479,7 @@ describe('Localization', () => { await runCopy(page) await expect(page.locator('#field-title')).toHaveValue(title) - const regexPattern = new RegExp(`locale=es`) + const regexPattern = /locale=es/ await expect(page).toHaveURL(regexPattern) await openCopyToLocaleDrawer(page) diff --git a/test/localization/payload-types.ts b/test/localization/payload-types.ts index 298be8db481..bcf74efe935 100644 --- a/test/localization/payload-types.ts +++ b/test/localization/payload-types.ts @@ -68,7 +68,7 @@ export interface Config { 'global-array': GlobalArraySelect | GlobalArraySelect; 'global-text': GlobalTextSelect | GlobalTextSelect; }; - locale: 'en' | 'es' | 'pt' | 'ar' | 'hu'; + locale: 'xx' | 'en' | 'es' | 'pt' | 'ar' | 'hu'; user: User & { collection: 'users'; };