fix(ui): switching languages does not update cached client config (#11725)
### What?
Fixed client config caching to properly update when switching languages
in the admin UI.
### Why?
Currently, switching languages doesn't fully update the UI because
client config stays cached with previous language translations.
### How?
Created a language-aware caching system that stores separate configs for
each language and only uses cached config when it matches the active
language.
Before:
```typescript
let cachedClientConfig: ClientConfig | null = global._payload_clientConfig
if (!cachedClientConfig) {
cachedClientConfig = global._payload_clientConfig = null
}
export const getClientConfig = cache(
(args: { config: SanitizedConfig; i18n: I18nClient; importMap: ImportMap }): ClientConfig => {
if (cachedClientConfig && !global._payload_doNotCacheClientConfig) {
return cachedClientConfig
}
// ... create new config ...
}
);
```
After:
```typescript
let cachedClientConfigs: Record<string, ClientConfig> = global._payload_localizedClientConfigs
if (!cachedClientConfigs) {
cachedClientConfigs = global._payload_localizedClientConfigs = {}
}
export const getClientConfig = cache(
(args: { config: SanitizedConfig; i18n: I18nClient; importMap: ImportMap }): ClientConfig => {
const { config, i18n, importMap } = args
const currentLocale = i18n.language
if (!global._payload_doNotCacheClientConfig && cachedClientConfigs[currentLocale]) {
return cachedClientConfigs[currentLocale]
}
// ... create new config with correct translations ...
}
);
```
Also added handling for cache clearing during HMR to ensure
compatibility with the existing system.
Fixes #11406
---------
Co-authored-by: Jacob Fletcher <jacobsfletch@gmail.com>
This commit is contained in:
@@ -65,8 +65,11 @@ import type {
|
||||
} from './types/index.js'
|
||||
import type { TraverseFieldsCallback } from './utilities/traverseFields.js'
|
||||
export type * from './admin/types.js'
|
||||
import type { SupportedLanguages } from '@payloadcms/translations'
|
||||
|
||||
import { Cron } from 'croner'
|
||||
|
||||
import type { ClientConfig } from './config/client.js'
|
||||
import type { TypeWithVersion } from './versions/types.js'
|
||||
|
||||
import { decrypt, encrypt } from './auth/crypto.js'
|
||||
@@ -865,10 +868,12 @@ export const reload = async (
|
||||
}
|
||||
|
||||
await payload.db.init()
|
||||
|
||||
if (payload.db.connect) {
|
||||
await payload.db.connect({ hotReload: true })
|
||||
}
|
||||
global._payload_clientConfig = null
|
||||
|
||||
global._payload_clientConfigs = {} as Record<keyof SupportedLanguages, ClientConfig>
|
||||
global._payload_schemaMap = null
|
||||
global._payload_clientSchemaMap = null
|
||||
global._payload_doNotCacheClientConfig = true // This will help refreshing the client config cache more reliably. If you remove this, please test HMR + client config refreshing (do new fields appear in the document?)
|
||||
|
||||
@@ -1,31 +1,38 @@
|
||||
import type { I18nClient } from '@payloadcms/translations'
|
||||
import type { I18nClient, SupportedLanguages } from '@payloadcms/translations'
|
||||
import type { ClientConfig, ImportMap, SanitizedConfig } from 'payload'
|
||||
|
||||
import { createClientConfig } from 'payload'
|
||||
import { cache } from 'react'
|
||||
|
||||
let cachedClientConfig: ClientConfig | null = global._payload_clientConfig
|
||||
let cachedClientConfigs = global._payload_clientConfigs as Record<
|
||||
keyof SupportedLanguages,
|
||||
ClientConfig
|
||||
>
|
||||
|
||||
if (!cachedClientConfig) {
|
||||
cachedClientConfig = global._payload_clientConfig = null
|
||||
if (!cachedClientConfigs) {
|
||||
cachedClientConfigs = global._payload_clientConfigs = {} as Record<
|
||||
keyof SupportedLanguages,
|
||||
ClientConfig
|
||||
>
|
||||
}
|
||||
|
||||
export const getClientConfig = cache(
|
||||
(args: { config: SanitizedConfig; i18n: I18nClient; importMap: ImportMap }): ClientConfig => {
|
||||
if (cachedClientConfig && !global._payload_doNotCacheClientConfig) {
|
||||
return cachedClientConfig
|
||||
const { config, i18n, importMap } = args
|
||||
const currentLanguage = i18n.language
|
||||
|
||||
if (cachedClientConfigs[currentLanguage] && !global._payload_doNotCacheClientConfig) {
|
||||
return cachedClientConfigs[currentLanguage]
|
||||
}
|
||||
|
||||
const { config, i18n, importMap } = args
|
||||
|
||||
cachedClientConfig = createClientConfig({
|
||||
const cachedClientConfig = createClientConfig({
|
||||
config,
|
||||
i18n,
|
||||
importMap,
|
||||
})
|
||||
|
||||
global._payload_clientConfig = cachedClientConfig
|
||||
|
||||
cachedClientConfigs[currentLanguage] = cachedClientConfig
|
||||
global._payload_clientConfigs = cachedClientConfigs
|
||||
global._payload_doNotCacheClientConfig = false
|
||||
|
||||
return cachedClientConfig
|
||||
|
||||
@@ -82,4 +82,27 @@ describe('i18n', () => {
|
||||
page.locator('.componentWithCustomI18n .componentWithCustomI18nCustomValidI18nT'),
|
||||
).toHaveText('My custom translation')
|
||||
})
|
||||
|
||||
test('ensure translations update correctly when switching language', async () => {
|
||||
await page.goto(serverURL + '/admin/account')
|
||||
|
||||
await page.locator('div.rs__control').click()
|
||||
await page.locator('div.rs__option').filter({ hasText: 'English' }).click()
|
||||
await expect(page.locator('div.payload-settings h3')).toHaveText('Payload Settings')
|
||||
|
||||
await page.goto(serverURL + '/admin/collections/collection1/create')
|
||||
await expect(page.locator('label[for="field-fieldDefaultI18nValid"]')).toHaveText(
|
||||
'Add {{label}}',
|
||||
)
|
||||
|
||||
await page.goto(serverURL + '/admin/account')
|
||||
await page.locator('div.rs__control').click()
|
||||
await page.locator('div.rs__option').filter({ hasText: 'Español' }).click()
|
||||
await expect(page.locator('div.payload-settings h3')).toHaveText('Configuración de la carga')
|
||||
|
||||
await page.goto(serverURL + '/admin/collections/collection1/create')
|
||||
await expect(page.locator('label[for="field-fieldDefaultI18nValid"]')).toHaveText(
|
||||
'Añadir {{label}}',
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user