You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在React+i18next中实现翻译懒加载优化初始性能?

实现i18next翻译懒加载优化(React项目)

完全可以实现你的需求,通过调整i18next配置和组件逻辑,就能做到初始仅加载单个翻译文件,切换语言时加载对应文件并显示加载状态,以下是具体实现步骤:

1. 修改i18n.ts配置,启用按需加载

首先移除直接导入所有翻译文件的代码,改用i18next-http-backend插件从public目录按需加载翻译资源,这样初始只会加载当前检测到的语言文件,而非全部三个:

import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import LanguageDetector from 'i18next-browser-languagedetector';
import Backend from 'i18next-http-backend';

i18n
  .use(Backend)
  .use(LanguageDetector)
  .use(initReactI18next)
  .init({
    debug: true,
    fallbackLng: 'en',
    interpolation: {
      escapeValue: false,
    },
    // 移除直接导入的resources配置,让Backend按需加载
    backend: {
      // 配置翻译文件的加载路径,对应public/locales下的文件结构
      loadPath: '/locales/{{lng}}/translation.json',
    },
    // 允许部分语言打包(这里初始只加载检测到的语言)
    partialBundledLanguages: true,
    // 禁用预加载,避免提前加载所有语言文件
    // preload: ['en', 'ar', 'fr']
  });

export default i18n;

2. 优化语言切换组件,添加加载状态与加载屏

在LanguageToggle组件中添加加载状态监听,当切换语言时i18n正在加载资源时,显示加载屏:

/* eslint-disable no-alert, no-console */
import i18n from "i18next";
import { Globe2 } from "lucide-react";
import React, { useState, useEffect } from "react";
import { useTranslation } from "react-i18next";
import { Button } from "./ui/button";
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "./ui/dropdown-menu";
import { cn } from "@/lib/utils";

interface Language {
  label: string;
  dir: "ltr" | "rtl";
  active: boolean;
}

const lngs: Record<string, Language> = {
  en: { label: "English", dir: "ltr", active: true },
  ar: { label: "العربية", dir: "rtl", active: false },
  fr: { label: "Français", dir: "ltr", active: false },
};

function LanguageToggle({ className }: { className: string }) {
  const storedLanguage = localStorage.getItem("i18nextLng") || "en";
  const selected = Object.keys(lngs).find((lng) => storedLanguage.startsWith(lng)) || "en";
  const { t } = useTranslation();
  // 添加加载状态
  const [isLoading, setIsLoading] = useState(false);

  useEffect(() => {
    document.body.dir = lngs[selected].dir;
  }, [selected]);

  // 监听i18n的加载状态事件
  useEffect(() => {
    const handleLoadStart = () => setIsLoading(true);
    const handleLoadEnd = () => setIsLoading(false);

    i18n.on('languageChanged', handleLoadEnd);
    i18n.on('loadingStart', handleLoadStart);
    i18n.on('loadingEnd', handleLoadEnd);

    // 组件卸载时移除事件监听
    return () => {
      i18n.off('languageChanged', handleLoadEnd);
      i18n.off('loadingStart', handleLoadStart);
      i18n.off('loadingEnd', handleLoadEnd);
    };
  }, []);

  const handleLanguageChange = async (lng: string) => {
    setIsLoading(true);
    try {
      await i18n.changeLanguage(lng);
    } catch (err) {
      console.error('切换语言失败:', err);
      setIsLoading(false);
    }
  };

  return (
    <>
      {/* 自定义加载屏,可根据项目需求调整样式 */}
      {isLoading && (
        <div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50">
          <div className="bg-white p-6 rounded-lg shadow-lg">
            <div className="animate-spin rounded-full h-10 w-10 border-b-2 border-blue-600 mx-auto"></div>
            <p className="mt-3 text-sm text-gray-700">{t('loading') || '加载中...'}</p>
          </div>
        </div>
      )}

      <div className={cn(className)}>
        <DropdownMenu>
          <DropdownMenuTrigger asChild className="cursor-pointer">
            <Button style={{ paddingBlock: "19px" }} className="" variant="outline" size="sm">
              <span className="me-2">{t('language')}</span>
              <Globe2 />
            </Button>
          </DropdownMenuTrigger>
          <DropdownMenuContent align="end">
            {Object.keys(lngs).map((lng) => (
              <DropdownMenuItem 
                className={`cursor-pointer ${selected === lng ? 'bg-gray-100' : ''}`} 
                key={lng} 
                onClick={() => handleLanguageChange(lng)}
                disabled={isLoading}
              >
                {lngs[lng].label}
              </DropdownMenuItem>
            ))}
          </DropdownMenuContent>
        </DropdownMenu>
      </div>
    </>
  );
}

export default LanguageToggle;

关键说明

  • 按需加载原理:通过i18next-http-backend插件,i18next会在初始时仅加载当前检测到的语言文件(或 fallbackLng),切换语言时才会请求对应语言的翻译文件,避免初始加载全部资源。
  • 加载状态监听:利用i18n的loadingStart、loadingEnd和languageChanged事件,准确控制加载屏的显示与隐藏,避免用户重复操作。
  • 样式自定义:加载屏的样式可以根据项目设计风格调整,比如修改背景透明度、加载动画、文字内容等。

内容的提问来源于stack exchange,提问作者TakiDDine

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 00:03:20