feat(with-lingui): dynamic load locale message catalogs

Made-with: Cursor
This commit is contained in:
zuiidea
2026-04-14 21:06:01 +08:00
parent d188afadb0
commit 81bb9e996c
3 changed files with 52 additions and 9 deletions
@@ -0,0 +1,24 @@
import type { Messages } from "@lingui/core";
import type { Locale } from "@/stores/settings";
const catalogCache: Partial<Record<Locale, Messages>> = {};
/**
* 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<Messages> {
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;
}
+8 -6
View File
@@ -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) {
+20 -3
View File
@@ -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(() => {