diff --git a/apps/with-lingui/src/locales/loadLocaleCatalog.ts b/apps/with-lingui/src/locales/loadLocaleCatalog.ts new file mode 100644 index 0000000..744ba51 --- /dev/null +++ b/apps/with-lingui/src/locales/loadLocaleCatalog.ts @@ -0,0 +1,24 @@ +import type { Messages } from "@lingui/core"; +import type { Locale } from "@/stores/settings"; + +const catalogCache: Partial> = {}; + +/** + * Dynamically loads compiled Lingui messages for the given locale. + * Results are cached so repeated calls (e.g. main + root) do not refetch chunks. + */ +export async function loadLocaleCatalog(locale: Locale): Promise { + const hit = catalogCache[locale]; + if (hit) { + return hit; + } + + const mod = + locale === "en" + ? await import("./en/messages.po") + : await import("./zh/messages.po"); + + const messages = mod.messages; + catalogCache[locale] = messages; + return messages; +} diff --git a/apps/with-lingui/src/main.tsx b/apps/with-lingui/src/main.tsx index 966b779..fad1834 100644 --- a/apps/with-lingui/src/main.tsx +++ b/apps/with-lingui/src/main.tsx @@ -3,16 +3,11 @@ import ReactDOM from "react-dom/client"; import { RouterProvider, createRouter } from "@tanstack/react-router"; import { routeTree } from "./routeTree.gen"; import { i18n } from "@lingui/core"; -import { messages as enMessages } from "./locales/en/messages.po"; -import { messages as zhMessages } from "./locales/zh/messages.po"; +import { loadLocaleCatalog } from "./locales/loadLocaleCatalog"; import { useSettingsStore } from "./stores/settings"; import { useAuthStore } from "./stores/auth"; import { fetchSessionAndApplyToStore } from "./utils/session"; -i18n.load("en", enMessages); -i18n.load("zh", zhMessages); -i18n.activate(useSettingsStore.getState().locale); - const router = createRouter({ routeTree }); declare module "@tanstack/react-router" { @@ -30,6 +25,13 @@ async function enableMocking() { enableMocking() .then(async () => { + await useSettingsStore.persist.rehydrate(); + const initialLocale = useSettingsStore.getState().locale; + const messages = await loadLocaleCatalog(initialLocale); + i18n.load(initialLocale, messages); + i18n.activate(initialLocale); + document.documentElement.lang = initialLocale === "zh" ? "zh" : "en"; + await useAuthStore.persist.rehydrate(); const { isAuthenticated, tokens } = useAuthStore.getState(); if (isAuthenticated && tokens) { diff --git a/apps/with-lingui/src/routes/__root.tsx b/apps/with-lingui/src/routes/__root.tsx index d195170..62d7292 100644 --- a/apps/with-lingui/src/routes/__root.tsx +++ b/apps/with-lingui/src/routes/__root.tsx @@ -1,4 +1,4 @@ -import { useEffect } from "react"; +import { useEffect, useRef } from "react"; import { createRootRoute, Outlet } from "@tanstack/react-router"; import { ConfigProvider, App } from "antd"; import enUS from "antd/locale/en_US"; @@ -6,6 +6,7 @@ import zhCN from "antd/locale/zh_CN"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { i18n } from "@lingui/core"; import { I18nProvider } from "@lingui/react"; +import { loadLocaleCatalog } from "@/locales/loadLocaleCatalog"; import { useSettingsStore } from "@/stores/settings"; import { useAppTheme } from "@/hooks/useAppTheme"; import { NotFound } from "@/components/NotFound"; @@ -23,10 +24,26 @@ function RootComponent() { const locale = useSettingsStore((s) => s.locale); const darkMode = useSettingsStore((s) => s.darkMode); const configProviderProps = useAppTheme(); + const localeEffectRunRef = useRef(0); useEffect(() => { - i18n.activate(locale); - document.documentElement.lang = locale === "zh" ? "zh" : "en"; + localeEffectRunRef.current += 1; + const runId = localeEffectRunRef.current; + let cancelled = false; + + void (async () => { + const messages = await loadLocaleCatalog(locale); + if (cancelled || runId !== localeEffectRunRef.current) { + return; + } + i18n.load(locale, messages); + i18n.activate(locale); + document.documentElement.lang = locale === "zh" ? "zh" : "en"; + })(); + + return () => { + cancelled = true; + }; }, [locale]); useEffect(() => {