国际化
动态国际化组件
基于 vue-i18n
封装的动态国际化组件
使用步骤
- 1、dyn-i18n.ts
// dyn-i18n.ts
import { type I18n, type I18nOptions, createI18n } from 'vue-i18n'
import type { WritableComputedRef } from 'vue'
export interface DynI18nOptions {
langKey?: string
defaultLang?: string
resourceUrl?: string
}
export interface LangList {
name: string
fileName: string
isLoaded?: boolean
}
export class DynI18n {
private _i18n: I18n
private _langKey = 'lang' // 本地缓存的语言设置字段名
private _lang = 'zh-CN'
private _defaultLang = 'zh-CN'
private _resourceUrl = '/langs'
private _langs: Array<LangList> = []
private _isLoadedLang = false
// step1: 实例化
constructor(options?: DynI18nOptions, i18nOptions?: I18nOptions) {
if (options?.langKey) {
this._langKey = options.langKey
}
if (options?.defaultLang) {
this._defaultLang = options.defaultLang
}
if (options?.resourceUrl) {
this._resourceUrl = options.resourceUrl
}
// 首先检查 localStorage 中保存的 lang ,再检查 浏览器首选语言,最后再设置默认语言
const cacheLang = localStorage.getItem(this._langKey) as string
if (cacheLang && cacheLang !== 'null') {
this._lang = cacheLang
} else {
const locale = navigator.language
if (locale) {
this._lang = locale
localStorage.setItem(this._langKey, this._lang)
}
}
// 初始化 vue-i18n
this._i18n = createI18n(
Object.assign(
{
legacy: false,
globalInjection: true,
fallbackLocale: this._defaultLang,
locale: this._lang,
},
i18nOptions
)
)
}
// step2: 初始化获取配置文件和当前语言包
async init() {
if (await this._loadLang(this._lang)) {
// 设置 vue-i18n 语言设置
;(this._i18n.global.locale as WritableComputedRef<string>).value =
this._lang
}
// 初始化的时候如果当前语言和默认语言不一致,加载一下默认语言
if (this._lang !== this._defaultLang) {
await this._loadLang(this._defaultLang)
}
this._isLoadedLang = true
}
// step3: 修改语言
async setLang(val: string) {
if (val !== this._lang) {
this._lang = val
this._isLoadedLang = false
// 请求语言包
if (await this._loadLang(val)) {
this._isLoadedLang = true
// 设置 vue-i18n 语言设置
;(this._i18n.global.locale as WritableComputedRef<string>).value = val
localStorage.setItem(this._langKey, val)
}
}
}
get i18n() {
return this._i18n
}
// 获取当前语言
get lang() {
return this._lang
}
// 获取当前语言列表
get langs() {
return this._langs
}
// 是否已经加载了语言包
get isLoaded() {
return this._isLoadedLang
}
// 读取 config.json 获取支持的语言列表
private async _loadLangs() {
if (!this._langs.length) {
const resJSON = await fetch(`${this._resourceUrl}/config.json`)
if (resJSON) {
const res = await resJSON.json()
if (res && res.langs && res.langs.length) {
this._langs = res.langs
}
}
}
return this._langs
}
// 加载语言包
private async _loadLang(lang: string) {
// 先检查语言列表中有没有当前设置的语言
const list = await this._loadLangs()
const currentLangItem = list.find((v) => v.fileName === lang)
if (list && list.length && currentLangItem) {
if (!currentLangItem.isLoaded) {
const resJSON = await fetch(`${this._resourceUrl}/${lang}.json`)
if (resJSON) {
const res = await resJSON.json()
if (res && this._i18n) {
this._i18n.global.mergeLocaleMessage(lang, res)
currentLangItem.isLoaded = true
}
}
}
return true
}
return false
}
}
export default DynI18n
- 2、实例化
// /src/utils/i18n.ts
import DynI18n from '@/utils/dynI18n'
// 默认读取当前项目下 /langs 中的 config.json 和 语言包 文件,默认语言为 zh-CN ,默认存储在 localStorage 中的国际化 key 为 lang
const dynI18n = new DynI18n()
export default dynI18n
- 3、注册
// /src/main.ts
import dynI18n from '@/utils/i18n'
app.use(dynI18n.i18n)
- 4、加载语言
在路由拦截中判断 dynI18n.isLoaded 是否已加载,如果已加载直接 next,如果未加载 await 加载语言包。
// router.ts
router.beforeEach(async (to, from, next) => {
document.title = to.meta.title
if (!dynI18n.isLoaded) {
await dynI18n.init()
}
next()
})
- 5、切换语言
import dynI18n from '@/utils/i18n'
await dynI18n.setLang('en-US')
config.json 文件说明
{
"langs": [
{
"name": "中文简体",
"fileName": "zh-CN"
},
{
"name": "English",
"fileName": "en-US"
}
]
}
表示当前项目有 2 种语言可选择,分别是 中文简体
和 English
,fileName 是语言包在 resourceUrl 下的文件名
推荐一个 i18n 在 vscode 中的辅助插件: i18n-ally
对于 json 文件支持度非常高,可以做到在源码中察看结果、修改语言信息、分析翻译覆盖度等。
// vscode 配置文件 setting.json
"i18n-ally.localesPaths": ["app/public/langs"],
"i18n-ally.extract.autoDetect": true,
"i18n-ally.sourceLanguage": "zh-CN",
"i18n-ally.displayLanguage": "zh-CN",
"i18n-ally.sortKeys": false,
"i18n-ally.enabledParsers": ["ts", "json"],
"i18n-ally.enabledFrameworks": ["vue", "vue-sfc"],
"i18n-ally.keystyle": "nested",
"i18n-ally.ignoreFiles": ["**/config.json"],