diff --git a/src/main/ipc.ts b/src/main/ipc.ts index e3592eff7a..a290e3391b 100644 --- a/src/main/ipc.ts +++ b/src/main/ipc.ts @@ -29,6 +29,11 @@ export function registerIpc(mainWindow: BrowserWindow, app: Electron.App) { ipcMain.handle('app:reload', () => mainWindow.reload()) ipcMain.handle('open:website', (_, url: string) => shell.openExternal(url)) + // language + ipcMain.handle('app:set-language', (_, language) => { + configManager.setLanguage(language) + }) + // theme ipcMain.handle('app:set-theme', (_, theme: 'light' | 'dark') => { configManager.setTheme(theme) diff --git a/src/main/services/ConfigManager.ts b/src/main/services/ConfigManager.ts index a6bb220114..6e2406ebc8 100644 --- a/src/main/services/ConfigManager.ts +++ b/src/main/services/ConfigManager.ts @@ -1,5 +1,9 @@ +import { app } from 'electron' import Store from 'electron-store' +type ThemeVarious = 'light' | 'dark' +type LanguageVarious = 'zh-CN' | 'zh-TW' | 'en-US' + export class ConfigManager { private store: Store @@ -7,11 +11,19 @@ export class ConfigManager { this.store = new Store() } - getTheme(): 'light' | 'dark' { - return this.store.get('theme', 'light') as 'light' | 'dark' + getLanguage(): LanguageVarious { + return this.store.get('language', app.getLocale()) as LanguageVarious } - setTheme(theme: 'light' | 'dark') { + setLanguage(theme: ThemeVarious) { + this.store.set('language', theme) + } + + getTheme(): ThemeVarious { + return this.store.get('theme', 'light') as ThemeVarious + } + + setTheme(theme: ThemeVarious) { this.store.set('theme', theme) } } diff --git a/src/main/utils/locales.ts b/src/main/utils/locales.ts new file mode 100644 index 0000000000..d927f25211 --- /dev/null +++ b/src/main/utils/locales.ts @@ -0,0 +1,11 @@ +import EnUs from '../../renderer/src/i18n/locales/en-us.json' +import ZhCn from '../../renderer/src/i18n/locales/zh-cn.json' +import ZhTw from '../../renderer/src/i18n/locales/zh-tw.json' + +const locales = { + 'en-US': EnUs, + 'zh-CN': ZhCn, + 'zh-TW': ZhTw +} + +export { locales } diff --git a/src/main/window.ts b/src/main/window.ts index 3014dcf47f..eb6783964c 100644 --- a/src/main/window.ts +++ b/src/main/window.ts @@ -6,6 +6,7 @@ import { join } from 'path' import icon from '../../build/icon.png?asset' import { titleBarOverlayDark, titleBarOverlayLight } from './config' import { configManager } from './services/ConfigManager' +import { locales } from './utils/locales' export function createMainWindow() { // Load the previous state with fallback to defaults @@ -48,10 +49,13 @@ export function createMainWindow() { mainWindowState.manage(mainWindow) mainWindow.webContents.on('context-menu', () => { + const locale = locales[configManager.getLanguage()] + const { common } = locale.translation + const menu = new Menu() - menu.append(new MenuItem({ label: '复制', role: 'copy' })) - menu.append(new MenuItem({ label: '粘贴', role: 'paste' })) - menu.append(new MenuItem({ label: '剪切', role: 'cut' })) + menu.append(new MenuItem({ label: common.copy, role: 'copy' })) + menu.append(new MenuItem({ label: common.paste, role: 'paste' })) + menu.append(new MenuItem({ label: common.cut, role: 'cut' })) menu.popup() }) diff --git a/src/preload/index.d.ts b/src/preload/index.d.ts index 245afac700..4a3834dfb0 100644 --- a/src/preload/index.d.ts +++ b/src/preload/index.d.ts @@ -1,6 +1,7 @@ import { ElectronAPI } from '@electron-toolkit/preload' import { FileType } from '@renderer/types' import { WebDavConfig } from '@renderer/types' +import { LanguageVarious } from '@renderer/types' import type { OpenDialogOptions } from 'electron' import { Readable } from 'stream' @@ -17,6 +18,7 @@ declare global { checkForUpdate: () => void openWebsite: (url: string) => void setProxy: (proxy: string | undefined) => void + setLanguage: (theme: LanguageVarious) => void setTheme: (theme: 'light' | 'dark') => void minApp: (options: { url: string; windowOptions?: Electron.BrowserWindowConstructorOptions }) => void reload: () => void diff --git a/src/preload/index.ts b/src/preload/index.ts index 76e4fc2eba..561e0b9366 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -8,6 +8,7 @@ const api = { reload: () => ipcRenderer.invoke('app:reload'), setProxy: (proxy: string) => ipcRenderer.invoke('app:proxy', proxy), checkForUpdate: () => ipcRenderer.invoke('app:check-for-update'), + setLanguage: (lang: string) => ipcRenderer.invoke('app:set-language', lang), setTheme: (theme: 'light' | 'dark') => ipcRenderer.invoke('app:set-theme', theme), openWebsite: (url: string) => ipcRenderer.invoke('open:website', url), minApp: (url: string) => ipcRenderer.invoke('minapp', url), diff --git a/src/renderer/src/i18n/locales/en-us.json b/src/renderer/src/i18n/locales/en-us.json index da3f06587f..cb143b71bf 100644 --- a/src/renderer/src/i18n/locales/en-us.json +++ b/src/renderer/src/i18n/locales/en-us.json @@ -17,6 +17,8 @@ "edit": "Edit", "duplicate": "Duplicate", "copy": "Copy", + "paste": "Paste", + "cut": "Cut", "regenerate": "Regenerate", "provider": "Provider", "you": "You", diff --git a/src/renderer/src/i18n/locales/zh-cn.json b/src/renderer/src/i18n/locales/zh-cn.json index 8452818cff..f9a83cb8de 100644 --- a/src/renderer/src/i18n/locales/zh-cn.json +++ b/src/renderer/src/i18n/locales/zh-cn.json @@ -17,6 +17,8 @@ "edit": "编辑", "duplicate": "复制", "copy": "复制", + "paste": "粘贴", + "cut": "剪切", "regenerate": "重新生成", "provider": "提供商", "you": "用户", diff --git a/src/renderer/src/i18n/locales/zh-tw.json b/src/renderer/src/i18n/locales/zh-tw.json index c6ab1fd7f6..7027a4ca2f 100644 --- a/src/renderer/src/i18n/locales/zh-tw.json +++ b/src/renderer/src/i18n/locales/zh-tw.json @@ -17,6 +17,8 @@ "edit": "編輯", "duplicate": "複製", "copy": "複製", + "paste": "貼上", + "cut": "剪下", "regenerate": "重新生成", "provider": "提供商", "you": "您", diff --git a/src/renderer/src/pages/settings/GeneralSettings.tsx b/src/renderer/src/pages/settings/GeneralSettings.tsx index 1fd610d32e..0e9234cf37 100644 --- a/src/renderer/src/pages/settings/GeneralSettings.tsx +++ b/src/renderer/src/pages/settings/GeneralSettings.tsx @@ -4,7 +4,7 @@ import i18n from '@renderer/i18n' import { useAppDispatch } from '@renderer/store' import { setClickAssistantToShowTopic, setLanguage, setShowTopicTime } from '@renderer/store/settings' import { setProxyUrl as _setProxyUrl } from '@renderer/store/settings' -import { ThemeMode } from '@renderer/types' +import { LanguageVarious, ThemeMode } from '@renderer/types' import { isValidProxyUrl } from '@renderer/utils' import { Input, Select, Switch } from 'antd' import { FC, useState } from 'react' @@ -30,9 +30,10 @@ const GeneralSettings: FC = () => { const dispatch = useAppDispatch() const { t } = useTranslation() - const onSelectLanguage = (value: string) => { + const onSelectLanguage = (value: LanguageVarious) => { dispatch(setLanguage(value)) localStorage.setItem('language', value) + window.api.setLanguage(value) i18n.changeLanguage(value) } diff --git a/src/renderer/src/store/settings.ts b/src/renderer/src/store/settings.ts index cf754b96e8..3091404a09 100644 --- a/src/renderer/src/store/settings.ts +++ b/src/renderer/src/store/settings.ts @@ -1,5 +1,5 @@ import { createSlice, PayloadAction } from '@reduxjs/toolkit' -import { ThemeMode } from '@renderer/types' +import { LanguageVarious, ThemeMode } from '@renderer/types' export type SendMessageShortcut = 'Enter' | 'Shift+Enter' @@ -7,7 +7,7 @@ export interface SettingsState { showAssistants: boolean showTopics: boolean sendMessageShortcut: SendMessageShortcut - language: string + language: LanguageVarious proxyUrl?: string userName: string showMessageDivider: boolean @@ -36,7 +36,7 @@ const initialState: SettingsState = { showAssistants: true, showTopics: true, sendMessageShortcut: 'Enter', - language: navigator.language, + language: navigator.language as LanguageVarious, proxyUrl: undefined, userName: '', showMessageDivider: false, @@ -79,7 +79,7 @@ const settingsSlice = createSlice({ setSendMessageShortcut: (state, action: PayloadAction) => { state.sendMessageShortcut = action.payload }, - setLanguage: (state, action: PayloadAction) => { + setLanguage: (state, action: PayloadAction) => { state.language = action.payload }, setProxyUrl: (state, action: PayloadAction) => { diff --git a/src/renderer/src/types/index.ts b/src/renderer/src/types/index.ts index 3cf010594d..22bc6cf8e5 100644 --- a/src/renderer/src/types/index.ts +++ b/src/renderer/src/types/index.ts @@ -136,6 +136,7 @@ export enum ThemeMode { dark = 'dark', auto = 'auto' } +export type LanguageVarious = 'zh-CN' | 'zh-TW' | 'en-US' export type WebDavConfig = { webdavHost: string