前言
项目使用 Vue3 + TS,使用 vue-i18n 实现翻译切换,实现流程为:
安装 => main.ts 引入 => 配置文件 => 使用
tips: 本文将 locale 存储于 localStorage 中
实现
- 安装
运行
pnpn install vue-i18n - main.ts 引入
import { createApp } from 'vue';
import { createPinia } from 'pinia';
import type { PiniaPluginContext } from 'pinia';
import { cloneDeep } from 'lodash-es';
import router from './router';
import App from './app.vue';
import i18n from '@/i18n';
import '@/styles/global.scss';
const pinia = createPinia();
const app = createApp(App);
function resetPlugin(ctx: PiniaPluginContext) {
const store = ctx.store;
const initialState = cloneDeep(store.$state);
store.$reset = () => {
store.$patch(($state) => {
Object.assign($state, cloneDeep(initialState));
});
};
store.$destroy = () => {
store.$dispose();
delete pinia.state.value[store.$id];
};
}
pinia.use(resetPlugin);
app.use(i18n).use(pinia).use(router).mount('#app');
- 配置文件
// i18n/locales/en_US.ts
export default {
error: {
404: 'Resource not found',
},
}
// i18n/locales/zh-CN.ts
export default {
error: {
404: '资源未找到',
},
}
import { type I18n, createI18n } from 'vue-i18n';
import type { AvailableLocale, MessageSchema } from '@/models/i18n';
import { STORAGE_KEYS, I18N_MESSAGES } from '@/constants';
function saveLocaleToLocalStorage(val: AvailableLocale) {
localStorage.setItem(STORAGE_KEYS.LOCALE_KEY, val);
}
/* 从浏览器偏好设置读取的语言可能是 en, en-US, zh, zh-CN, zh-HK 等格式、最终返回的是"zh-CN"形式(这与message的设置有关) */
function getSimilarLocale(val: unknown) {
if (typeof val !== 'string') {
return;
}
// 校验语言格式
const localeSplitArr = val.split('-');
const lang = localeSplitArr[0];
const country = localeSplitArr[localeSplitArr.length - 1];
// lang 格式 e.g. en, zh
if (!country) {
// 根据 lang 找近似语言
for (const key in I18N_MESSAGES) {
const [itemLang] = key.split('-');
if (itemLang.toLowerCase() === lang.toLowerCase()) {
return key as AvailableLocale; // 推断不了字面量, 只能断言
}
}
return;
}
// lang-country 格式, e.g. en-US, zh-CN
for (const key in I18N_MESSAGES) {
if (key.toLowerCase() === val.toLowerCase()) {
return key as AvailableLocale; // 推断不了字面量, 只能断言
}
}
}
/* 获取默认语言:优先从缓存读,其次从浏览器偏好设置读 */
function getDefaultLocale() {
// 从缓存读
const cacheLocale = getSimilarLocale(localStorage.getItem(STORAGE_KEYS.LOCALE_KEY));
if (cacheLocale) {
return cacheLocale;
}
let result: AvailableLocale = 'en-US';
// 从浏览器偏好设置读
const browserLocale = getSimilarLocale(navigator.language);
if (browserLocale) {
result = browserLocale;
}
// 保存到缓存
saveLocaleToLocalStorage(result);
return result;
}
const defaultLocale = getDefaultLocale();
const i18n = createI18n<[MessageSchema], AvailableLocale>({
locale: defaultLocale, // 默认使用的语言
fallbackLocale: defaultLocale, // 未找到对应语言时使用的语言
legacy: true, // 启用传统模式,这意味着插件将使用旧版的 Vue 2 API。
globalInjection: false, // 是否将 $i18n 注入到全局 Vue 实例中
fallbackRoot: true, // 如果未找到翻译,是否回退到根语言
messages: I18N_MESSAGES, // 语言包
}) as I18n<typeof I18N_MESSAGES, {}, {}, AvailableLocale, true>; // createI18n 返回值类型推断有问题, 必须用断言
export const { t } = i18n.global;
/* 切换语言 */
export function toggle(val: AvailableLocale) {
if (val === i18n.global.locale) {
return;
}
saveLocaleToLocalStorage(val);
i18n.global.locale = val;
location.reload();
}
export const languages: Record<AvailableLocale, string> = {
'en-US': 'English',
'zh-CN': '简体中文',
};
export default i18n;
- 使用
-
- 正常使用
<script lang="ts" setup>
import { onMounted } from 'vue';
import { t } from '@/i18n';
onMounted(() => {});
</script>
<template>
<div class="not-found-page">
404({{ t('error.404') }})
</div>
</template>
-
- 语言切换
通过
i18n.global.locale切换
- 语言切换
通过
/* 切换语言 */
export function toggle(val: AvailableLocale) {
if (val === i18n.global.locale) {
return;
}
saveLocaleToLocalStorage(val);
i18n.global.locale = val;
location.reload();
}
``