Skip to content

Commit

Permalink
feat: add i18n support with English translations
Browse files Browse the repository at this point in the history
  • Loading branch information
pompurin404 committed Feb 3, 2025
1 parent fa3c412 commit 8210b47
Show file tree
Hide file tree
Showing 72 changed files with 2,500 additions and 756 deletions.
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,9 @@
"crypto-js": "^4.2.0",
"dayjs": "^1.11.13",
"express": "^5.0.1",
"i18next": "^24.2.2",
"iconv-lite": "^0.6.3",
"react-i18next": "^15.4.0",
"webdav": "^5.7.1",
"ws": "^8.18.0",
"yaml": "^2.6.0"
Expand Down
55 changes: 55 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 11 additions & 7 deletions src/main/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import path from 'path'
import { startMonitor } from './resolve/trafficMonitor'
import { showFloatingWindow } from './resolve/floatingWindow'
import iconv from 'iconv-lite'
import { initI18n } from '../shared/i18n'
import i18next from 'i18next'

let quitTimeout: NodeJS.Timeout | null = null
export let mainWindow: BrowserWindow | null = null
Expand Down Expand Up @@ -48,8 +50,8 @@ if (process.platform === 'win32' && !is.dev && !process.argv.includes('noadmin')
// ignore
}
dialog.showErrorBox(
'首次启动请以管理员权限运行',
`首次启动请以管理员权限运行\n${createErrorStr}\n${eStr}`
i18next.t('main.error.adminRequired'),
`${i18next.t('main.error.adminRequired')}\n${createErrorStr}\n${eStr}`
)
} finally {
app.exit()
Expand Down Expand Up @@ -122,9 +124,11 @@ app.whenReady().then(async () => {
// Set app user model id for windows
electronApp.setAppUserModelId('party.mihomo.app')
try {
const appConfig = await getAppConfig()
await initI18n({ lng: appConfig.language })
await initPromise
} catch (e) {
dialog.showErrorBox('应用初始化失败', `${e}`)
dialog.showErrorBox(i18next.t('main.error.initFailed'), `${e}`)
app.quit()
}
try {
Expand All @@ -133,7 +137,7 @@ app.whenReady().then(async () => {
await initProfileUpdater()
})
} catch (e) {
dialog.showErrorBox('内核启动出错', `${e}`)
dialog.showErrorBox(i18next.t('main.error.coreStartFailed'), `${e}`)
}
try {
await startMonitor()
Expand Down Expand Up @@ -174,18 +178,18 @@ async function handleDeepLink(url: string): Promise<void> {
const profileUrl = urlObj.searchParams.get('url')
const profileName = urlObj.searchParams.get('name')
if (!profileUrl) {
throw new Error('缺少参数 url')
throw new Error(i18next.t('main.error.urlParamMissing'))
}
await addProfileItem({
type: 'remote',
name: profileName ?? undefined,
url: profileUrl
})
mainWindow?.webContents.send('profileConfigUpdated')
new Notification({ title: '订阅导入成功' }).show()
new Notification({ title: i18next.t('main.notification.importSuccess') }).show()
break
} catch (e) {
dialog.showErrorBox('订阅导入失败', `${url}\n${e}`)
dialog.showErrorBox(i18next.t('main.error.importFailed'), `${url}\n${e}`)
}
}
}
Expand Down
42 changes: 24 additions & 18 deletions src/main/resolve/tray.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,16 @@ import { dataDir, logDir, mihomoCoreDir, mihomoWorkDir } from '../utils/dirs'
import { triggerSysProxy } from '../sys/sysproxy'
import { quitWithoutCore, restartCore } from '../core/manager'
import { floatingWindow, triggerFloatingWindow } from './floatingWindow'
import { t } from 'i18next'

export let tray: Tray | null = null

export const buildContextMenu = async (): Promise<Menu> => {
// 添加调试日志
console.log('Current translation for tray.showWindow:', t('tray.showWindow'))
console.log('Current translation for tray.hideFloatingWindow:', t('tray.hideFloatingWindow'))
console.log('Current translation for tray.showFloatingWindow:', t('tray.showFloatingWindow'))

const { mode, tun } = await getControledMihomoConfig()
const {
sysProxy,
Expand Down Expand Up @@ -86,7 +92,7 @@ export const buildContextMenu = async (): Promise<Menu> => {
{
id: 'show',
accelerator: showWindowShortcut,
label: '显示窗口',
label: t('tray.showWindow'),
type: 'normal',
click: (): void => {
showMainWindow()
Expand All @@ -95,15 +101,15 @@ export const buildContextMenu = async (): Promise<Menu> => {
{
id: 'show-floating',
accelerator: showFloatingWindowShortcut,
label: floatingWindow?.isVisible() ? '关闭悬浮窗' : '显示悬浮窗',
label: floatingWindow?.isVisible() ? t('tray.hideFloatingWindow') : t('tray.showFloatingWindow'),
type: 'normal',
click: async (): Promise<void> => {
await triggerFloatingWindow()
}
},
{
id: 'rule',
label: '规则模式',
label: t('tray.ruleMode'),
accelerator: ruleModeShortcut,
type: 'radio',
checked: mode === 'rule',
Expand All @@ -117,7 +123,7 @@ export const buildContextMenu = async (): Promise<Menu> => {
},
{
id: 'global',
label: '全局模式',
label: t('tray.globalMode'),
accelerator: globalModeShortcut,
type: 'radio',
checked: mode === 'global',
Expand All @@ -131,7 +137,7 @@ export const buildContextMenu = async (): Promise<Menu> => {
},
{
id: 'direct',
label: '直连模式',
label: t('tray.directMode'),
accelerator: directModeShortcut,
type: 'radio',
checked: mode === 'direct',
Expand All @@ -146,7 +152,7 @@ export const buildContextMenu = async (): Promise<Menu> => {
{ type: 'separator' },
{
type: 'checkbox',
label: '系统代理',
label: t('tray.systemProxy'),
accelerator: triggerSysProxyShortcut,
checked: sysProxy.enable,
click: async (item): Promise<void> => {
Expand All @@ -165,7 +171,7 @@ export const buildContextMenu = async (): Promise<Menu> => {
},
{
type: 'checkbox',
label: '虚拟网卡',
label: t('tray.tun'),
accelerator: triggerTunShortcut,
checked: tun?.enable ?? false,
click: async (item): Promise<void> => {
Expand All @@ -190,7 +196,7 @@ export const buildContextMenu = async (): Promise<Menu> => {
{ type: 'separator' },
{
type: 'submenu',
label: '订阅配置',
label: t('tray.profiles'),
submenu: items.map((item) => {
return {
type: 'radio',
Expand All @@ -208,34 +214,34 @@ export const buildContextMenu = async (): Promise<Menu> => {
{ type: 'separator' },
{
type: 'submenu',
label: '打开目录',
label: t('tray.openDirectories.title'),
submenu: [
{
type: 'normal',
label: '应用目录',
label: t('tray.openDirectories.appDir'),
click: (): Promise<string> => shell.openPath(dataDir())
},
{
type: 'normal',
label: '工作目录',
label: t('tray.openDirectories.workDir'),
click: (): Promise<string> => shell.openPath(mihomoWorkDir())
},
{
type: 'normal',
label: '内核目录',
label: t('tray.openDirectories.coreDir'),
click: (): Promise<string> => shell.openPath(mihomoCoreDir())
},
{
type: 'normal',
label: '日志目录',
label: t('tray.openDirectories.logDir'),
click: (): Promise<string> => shell.openPath(logDir())
}
]
},
envType.length > 1
? {
type: 'submenu',
label: '复制环境变量',
label: t('tray.copyEnv'),
submenu: envType.map((type) => {
return {
id: type,
Expand All @@ -249,7 +255,7 @@ export const buildContextMenu = async (): Promise<Menu> => {
}
: {
id: 'copyenv',
label: '复制环境变量',
label: t('tray.copyEnv'),
type: 'normal',
click: async (): Promise<void> => {
await copyEnv(envType[0])
Expand All @@ -258,14 +264,14 @@ export const buildContextMenu = async (): Promise<Menu> => {
{ type: 'separator' },
{
id: 'quitWithoutCore',
label: '轻量模式',
label: t('actions.lightMode.button'),
type: 'normal',
accelerator: quitWithoutCoreShortcut,
click: quitWithoutCore
},
{
id: 'restart',
label: '重启应用',
label: t('actions.restartApp'),
type: 'normal',
accelerator: restartAppShortcut,
click: (): void => {
Expand All @@ -275,7 +281,7 @@ export const buildContextMenu = async (): Promise<Menu> => {
},
{
id: 'quit',
label: '退出应用',
label: t('actions.quit.button'),
type: 'normal',
accelerator: 'CommandOrControl+Q',
click: (): void => app.quit()
Expand Down
8 changes: 8 additions & 0 deletions src/main/utils/ipc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ import { getGistUrl } from '../resolve/gistApi'
import { getImageDataURL } from './image'
import { startMonitor } from '../resolve/trafficMonitor'
import { closeFloatingWindow, showContextMenu, showFloatingWindow } from '../resolve/floatingWindow'
import i18next from 'i18next'

function ipcErrorWrapper<T>( // eslint-disable-next-line @typescript-eslint/no-explicit-any
fn: (...args: any[]) => Promise<T> // eslint-disable-next-line @typescript-eslint/no-explicit-any
Expand Down Expand Up @@ -254,4 +255,11 @@ export function registerIpcMainHandlers(): void {
})
ipcMain.handle('quitWithoutCore', ipcErrorWrapper(quitWithoutCore))
ipcMain.handle('quitApp', () => app.quit())

// Add language change handler
ipcMain.handle('changeLanguage', async (_e, lng) => {
await i18next.changeLanguage(lng)
// 触发托盘菜单更新
ipcMain.emit('updateTrayMenu')
})
}
Loading

0 comments on commit 8210b47

Please sign in to comment.