小程序切换语言封装
最近做的一个小程序的项目, 涉及到了多语言的切换, 就想到了之前vue用的多语言插件i18n, 就尝试着在微信开放社区搜了一下, 没有具体的实现, 但是提供了大致的实现思路, 如下:
又结合了很多大佬的分享经验, 试着去封装了一个微信的i18n方法
🧐如何实现?
首先, 我们需要明确一下需要实现的功能
- 加载翻译文件的方法
loadTranslations
为i18n加载翻译文件(翻译文件单独抽离出来) - 设置默认语言的方法
setLocale
用来设置一个默认的语言 - 获取当前语言的方法
getLocale
用来获取当前设置的语言 - 切换当前语言的方法
toggleLanguage
用来切换当前的语言 - 获取当前语言对应的翻译文件
getLanguage
用来获取对应语言的翻译文件 - 将翻译注入页面/组件的方法
effect
用来使页面/组件拿到当前语言对应的翻译文件 - 动态添加语言的方法
mergeTranslations
可以动态的添加语言
具体实现代码如下:
// i18n.js
class t {
locale;
locales;
context;
constructor(t) {
this.locale = "", this.locales = t || {}
}
setLocale(t) {
this.locale = t
}
getLocale() {
return this.locale
}
loadTranslations(t) {
return this.locales = t, this.context && this.effect(this.context)
}
mergeTranslations(t) {
for (const e in t) this.locales[e] ? this.locales[e] = { ...this.locales[e], ...t[e] } : this.locales[e] = t[e];
return this.context && this.effect(this.context)
}
getLanguage() {
return this.locales[this.locale]
}
effect(t) {
if (this.context = t, t.setData) return new Promise((e => { t.setData({ $t: this.getLanguage() }, (() => { e(t.$t) })) }))
}
toggleLanguage(t) {
return this.setLocale(t), this.effect(this.context)
}
}
const e = new t;
export { t as I18n, e as default, e as i18nInstance };
🧐如何使用?
-
首先我们要创建对应的翻译文件
根目录下创建lang
文件夹, 用来放翻译的文件, 有几种语言就分别创建几种语言对应的js文件, 以及一个入口文件index.js
我需要四种语言, 所以要有四种语言对应的翻译文案, 以及一个入口文件, 文件具体内容如下:
语言对应的翻译文件, 导出一个大的对象, 大对象里面是一个一个模块的小的对象, 接着就是用到的翻译文案, 因此这四个文件需要尽可能保持一致, 不然就会导致某些文案没有对应的语言翻译 -
我们要将这些文件, 放到入口文件里统一处理:
import cnLocale from './cn';
import twLocale from './tw';
import enLocale from './en';
import ptLocale from './pt';
const messages = {
cn: {
...cnLocale,
},
tw: {
...twLocale,
},
en: {
...enLocale,
},
pt: {
...ptLocale,
}
};
export default messages;
- 初始化插件
// app.js
import message from "./lang/index";
import { i18nInstance, I18n } from "./utils/i18n";
App({
renderI18nData: {},
globalData: {
currentLanguage: '',
},
onLaunch() {
/**
* i18nInstance.loadTranslations(); ->load translations for i18nInstance
* i18nInstance.setLocale(); ->set default language
* i18nInstance.getLocale(); ->get current language name
* i18nInstance.toggleLanguage(); ->toggle language quickly
* i18nInstance.effect(context: any); ->必须指定page和components传递的context为this, 该方法会在this.data中设置一个$t属性
* i18nInstance.mergeTranslations(); ->merge new locales into origin locales
*/
const curL = this.getCurrentLanguage();
i18nInstance.setLocale(curL);
i18nInstance.loadTranslations(message);
i18nInstance.effect(this);
this.watch('currentLanguage',()=> {
this.eachRenderI18nCallback();
})
},
getCurrentLanguage() {
return wx.getStorageSync('appLanguage') || i18nInstance.getLocale() || 'tw';;
},
switchLanguage(language) {
wx.setStorageSync('appLanguage', language);
i18nInstance.toggleLanguage(language);
this.setGlobalData('currentLanguage',language);
},
setGlobalData(keyName,value) {
this.globalData[keyName] = value;
},
watch(keyName,method = ()=> {},value) {
let globalModel = this.globalData;
Object.defineProperty(globalModel,keyName,{
configurable: true,
enumerable: true,
set: function (_value) {
value = _value;
method(value);
},
get: function () {
return value;
}
})
},
getCurrentI18n() {
let lanModel = new I18n;
const curL = i18nInstance.getLocale() || 'tw';
lanModel.setLocale(curL);
lanModel.loadTranslations(message);
return lanModel;
},
renderI18nData(_this,callback,type) {
const recordKey = _this.__wxExparserNodeId__ || _this.is || _this.route;
_this.setData({$t: i18nInstance.getLanguage()});
this.renderI18nData[recordKey] = {
_this: _this,
callback: callback
};
},
eachRenderI18nCallback() {
for (const key in this.renderI18nData) {
if (Object.hasOwnProperty.call(this.renderI18nData, key)) {
const element = this.renderI18nData[key];
element._this.setData({$t: i18nInstance.getLanguage()});
element.callback && element.callback();
}
}
},
handleLanguageChange() {
// 获取当前页面实例,并重新渲染页面
const pages = getCurrentPages();
pages.forEach(page => {
if (page.renderPage) {
page.renderPage();
}
})
}
})
之所以这么实现, 是因为page和component一起使用多语言时候会出问题, 传入了两个或者多个this, 导致i18n不知道该将翻译文件注入到哪里, 进而导致无法切换语言, 所以放到了 app.js
里面统一处理, 也更加符合逻辑;
- page/component 的具体使用
// page的使用
---------xxx.js
import i18nInstance from '../../utils/i18n';
onLoad(options) {
i18nInstance.effect(this);
...
// js 里通过 this.data.$t.route.test; (举个🌰) 来获取翻译文件
}
---------xxx.wxml
<text>{{ $t.route.test}}</text>
// component的使用, 与page只有初始化不同, 使用翻译文件方法相同
---------xxx.js
const app = getApp();
Component({
options: {
addGlobalClass: true
},
properties: {},
ready() {
app.renderI18nData(this, ()=>{});
}
})
因为没有vue的重定向组件用来重新加载页面, 我们需要在切换语言的页面手动刷新接口并把当前的语言传递给服务端
我用的方法是, 把当前的语言添加到请求头, 这样下发的数据就是对应语言的数据啦
import i18nInstance from '../utils/i18n';
axios.interceptors.request.use((request) => {
const currentLe = i18nInstance.getLocale();
const languageMap = {
cn: 'zh_CN',
tw: 'zh_TW',
en: 'en_US',
pt: 'pt_PT'
}
request.headers['lang'] = languageMap[currentLe];
...
}, (error) => {
return Promise.reject(error);
})
🎉🎉
最后, 我们的小程序就能正常的切换多语言啦🥳
欢迎大家一起讨论学习😊~