Umi @umi/max 国际化(i18n)插件完全指南:配置、接口与源码级实现解析

发布时间:2026/9/14 8:42:57
Umi @umi/max 国际化(i18n)插件完全指南:配置、接口与源码级实现解析 Umi umi/max 国际化i18n插件完全指南配置、接口与源码级实现解析【免费下载链接】umiA framework in react community ✨项目地址: https://gitcode.com/GitHub_Trending/um/umi本文基于umi/max官方国际化文档系统讲解 Umi 国际化插件的约定式目录、全部配置项、运行时接口useIntl、setLocale、addLocale等及其底层模板生成机制。读完你可以独立完成一个 Umi 项目的多语言接入从src/locales目录约定到标题国际化、语言无刷新切换再到语言检测优先级和 antd/moment 联动同步的原理。开始使用约定式目录与文件命名umi/max内置了国际化插件它可以轻松地将国际化功能集成到 Umi 应用程序之中。国际化插件采用约定式目录结构约定在src/locales目录下引入多语言文件。多语言文件的命名需遵循规范langseparatorCOUNTRY.(js|json|ts)其中separator为分隔符默认为-可以通过baseSeparator配置项修改。从源码 getLocaleList 可以看到文件匹配使用的正则是^([a-z]{2})separator?([A-Z]{2})?\.(js|json|ts)$即要求小写两位语言码 可选的大写两位国家码同时扫描范围不只是src/locales还会递归查找src/pages下任意深度的locales目录**/locales/*.{ts,js,json}因此按路由就近组织多语言文件也是被支持的。例如如果需要简体中文和英文支持可以创建zh-CN.ts和en-US.ts两个文件src locales zh-CN.ts en-US.ts pages在.umirc.ts中配置国际化插件export default { locale: { // 默认使用 src/locales/zh-CN.ts 作为多语言文件 default: zh-CN, baseSeparator: -, }, };关于配置的更多介绍可参见配置插件章节。现在添加第一条多语言内容// src/locales/zh-CN.ts export default { welcome: 欢迎光临 Umi 的世界, };// src/locales/en-US.ts export default { welcome: Welcome to Umis world!, };也可以使用.json文件存放多语言内容// src/locales/zh-CN.json { welcome: 欢迎光临 Umi 的世界, } // src/locales/en-US.json { welcome: Welcome to Umis world!, }一切就绪可以使用FormattedMessage /组件消费多语言内容只需将welcome作为参数id的值传入import { FormattedMessage } from umi; export default function Page() { return ( div FormattedMessage idwelcome / /div ); };渲染结果如下!-- zh-CN -- div欢迎光临 Umi 的世界/div !-- en-US -- divWelcome to Umis world!/div仓库中的 examples/max 示例工程就是一个完整的参照其 examples/max/locales/zh-CN.js 同时演示了平铺键HELLO、标题键site.title与嵌套对象user.welcome三种写法。嵌套对象与点路径 idflattenMessages 机制上面的嵌套对象写法之所以能用iduser.welcome访问是因为插件在生成临时文件时会执行一个扁平化函数。从 localeExports.tpl 可以看到flattenMessages的实现它递归遍历多语言对象把每一层键用.连接成点路径如user.welcome最终合并为一个扁平的 messages 表。因此多语言文件既可以用export default { user: { welcome: ... } }的嵌套结构也可以用user.welcome: ...的扁平字符串键二者等价。在组件的参数中使用useIntl在某些情况下需要将多语言内容作为参数传递给某个组件如 antd 的Alert可以通过intl对象实现import { Alert } from antd; import { useIntl } from umi; export default function Page() { const intl useIntl(); const msg intl.formatMessage({ id: welcome, }); return Alert message{msg} typesuccess /; };在底层国际化插件基于react-intl封装并支持它的所有接口。在上面的代码中我们运用到了react-intl提供的useIntl()接口来初始化intl对象并调用此对象的formatMessage()方法来格式化字符串。从源码 locale.ts 生成index.ts的导出列表可以看到插件不仅转出了useIntl、injectIntl、FormattedMessage等 react-intl 核心 API还转出了FormattedDate、FormattedNumber、FormattedPlural、FormattedRelativeTime等完整的 react-intl 组件集合——也就是说日期、数字、复数等格式化能力开箱即用。格式化字符串动态插值如果希望在多语言翻译中动态插值可以这样编写多语言内容// src/locales/zh-CN.ts export default { user: { welcome: {name}今天也是美好的一天, }, };// src/locales/en-US.ts export default { user: { welcome: {name}, what a nice day!, }, };特殊的语法{name}允许在运行时动态赋值import { FormattedMessage } from umi; export default function Page() { return ( p FormattedMessage iduser.welcome values{{ name: 张三 }} / /p ); };如果希望通过intl对象实现可以这样赋值import { useIntl } from umi; export default function Page() { const intl useIntl(); const msg intl.formatMessage( { id: user.welcome, }, { name: 张三, }, ); return p{msg}/p; };注意用于赋值的键值对对象应当作为formatMessage()方法的第二个参数传递。渲染结果如下!-- zh-CN -- p张三今天也是美好的一天/p !-- en-US -- p张三, what a nice day!/p切换语言SelectLang 组件与 setLocale通过预设的SelectLang /组件可以快速添加切换语言的功能import { SelectLang } from umi; export default function Page() { return SelectLang /; };从 SelectLang.tpl 源码可以确认其两个前提与更多用法前提条件模板中ShowSelectLang变量由 locale.ts 计算为「语言列表大于 1 且项目安装了 antd」。不满足时SelectLang /渲染为空标签并会提示antd is not installed. SelecLang / unavailable见 locale.ts L34-L39。可定制属性组件接收globalIconClassName、postLocalesData改写语言菜单数据如自定义 label/国旗、onItemClick自定义点击行为、reload切换时是否刷新、icon、style等 props。内置了 40 种语言的默认 label 与国旗图标映射如zh-CN显示「简体中文 」未知语言回退为 key 本身加 图标。更多情况下你可能需要自己编写切换语言的组件这时轮到setLocale()接口大显身手import { setLocale } from umi; // 切换时刷新页面 setLocale(en-US);使用该方法切换语言时默认情况下会刷新当前的页面。可以设置它的第二个参数为false来实现无刷新切换语言// 切换时不刷新页面 setLocale(en-US, false);如果需要切换为默认的语言只需要调用此方法而不用传递任何参数// 如果您的默认语言为 zh-CN // 那么以下调用具有与 setLocale(zh-CN) 同样的效果 setLocale();从 localeExports.tpl 的 setLocale 实现 可以看到无刷新切换的完整机制setLocale先把语言写入localStorage的umi_locale键若开启useLocalStorage然后调用setIntl重建全局intl对象当realReload为true时执行window.location.reload()否则发出LANG_CHANGE_EVENT事件并手动派发languagechange事件。而_LocaleContainer见 locale.tpl在挂载时监听该事件触发useState更新与moment.locale同步从而实现不刷新整页的切换体验。多语言默认值与 defaultMessage为了页面的一致性当 Umi 没有在当前的多语言文件中找到id对应的内容时它会直接将id渲染为页面上的内容。例如编写了如下多语言文件// src/locales/zh-CN.ts export default { table: { submit: 提交表单, }, };// src/locales/en-US.ts export default { // table: { // submit: SUBMIT TABLE, // }, };有如下组件import { Button } from antd; import { FormattedMessage } from umi; export default function Page() { return ( Button typeprimary FormattedMessage idtable.submit / /Button ); };渲染的结果为!-- zh-CN -- button typeprimary提交表单/button !-- en-US -- button typeprimarytable.submit/button特别的如果需要在没有完成国际化适配的情况下给出一个默认的值可以使用defaultMessage参数import { Button } from antd; import { FormattedMessage } from umi; export default function Page() { return ( Button typeprimary FormattedMessage idtable.submit defaultMessageSUBMIT TABLE / /Button ); };使用formatMessage()方法时也可以这么做import { Button } from antd; import { useIntl } from umi; export default function Page() { const intl useIntl(); const msg intl.formatMessage({ id: table.submit, defaultMessage: SUBMIT TABLE, }); return Button typeprimary{msg}/Button; };不推荐使用defaultMessage配置默认值因为这会编写大量重复的国际化内容。最好的情况是在进行国际化适配时确保每个多语言文件中都包含所有用到的键。常用接口介绍addLocale 动态添加多语言支持无需创建并编写单独的多语言文件使用addLocale()接口可以在运行时动态添加语言支持。它接受三个参数参数类型介绍nameString多语言的 KeymessageObject多语言的内容对象optionsObjectmomentLocale和antd配置例如动态引入繁体中文的多语言支持import { addLocale } from umi; import zhTW from antd/es/locale/zh_TW; addLocale( zh-TW, { welcome: 歡迎光臨 Umi 的世界, }, { momentLocale: zh-tw, antd: zhTW, }, );从 localeExports.tpl 的 addLocale 实现 可以看到两个细节其一若该语言已存在文件如已有zh-TW.ts传入的messages会与原有 messages合并Object.assign而非覆盖其二如果name恰好等于当前语言会立即发出LANG_CHANGE_EVENT触发界面刷新否则新语言要到下次切换时才生效。另外当仅追加 messages 时options中的momentLocale/antd可以省略会沿用已有的配置。getAllLocales 获取多语言列表通过getAllLocales()接口可以获取当前所有多语言选项的数组包括通过addLocale()方法添加的多语言选项。该接口默认会在src/locales目录下寻找形如zh-CN.(js|json|ts)的文件并返回多语言的 Keyimport { getAllLocales } from umi; getAllLocales(); // [en-US, zh-CN, ...]从源码看getAllLocales即Object.keys(localeInfo)其中localeInfo是构建期由模板根据扫描到的语言文件预生成的注册表见 localeExports.tpl L73-L88每条记录包含该语言的messages、locale统一转成-分隔避免Function.supportedLocalesOf的RangeError、antd语言包与momentLocale。getLocale 获取当前选择的语言通过getLocale()接口可以获取当前选择的语言import { getLocale } from umi; getLocale(); // zh-CN其检测优先级可以从 getLocale 实现 精确确认运行时插件自定义若src/app.ts中的locale.getLocale()已定义优先返回其结果见下文「运行时拓展」localStorageumi_locale键的值前提useLocalStorage为 true浏览器语言检测navigator.language且其中的-会被替换为baseSeparator前提baseNavigator为 truedefault 默认语言配置中的default值若以上都没有则回退到zh-CN由 locale.ts L127 的defaultLocale计算得出。useIntl 获取 intl 对象useIntl()很有可能是开发中最常用的接口通过它可以获取intl对象并进一步执行formatMessage()等方法// src/locales/en-US.json { welcome: Hi, {name}. }import { useIntl } from umi; const intl useIntl(); const msg intl.formatMessage( { id: welcome, }, { name: Jackson, }, ); console.log(msg); // Hi, Jackson.intl对象由 react-intl 的createIntl创建见 localeExports.tpl 的 _createIntl创建前会经过applyRuntimeLocalePlugin走一次运行时modify插件钩子这意味着你可以在运行时对传给createIntl的完整配置locale、messages、formats、cache 等做定制详见下文「自定义选项配置」。setLocale 设置语言通过setLocale()接口可以使用编程的方法动态设置当前的语言。它有两个参数参数类型介绍langString切换到的语言realReloadBoolean切换时是否刷新页面默认为true刷新页面import { setLocale } from umi; // 切换时刷新页面 setLocale(en-US); // 切换时不刷新页面 setLocale(en-US, false);配置插件可以在.umirc.ts中配置国际化插件。默认值如下export default { locale: { antd: false, // 如果项目依赖中包含 antd则默认为 true baseNavigator: true, baseSeparator: -, default: zh-CN, title: false, useLocalStorage: true, }, };配置项在 locale.ts 中通过 zod schema 做了校验六个字段均为可选的partial()对象并且插件是EnableBy.config模式——即只有配置了locale时插件才启用。配置的详细介绍如下配置项类型默认值介绍antdBooleanfalse如果项目包含antd依赖则为trueantd的国际化支持语言切换时会自动同步 antd 组件的语言包与 RTL 方向。baseNavigatorBooleantrue开启浏览器语言检测。默认情况下当前语言环境的识别按照localStorage中umi_locale值 浏览器检测 default设置的默认语言 zh-CNbaseSeparatorString-语言Language与国家Country之间的分割符。默认情况下为-返回的语言及目录文件为zh-CN、en-US和sk等。若指定为_则default默认为zh_CN。defaultStringzh-CN项目默认语言。当检测不到具体语言时使用default设置的默认语言。titleBooleanfalse开启标题国际化。useLocalStorageBooleantrue自动使用localStorage保存当前使用的语言。几个值得注意的源码级细节antd 默认值的判定插件初始化时会require.resolve(antd)探测项目是否安装 antdlocale.ts L34-L39据此设置antd: hasAntd默认值若未安装 antd会打印antd is not installed. SelecLang / unavailable警告。baseSeparator 的副作用localeInfo中各语言的 locale 在生成时以baseSeparator拼接但 react-intl 要求标准-分隔格式模板内会把分隔符统一替换回-name.split({{BaseSeparator}}).join(-)同时 getLocale 中的注释 明确提示修改 baseSeparator 配置后需要清空 localStorage否则会破坏应用。Intl polyfill若项目的targets命中旧浏览器IE 10、Firefox 28、Chrome 23 等见 isNeedPolyfill插件会通过addEntryImportsAhead在入口最前面注入intlpolyfill 包保证 Intl API 可用。标题国际化在路由配置中添加title项即可启用国际化支持自动将页面的标题转为对应的多语言内容。例如编写多语言文件如下// src/locales/zh-CN.ts export default { site.title: Umi - 企业级 React 应用开发框架, about.title: Umi - 关于我, };// src/locales/en-US.ts export default { site.title: Umi - Enterprise-level React Application Framework, about.title: Umi - About me, };配置路由内容如下// .umirc.ts export default { title: site.title, routes: [ { path: /, component: Index, }, { path: /about, component: About, title: about.title, }, ], };访问页面时/路由。多语言选项为zh-CN时页面标题为Umi - 企业级 React 应用开发框架为en-US时页面标题为Umi - Enterprise-level React Application Framework。/about路由。多语言选项为zh-CN时页面标题为Umi - 关于我为en-US时页面标题为Umi - About me。标题国际化的底层机制可以从 runtime.tpl 的 patchRoutes 看到它会在构建产物中遍历整棵路由树把每个路由的title字段视为多语言 id——若该 id 存在于当前语言的 messages 中则用intl.formatMessage({ id })翻译并写回route.title同时更新route.name原 id 被保留到route.locale字段以供运行时切换语言时再次翻译。同时 locale.tpl 会在容器挂载和语言切换时用全局title键更新document.title保证切换语言后标题也同步变化。运行时拓展国际化插件允许在运行时对它进行拓展与定制。插件通过 api.addRuntimePluginKey(() [locale]) 注册了locale运行时插件键因此src/app.ts中导出的locale导出会被自动接入。自定义 getLocale可以自定义获取页面语言getLocale()方法的逻辑例如通过识别链接?localeen-US将en-US作为当前页面的语言// src/app.ts import qs from qs; export const locale { getLocale() { const { search } window.location; const { locale zh-CN } qs.parse(search, { ignoreQueryPrefix: true }); return locale; }, };从模板 applyRuntimeLocalePlugin 可以看到所有对语言信息的读取getLocale、getIntl等都会先经过该运行时modify钩子因此自定义getLocale具有最高优先级。自定义选项配置Umi 的 i18n 是基于react-intl实现的当需要配置更多react-intl初始化选项的时候可以在app.ts中配置具体配置选项可以参考 react-intl 文档// src/app.ts import { RuntimeConfig } from umijs/max export const locale: RuntimeConfig[locale] { textComponent: span, onError: () { console.log(error handler...); } // locale: string // formats: CustomFormats // messages: Recordstring, string | Recordstring, MessageFormatElement[] // defaultLocale: string // defaultFormats: CustomFormats // timeZone?: string // textComponent?: React.ComponentType | keyof React.ReactHTML // wrapRichTextChunksInFragment?: boolean // defaultRichTextElements?: Recordstring, FormatXMLElementFnReact.ReactNode // onError(err: string): void }这些字段的类型定义由插件在构建期写入运行类型文件见 locale.ts L248-L262IRuntimeConfig.locale由getLocale?、cache?与OmitParameterstypeof createIntl[0], locale | defaultLocale合并而成即除locale/defaultLocale外createIntl接受的所有配置formats、messages、timeZone、onError等都可以在运行时传入。antd 与 moment 的自动联动当antd: true时_LocaleContainer会把当前语言对应的 antd 语言包注入 antd 的ConfigProvider并根据语言前缀he/ar/fa/ku自动设置direction为rtl否则为ltr见 locale.tpl L65-L78 与 getDirection。对时间库插件同样做了联动生成locale.tsx时会把各语言对应的moment或配置moment2dayjs后的dayjs语言包一并 import并在应用创建和语言切换时调用moment.locale(localeInfo[locale]?.momentLocale)同步时间库的语言见 locale.tpl L20-L28、L42-L50。momentLocale的解析规则见 getMomentLocale优先尝试lang-country国家码小写文件退化为纯语言码找不到则为空。FAQ为什么不直接使用 formatMessage 这个语法糖虽然formatMessage直接使用起来会非常方便但是它脱离了 React 的生命周期最严重的问题就是切换语言时无法触发 DOM 的重新渲染。为了解决这个问题我们切换语言时就需要刷新一下浏览器用户体验很差。所以推荐大家使用useIntl或者injectIntl可以实现同样的功能。这一点在源码中得到了直接印证localeExports.tpl 的 formatMessage 被标注为deprecated它读取的是构建期固定的全局g_intl单例而非 React 上下文并且首次调用时会在控制台打印警告Using this API will cause automatic refresh when switching languages, please use useIntl or injectIntl.——与本文档的 FAQ 结论完全一致。小结从配置到生成的完整链路从源码结构看整个国际化插件的工作链路可以概括为构建期扫描getLocaleList按命名正则扫描src/locales与pages/**/locales产出每种语言的 messages 路径、antd 语言包与 moment 语言包映射模板生成onGenerateFiles阶段用 Mustache 渲染 packages/plugins/templates/locale/ 下的四个模板生成localeExports.ts核心 API 实现、locale.tsx_LocaleContainer容器、runtime.tsxi18nProvider与patchRoutes、SelectLang.tsx并通过addTmpGenerateWatcherPaths监听多语言文件变更自动重新生成运行期消费_LocaleContainer通过RawIntlProviderantd 场景外裹一层ConfigProvider向整个应用注入intl上下文useIntl/FormattedMessage等 react-intl API 据此消费消息setLocale通过LANG_CHANGE_EVENT驱动无刷新切换。理解了这条链路你在排查「新增语言文件不生效」「切换语言后标题未更新」「localStorage 语言被覆盖」等问题时就能快速定位到对应环节。【免费下载链接】umiA framework in react community ✨项目地址: https://gitcode.com/GitHub_Trending/um/umi创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询