diff --git a/src/i18n/request.ts b/src/i18n/request.ts index e5eb20991c..0e647263f3 100644 --- a/src/i18n/request.ts +++ b/src/i18n/request.ts @@ -3,6 +3,34 @@ import { cookies, headers } from "next/headers"; import { LOCALES, DEFAULT_LOCALE, LOCALE_COOKIE } from "./config"; import type { Locale } from "./config"; +// Deep-merge fallback messages so locales that lack newer keys (e.g. memory.* +// added by plan 21) silently fall back to EN instead of rendering the key path. +function mergeMessages( + fallback: Record, + override: Record +): Record { + const out: Record = { ...fallback }; + for (const [k, v] of Object.entries(override)) { + const baseVal = out[k]; + if ( + v !== null && + typeof v === "object" && + !Array.isArray(v) && + baseVal !== null && + typeof baseVal === "object" && + !Array.isArray(baseVal) + ) { + out[k] = mergeMessages( + baseVal as Record, + v as Record + ); + } else { + out[k] = v; + } + } + return out; +} + export default getRequestConfig(async () => { // 1. Try cookie const cookieStore = await cookies(); @@ -19,10 +47,16 @@ export default getRequestConfig(async () => { locale = DEFAULT_LOCALE; } - const messages = (await import(`./messages/${locale}.json`)).default; + const enMessages = (await import("./messages/en.json")) + .default as Record; + if (locale === "en") { + return { locale, messages: enMessages }; + } + const localeMessages = (await import(`./messages/${locale}.json`)) + .default as Record; return { locale, - messages, + messages: mergeMessages(enMessages, localeMessages), }; });