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

i18next含短横线语言(如zh-Hans)无法正常工作的问题

i18next带短横线语言标识资源加载失败问题

配置i18next前端国际化时,带短横线的语言标识zh-Hans无法正常加载资源,无任何错误日志。已确认:

  • supportedLngs包含zh-Hans
  • 对应资源文件夹命名为zh-Hans
  • 去掉短横线的标识(如zhhant)或硬编码导入路径时可正常工作
  • 尝试zh-Hans-CH、zh-CH等带短横线的标识也无效

代码示例

import i18n, { type InitOptions } from 'i18next';
import { initReactI18next } from 'react-i18next';
import LanguageDetector from 'i18next-browser-languagedetector';
import resourcesToBackend from 'i18next-resources-to-backend';

function normalizeLocale(lng: string): string {
  const x = lng.toLowerCase();
  if (x.startsWith('en')) return 'en';
  if (x.startsWith('fr')) return 'fr';
  if (x.startsWith('es')) return 'es';
  if (x.startsWith('ja')) return 'ja';
  if (x.startsWith('ko')) return 'ko';
  if (x.startsWith('zh-hans')) return 'zh-Hans';
  if (x.startsWith('zhhant')) return 'zhhant';
  return 'en';
}

const baseOptions: InitOptions = {
  fallbackLng: 'en',
  supportedLngs: ['en', 'fr', 'es', 'zh-Hans', 'zhhant', 'ja', 'ko'],
  defaultNS: 'common',
  load: 'currentOnly',
  cleanCode: true,
  nonExplicitSupportedLngs: true,
  interpolation: { escapeValue: false },
  detection: {
    order: ['querystring', 'cookie', 'localStorage', 'navigator'],
    caches: ['localStorage', 'cookie']
  },
  react: { useSuspense: true },
  debug: true,
};

// Dynamic import per (lng, ns) → tiny chunks, loaded on demand
const backend = resourcesToBackend(
  (lng: string, ns: string) => import(`./locales/${normalizeLocale(lng)}/${ns}.json`)
);

i18n
  .use(backend)
  .use(LanguageDetector)
  // .use(ICU) // enable if using ICU messages now
  .use(initReactI18next)
  .init(baseOptions)

i18n.on('initialized', () => {
  const normalized = normalizeLocale(i18n.resolvedLanguage || 'en');
  if (normalized !== i18n.language) i18n.changeLanguage(normalized);
});
i18n.on('languageChanged', (lng) => {
  const normalized = normalizeLocale(lng);
  if (normalized !== lng) i18n.changeLanguage(normalized);
});

export default i18n;

调试信息

i18next: initialized {
  debug: true,
  initAsync: true,
  ns: [ 'translation' ],
  defaultNS: 'common',
  fallbackLng: [ 'en' ],
  fallbackNS: false,
  supportedLngs: [
    'en',     'fr',
    'es',     'zh-Hans',
    'zhhant', 'ja',
    'ko',     'cimode'
  ],
  nonExplicitSupportedLngs: true,
  load: 'currentOnly',
  preload: false,
  simplifyPluralSuffix: true,
  keySeparator: '.',
  nsSeparator: ':',
  pluralSeparator: '_',
  contextSeparator: '_',
  partialBundledLanguages: false,
  saveMissing: false,
  updateMissing: false,
  saveMissingTo: 'fallback',
  saveMissingPlurals: true,
  missingKeyHandler: false,
  missingInterpolationHandler: false,
  postProcess: false,
  postProcessPassResolved: false,
  returnNull: false,
  returnEmptyString: true,
  returnObjects: false,
  joinArrays: false,
  returnedObjectHandler: false,
  parseMissingKeyHandler: false,
  appendNamespaceToMissingKey: false,
  appendNamespaceToCIMode: false,
  overloadTranslationOptionHandler: [Function: overloadTranslationOptionHandler],
  interpolation: {
    escapeValue: false,
    format: [Function: bound format],
    prefix: '{{',
    suffix: '}}',
    formatSeparator: ',',
    unescapePrefix: '-',
    nestingPrefix: '$t(',
    nestingSuffix: ')',
    nestingOptionsSeparator: ',',
    maxReplaces: 1000,
    skipOnVariables: true
  },
  cacheInBuiltFormats: true,
  cleanCode: true,
  detection: {
    order: [ 'querystring', 'cookie', 'localStorage', 'navigator' ],
    caches: [ 'localStorage', 'cookie' ],
    lookupQuerystring: 'lng',
    lookupCookie: 'i18next',
    lookupLocalStorage: 'i18nextLng',
    lookupSessionStorage: 'i18nextLng',
    excludeCacheFor: [ 'cimode' ],
    convertDetectedLanguage: [Function: convertDetectedLanguage]
  },
  react: { useSuspense: true },
  ignoreJSONStructure: true
}

解决方案

1. 统一路径大小写与标识格式

打包工具对路径大小写敏感,短横线标识可能存在解析差异:

  • 将normalizeLocale中返回的zh-Hans改为全小写zh-hans,同时把资源文件夹重命名为zh-hans,保持路径与标识完全一致
  • 在动态导入时添加日志,确认实际请求的路径:
    const backend = resourcesToBackend(
      (lng: string, ns: string) => {
        const normalized = normalizeLocale(lng);
        console.log(`Loading resource: ./locales/${normalized}/${ns}.json`);
        return import(`./locales/${normalized}/${ns}.json`);
      }
    );
    

2. 关闭模糊语言匹配

nonExplicitSupportedLngs会让i18n自动匹配相似语言,可能干扰精确标识的加载:

const baseOptions: InitOptions = {
  // ...其他配置
  nonExplicitSupportedLngs: false,
};

3. 修复语言切换循环问题

当前languageChanged事件中重复调用changeLanguage会触发循环,干扰资源加载:

function handleLanguageChange(lng: string) {
  const normalized = normalizeLocale(lng);
  if (normalized !== lng) {
    // 临时解绑事件避免循环
    i18n.off('languageChanged', handleLanguageChange);
    i18n.changeLanguage(normalized).then(() => {
      // 重新绑定事件
      i18n.on('languageChanged', handleLanguageChange);
    });
  }
}

// 初始化绑定事件
i18n.on('languageChanged', handleLanguageChange);

i18n.on('initialized', () => {
  const normalized = normalizeLocale(i18n.resolvedLanguage || 'en');
  if (normalized !== i18n.language) {
    i18n.off('languageChanged', handleLanguageChange);
    i18n.changeLanguage(normalized).then(() => {
      i18n.on('languageChanged', handleLanguageChange);
    });
  }
});

4. 验证资源文件有效性

确认./locales/zh-Hans/common.json(或修改后的路径)存在且格式正确,可直接在浏览器地址栏访问该文件路径,检查是否能正常返回内容。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.12 12:13:10