likes
comments
collection
share

require.context api 的实践应用(基于飞冰ice.js 2.x进行国际化功能的实践)

作者站长头像
站长
· 阅读数 19

前言

本文是以国际化功能为背景,介绍require.context api 的应用场景以及具体使用方法。另外,本文用到的react项目是基于ice创建出来的,故有ice使用经验者阅读起来会更顺畅,当然非ice使用者,阅读起来问题也是不大的。

介绍

require.context 由webpack提供,其作用是去获取指定目录下面的指定文件,该api接收3个参数。

  • 第一个参数是要搜索的目录 (string 类型)
  • 第二个参数是否要搜索指定目录下面的子目录 (boolean 类型)
  • 第三个参数是一个正则表达式,用来指定要被搜到的文件。(RegExp 类型)

require.context 官方介绍

案例

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配置代码。目录结构如图所示

require.context  api 的实践应用(基于飞冰ice.js 2.x进行国际化功能的实践)

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提供的useTranslationhook返回的t函数进行翻译,由于我们采用的是namespace的方式,故useTranslation的参数为要使用的翻译json文件名称。

案例代码如下:

require.context  api 的实践应用(基于飞冰ice.js 2.x进行国际化功能的实践)

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可以帮助我们减少很多重复性极高的操作。