require.context api 的实践应用(基于飞冰ice.js 2.x进行国际化功能的实践)
前言
本文是以国际化功能为背景,介绍require.context
api 的应用场景以及具体使用方法。另外,本文用到的react项目是基于ice创建出来的,故有ice使用经验者阅读起来会更顺畅,当然非ice使用者,阅读起来问题也是不大的。
介绍
require.context
由webpack提供,其作用是去获取指定目录下面的指定文件,该api接收3个参数。
- 第一个参数是要搜索的目录 (string 类型)
- 第二个参数是否要搜索指定目录下面的子目录 (boolean 类型)
- 第三个参数是一个正则表达式,用来指定要被搜到的文件。(RegExp 类型)
案例
const files = require.context('./locales', true, /.json$/);
应用
本次该api的应用场景介绍以在react项目中实现国际化功能作为背景
1. 创建react项目
使用飞冰ice.js命令快速创建一个react项目 npm init ice <projectName>
2. 安装国际化功能的相关库
yarn add i18next i18next-browser-languagedetector react-i18next
yarn add build-plugin-ice-i18n -D
3. 创建i18n目录
在src下面创建i18n目录,并且i18n文件夹里面新建locales文件夹用于存放我们的翻译文件,新建一个config.ts文件,用于书写i18n配置代码。目录结构如图所示
4. 书写i18n配置代码
在config.ts中书写配置代码,写完之后要在app.tsx里面import引用
我们采用namespace的方式,去使用翻译文件,所以每创建一个翻译json文件,都需要在config.ts中配置一下新创建的翻译文件对应的namespace。这个操作属于重复性工作。那么如果有接口能自动读到所有的json文件,那么我们就可以通过js去创建i8next所需要的namespace配置项了。
如果这一步看不太懂的话,说明需要去看i8next的文档,本文旨在阐述require.context的使用,不对i8next进行详细阐述。 点击查看react-i18next文档
5. 利用require.context去获取翻译文件
locales文件夹中,会有子目录以及翻译json文件,通过require.context api,去拿到所有的翻译文件。详细代码如下:
const path = require('path');
const files = require.context('./locales', true, /.json$/); // files 是locales文件夹下面所有的json文件,以及json文件所在的目录
const localeFileObj = {};
const namespace: string[] = [];
files.keys().forEach((filePath: string) => {
const fileName = path.basename(filePath, '.json'); // json文件名称
const firstIndex = filePath.indexOf('/');
const lastIndex = filePath.lastIndexOf('/');
const fileDirName = filePath.substring(firstIndex + 1, lastIndex); // json文件所在目录名称
// 组合命名空间数组 --- 以json文件名称作为namespace
!namespace.includes(fileName) && namespace.push(fileName);
// 组合source对象
if (localeFileObj[fileDirName]) {
localeFileObj[fileDirName][fileName] = files(filePath);
} else {
localeFileObj[fileDirName] = {
[fileName]: files(filePath),
};
}
});
export const resources = localeFileObj;
6. 在飞冰的打包配置文件中配置语言环境
新创建的ice项目根目录下是build.json
文件,我们可以更改为build.config.js
的方式,这样就可以写js了.
build.config.js
的执行环境是在node,故我们可以直接通过 fs.readdirSync
去读取locales文件夹中配置的所有国家的语言,
const fs = require('fs');
const locales = fs.readdirSync(path.join(__dirname, './src/i18n/locales'));
module.exports = {
plugins: [
[
'build-plugin-ice-i18n',
{
locales,
defaultLocale: 'zh-CN',
redirect: true,
},
]
]
}
7. 切换语言环境
通过上面这种配置方式,当在路由的url中添加/en-US
或者 /zh-CN
之后,就可以借助ice提供的getLocale
方法拿到当前的语言环境,在借助react-i18next
提供的 changeLanguage
方法切换语言环境。
import { useTranslation } from 'react-i18next';
import { getLocale } from 'ice';
const locale = getLocale();
const { i18n } = useTranslation();
useEffect(() => {
i18n.changeLanguage(locale); // 根据国际化路由指定语言版本
}, []);
8. 在组件中使用翻译
借助react-i18next
提供的useTranslation
hook返回的t
函数进行翻译,由于我们采用的是namespace
的方式,故useTranslation
的参数为要使用的翻译json文件名称。
案例代码如下:
9. 完整的i18next配置项config.ts的代码
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import LanguageDetector from 'i18next-browser-languagedetector';
const path = require('path');
const files = require.context('./locales', true, /.json$/);
const localeFileObj = {};
const namespace: string[] = [];
files.keys().forEach((filePath: string) => {
const fileName = path.basename(filePath, '.json'); // 文件名称
const firstIndex = filePath.indexOf('/');
const lastIndex = filePath.lastIndexOf('/');
const fileDirName = filePath.substring(firstIndex + 1, lastIndex); // 文件所在目录名称
// 组合命名空间数组
!namespace.includes(fileName) && namespace.push(fileName);
// 组合source对象
if (localeFileObj[fileDirName]) {
localeFileObj[fileDirName][fileName] = files(filePath);
} else {
localeFileObj[fileDirName] = {
[fileName]: files(filePath),
};
}
});
export const resources = localeFileObj;
i18n
.use(LanguageDetector)
.use(initReactI18next)
.init({
ns: namespace,
interpolation: {
escapeValue: false, // not needed for react as it escapes by default
},
resources,
debug: process.env.NODE_ENV === 'development',
});
总结
require.context
的应用场景很多,本文主要阐述require.context
在做国际化功能的应用场景。其实看案例我们可以看出来require.context
api 的应用场景在做前端基建方向的应用场景应该会更多,对新创建的文件进行导入处理,获取新创建的文件名,文件路径以及文件内容这种场景下,利用该api可以帮助我们减少很多重复性极高的操作。