perf(frontend): U48 i18n locale 懒加载 — en-US JSON 动态导入拆分为独立 chunk
问题: src/i18n/index.ts 静态导入 zh-CN.json (200KB compact) + en-US.json (292KB compact),两者共 486KB 全部内联到主 index chunk,致主 chunk 696KB 超过 Vite 600KB 警告阈值。 修复: - src/i18n/index.ts: 移除 en-US.json 静态导入,改为 ensureLocaleLoaded() 动态导入 + i18n.addResourceBundle() 按需加载 - zh-CN (默认/回退语言) 保持静态内联,无行为变化 - en-US 仅在用户切换英文时按需 fetch (290KB 独立 chunk) - 已选英文的用户刷新页面时异步加载 en-US (短暂中文闪烁,可接受) - src/store/index.ts: setLanguage 调用 ensureLocaleLoaded 确保目标语言包 加载完成后再切换 结果: - 主 index chunk: 696KB → 423KB (-39%, -273KB) - en-US chunk: 290KB 独立懒加载 (仅英文用户首次切换时 fetch) - Vite 600KB chunk 警告消除 - tsc -b=0, npm build✓ (18.18s), ESLint=0/43/any=0, go build=0 - 17/17 集成测试通过, Docker healthy
This commit is contained in:
1 parent
ca036a85ca
commit
8e211b64af
2 files changed
+35
-5
No files matched your search
@@ -1,7 +1,6 @@
|
||||
import i18n from 'i18next';
|
||||
import { initReactI18next } from 'react-i18next';
|
||||
import zhCN from '@/locales/zh-CN.json';
|
||||
import enUS from '@/locales/en-US.json';
|
||||
|
||||
export type AppLanguage = 'zh-CN' | 'en-US';
|
||||
|
||||
@@ -12,6 +11,24 @@ export const SUPPORTED_LANGUAGES: { code: AppLanguage; label: string; flag: stri
|
||||
|
||||
export const DEFAULT_LANGUAGE: AppLanguage = 'zh-CN';
|
||||
|
||||
/** Track which locale resource bundles have been registered with i18next. */
|
||||
const loadedLocales = new Set<string>(['zh-CN']);
|
||||
|
||||
/**
|
||||
* Ensure a locale resource bundle is loaded before use.
|
||||
* zh-CN is bundled inline (default/fallback language).
|
||||
* en-US is lazy-loaded via dynamic import to reduce the main bundle by ~290 kB.
|
||||
* Subsequent calls for an already-loaded locale are instant no-ops.
|
||||
*/
|
||||
export async function ensureLocaleLoaded(lang: string): Promise<void> {
|
||||
if (loadedLocales.has(lang)) return;
|
||||
if (lang === 'en-US') {
|
||||
const mod = await import('@/locales/en-US.json');
|
||||
i18n.addResourceBundle('en-US', 'translation', mod.default, true, true);
|
||||
loadedLocales.add('en-US');
|
||||
}
|
||||
}
|
||||
|
||||
/** Read persisted language from localStorage (zustand persist key) */
|
||||
function getPersistedLanguage(): AppLanguage {
|
||||
try {
|
||||
@@ -27,12 +44,15 @@ function getPersistedLanguage(): AppLanguage {
|
||||
return DEFAULT_LANGUAGE;
|
||||
}
|
||||
|
||||
const persistedLang = getPersistedLanguage();
|
||||
|
||||
i18n.use(initReactI18next).init({
|
||||
resources: {
|
||||
'zh-CN': { translation: zhCN },
|
||||
'en-US': { translation: enUS },
|
||||
},
|
||||
lng: getPersistedLanguage(),
|
||||
// Start with zh-CN if the persisted language (en-US) hasn't been loaded yet.
|
||||
// en-US is fetched asynchronously below and the language is switched once ready.
|
||||
lng: persistedLang === 'en-US' ? 'zh-CN' : persistedLang,
|
||||
fallbackLng: 'zh-CN',
|
||||
interpolation: {
|
||||
escapeValue: false, // React already escapes
|
||||
@@ -42,6 +62,15 @@ i18n.use(initReactI18next).init({
|
||||
},
|
||||
});
|
||||
|
||||
// If the user previously selected en-US, load it asynchronously then switch.
|
||||
// There is a brief flash of Chinese before English loads — acceptable trade-off
|
||||
// for a ~290 kB reduction in the main entry chunk.
|
||||
if (persistedLang === 'en-US') {
|
||||
void ensureLocaleLoaded('en-US').then(() => {
|
||||
i18n.changeLanguage('en-US');
|
||||
});
|
||||
}
|
||||
|
||||
// Sync <html lang="..."> with current language
|
||||
i18n.on('languageChanged', (lng) => {
|
||||
if (typeof document !== 'undefined') {
|
||||
|
||||
@@ -29,9 +29,10 @@ export const useLanguageStore = create<LanguageState>()(
|
||||
(set) => ({
|
||||
language: "zh-CN" as AppLanguage,
|
||||
setLanguage: (lang) => {
|
||||
// 同步切换 i18next 实例
|
||||
// Ensure the target locale bundle is loaded (en-US is lazy) before switching.
|
||||
import("@/i18n")
|
||||
.then(({ default: i18n }) => {
|
||||
.then(async ({ default: i18n, ensureLocaleLoaded }) => {
|
||||
await ensureLocaleLoaded(lang);
|
||||
i18n.changeLanguage(lang);
|
||||
})
|
||||
.catch(() => {
|
||||
|
||||
Reference in new issue
Block a user