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
相关产品推荐
相关产品推荐

