文章目录
- 引入
- 问题演示
- 补充逻辑
- 注意
- 封装缓存工具类
- 补充状态管理
- 调整多语言初始化
- 调整多语言切换组件
 
- 解决方案
- 思路整理
- 渲染进程监听语言切换
- 主进程创建多语言切换处理
- 语言切换组件通知主进程语言切换
 
- 最终实现效果演示
 
引入
我们之前在这篇文章中集成了 多语言切换,但随着我们项目越来越复杂,单页面已经无法满足我们的需求,我们需要多个窗口去进行页面展示,此时会暴露很多问题,例如多窗口时,某个窗口切换了语言,其他窗口并不会同步切换
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, // 全局模式,可以直接使用 $t
  locale: cacheUtils.get("lang") || "zhCn", // 从本地缓存中取语言,如果没有 默认为中文
  fallbackLocale: "en", // set fallback locale
  messages: {
    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);
};

最终实现效果演示




















