Skip to content

i18n 在 vue3 中的实现

通过对 TDesign 的代码阅读弄懂 i18n在 vue 中的实现, 通过导入加载完成多语言在前端的实现

组件 : intlify/vue-i18n-next

文档 : https://vue-i18n.intlify.dev/

创建 i18n 实例

创建实例并应用

定义 i18n, 返回 i18n 实例

typescript
// src/locales/index.ts
export const i18n = createI18n({
    // 传统模式
    legacy: false,   
    // 当前的语言模式
    locale: localStorage.getItem(localeConfigKey) || browserLanguage || 'zh_CN',
    // 无匹配语言模式的默认语言模式
    fallbackLocale: 'zh_CN',
    // 所有的 
    messages: importMessages.value,
    // 全局注入, 可以使用 $ 开始的变量
    globalInjection: true,
});
export default i18n;

在 vue 中的引入

typescript
// src/main.ts
import i18n from './locales';
app.use(i18n);

locales 语言加载

文件目录

位置 : src/locales

.
├── index.ts       // 定义 i18n
├── lang           // 语言目录
│   ├── en_US
│   │   ├── components.ts
│   │   ├── index.ts
│   │   ├── layout.ts
│   │   └── pages
│   │       ├── ...
│   │       └── user.ts
│   └── zh_CN
│       ├── components.ts
│       ├── index.ts
│       ├── layout.ts
│       └── pages
│           ├── ...
│           └── user.ts
└── useLocale.ts   // 便捷的函数

加载步骤

加载文件夹下的所有的文件, 这里用到了 vue3 的 glob 导入, 这里引入所有的模块, 所以第二个参数将 { eager: true } 会立即加载所有的文件, 否则会是动态导入的

typescript
const langModules = import.meta.glob('./lang/*/index.ts', { eager: true });

index.ts 文件的内容如下所示

typescript
export default {
  lang: "English",
  layout,
  pages,
};

这里加载的值内容是

json
{
  "./lang/en_US/index.ts": {
    "default": {
      "lang": "English",
      "layout": {
        "header": {
          "code": "Code Repository",
          "...": "...",
          "setting": "Setting"
        }
      },
      "pages": {
        "user": {
          "markDay": "Good afternoon, today marks your 100th day at Tencent",
          "personalInfo": {
            "title": "Personal Info",
            "position": "Employee of the Hong Kong and Macau Business Expansion team",
            "desc": {
              "phone": "Phone"
            }
          }
        }
      }
    }
  }
}

根据加载的语言模块内容处理, 这里使用到了 浏览器的语言变量, 是一个在 BCP47 中定义的语言字符串 🔗 Navigator.language

typescript
// 语言定义存储
const langModuleMap = new Map<string, Object>();

// 语言代码
const langCode: Array<string> = [];

// 本地配置KEY
const localeConfigKey = 'tdesign-starter-locale';

// 浏览器语言
const browserLanguage = navigator.language.replace('-', '_');

对语言进行处理

typescript
// 获取到所有路径
const fullPaths = Object.keys(langModules);
fullPaths.forEach((fullPath) => {
    // fullPath : ./lang/en_US/index.ts
    const k = fullPath.replace('./lang', '');
    const startIndex = 1;
    const lastIndex = k.lastIndexOf('/');
    const code = k.substring(startIndex, lastIndex);
    // langCode ['en_US']
    langCode.push(code);
    langModuleMap.set(code, langModules[fullPath]);
});

对拿到的语言包进行遍历, 得到和 vue-i18n 相匹配的格式

typescript
const importMessages = computed(() => {
    const message: Recordable = {};
    langModuleMap.forEach((value: any, key) => {
        message[key] = value.default;
    });
    return message;
});

useLocale 便捷函数定义

typescript
import { computed } from 'vue';
import { useI18n } from 'vue-i18n';

// 引入定义的 KEY 以及对象
import { i18n, langCode, localeConfigKey } from '@/locales';

export function useLocale() {
    // 获取 locale
    const { locale } = useI18n({ useScope: 'global' });

    // 改变 locale 并存储
    function changeLocale(lang: string) {
        // 如果切换的语言不在对应语言文件里则默认为简体中文
        if (!langCode.includes(lang)) {
            lang = 'zh_CN';
        }

        locale.value = lang;
        localStorage.setItem(localeConfigKey, lang);
    }

    // 获取指定的 KEY 内容, 指定语言变量的
    const getComponentsLocale = computed(() => {
        return i18n.global.getLocaleMessage(locale.value).componentsLocale;
    });

    return {
        changeLocale,
        getComponentsLocale,
        locale,
    };
}

使用

在 vue 中的使用, 直接使用 $t 函数, 这个函数在定义中被全局注入, 所以可以直接使用

html
<t-step-item
    :title="$t('pages.detailBase.changelog.step3.title')"
    :content="$t('pages.detailBase.changelog.step3.desc')"/>

在 script 中使用, 这里已用 locales 中定义的 i18n

html
<script setup lang="ts">
  import { t } from '@/locales';

  const BASE_INFO_DATA = [
      {
          name: t('constants.contract.name'),
          value: '总部办公用品采购项目',
          type: null,
      }
  ]
</script>

说明

创建时间: 2023-12-16 16:49:00 , 最后编辑于 2024-01-05 12:20:00