vue-i18n结合vite动态导入完成国际化文件目录划分

402 阅读4分钟

vite中实现动态加载资源有两种方式,一种是import来加载单个文件资源,还有一种是import.meta.global来加载制定目录下的文件资源。那么我们就可以将vue-i18n结合vite动态加载资源的方式来实现国际化的目录划分,可以让我们代码的整体目录看起来很干净并且也很容易的去更新这些国际化的信息。

在开始之前我们先了解一下vite的动态加载资源。

import

import的方式是加载单个文件资源,可以在后面拼接url或者raw来实现获取结果为url还是base64格式的内容。

import('./1.png').then(res => {
    console.log(res);
})

默认是url的方式

image.png

获取base64结果

import('./1.png?raw').then(res => {
    console.log(res)
})

image.png

import.meta.global

import.meta.global实现加载多个文件资源。可以配置eager是否是同步加载,as结果是url还是base64格式。

获取assets目录下的所有的jpg文件。

import.meta.global('./assets/**/*.jpg').then(res => {
    console.log(res)
})

默认是异步获取,查看结果就可以看出它的value是一个函数的方式,需要调用这个函数才可以拿到文件的内容。

image.png

可以通过eager这个配置属性改成true就可以实现同步获取。

import.meta.global('./assets/**/*.jpg', {eager: true}).then(res => {
    console.log(res);
})

这样它的value就不再是一个函数了而是一个内容。

image.png

也可以使用as这个配置项来更改获取内容为url还是base64

import.meta.global('./assets/**/*.jpg', {eager: true, as: 'raw'}).then(res => {
    console.log(res)
})

这样就获取了base64的内容。

image.png

还有很多其它配置,比如具名导入、自定义查询等,感觉这些不是很常用。如果想了解的话可以去vite官网查看。这里我就不介绍了。

vue-i18n 结合 import.meta.global

上面已经介绍完了vite的动态导入的内容以及如何去使用,那么接下来就直接进入主题vue-i18n结合import.meta.global来完成国际化文件目录的划分。

先安装vue-i18n这个国际化库。

npm i vue-i18n

下载完成之后单独创建一个i18n的文件夹来存放国际化的相关内容。这里存的是公共国际化的内容。

image.png

来完成index.ts部分,这也是最主要的部分。

const itemize: Record<string, any> = { en: [], "zh-cn": [] };
// 读取当前目录下的国际化文件
const modules: Record<string, any> = import.meta.glob("./**/*.ts", {
  eager: true,
});

创建了一个itemize的变量用来存放国际化的内容的。然后就动态导入当前目录下的.ts后缀的文件来获取这些国际化的内容。

image.png

通过default属性就可以拿到当前文件导出的内容了。

接下来我们对获取到的内容进行处理。

for (const path in modules) {
  const key = path.match(/(\S+)\/(\S+).ts/);
  if (itemize[key![2]]) {
    itemize[key![2]].push(modules[path].default);
  } else {
    itemize[key![2]] = [modules[path].default];
  }
}

这里的正则表达式是将key的文件名称拿到,这样就获取了当前的文件是中文还是英文。然后将内容保存在itemize对应的key中。这样itemize的大致结构就是这样:

{
    'en': [{jack: 'jack'}],
    'zh-cn': [{jack: '杰克'}]
}

使用同样的方式我们来加载views的各个页面和各个组件下的i18n国际化文件。

image.png

读取目录下的这些文件。

// 读取每个页面下的国际化文件
const pages: Record<string, any> = import.meta.glob("./../views/**/i18n/*.ts", {
  eager: true,
})

for (const path in pages) {
  const key = path.match(/(\S+)\/(\S+).ts/);
  if (itemize[key![2]]) itemize[key![2]].push(pages[path].default);
  else itemize[key![2]] = [pages[path]];
}

这样itemize里就存入了所有的国际化的信息,那么接下来就要对itemize进行处理了,处理成vue-i18n所需要的数据结构。

import { createI18n } from "vue-i18n";

const messages: Record<string, any> = {};
// 合并对象
function mergeArrObj(list: Record<string, any>, key: string) {
  let obj = {};
  list[key].forEach((i: any) => {
    obj = Object.assign({}, obj, i);
  });
  return obj;
}

for (const key in itemize) {
  messages[key] = {
    ...mergeArrObj(itemize, key),
  };
}

export const i18n = createI18n({
  messages: messages,
  locale: "zh-cn", // 默认中文
});

有一个mergeArrObj合并函数,主要的功能就是合并itemize数据对应key下面的数据,遍历整个数组使用Object.assign进行合并。使用Object.assign这样的合并方式会将相同的key进行替换,是一个非常不错的选择。

这样就完成了所有的功能,接下来就将导出的i18nmain.js中使用,然后验证一下。

import { i18n } from "./i18n/index";

createApp(App).use(i18n).mount("#app");

验证一下

<script setup lang="ts">
import { i18n } from "./i18n/index";

const changeLang = (lang: string) => {
  i18n.global.locale = lang;
};
</script>
<template>
  <div>{{ $t("title") }}</div>
  <button @click="changeLang('en')">切换英文</button>
  <button @click="changeLang('zh-en')">切换中文</button>
</template>

image.png