From 8f1362fd1313fb371262204aaffdb5caf36274e2 Mon Sep 17 00:00:00 2001 From: "Anthony Fu (via agent)" Date: Wed, 4 Nov 2026 04:02:56 +0000 Subject: [PATCH] perf(client): load locale messages on demand --- packages/devtools/client/i18n/index.ts | 80 +++++++++++++++++------- packages/devtools/client/plugins/i18n.ts | 12 ++-- 2 files changed, 64 insertions(+), 28 deletions(-) diff --git a/packages/devtools/client/i18n/index.ts b/packages/devtools/client/i18n/index.ts index 7843cbb3e3..bb323edb06 100644 --- a/packages/devtools/client/i18n/index.ts +++ b/packages/devtools/client/i18n/index.ts @@ -1,30 +1,25 @@ import type { DevToolsLocale } from './locales' import { createI18n } from 'vue-i18n' -import de from '../locales/de.json' import en from '../locales/en.json' -import es from '../locales/es.json' -import fr from '../locales/fr.json' -import ja from '../locales/ja.json' -import ko from '../locales/ko.json' -import ptBR from '../locales/pt-BR.json' -import ru from '../locales/ru.json' -import zhCN from '../locales/zh-CN.json' -import zhTW from '../locales/zh-TW.json' import { DEFAULT_LOCALE } from './locales' export type MessageSchema = typeof en -const messages: Record = { - 'en': en, - 'zh-CN': zhCN, - 'zh-TW': zhTW, - 'ja': ja, - 'ko': ko, - 'es': es, - 'fr': fr, - 'de': de, - 'pt-BR': ptBR, - 'ru': ru, +/** + * Lazy loaders for every non-default locale. English is bundled eagerly as + * the fallback (and the message schema); the others are split into their own + * chunks and only fetched when the visitor actually switches to them. + */ +const loaders: Record, () => Promise<{ default: MessageSchema }>> = { + 'zh-CN': () => import('../locales/zh-CN.json'), + 'zh-TW': () => import('../locales/zh-TW.json'), + 'ja': () => import('../locales/ja.json'), + 'ko': () => import('../locales/ko.json'), + 'es': () => import('../locales/es.json'), + 'fr': () => import('../locales/fr.json'), + 'de': () => import('../locales/de.json'), + 'pt-BR': () => import('../locales/pt-BR.json'), + 'ru': () => import('../locales/ru.json'), } /** @@ -56,10 +51,53 @@ export const i18n = createI18n<[MessageSchema], DevToolsLocale, false>({ globalInjection: true, locale: DEFAULT_LOCALE, fallbackLocale: DEFAULT_LOCALE, - messages, + messages: { [DEFAULT_LOCALE]: en } as Record, pluralRules: { ru: russianPlural, }, missingWarn: false, fallbackWarn: false, }) + +const loaded = new Set([DEFAULT_LOCALE]) +const pending = new Map>() + +/** Fetch and register the messages of `locale`, once. */ +function loadLocaleMessages(locale: DevToolsLocale): Promise { + if (loaded.has(locale)) + return Promise.resolve() + let promise = pending.get(locale) + if (!promise) { + promise = loaders[locale as keyof typeof loaders]() + .then((mod) => { + i18n.global.setLocaleMessage(locale, mod.default) + loaded.add(locale) + }) + .finally(() => pending.delete(locale)) + pending.set(locale, promise) + } + return promise +} + +let switchId = 0 + +/** + * Switch the UI to `locale`, loading its messages first so the UI never + * renders missing keys. Rapid switches resolve to the last requested locale; + * if a chunk fails to load, the current language stays in place. + */ +export async function setLocale(locale: DevToolsLocale): Promise { + const id = ++switchId + try { + await loadLocaleMessages(locale) + } + catch (error) { + console.error(`[Nuxt DevTools] Failed to load locale "${locale}"`, error) + return + } + if (id !== switchId) + return + i18n.global.locale.value = locale + if (typeof document !== 'undefined') + document.documentElement.lang = locale +} diff --git a/packages/devtools/client/plugins/i18n.ts b/packages/devtools/client/plugins/i18n.ts index 2f9e2ea650..aba6757c29 100644 --- a/packages/devtools/client/plugins/i18n.ts +++ b/packages/devtools/client/plugins/i18n.ts @@ -1,15 +1,13 @@ import { watch } from 'vue' import { defineNuxtPlugin } from '#imports' import { useDevToolsLocale } from '../composables/locale' -import { i18n } from '../i18n' +import { i18n, setLocale } from '../i18n' -export default defineNuxtPlugin((nuxtApp) => { +export default defineNuxtPlugin(async (nuxtApp) => { nuxtApp.vueApp.use(i18n) const locale = useDevToolsLocale() - watch(locale, (value) => { - i18n.global.locale.value = value - if (typeof document !== 'undefined') - document.documentElement.lang = value - }, { immediate: true }) + // Load the initial locale before the first render to avoid an English flash. + await setLocale(locale.value) + watch(locale, value => setLocale(value)) })