【大前端】50行代码,教你实现轻量级 i18n 翻译插件
你还在使用 i18n 实现网站国际化吗?那你也 太OUT了
今天教你 50行 代码实现轻量级i18n 插件
第一步
首先你需要清楚i18n 插件主要用来干了什么事,它的原理是什么
答案:就是把代码中的文本翻译成我们想要的语言,而要想实现翻译成对应的语言,就需要有一个语言映射表,所以我们首先需要设置一个语言映射表,代码如下:
typescript
import type { I18nTranslateRawOptions } from "..";
export default [
{
source: "$nbsp",
translate: " ",
},
{
source: "邮编",
translate: "Postal Code",
},
{
source: "地址",
translate: "Address",
},
] as Array<I18nTranslateRawOptions>;点上面代码就是映射了代码中对应的中文映射对应的英文
第二步
有了映射表,我们就需要解决如何实现映射
答案:传统的i18n插件都是在代码中写入一个key,然后通过key去获取当前语言的文本,但是这不是我想要的,因为太麻烦了
我想要的就是,我只需要在需要翻译的地方将需要翻译的文本传给插件,插件给我翻译成对应的语言,如果匹配不到就不翻译,我不需要关心他当前是哪个key
这样是不是就简单很多,那么问题来了,如何实现呢?
代码如下:
typescript
import zhCn from "./lang/zh-cn";
import en from "./lang/en";
import CryptoJS from "crypto-js";
import { shallowRef } from "vue";
export interface I18nTranslateRawOptions {
source: string;
translate: string;
}
export interface I18nLang {
name: string;
list: I18nTranslateRawOptions[];
}
// 编码需要翻译的文本,确保匹配到对应的翻译结果
const encodeSource = (source: string) => {
return CryptoJS.MD5(source).toString();
};
const _langs: Record<string, I18nLang> = {
zhCn: {
name: "简体中文",
list: zhCn,
},
en: {
name: "英文",
list: en,
},
};
const I18N_STORE_KEY = "PORTAL_I18N";
class I18n {
private _activeLang = shallowRef<Record<string, string>>({});
private _langMap: Record<string, Record<string, string>> = {};
constructor(langMap: Record<string, I18nLang>, defaultLang: string) {
// 转换语言数据,为key:value 形式 的映射报表
Object.keys(langMap).forEach((langKey) => {
const _lang = langMap[langKey].list.reduce(
(map: Record<string, string>, item) => {
// 储存编码后的key,方便数据检索
map[encodeSource(item.source)] = item.translate;
return map;
},
{}
);
this._langMap[langKey] = _lang;
});
// 切换到浏览器中缓存的语言
this.change(localStorage.getItem(I18N_STORE_KEY) || defaultLang);
}
// 更换语言
change(lang: string) {
// 设置当前语言标识,方便代码中根据不同语言设置不同的样式
document.body.classList.remove(
`lang-${localStorage.getItem(I18N_STORE_KEY)}`
);
document.body.classList.add(`lang-${lang}`);
// 切换当前语言映射表,并缓存当前语言
this._activeLang.value = this._langMap[lang] || {};
localStorage.setItem(I18N_STORE_KEY, lang);
}
// 翻译
translate(source: string) {
const _res = this._activeLang.value[encodeSource(source)];
// 匹配不到,则返回原始文本,匹配成功,返回翻译结果
return _res === undefined ? source : _res;
}
}
// 创建实例
const i18n = new I18n(_langs, localStorage.getItem(I18N_STORE_KEY) || "zhCn");
// 当前配置的语言列表,方便渲染语言切换列表
export const i18nLangList = Object.keys(_langs).map((langKey) => {
return {
name: _langs[langKey].name,
key: langKey,
};
});
// 通过hook调用实例,方便支持react、vue
export function useI18n() {
return i18n;
}最后一步:翻译
以下使用 vue 框架示例:
html
<template>
<title-card>
<template #title>
{{ useI18n().translate("近期") }}
<span v-html="useI18n().translate('$nbsp')"></span>
<span style="color: #1e50ae">{{
useI18n().translate("热点")
}}</span>
</template>
</title-card>
</template>
<script setup lang="ts">
import { useI18n } from "@/plugins/i18n";
useI18n().change('en')
</script>
<style scoped lang="scss">
</style>总结
1️⃣ 支持翻译异步数据, 2️⃣ 轻量级,50行代码