You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何基于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类型,避免拼写错误:

  1. 安装开发依赖:npm install -D i18next-parser
  2. 创建配置文件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,
};
  1. 在package.json添加脚本:
{
  "scripts": {
    "extract-translations": "i18next-parser"
  }
}
  1. 运行脚本生成类型后,在共享配置中引入:
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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 12:27:37