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

React+i18next测试中无法加载public/locales翻译文件的问题

问题

我有一个基于React的项目,用i18next实现国际化,jest做测试。运行测试时,Backend插件尝试从public/locales文件夹加载翻译资源,控制台报错:

Error: Error: connect ECONNREFUSED 127.0.0.1:80 at Object.dispatchError

所有i18n功能测试都失败,比如i18n.exists(message)始终返回false。推测是测试阶段没有服务器监听,Backend的fetch请求失败导致翻译未加载。

项目配置如下:

i18n.js

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: false,
    defaultNS: 'common',
    fallbackLng: ['es'],
    interpolation: {
      escapeValue: false, // not needed for react as it escapes by default
    },
    ns: ['common', 'comments'],
    react: {
      useSuspense: false,
    },
  });

export default i18n;

setupTests.js

import '@testing-library/jest-dom';

jest.mock('react-i18next', () => ({
  ...jest.requireActual('react-i18next'),
  useTranslation: () => ({
    ...jest.requireActual('react-i18next').useTranslation(),
    t: (key: string, interpolation: any) =>
      `T_${key}${
        interpolation
          ? `--${Object.keys(interpolation)
              ?.map((k) => `${k}:${interpolation[k]}`)
              .join('--')}`
          : ''
      }`,
  }),
}));

我试过移除Backend插件,错误消失但生产环境无法加载翻译,测试还是失败;也试过手动导入JSON文件作为resources,测试和生产都正常,但不想手动维护多语言多命名空间的JSON文件。

想知道:

  1. 如何让Backend在测试中像生产环境一样加载public/locales的翻译资源?
  2. 或者如何用fetch-mock-jest全局模拟对public/locales的请求?

解决方案

方法一:测试环境动态读取本地JSON文件替代Backend

通过环境判断,在测试时用Node.js的文件系统直接读取public/locales下的资源,不需要手动维护导入语句:

  1. 安装fs-extra(可选,原生fs也可,fs-extra简化了文件操作):
npm install fs-extra --save-dev
  1. 修改i18n.js,根据环境切换资源加载逻辑:
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import LanguageDetector from 'i18next-browser-languagedetector';
import Backend from 'i18next-http-backend';
import fs from 'fs-extra';
import path from 'path';

let resources = {};

// 测试环境下直接读取本地locales文件
if (process.env.NODE_ENV === 'test') {
  const localesDir = path.resolve(__dirname, '../public/locales');
  const languages = fs.readdirSync(localesDir);
  
  languages.forEach(lang => {
    resources[lang] = {};
    const nsFiles = fs.readdirSync(path.join(localesDir, lang));
    
    nsFiles.forEach(file => {
      const ns = file.replace('.json', '');
      resources[lang][ns] = fs.readJsonSync(path.join(localesDir, lang, file));
    });
  });
}

i18n
  .use(process.env.NODE_ENV === 'test' ? null : Backend) // 测试环境禁用Backend
  .use(LanguageDetector)
  .use(initReactI18next)
  .init({
    debug: false,
    defaultNS: 'common',
    fallbackLng: ['es'],
    interpolation: {
      escapeValue: false,
    },
    ns: ['common', 'comments'],
    react: {
      useSuspense: false,
    },
    resources: process.env.NODE_ENV === 'test' ? resources : undefined, // 测试环境注入本地资源
  });

export default i18n;

jest默认会将NODE_ENV设置为test,无需额外配置。

方法二:用fetch-mock-jest全局模拟locales请求

全局拦截public/locales相关的fetch请求,返回本地JSON文件内容:

  1. 安装fetch-mock-jest:
npm install fetch-mock-jest --save-dev
  1. 修改setupTests.js,添加全局请求模拟:
import '@testing-library/jest-dom';
import fetchMock from 'fetch-mock-jest';
import fs from 'fs';
import path from 'path';

// 全局拦截locales的JSON请求
fetchMock.mock(/^\/locales\/.*\.json$/, (url) => {
  // 从URL中解析语言和命名空间
  const urlSegments = url.split('/');
  const lang = urlSegments[urlSegments.length - 2];
  const ns = urlSegments[urlSegments.length - 1].replace('.json', '');
  
  // 读取对应本地文件
  const targetPath = path.resolve(__dirname, `../public/locales/${lang}/${ns}.json`);
  if (fs.existsSync(targetPath)) {
    return JSON.parse(fs.readFileSync(targetPath, 'utf8'));
  }
  // 文件不存在时返回空对象避免报错
  return {};
});

jest.mock('react-i18next', () => ({
  ...jest.requireActual('react-i18next'),
  useTranslation: () => ({
    ...jest.requireActual('react-i18next').useTranslation(),
    t: (key: string, interpolation: any) =>
      `T_${key}${
        interpolation
          ? `--${Object.keys(interpolation)
              ?.map((k) => `${k}:${interpolation[k]}`)
              .join('--')}`
          : ''
      }`,
  }),
}));

注意:如果测试环境使用jsdom,默认自带fetch实现,fetch-mock可以直接拦截请求;如果是其他环境,可能需要额外配置全局fetch。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 05:01:01