vite3-vue3-anyts 使用路由级vue-i18n

782 阅读2分钟

1.前言

最后修改时间2022.8.15

本篇文章主要介绍如何在vite-vue3-ts项目中使用vue-i18n,并且对i18n的懒加载进行优化,达到路由层面,废话不多说直接开冲.

2.开冲前准备

技术栈有

1.vue-i18n

2.pinia(可不需要)

3.vue-router

安装:

我vite版本//3.0.9

pnpm i vue-router //4.0.16
pnpm i pinia //2.0.14
pnpm i vue-i18n //9.2.2

不需要区分放在哪个环境,所以不用后面声明放哪里 基本都是最新版本

3.开冲

怎么搭建项目和使用vue-router我就不写了,这边的区域以后来探索吧

首先在src文件下新创建一个locals文件夹

一级文件夹为国家文件夹和index.ts

二级文件夹为语言文件夹

最后三级文件就是以路由为单位的文件啦

文件夹下层级示例

-locals
  -index.ts
  -{国家}
    -{语言}
      -{路由}
    -en
      -home.ts
    -zh
      -home.ts
  -{国家}
  -{国家}

4.index.ts

直接上代码我注释的比较详细了

import { nextTick, reactive } from "vue";
import { createI18n } from "vue-i18n";
import type { VueI18n, Composer } from 'vue-i18n';
//pinia
import { mainStore } from "@/store";

// 预装语言
const obj = reactive<any>({
    store: 0,
    country: ''
})

// 实例化 // 防止加载过快未挂载上报错
nextTick(() => {
    obj.store = mainStore();
})

// i18n实例
export const i18n = createI18n({
    // 关闭未使用函数组件显示HTML片段警告(警告为你的v-html需要使用函数组件)
    warnHtmlMessage: false,
    // 开启全局注入(template模板可以使用$t)
    globalInjection: true,
    messages: {
        // 设置语言环境信息
        // 按需加载不能提前设置语言
    }
})

// 修改i18n语言(注:repeat为自调用传参,忽略即可)
const setLanguage = (lang: string, repeat?: 0 | 1) => {
    // 第一次直接设置语言
    if (!repeat) {
        if (i18n.mode === 'legacy') (i18n.global as unknown as VueI18n).locale = lang
        else (i18n.global as unknown as Composer).locale.value = lang
    }

    // pinia挂没挂载上都返回lang
    if (obj.store !== 0) {
        obj.store.language = lang
        return lang
    }

    //pinia 没挂载上,反复调用方法直到挂载上 
    if (obj.store === 0) {
        setLanguage(lang, 1)
        return lang
    }
    return lang
}

// 增加
const add = ({ lang, name, value }: { lang: string, name: string, value: object }): void => {
    let obj: any = i18n.global.getLocaleMessage(lang);
    obj[name] = value;
    i18n.global.setLocaleMessage(lang, obj);
}

// 查询
const inquire = ({ lang, name }: { lang: string, name: string }): 0 | 1 => {
    let obj: any = i18n.global.getLocaleMessage(lang);
    if (obj[name]) return 1;
    return 0;
}

// 导出修改语言方法
export const loadLanguageAsync = async (lang: string, country: string, routerStr: string) => {
    let str = routerStr, newLang = lang;
    // 修改首页路由传参
    if (routerStr === '' || routerStr === ':lang') str = 'home';
    // (某些国家)没有某语言直接重置为en
    if (country === 'xx') newLang = 'en';

    // 如果国家不一样,语言配置全部清空,并更改国家
    if (obj.country !== country) {
        let arr = i18n.global.availableLocales;
        if (arr.length) for (let i = 0, length = arr.length; i < length; i++) i18n.global.setLocaleMessage(arr[i], {});
        obj.country = country;
    }

    // 如果修改语言
    if (i18n.global.locale !== newLang) {
        // 修改i18n配置语言
        setLanguage(newLang);
        // 装上默认加载的语言配置
        let arr = ['global', 'home'];(有什么路由需要默认装上的可以放进这个数组,前提是名字和文件一致)
        for (let i = 0, length = arr.length; i < length; i++) {
            // 没有
            if (!inquire({ lang: newLang, name: arr[i] })) {
                let res = await import(/* @vite-ignore */ `../locals/${country}/${newLang}/${arr[i]}.ts`);
                add({ lang: newLang, name: arr[i], value: res.default })
            }
        }
    }

    // 查询到当前路由没有语言配置
    if (!inquire({ lang: newLang, name: str })) {
        let res = await import(/* @vite-ignore */ `../locals/${country}/${newLang}/${str}.ts`);
        add({ lang: newLang, name: str, value: res.default })
    }
}

这里说一下,为什么要用pinia,因为方便呗

还有为什么有国家的区分?

如果项目和商品挂钩,免不了需要区分货币,有些时候货币单位又不得不在文本里,所以要区分国家,不需要的可以试着自己修改哈

:lang是啥?

这个是vue-router给写法,代表这里是个变量,本来是用来表示当前语言的,但是路由截取后出来变成字符串了

为什么首页不判断'/',因为哪怕用户手动输入'/',也会被重置掉

5.文本配置

其实配置是一样的,但我还是发出来

home.ts

export default {
    home: 'xxx'
}

注意,你的路由名字必须有能匹配的文件名否则出问题别来找我

6. main.ts

来main.ts配置一下

import { createApp } from 'vue';
import App from '@/App.vue';
import { createPinia } from 'pinia';
import { mainStore } from "@/store";
import router from '@/router';
// 多语言配置
import { i18n, loadLanguageAsync } from '@/locals';
// 挂载
app.use(pinia).use(router).use(i18n).mount('#app');

// 路由守卫
router.beforeEach((to, from, next) => {
    // 截获配置语言的路由名称
   // let routerStr = "", routerIndex = 0;
   // if (to.matched.length >= 2) routerIndex = 1;
   // let routerStrArr = to.matched[routerIndex].path.split(/\//);
   // routerStr = routerStrArr[routerStrArr.length - 1];

    // 懒加载语言配置
    loadLanguageAsync(lang, country, routerStr).then(() => {
       // app.config.globalProperties.$root = {
       //     country,
       //     locale: lang
       // }
        next()
    }).catch((err) => {
        next()
    });
});

这里后期我注释了一些代码,意思是别抄我的,因为需求不一样,我的路由层级是这样的,可以这样去取路由名

7.页面使用

home.vue

<template>
  <div>
    {{$t('home.home')}}
    {{t('home.home')}}
    <div v-html="$t('home.home')" />
    <div v-html="t('home.home')" />
  </div>
</template>

<script setup lang="ts">
// i18n
import { useI18n } from 'vue-i18n'

// 解构
const { t } = useI18n()
</script>

各两种基本写法

$t 是全局使用的,但只能在模板里

t是引入解构出来的,模板和脚本都可以

8.最后

我不太会写文章,只是把实现的功能以大家能最快cv的形式写出来,抱歉了各位

要是有不明白的评论区说,我看看我知不知道或者能不能解决

还有这个功能没有打过包,还需要大家自己配置咯