基于React-hook实现的简易前端国际化方案

基于React-hook实现的简易前端国际化方案

前言 💬

  • 实现 React 项目国际化,不得不提在业界中较受欢迎的库:react-intl,它是雅虎的语言国际化开源项目 formatjs 的一部分,通过其提供的组件和 api 可以在 React 项目中实现多语言支持。
  • react-intl最大的优势是它用 props 的方式注入语言包,无需重新加载页面就可以直接更改显示的语言,而其缺点主要有两个:
    • 只能应用于视图层,仅支持在jsx文件的内容(即React.Component)中使用,不支持在普通 js 对象中使用 😫。
    • 它用包裹组件的形式装饰 React.Component,因此组件行为在很多方面都发生了改变,具有较强的侵入性 😱。
  • 在开发中发现,为了实现国际化,使用react-intl库的国际化步骤还是比较繁琐的,具体步骤不是本文的重点(传送门👉:react-intl 实现 React 国际化多语言),而且虽然这个库提供了许多组件,但是大多时候只需要使用 <FormattedMessage/> 这个组件,实在是没必要为了一棵🌲,买下整个森林 🤷‍♀️。
  • 基于以上提到的问题,本文将介绍基于 React-hook 实现的一套简易的前端国际化方案(已在 React 项目中投入使用,目前没遇到什么问题)。

效果展示 🤩

开发思路 🤔

  • 用过 react-intl 库的同学都知道,该库提供了 <IntlProvider/>组件用于提供国际化的上下文:当使用<IntlProvider/> 包裹住某个组件的时候,这个组件本身和组件内部包含的子组件就可以获得所有 react-intl 提供的接口以及在<IntlProvider/> 中引入的 locale 配置文件的内容。
  • 实际上,当我们创建了不同语言版本的国际化资源文件后,只要确定了语言,对应语言版本文件就可作为 dictionary 在组件中进行查询使用。中英文的国际化资源文件大致长这个样子 👇:
  • 我们可以使用 React 提供的 createContext 方法创建一个 React 的上下文(Context)用于存放当前的语言版本,然后将 Context.Provider 作为最外层包装我们的组件(一般是<App/>),则 <App/>组件本身和组件内部包含的子组件,只要是函数式组件,均可以使用useContext方法获取到当前的语言版本,根据对应的 dictionary 对文案进行翻译。
  • 前面提到,react-intl 库只能在组件中实现国际化,不支持在普通 js 对象中使用。为了解决这个问题,我们可以在全局存放当前的语言版本 currentLang,当切换语言版本时,对该全局变量进行更新,对 js 文件提供的 api 只需要根据当前currentLang 值选取相关的语言版本 dictionary,并通过传入的key进行文案翻译(取value)即可。

  • 听不懂的同学不要慌,这里先介绍整个方案实现的思路,具体怎么实现,再往下看看吧 🚶,看完代码回头看看思路,可能会更加清晰。

获取/切换当前语言版本

  • 提供 getLang 方法用于获取当前语言版本(主要是 js 文件使用),用于和setLang 方法用于切换当前语言版本,相关代码如下:

  • 细心的同学发现代码中也有 updaterList 这个变量,在调用setLang 方法时,会依次遍历 updaterList 里面的方法并执行相关的方法。下面将提到的 <IntlProvider/> 组件将涉及到 updaterList 值的修改。

实现 IntlProvider 组件

  • 使用 React 提供的 createContext 方法创建一个 React 的上下文(Context)用于存放当前的语言版本,相关代码如下:
  • Context.Provider 作为最外层包装我们的组件:

实现 FormattedMessage 组件

  • <FormattedMessage/> 组件支持传入 id (必填)和参数args (可选),相关代码如下:
  • <FormattedMessage/> 组件的实现代码可以看到,核心代码是调用了自定义hook(即useFormatMessage方法),该方法利用了字符串的 split 方法能使用正则来分割使结果中包含分隔块的特性(传送门 👉: MDN文档),并使用 useContext 方法获取到<IntlProvider/> 组件存放的语言版本。
  • 在组件中使用:

实现支持普通 js 对象的 api

  • 为了让普通 js 对象也能使用国际化的功能,我们提供了两种方法用于处理不带参数(getRawText方法)和带参数(getFormattedText方法,不传参数时等价于getRawText方法)的情况:

  • 如需在接口报错时对错误信息进行国际化:

  • 以上就是整套前端国际化方案的完整实现,看到这里,还不明白的同学可以结合前面的开发思路再缕缕。

总结 👀

  • 针对目前较为热门的国际化开源库 react-intl 在项目中使用起来存在配置繁琐、只能应用于视图层、有较强的侵入性等缺点,本文介绍了基于 React-hook 实现的简易前端国际化方案,即实现了常用的组件和 api,并在项目中投入使用。
  • 题外话:针对 react-intl 库的缺陷,阿里推出了 react-intl-universal 库,这个库使用起来也挺方便的,提供了更多丰富的功能,至于为啥要自己写一套方案,这就见仁见智了,因为项目中仅使用到少量的组件和 api,通过理解原理,在项目中自己实现一套国际化方案也是可以的。

以上内容如有遗漏错误,欢迎留言 ✍️指出,一起进步💪💪💪

如果觉得本文对你有帮助,🏀🏀留下你宝贵的 👍

参考资料 📖

  1. 知乎 - React前端国际化
  2. React Intl 使用文档
  3. react-intl 实现 React 国际化多语言
  4. React Hook 中 createContext & useContext 跨组件透传上下文与性能优化
  5. 拓展:react-intl-universal
分类:
前端