文章目录
- 引入
- 问题演示
- 补充逻辑
- 注意
- 封装缓存工具类
- 补充状态管理
- 调整多语言初始化
- 调整多语言切换组件
- 解决方案
- 思路整理
- 渲染进程监听语言切换
- 主进程创建多语言切换处理
- 语言切换组件通知主进程语言切换
- 最终实现效果演示
引入
我们之前在这篇文章中集成了 多语言切换,但随着我们项目越来越复杂,单页面已经无法满足我们的需求,我们需要多个窗口去进行页面展示,此时会暴露很多问题,例如多窗口时,某个窗口切换了语言,其他窗口并不会同步切换
demo项目地址
问题演示
我们在src\components\demo\Index.vue页面中也显示一个多语言文本:
<template><h1>{{ $t(langMap.app_title) }}</h1>
</template>
<script setup lang="ts">import langMap from "@/locales/langMap";
</script>
问题如下:
补充逻辑
注意
这里主要是对之前多语言文章的补充,之前写的是个极简的整合,没有考虑很多,如只需要解决多窗口同步问题可直接跳过!!
封装缓存工具类
首先为了记住我们当前所选语言,避免每次重启重新选择,我们将语言持久化在本地,这里参考这篇博客,封装一个缓存工具类:
- src\utils\cacheUtils.ts
/* localstorage封装,缓存工具类 参考:https://blog.csdn.net/w544924116/article/details/120906411/** key前缀 */
const keyPrefix = 'app_cahce$_';/*** @param value 内容* @param addTime 存入时间* @param expires 有效时间*/
interface valObjParams {value: any;addTime: number;expires: number;
}interface StorageInterface {/*** 设置localStorage* @param value 内容* @param expires 有效时间 单位 s*/set: (key: string, value: any, expires?: number) => void;/** 获取localStorage,会自动转json */get: (key: string) => any;/** 是否含有key */has: (key: string) => boolean;/** 移除 */remove: (key: string) => void;/** 移除全部缓存 */clear: () => void;/** 移除自己前缀的全部缓存 */clearSelf: () => void;
}const storage: StorageInterface = {set: () => {},get: () => '',has: () => false,remove: () => {},clear: () => {},clearSelf: () => {}
};/*** 是否过期*/
const isFresh = (valObj: valObjParams) => {const now = new Date().getTime();return valObj.addTime + valObj.expires >= now;
};/* 给key值添加前缀 */
const addPrefix = (key: string) => {return `${keyPrefix}${key}`;
};/* 加方法 */
const extend = (s: Storage) => {return {set(key: string, value: any, expires?: number) {const skey = addPrefix(key);if (expires) {expires *= 1000; // 将过期时间从微秒转为秒s.setItem(skey,JSON.stringify({value,addTime: new Date().getTime(),expires}));} else {const val = JSON.stringify(value);s.setItem(skey, val);}if (value === undefined || value === null) {s.removeItem(skey);}},get(key: string) {const skey = addPrefix(key);const item = JSON.parse(s.getItem(skey) as any);// 如果有addTime的值,说明设置了失效时间if (item && item.addTime) {if (isFresh(item)) {return item.value;}/* 缓存过期,清除缓存,返回null */s.removeItem(skey);return null;}return item;},has(key: string) {const skey = addPrefix(key);return !!s.getItem(skey);},remove: (key: string) => {const skey = addPrefix(key);s.removeItem(skey);},clear: () => {s.clear();},clearSelf: () => {const arr = Array.from({ length: s.length }, (_, i) => s.key(i)).filter(str => str?.startsWith(keyPrefix));arr.forEach(str => s.removeItem(str as string));}};
};Object.assign(storage, extend(window.localStorage));export default storage;
补充状态管理
我们可以创建一个appStore用来保存整个应用的全局状态:
import { defineStore } from "pinia";
import cacheUtils from "@/utils/cacheUtils";/**应用相关状态管理 */
export const useAppStore = defineStore("appStore", {state() {return {lang: cacheUtils.get("lang") || "zhCn", // app的语言};},
});
调整多语言初始化
- 补充从缓存中取初始值
- src\locales\index.ts
import { createI18n } from "vue-i18n";
import en from "./packages/en";
import zhCn from "./packages/zh-cn";
import cacheUtils from "@/utils/cacheUtils";// 初始化i18n
const i18n = createI18n({legacy: false, // 解决Not available in legacy mode报错globalInjection: true, // 全局模式,可以直接使用 $tlocale: cacheUtils.get("lang") || "zhCn", // 从本地缓存中取语言,如果没有 默认为中文fallbackLocale: "en", // set fallback localemessages: {en,zhCn,},
});export default i18n;
调整多语言切换组件
- 我们在语言切换的时候补充状态更新、缓存设置
import cacheUtils from "@/utils/cacheUtils";
import { useAppStore } from "@/store/modules/appStore";const appStore = useAppStore();// 切换语言
function handleCommand(lang: string) {i18n.locale.value = lang;// 设置缓存的值cacheUtils.set("lang", lang);// 更新全局状态appStore.lang = lang;
}
解决方案
思路整理
我们可以在多语言初始化的时候,让渲染进程监听多语言改变消息,然后主进程创建一个多语言改变handle,然后在语言切换组件中当语言切换时告知主进程语言切换了,并传参当前的语言,接着主进程的handle遍历所有窗口,除通知主进程的窗口外的其他窗口都触发 多语言改变通知,然后窗口自行更新即可。
渲染进程监听语言切换
1.我们在多语言初始化时监听多语言切换通知:
- 调整src\locales\index.ts代码:
import { useAppStore } from "@/store/modules/appStore";
import { ipcRenderer } from "electron";// ......// 注意,因为 pinia还没初始化就进行取值会有问题,所以这里我们单独暴露一个方法,在 src/main.ts中的 app.mount("#app").$nextTick 中调用
// 初始化语言监听
export function initLangListener() {const appStore = useAppStore();// 监听语言切换时,同步本窗口更新ipcRenderer.on("lang:change", (event, lang: string) => {i18n.global.locale.value = lang;appStore.lang = lang;});
}
2.在src/main.ts中执行初始化监听:
import { initLangListener } from "@/locales";
// .....app.mount("#app").$nextTick(() => {postMessage({ payload: "removeLoading" }, "*");// 初始化多语言切换监听initLangListener();
});
主进程创建多语言切换处理
主进程中创建多语言处理监听,我们在electron\main\index.ts中补充代码:
/**语言修改同步 */
ipcMain.handle("lang:change", (event, lang) => {// 通知所有窗口同步更改语言for (const currentWin of BrowserWindow.getAllWindows()) {const webContentsId = currentWin.webContents.id;// 这里排除掉发送通知的窗口if (webContentsId !== event.sender.id) {currentWin.webContents.send("lang:change", lang);}}
});
语言切换组件通知主进程语言切换
当语言切换时,我们需要通知主进程告诉其他窗口同步修改,所以我们调整 多语言切换组件:
import { ipcRenderer } from 'electron';// 多语言切换时
const handleCommand = (lang: string) => {// ...// 主进程通知其他窗口同步修改语言ipcRenderer.invoke('lang:change', lang);
};