refactor: update language handling and remove unused locales

- Commented out the App_SetLanguage IPC channel and related language management code.
- Updated preferences to use LanguageVarious type for language settings.
- Removed the locales utility file and adjusted imports to utilize preferenceService for language management.
- Cleaned up unused code and comments related to language handling for improved maintainability.
This commit is contained in:
fullex 2025-09-02 01:39:04 +08:00
parent 0ef3852029
commit 7b2570974e
26 changed files with 111 additions and 95 deletions

View File

@ -2,7 +2,7 @@ export enum IpcChannel {
App_GetCacheSize = 'app:get-cache-size',
App_ClearCache = 'app:clear-cache',
App_SetLaunchOnBoot = 'app:set-launch-on-boot',
App_SetLanguage = 'app:set-language',
// App_SetLanguage = 'app:set-language',
App_SetEnableSpellCheck = 'app:set-enable-spell-check',
App_SetSpellCheckLanguages = 'app:set-spell-check-languages',
App_ShowUpdateDialog = 'app:show-update-dialog',

View File

@ -33,3 +33,6 @@ export enum ThemeMode {
dark = 'dark',
system = 'system'
}
/** 有限的UI语言 */
export type LanguageVarious = 'zh-CN' | 'zh-TW' | 'el-GR' | 'en-US' | 'es-ES' | 'fr-FR' | 'ja-JP' | 'pt-PT' | 'ru-RU'

View File

@ -1,6 +1,6 @@
/**
* Auto-generated preferences configuration
* Generated at: 2025-09-01T14:36:18.273Z
* Generated at: 2025-09-01T16:31:20.203Z
*
* This file is automatically generated from classification.json
* To update this file, modify classification.json and run:
@ -9,8 +9,9 @@
* === AUTO-GENERATED CONTENT START ===
*/
import type { SelectionActionItem, SelectionFilterMode, SelectionTriggerMode } from './preferenceTypes'
import { ThemeMode } from './preferenceTypes'
import { TRANSLATE_PROMPT } from '@shared/config/prompts'
import type { SelectionActionItem, SelectionFilterMode, SelectionTriggerMode } from '@shared/data/preferenceTypes'
import { LanguageVarious, ThemeMode } from '@shared/data/preferenceTypes'
/* eslint @typescript-eslint/member-ordering: ["error", {
"interfaces": { "order": "alphabetically" },
@ -29,8 +30,8 @@ export interface PreferencesType {
'app.dist.test_plan.channel': string
// redux/settings/testPlan
'app.dist.test_plan.enabled': boolean
// electronStore/Language/Language
'app.language': string | null
// redux/settings/language
'app.language': LanguageVarious | null
// redux/settings/launchOnBoot
'app.launch_on_boot': boolean
// redux/settings/notification.assistant
@ -311,6 +312,8 @@ export interface PreferencesType {
'feature.selection.remember_win_size': boolean
// redux/selectionStore/triggerMode
'feature.selection.trigger_mode': SelectionTriggerMode
// redux/settings/translateModelPrompt
'feature.translate.model_prompt': string
// redux/shortcuts/shortcuts.exit_fullscreen
'shortcut.app.exit_fullscreen': Record<string, unknown>
// redux/shortcuts/shortcuts.search_message
@ -540,6 +543,7 @@ export const DefaultPreferences: PreferencesType = {
'feature.selection.follow_toolbar': true,
'feature.selection.remember_win_size': false,
'feature.selection.trigger_mode': 'selected',
'feature.translate.model_prompt': TRANSLATE_PROMPT,
'shortcut.app.exit_fullscreen': { editable: false, enabled: true, key: ['Escape'], system: true },
'shortcut.app.search_message': {
editable: true,
@ -592,8 +596,8 @@ export const DefaultPreferences: PreferencesType = {
/**
* :
* - 总配置项: 171
* - electronStore项: 2
* - redux项: 169
* - 总配置项: 172
* - electronStore项: 1
* - redux项: 171
* - localStorage项: 0
*/

View File

@ -129,9 +129,9 @@ export function registerIpc(mainWindow: BrowserWindow, app: Electron.App) {
ipcMain.handle(IpcChannel.App_ShowUpdateDialog, () => appUpdater.showUpdateDialog(mainWindow))
// language
ipcMain.handle(IpcChannel.App_SetLanguage, (_, language) => {
configManager.setLanguage(language)
})
// ipcMain.handle(IpcChannel.App_SetLanguage, (_, language) => {
// configManager.setLanguage(language)
// })
// spell check
ipcMain.handle(IpcChannel.App_SetEnableSpellCheck, (_, isEnable: boolean) => {

View File

@ -1,7 +1,7 @@
import { loggerService } from '@logger'
import { isWin } from '@main/constant'
import { getIpCountry } from '@main/utils/ipService'
import { locales } from '@main/utils/locales'
import { getI18n } from '@main/utils/language'
import { generateUserAgent } from '@main/utils/systemInfo'
import { FeedUrl, UpgradeChannel } from '@shared/config/constant'
import { IpcChannel } from '@shared/IpcChannel'
@ -237,12 +237,12 @@ export default class AppUpdater {
}
}
public async showUpdateDialog(mainWindow: BrowserWindow) {
public showUpdateDialog(mainWindow: BrowserWindow) {
if (!this.releaseInfo) {
return
}
const locale = locales[configManager.getLanguage()]
const { update: updateLocale } = locale.translation
const i18n = getI18n()
const { update: updateLocale } = i18n.translation
let detail = this.formatReleaseNotes(this.releaseInfo.releaseNotes)
if (detail === '') {

View File

@ -1,10 +1,7 @@
import { defaultLanguage, UpgradeChannel, ZOOM_SHORTCUTS } from '@shared/config/constant'
import { LanguageVarious, Shortcut } from '@types'
import { app } from 'electron'
import { UpgradeChannel, ZOOM_SHORTCUTS } from '@shared/config/constant'
import { Shortcut } from '@types'
import Store from 'electron-store'
import { locales } from '../utils/locales'
export enum ConfigKeys {
Language = 'language',
Theme = 'theme',
@ -38,14 +35,14 @@ export class ConfigManager {
this.store = new Store()
}
getLanguage(): LanguageVarious {
const locale = Object.keys(locales).includes(app.getLocale()) ? app.getLocale() : defaultLanguage
return this.get(ConfigKeys.Language, locale) as LanguageVarious
}
// getLanguage(): LanguageVarious {
// const locale = Object.keys(locales).includes(app.getLocale()) ? app.getLocale() : defaultLanguage
// return this.get(ConfigKeys.Language, locale) as LanguageVarious
// }
setLanguage(lang: LanguageVarious) {
this.setAndNotify(ConfigKeys.Language, lang)
}
// setLanguage(lang: LanguageVarious) {
// this.setAndNotify(ConfigKeys.Language, lang)
// }
// getTheme(): ThemeMode {
// return this.get(ConfigKeys.Theme, ThemeMode.system)

View File

@ -1,8 +1,6 @@
import { getI18n } from '@main/utils/language'
import { Menu, MenuItemConstructorOptions } from 'electron'
import { locales } from '../utils/locales'
import { configManager } from './ConfigManager'
class ContextMenu {
public contextMenu(w: Electron.WebContents) {
w.on('context-menu', (_event, properties) => {
@ -27,8 +25,8 @@ class ContextMenu {
}
private createInspectMenuItems(w: Electron.WebContents): MenuItemConstructorOptions[] {
const locale = locales[configManager.getLanguage()]
const { common } = locale.translation
const i18n = getI18n()
const { common } = i18n.translation
const template: MenuItemConstructorOptions[] = [
{
id: 'inspect',
@ -44,8 +42,8 @@ class ContextMenu {
}
private createEditMenuItems(properties: Electron.ContextMenuParams): MenuItemConstructorOptions[] {
const locale = locales[configManager.getLanguage()]
const { common } = locale.translation
const i18n = getI18n()
const { common } = i18n.translation
const hasText = properties.selectionText.trim().length > 0
const can = (type: string) => properties.editFlags[`can${type}`] && hasText

View File

@ -1,14 +1,13 @@
import { preferenceService } from '@data/PreferenceService'
import { isLinux, isMac, isWin } from '@main/constant'
import { locales } from '@main/utils/locales'
import { getI18n } from '@main/utils/language'
import { app, Menu, MenuItemConstructorOptions, nativeImage, nativeTheme, Tray } from 'electron'
import icon from '../../../build/tray_icon.png?asset'
import iconDark from '../../../build/tray_icon_dark.png?asset'
import iconLight from '../../../build/tray_icon_light.png?asset'
import { ConfigKeys, configManager } from './ConfigManager'
import selectionService from './SelectionService'
import { windowService } from './WindowService'
export class TrayService {
private static instance: TrayService
private tray: Tray | null = null
@ -60,7 +59,10 @@ export class TrayService {
})
this.tray.on('click', () => {
if (configManager.getEnableQuickAssistant() && configManager.getClickTrayToShowQuickAssistant()) {
const quickAssistantEnabled = preferenceService.get('feature.quick_assistant.enabled')
const clickTrayToShowQuickAssistant = preferenceService.get('feature.quick_assistant.click_tray_to_show')
if (quickAssistantEnabled && clickTrayToShowQuickAssistant) {
windowService.showMiniWindow()
} else {
windowService.showMainWindow()
@ -69,11 +71,11 @@ export class TrayService {
}
private updateContextMenu() {
const locale = locales[configManager.getLanguage()]
const { tray: trayLocale, selection: selectionLocale } = locale.translation
const i18n = getI18n()
const { tray: trayLocale, selection: selectionLocale } = i18n.translation
const quickAssistantEnabled = configManager.getEnableQuickAssistant()
const selectionAssistantEnabled = configManager.getSelectionAssistantEnabled()
const quickAssistantEnabled = preferenceService.get('feature.quick_assistant.enabled')
const selectionAssistantEnabled = preferenceService.get('feature.selection.enabled')
const template = [
{
@ -104,7 +106,7 @@ export class TrayService {
}
private updateTray() {
const showTray = configManager.getTray()
const showTray = preferenceService.get('app.tray.enabled')
if (showTray) {
this.createTray()
} else {
@ -120,19 +122,10 @@ export class TrayService {
}
private watchConfigChanges() {
configManager.subscribe(ConfigKeys.Tray, () => this.updateTray())
configManager.subscribe(ConfigKeys.Language, () => {
this.updateContextMenu()
})
configManager.subscribe(ConfigKeys.EnableQuickAssistant, () => {
this.updateContextMenu()
})
configManager.subscribe(ConfigKeys.SelectionAssistantEnabled, () => {
this.updateContextMenu()
})
preferenceService.subscribeChange('app.tray.enabled', () => this.updateTray())
preferenceService.subscribeChange('app.language', () => this.updateContextMenu())
preferenceService.subscribeChange('feature.quick_assistant.enabled', () => this.updateContextMenu())
preferenceService.subscribeChange('feature.selection.enabled', () => this.updateContextMenu())
}
private quit() {

View File

@ -1,3 +1,8 @@
import { preferenceService } from '@data/PreferenceService'
import { defaultLanguage } from '@shared/config/constant'
import { LanguageVarious } from '@shared/data/preferenceTypes'
import { app } from 'electron'
import EnUs from '../../renderer/src/i18n/locales/en-us.json'
import JaJP from '../../renderer/src/i18n/locales/ja-jp.json'
import RuRu from '../../renderer/src/i18n/locales/ru-ru.json'
@ -9,7 +14,7 @@ import esES from '../../renderer/src/i18n/translate/es-es.json'
import frFR from '../../renderer/src/i18n/translate/fr-fr.json'
import ptPT from '../../renderer/src/i18n/translate/pt-pt.json'
const locales = Object.fromEntries(
export const locales = Object.fromEntries(
[
['en-US', EnUs],
['zh-CN', ZhCn],
@ -23,4 +28,18 @@ const locales = Object.fromEntries(
].map(([locale, translation]) => [locale, { translation }])
)
export { locales }
export const getAppLanguage = (): LanguageVarious => {
const language = preferenceService.get('app.language')
const appLocale = app.getLocale()
if (language) {
return language
}
return (Object.keys(locales).includes(appLocale) ? appLocale : defaultLanguage) as LanguageVarious
}
export const getI18n = (): Record<string, any> => {
const language = getAppLanguage()
return locales[language]
}

View File

@ -5,7 +5,6 @@ import {
isOpenAIModel,
isSupportFlexServiceTierModel
} from '@renderer/config/models'
import { REFERENCE_PROMPT } from '@renderer/config/prompts'
import { isSupportServiceTierProvider } from '@renderer/config/providers'
import { getLMStudioKeepAliveTime } from '@renderer/hooks/useLMStudio'
import { getAssistantSettings } from '@renderer/services/AssistantService'
@ -46,6 +45,7 @@ import { isJSON, parseJSON } from '@renderer/utils'
import { addAbortController, removeAbortController } from '@renderer/utils/abortController'
import { findFileBlocks, getMainTextContent } from '@renderer/utils/messageUtils/find'
import { defaultTimeout } from '@shared/config/constant'
import { REFERENCE_PROMPT } from '@shared/config/prompts'
import { isEmpty } from 'lodash'
import { CompletionsContext } from '../middleware/types'

View File

@ -161,9 +161,9 @@ import {
ThinkingOptionConfig
} from '@renderer/types'
import { getLowerBaseModelName, isUserSelectedModelType } from '@renderer/utils'
import { WEB_SEARCH_PROMPT_FOR_OPENROUTER } from '@shared/config/prompts'
import OpenAI from 'openai'
import { WEB_SEARCH_PROMPT_FOR_OPENROUTER } from './prompts'
import { getWebSearchTools } from './tools'
// Vision models

View File

@ -1,8 +1,7 @@
import { Model } from '@renderer/types'
import { WEB_SEARCH_PROMPT_FOR_ZHIPU } from '@shared/config/prompts'
import { ChatCompletionTool } from 'openai/resources'
import { WEB_SEARCH_PROMPT_FOR_ZHIPU } from './prompts'
export function getWebSearchTools(model: Model): ChatCompletionTool[] {
if (model?.provider === 'zhipu') {
if (model.id === 'glm-4-alltools') {

View File

@ -1,5 +1,5 @@
import { useSettings } from '@renderer/hooks/useSettings'
import { LanguageVarious } from '@renderer/types'
import { LanguageVarious } from '@shared/data/preferenceTypes'
import { ConfigProvider, theme } from 'antd'
import elGR from 'antd/locale/el_GR'
import enUS from 'antd/locale/en_US'

View File

@ -15,7 +15,6 @@ import {
setTargetLanguage,
setTestChannel as _setTestChannel,
setTestPlan as _setTestPlan,
setTheme,
SettingsState,
setTopicPosition,
setTray as _setTray,
@ -24,7 +23,6 @@ import {
} from '@renderer/store/settings'
import { SidebarIcon, TranslateLanguageCode } from '@renderer/types'
import { UpgradeChannel } from '@shared/config/constant'
import { ThemeMode } from '@shared/data/preferenceTypes'
export function useSettings() {
const settings = useAppSelector((state) => state.settings)
@ -74,9 +72,9 @@ export function useSettings() {
window.api.setTestChannel(channel)
},
setTheme(theme: ThemeMode) {
dispatch(setTheme(theme))
},
// setTheme(theme: ThemeMode) {
// dispatch(setTheme(theme))
// },
setWindowStyle(windowStyle: 'transparent' | 'opaque') {
dispatch(setWindowStyle(windowStyle))
},

View File

@ -1,3 +1,4 @@
import { preferenceService } from '@data/PreferenceService'
import { loggerService } from '@logger'
import { defaultLanguage } from '@shared/config/constant'
import i18n from 'i18next'
@ -31,17 +32,17 @@ const resources = Object.fromEntries(
].map(([locale, translation]) => [locale, { translation }])
)
export const getLanguage = () => {
return localStorage.getItem('language') || navigator.language || defaultLanguage
export const getLanguage = async () => {
return (await preferenceService.get('app.language')) || navigator.language || defaultLanguage
}
export const getLanguageCode = () => {
return getLanguage().split('-')[0]
export const getLanguageCode = async () => {
return (await getLanguage()).split('-')[0]
}
i18n.use(initReactI18next).init({
resources,
lng: getLanguage(),
lng: await getLanguage(),
fallbackLng: defaultLanguage,
interpolation: {
escapeValue: false

View File

@ -4,7 +4,6 @@ import { CheckOutlined, LoadingOutlined, RollbackOutlined, ThunderboltOutlined }
import { loggerService } from '@logger'
import EmojiPicker from '@renderer/components/EmojiPicker'
import { TopView } from '@renderer/components/TopView'
import { AGENT_PROMPT } from '@renderer/config/prompts'
import { useAgents } from '@renderer/hooks/useAgents'
import { useSidebarIconShow } from '@renderer/hooks/useSidebarIcon'
import { fetchGenerate } from '@renderer/services/ApiService'
@ -13,6 +12,7 @@ import { estimateTextTokens } from '@renderer/services/TokenService'
import { useAppSelector } from '@renderer/store'
import { Agent, KnowledgeBase } from '@renderer/types'
import { getLeadingEmoji, uuid } from '@renderer/utils'
import { AGENT_PROMPT } from '@shared/config/prompts'
import { Button, Form, FormInstance, Input, Modal, Popover, Select, SelectProps } from 'antd'
import TextArea from 'antd/es/input/TextArea'
import { useEffect, useRef, useState } from 'react'

View File

@ -1,4 +1,5 @@
import { InfoCircleOutlined } from '@ant-design/icons'
import { usePreference } from '@data/hooks/usePreference'
import InfoTooltip from '@renderer/components/InfoTooltip'
import { HStack } from '@renderer/components/Layout'
import Selector from '@renderer/components/Selector'
@ -10,17 +11,16 @@ import { RootState, useAppDispatch } from '@renderer/store'
import {
setEnableDataCollection,
setEnableSpellCheck,
setLanguage,
setNotificationSettings,
setProxyBypassRules as _setProxyBypassRules,
setProxyMode,
setProxyUrl as _setProxyUrl,
setSpellCheckLanguages
} from '@renderer/store/settings'
import { LanguageVarious } from '@renderer/types'
import { NotificationSource } from '@renderer/types/notification'
import { isValidProxyUrl } from '@renderer/utils'
import { defaultByPassRules, defaultLanguage } from '@shared/config/constant'
import { LanguageVarious } from '@shared/data/preferenceTypes'
import { Flex, Input, Switch, Tooltip } from 'antd'
import { FC, useState } from 'react'
import { useTranslation } from 'react-i18next'
@ -30,7 +30,6 @@ import { SettingContainer, SettingDivider, SettingGroup, SettingRow, SettingRowT
const GeneralSettings: FC = () => {
const {
language,
proxyUrl: storeProxyUrl,
proxyBypassRules: storeProxyBypassRules,
setLaunch,
@ -51,6 +50,8 @@ const GeneralSettings: FC = () => {
const { enableDeveloperMode, setEnableDeveloperMode } = useEnableDeveloperMode()
const { setTimeoutTimer } = useTimer()
const [language, setLanguage] = usePreference('app.language')
const updateTray = (isShowTray: boolean) => {
setTray(isShowTray)
//only set tray on close/launch to tray when tray is enabled
@ -83,10 +84,11 @@ const GeneralSettings: FC = () => {
const { t } = useTranslation()
const onSelectLanguage = (value: LanguageVarious) => {
dispatch(setLanguage(value))
localStorage.setItem('language', value)
window.api.setLanguage(value)
// dispatch(setLanguage(value))
// localStorage.setItem('language', value)
// window.api.setLanguage(value)
i18n.changeLanguage(value)
setLanguage(value)
}
const handleSpellCheckChange = (checked: boolean) => {

View File

@ -3,7 +3,7 @@ import InfoTooltip from '@renderer/components/InfoTooltip'
import { HStack } from '@renderer/components/Layout'
import ModelSelector from '@renderer/components/ModelSelector'
import { isEmbeddingModel, isRerankModel, isTextToImageModel } from '@renderer/config/models'
import { TRANSLATE_PROMPT } from '@renderer/config/prompts'
import { TRANSLATE_PROMPT } from '@shared/config/prompts'
import { useTheme } from '@renderer/context/ThemeProvider'
import { useDefaultModel } from '@renderer/hooks/useAssistant'
import { useProviders } from '@renderer/hooks/useProvider'

View File

@ -1,6 +1,6 @@
import { RedoOutlined } from '@ant-design/icons'
import { HStack } from '@renderer/components/Layout'
import { TRANSLATE_PROMPT } from '@renderer/config/prompts'
import { TRANSLATE_PROMPT } from '@shared/config/prompts'
import { useTheme } from '@renderer/context/ThemeProvider'
import { useSettings } from '@renderer/hooks/useSettings'
import { useAppDispatch } from '@renderer/store'

View File

@ -12,12 +12,6 @@ import {
isSupportedThinkingTokenModel,
isWebSearchModel
} from '@renderer/config/models'
import {
LANG_DETECT_PROMPT,
SEARCH_SUMMARY_PROMPT,
SEARCH_SUMMARY_PROMPT_KNOWLEDGE_ONLY,
SEARCH_SUMMARY_PROMPT_WEB_ONLY
} from '@renderer/config/prompts'
import { getModel } from '@renderer/hooks/useModel'
import { getStoreSetting } from '@renderer/hooks/useSettings'
import i18n from '@renderer/i18n'
@ -52,6 +46,12 @@ import {
replacePromptVariables
} from '@renderer/utils/prompt'
import { getTranslateOptions } from '@renderer/utils/translate'
import {
LANG_DETECT_PROMPT,
SEARCH_SUMMARY_PROMPT,
SEARCH_SUMMARY_PROMPT_KNOWLEDGE_ONLY,
SEARCH_SUMMARY_PROMPT_WEB_ONLY
} from '@shared/config/prompts'
import { findLast, isEmpty, takeRight } from 'lodash'
import AiProvider from '../aiCore'

View File

@ -9,7 +9,7 @@ import {
SYSTEM_MODELS
} from '@renderer/config/models'
import { BUILTIN_OCR_PROVIDERS, BUILTIN_OCR_PROVIDERS_MAP, DEFAULT_OCR_PROVIDER } from '@renderer/config/ocr'
import { TRANSLATE_PROMPT } from '@renderer/config/prompts'
import { TRANSLATE_PROMPT } from '@shared/config/prompts'
import {
isSupportArrayContentProvider,
isSupportDeveloperRoleProvider,

View File

@ -1,12 +1,10 @@
import { createSlice, PayloadAction } from '@reduxjs/toolkit'
import { isMac } from '@renderer/config/constant'
import { TRANSLATE_PROMPT } from '@renderer/config/prompts'
import { DEFAULT_SIDEBAR_ICONS } from '@renderer/config/sidebar'
import {
ApiServerConfig,
AssistantsSortType,
CodeStyleVarious,
LanguageVarious,
MathEngine,
OpenAIServiceTier,
OpenAISummaryText,
@ -17,7 +15,8 @@ import {
} from '@renderer/types'
import { uuid } from '@renderer/utils'
import { UpgradeChannel } from '@shared/config/constant'
import { ThemeMode } from '@shared/data/preferenceTypes'
import { TRANSLATE_PROMPT } from '@shared/config/prompts'
import { LanguageVarious, ThemeMode } from '@shared/data/preferenceTypes'
import { OpenAIVerbosity } from '@types'
import { RemoteSyncState } from './backup'

View File

@ -533,7 +533,7 @@ export type MinAppType = {
}
/** 有限的UI语言 */
export type LanguageVarious = 'zh-CN' | 'zh-TW' | 'el-GR' | 'en-US' | 'es-ES' | 'fr-FR' | 'ja-JP' | 'pt-PT' | 'ru-RU'
// export type LanguageVarious = 'zh-CN' | 'zh-TW' | 'el-GR' | 'en-US' | 'es-ES' | 'fr-FR' | 'ja-JP' | 'pt-PT' | 'ru-RU'
export type CodeStyleVarious = 'auto' | string

View File

@ -1,4 +1,5 @@
import { LoadingOutlined } from '@ant-design/icons'
import { usePreference } from '@data/hooks/usePreference'
import { loggerService } from '@logger'
import CopyButton from '@renderer/components/CopyButton'
import LanguageSelect from '@renderer/components/LanguageSelect'
@ -31,7 +32,9 @@ const logger = loggerService.withContext('ActionTranslate')
const ActionTranslate: FC<Props> = ({ action, scrollToBottom }) => {
const { t } = useTranslation()
const { translateModelPrompt, language } = useSettings()
const { language } = useSettings()
const [translateModelPrompt] = usePreference('feature.translate.model_prompt')
const [targetLanguage, setTargetLanguage] = useState<TranslateLanguage>(LanguagesEnum.enUS)
const [alterLanguage, setAlterLanguage] = useState<TranslateLanguage>(LanguagesEnum.zhCN)