首页
/ LibreChat 多语言本地化:新增语言全流程与 i18n 底层机制深度解析

LibreChat 多语言本地化:新增语言全流程与 i18n 底层机制深度解析

2026-09-05 11:57:29作者:温玫谨Lighthearted

本篇以 LibreChat 仓库中的 本地化指南 为主体,完整讲解为 LibreChat 新增一种语言的操作流程(Locize 平台接入、语言选择器注册、翻译键添加、i18n 配置与回退设置),并结合 i18n 核心实现、语言状态持久化与测试用例,剖析语言代码归一化、懒加载资源包、并发切换竞态处理等底层机制。读完后可独立完成新语言接入,并理解语言偏好如何持久化、回退到英文的完整链路。

本地化系统总览

LibreChat 的前端国际化基于 i18next + react-i18next,所有语言资源位于 client/src/locales/ 目录下,每个语言对应一个子目录(如 en/fr/zh-Hans/),内含一个 translation.json 翻译文件。当前仓库已支持 42 种语言/变体,从 i18n.ts 导出的 supportedLocales 常量数组可以看到完整清单:enzh-Hanszh-Hantpt-BRpt-PTbo(藏语)、uk(乌克兰语)、ug(维吾尔语)、he(希伯来语)等。

一个关键的架构细节:从源码看,只有英文是静态打包的——i18n.ts 顶部只有 import translationEn from './en/translation.json' 这一个静态导入,resources 对象(L55-L57)也只注册了 en。其余 41 种语言全部通过 localeLoaders 映射表(L59-L103)以动态 import() 方式懒加载,配合 i18next 的 partialBundledLanguages: trueload: 'currentOnly'L288-L306),每种语言的翻译 JSON 被打成独立 chunk,用户切换到某语言时才下载,从而控制首屏体积。

新增语言:官方指南的完整步骤

以下内容继承自 client/src/locales/README.md,并针对当前仓库实现做了补充。

第 1 步:在 Locize 项目中添加语言

LibreChat 的翻译托管在 Locize 平台(项目地址见官方指南)。进入项目总览页,点击 "Start to translate" 卡片 "..." 菜单中的 "ADD LANGUAGE" 按钮即可添加新语言。

第 2 步:更新语言选择器组件

编辑 client/src/components/Appearance/Selectors.tsx,在 LangSelector 组件的 languageOptions 数组中加入新语言选项:

{ value: 'language-code', label: localize('com_nav_lang_language_name') },

指南给出的示例:

{ value: 'bo', label: localize('com_nav_lang_tibetan') },
{ value: 'uk-UA', label: localize('com_nav_lang_ukrainian') },

语言代码格式要点(来自指南,且与源码行为一致):

  • 无区域变体的语言使用简单代码,如 bo
  • 需要区分区域变体时使用带区域代码的格式,如 uk-UApt-BRzh-Hans

当前 Selectors.tsx 中的 languageOptions 实际以「auto」项开头({ value: 'auto', label: localize('com_nav_lang_auto') },对应英文 "Auto detect"),其后是带区域标签的变体值(如 en-USde-DE)。选择器使用带搜索的 Dropdown 组件,并在语言包下载期间显示加载 Spinner(由 Recoil 的 languageLoading 状态驱动)。

第 3 步:添加本地化键(语言自身名称)

client/src/locales/en/translation.json 中添加该语言名称的键值,最佳实践是使用目标语言的本地文字(native script)作为值。以指南示例为例,当前英文翻译文件中已有:

"com_nav_lang_tibetan": "བོད་སྐད་",
"com_nav_lang_ukrainian": "Українська",

(见 translation.json 对应行)

第 4 步:创建翻译文件目录

mkdir -p client/src/locales/[language-code]

然后创建 client/src/locales/[language-code]/translation.json,初始内容为空 JSON 对象:

{}

即新增 client/src/locales/bo/translation.jsonclient/src/locales/uk/translation.json 等。

第 5 步:注册到 i18n

指南要求更新 client/src/locales/i18n.ts:导入新的翻译文件并加入 resources 对象:

import translationLanguageCode from './language-code/translation.json';

export const resources = {
  // ... existing languages
  'language-code': { translation: translationLanguageCode },
} as const;

结合当前源码的补充说明:仓库现有实现已演进为懒加载模式,因此新语言的实际注册点有三处,均位于 i18n.ts

  1. 将语言代码加入 supportedLocales 常量数组(L8-L50);
  2. localeLoaders 映射表中添加动态导入项(L59-L103):
localeLoaders = {
  // ...
  'language-code': () => import('./language-code/translation.json'),
};
  1. 若语言代码需要归一化映射(例如选择器里用 uk-UA 而资源目录是 uk),确保 localeAliases 表(L113-L151)中存在对应条目,如 'uk-ua': 'uk''zh-cn': 'zh-Hans'。该表由 normalizeLocale 统一消费,因此无论用户在 UI 中选择 uk-UA 还是浏览器报告 uk,最终都解析到同一资源。

第 6 步:配置回退语言(可选)

如果某语言缺失翻译时应回退到特定语言,更新 i18n.ts 中 i18next 初始化的 fallbackLng 配置。指南的写法:

fallbackLng: {
  'language-variant': ['fallback-language', 'en'],
  // ... existing fallbacks
},

当前实现(L288-L306)中的配置为:

fallbackLng: {
  'zh-TW': ['zh-Hant', 'en'],
  'zh-HK': ['zh-Hant', 'en'],
  zh: ['zh-Hans', 'en'],
  default: ['en'],
},

即所有未特别配置的语言最终都回退到英文;中文繁体变体(zh-TW/zh-HK)优先回退到 zh-Hant 再回退英文。

底层机制:语言代码归一化与资源加载

normalizeLocale:一切语言标识的入口

changeLanguageSafely、detectInitialLanguage 都依赖 normalizeLocaleL197-L216),其处理顺序为:

  1. 若传入 'auto',取 navigator.language(浏览器语言);
  2. 将下划线替换为连字符并整体小写(zh_CNzh-cn);
  3. 先查小写化的 supportedLocales 精确表(localeByLowercase);
  4. 再查 localeAliases 别名表;
  5. 最后截取基础语言代码再查一次,全部失败则回退 'en'

Translation.spec.ts 对这套归一化做了断言:normalizeLocale('uk-UA') 应为 'uk'normalizeLocale('pt-BR') 应保持 'pt-BR'normalizeLocale('en-US') 应为 'en'——这正是指南第 2 步「区域代码在 UI 中书写、基础代码在资源中注册」设计的验证。

ensureLocale:懒加载、去重与失败回退

ensureLocale 负责按需加载语言资源,包含三个从测试用例可确认的行为:

  • 在途请求去重:同一语言的并发加载共享一个 Promise,loader 只调用一次(测试 should reuse an in-flight locale load 验证了这一点,Translation.spec.ts);
  • 加载失败回退英文:chunk 加载失败时打印 [i18n] Failed to load locale "..." 并返回 'en',且下一次调用可以重试(测试 should retry a locale load after a transient failureL106-L128);
  • 已注册检测:若 i18next 中已存在该语言的 resource bundle,直接复用而不再触发网络请求。

加载完成后通过 i18n.addResourceBundle(locale, 'translation', module.default, true, true) 将翻译合并进 i18next 实例。

changeLanguageSafely:并发切换竞态处理

changeLanguageSafelyL308-L329)用自增的 languageRequestId 保证只有最后一次语言切换生效:若某次切换在等待 chunk 下载期间被更新的切换超越,它会直接返回当前 i18n 语言;若旧请求晚于新请求完成,代码会主动重新加载并切换到 latestRequestedLocale。对应测试 should only apply the newest rapid language switchshould restore the newest language if an older change finishes lateTranslation.spec.ts)用三个延迟解析的 Promise 精确模拟了「快速连续切换」场景。

syncDocumentLanguage:RTL 与无障碍同步

每次切换成功后,syncDocumentLanguageL279-L286)会同步 document.documentElement.langdir 属性——后者由 i18n.dir(locale) 计算,因此希伯来语(he)、阿拉伯语(ar)等会自动获得 dir="rtl" 的整页右到左排版。

语言偏好的持久化与 UI 同步链路

用户选择语言后的数据流如下,各环节均有源码可查:

  1. Recoil 状态 + localStorageclient/src/store/language.tslang 是一个 atomWithLocalStorage('lang', ...),默认值优先读 lang Cookie,其次 localStorage,最后取 navigator.language
  2. 副作用同步client/src/components/System/LanguageSync.tsx 监听 store.lang 变化,若与当前 i18n.language 不一致则置 languageLoading 为 true 并调用 changeLanguageSafely(lang),完成后关闭 Loading;
  3. i18next 应用翻译 + 文档属性同步,即上一节的 changeLanguageSafely 链路。

启动时则由 initializeI18nL331-L335)执行 detectInitialLanguage()——按「Cookie → localStorage → 浏览器语言」的顺序探测并归一化后完成首次切换。

翻译键的类型安全

client/src/hooks/useLocalize.ts 中定义了:

export type TranslationKeys = keyof typeof translationEn;

export default function useLocalize() {
  const { t } = useTranslation();
  return useCallback(
    (phraseKey: TranslationKeys, options?: TOptions) => t(phraseKey, options),
    [t],
  );
}

由于 TranslationKeys 直接派生自英文翻译文件的键,任何在英文 translation.json 中不存在的新键都会在 TypeScript 编译期报错。这正是指南第 3 步「先改英文文件」的强制性原因:英文是唯一事实来源(single source of truth),其他语言的 translation.json 只需保持键集合与其一致。

翻译管理流程:只手工维护英文

指南「Translation Process」一节的规则与仓库脚本相互印证:

  1. 新建语言的翻译文件以空对象 {} 起步,后续由 LibreChat 的自动化翻译平台(Locize)填充;
  2. 只有 en 翻译文件应当手工更新
  3. 其他语言的翻译在外部平台管理,下载后通过仓库根目录的 scripts/merge-locize-download.mjs 与现有翻译做深度合并——该脚本会递归比较基线(base)与当前(current)JSON 树,恢复平台导出中缺失的键并统计恢复数量,防止新键丢失。

语言代码标准(指南原文要点)

  • 大多数语言使用 ISO 639-1 代码(enfrde);
  • 需要区分变体时组合区域/写法代码(pt-BRzh-Hanszh-Hant);
  • 藏语使用 bo(Bodic),乌克兰语使用 uk 或带区域的 uk-UA

注意 UI 选择器值与资源目录代码可以不同(如选择器写 uk-UA、资源目录是 uk),只要 localeAliases 中登记了 'uk-ua': 'uk' 这类映射即可——归一化逻辑会把两者统一。

验证新语言是否生效

指南给出的验证步骤,结合源码与测试补充如下:

  1. 重启开发服务器;
  2. 进入 Settings > General,确认新语言出现在语言下拉框(该下拉框即 LangSelector 组件渲染,带搜索功能);
  3. 选择该语言,确认 UI 语言随之切换——若翻译文件为空,界面会按 fallbackLng 回退到英文文案(测试 should fallback to English for an invalid language code 验证了无效代码的回退行为,Translation.spec.ts);
  4. 可运行 client/src/locales/Translation.spec.ts 中的测试套件,覆盖:有效键在英文/法文/西班牙文下的取值、无效键原样返回(i18n.t('invalid-key') 返回 'invalid-key')、占位符插值(如 com_endpoint_default_with_num 传入 { 0: 'John' } 得到 default: John)、以及上文所述的归一化/去重/竞态场景。

最佳实践(指南 Notes 与补充)

  • 语言下拉选项保持按字母顺序排序,便于用户查找;
  • 下拉框中的语言名一律使用本地文字(如 བོད་སྐད་Українська),保证母语用户可识别;
  • 任何缺失的翻译都会以英文兜底,因此新语言即使翻译不完整也可安全上线;
  • 从源码结构看,若新增语言的 UI 选择器值带区域后缀(如 xx-XX),记得在 localeAliases 中补充小写化的别名条目,否则 normalizeLocale 只能靠「基础语言」兜底解析,无法精确命中带变体的资源。

以上流程完整覆盖了 本地化指南 的全部六个接入步骤,并给出了每个步骤在 i18n.tsSelectors.tsxstore/language.ts 与测试文件中的对应实现,可直接作为新语言接入的操作手册。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
528
588
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
906
1.83 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
docsdocs
暂无描述
Markdown
891
5.78 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.53 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.34 K
1.45 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
987
506
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384