如何基于electron-vite+React+TypeScript实现Electron全进程i18n?
Electron + React + TypeScript 全进程国际化(i18n)实现指南
一、主/渲染进程i18n统一配置指南
1. 安装核心依赖
npm install i18next react-i18next i18next-fs-backend i18next-http-backend electron-store @types/i18next-fs-backend
2. 共享基础配置文件
在src/shared/i18n/config.ts定义通用规则,让主/渲染进程复用:
import i18next from 'i18next'; export const defaultNS = 'common'; export const resources = { en: { common: require('../../public/locales/en/common.json'), main: require('../../public/locales/en/main.json'), // 主进程专属翻译 renderer: require('../../public/locales/en/renderer.json'), // 渲染进程专属翻译 }, zh: { common: require('../../public/locales/zh/common.json'), main: require('../../public/locales/zh/main.json'), renderer: require('../../public/locales/zh/renderer.json'), }, } as const; export const initI18nBase = (i18nInstance: typeof i18next, backendOptions: any) => { return i18nInstance.init({ lng: 'zh', // 默认语言 fallbackLng: 'en', defaultNS, ns: ['common', 'main', 'renderer'], resources, backend: backendOptions, interpolation: { escapeValue: false, // React已处理XSS,主进程无需额外处理 }, }); };
3. 主进程i18n初始化
在src/main/i18n.ts完成主进程初始化,结合本地存储同步语言设置:
import i18next from 'i18next'; import Backend from 'i18next-fs-backend'; import { app } from 'electron'; import Store from 'electron-store'; import { initI18nBase } from '../../shared/i18n/config'; const store = new Store(); const savedLng = store.get('app.language') as string | undefined; export const initMainI18n = async () => { await initI18nBase(i18next, { loadPath: `${app.getAppPath()}/public/locales/{{lng}}/{{ns}}.json`, // 适配electron-vite打包路径 }); // 恢复用户上次设置的语言 if (savedLng) { await i18next.changeLanguage(savedLng); } // 语言变化时同步到本地存储,并通知所有渲染进程 i18next.on('languageChanged', (lng) => { store.set('app.language', lng); globalThis.mainWindow?.webContents.send('language-changed', lng); }); return i18next; }; // 导出翻译函数,供主进程其他模块调用 export const t = i18next.t;
4. 渲染进程i18n初始化
在src/renderer/i18n.ts完成React环境的初始化:
import i18next from 'i18next'; import { initReactI18next } from 'react-i18next'; import Backend from 'i18next-http-backend'; import { ipcRenderer } from 'electron'; import { initI18nBase } from '../../shared/i18n/config'; export const initRendererI18n = async () => { await initI18nBase(i18next, { loadPath: '/locales/{{lng}}/{{ns}}.json', // 从public目录加载翻译文件 }); i18next.use(initReactI18next); // 监听主进程的语言切换通知 ipcRenderer.on('language-changed', (_event, lng) => { i18next.changeLanguage(lng); }); return i18next; }; // 导出React组件可用的hooks export { useTranslation, Trans } from 'react-i18next';
在渲染进程入口src/renderer/main.tsx中启动初始化:
import React from 'react'; import ReactDOM from 'react-dom/client'; import App from './App'; import { initRendererI18n } from './i18n'; (async () => { await initRendererI18n(); ReactDOM.createRoot(document.getElementById('root')!).render( <React.StrictMode> <App /> </React.StrictMode>, ); })();
二、主进程TypeScript使用示例
1. 菜单翻译示例
在src/main/menu.ts中用翻译函数生成国际化菜单:
import { t } from './i18n'; import { Menu, MenuItemConstructorOptions } from 'electron'; export const createAppMenu = () => { const template: MenuItemConstructorOptions[] = [ { label: t('main:menu.file'), submenu: [ { label: t('main:menu.file.open'), click: () => { /* 打开文件逻辑 */ }, }, { type: 'separator' }, { label: t('main:menu.file.exit'), role: 'quit', }, ], }, { label: t('main:menu.view'), submenu: [ { role: 'reload' }, { role: 'toggleDevTools' }, ], }, ]; const menu = Menu.buildFromTemplate(template); Menu.setApplicationMenu(menu); };
2. 语言切换IPC处理
在src/main/ipcHandlers.ts中处理渲染进程发起的语言切换请求:
import { ipcMain } from 'electron'; import i18next from 'i18next'; ipcMain.handle('change-language', async (_event, lng: string) => { await i18next.changeLanguage(lng); return i18next.language; });
3. 系统对话框翻译
主进程调用系统对话框时使用翻译文本:
import { dialog } from 'electron'; import { t } from './i18n'; export const showConfirmDialog = async () => { const result = await dialog.showMessageBox(globalThis.mainWindow!, { title: t('main:dialog.confirm.title'), message: t('main:dialog.confirm.message'), buttons: [t('common:cancel'), t('common:confirm')], }); return result.response === 1; };
三、结合electron-vite的翻译管理最佳实践
1. 标准化翻译文件目录
统一将翻译文件放在public/locales,按语言和命名空间划分:
public/ └── locales/ ├── en/ │ ├── common.json │ ├── main.json │ └── renderer.json └── zh/ ├── common.json ├── main.json └── renderer.json
2. 确保翻译文件被打包
在electron.vite.config.ts中配置public目录,确保翻译文件被复制到打包产物:
import { defineConfig } from 'electron-vite'; import react from '@vitejs/plugin-react'; export default defineConfig({ main: {}, renderer: { plugins: [react()], publicDir: 'public', // 强制复制public目录到渲染进程资源 }, });
3. 类型安全的翻译键
使用i18next-parser生成翻译键的TypeScript类型,避免拼写错误:
- 安装开发依赖:
npm install -D i18next-parser - 创建配置文件
i18next-parser.config.js:
module.exports = { locales: ['en', 'zh'], output: 'src/shared/i18n/types.ts', input: ['src/**/*.{ts,tsx}'], defaultValue: (lng, ns, key) => key, typeScript: true, };
- 在
package.json添加脚本:
{ "scripts": { "extract-translations": "i18next-parser" } }
- 运行脚本生成类型后,在共享配置中引入:
import type { Resources } from './types'; export const resources = { // ... 原有内容 } as Resources;
4. 开发时热更新
在渲染进程配置中添加监听,修改翻译文件后自动刷新:
// electron.vite.config.ts -> renderer配置 server: { watch: { include: ['public/locales/**/*.json'], }, }
内容的提问来源于stack exchange,提问作者VQH DEV
相关产品推荐
相关产品推荐

