From 3349d41881def39bb4d1649d6ae075d41b5a7f03 Mon Sep 17 00:00:00 2001 From: DEV_DSW <562304744@qq.com> Date: Tue, 21 Apr 2026 16:52:45 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E8=AF=AD=E8=A8=80=E5=9B=BD=E9=99=85?= =?UTF-8?q?=E5=8C=96=E9=87=8D=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dist-electron/main/main.js | 2 +- .../channels/ChannelConfigFields.tsx | 15 +- .../channels/ChannelConfigModal.tsx | 50 +- src/components/channels/ChannelTokenField.tsx | 6 +- .../channels/ChannelTypeSelector.tsx | 30 +- src/components/chat/ChatComposer.tsx | 11 +- src/components/chat/ChatHistoryPanel.tsx | 95 +- src/components/chat/ChatMessageList.tsx | 4 +- src/components/chat/TaskBoard.tsx | 20 +- src/constants/taskCenterList.ts | 12 +- src/i18n/constants.ts | 75 +- src/i18n/index.ts | 292 ++- src/i18n/locales/en/agents.json | 82 + src/i18n/locales/en/channels.json | 85 + src/i18n/locales/en/chat.json | 53 + src/i18n/locales/en/common.json | 46 + src/i18n/locales/en/cron.json | 187 ++ src/i18n/locales/en/dashboard.json | 34 + src/i18n/locales/en/knowledge.json | 51 + src/i18n/locales/en/login.json | 22 + src/i18n/locales/en/models.json | 99 + src/i18n/locales/en/scripts.json | 116 ++ src/i18n/locales/en/settings.json | 57 + src/i18n/locales/en/skills.json | 101 + src/i18n/locales/en/task.json | 94 + src/i18n/locales/ja/agents.json | 141 ++ src/i18n/locales/ja/channels.json | 85 + src/i18n/locales/ja/chat.json | 53 + src/i18n/locales/ja/common.json | 46 + src/i18n/locales/ja/cron.json | 187 ++ src/i18n/locales/ja/dashboard.json | 34 + src/i18n/locales/ja/knowledge.json | 51 + src/i18n/locales/ja/login.json | 22 + src/i18n/locales/ja/models.json | 99 + src/i18n/locales/ja/scripts.json | 116 ++ src/i18n/locales/ja/settings.json | 57 + src/i18n/locales/ja/skills.json | 101 + src/i18n/locales/ja/task.json | 94 + src/i18n/locales/zh/agents.json | 141 ++ src/i18n/locales/zh/channels.json | 85 + src/i18n/locales/zh/chat.json | 53 + src/i18n/locales/zh/common.json | 46 + src/i18n/locales/zh/cron.json | 187 ++ src/i18n/locales/zh/dashboard.json | 34 + src/i18n/locales/zh/knowledge.json | 51 + src/i18n/locales/zh/login.json | 22 + src/i18n/locales/zh/models.json | 99 + src/i18n/locales/zh/scripts.json | 116 ++ src/i18n/locales/zh/settings.json | 57 + src/i18n/locales/zh/skills.json | 101 + src/i18n/locales/zh/task.json | 94 + src/i18n/messages.ts | 1636 ----------------- src/lib/channel-meta.ts | 477 +++-- .../Agents/components/AgentSettingsDialog.tsx | 49 +- .../Agents/components/AgentsDialogSurface.tsx | 5 +- src/pages/Agents/index.tsx | 187 +- src/pages/Channels/index.tsx | 38 +- .../Home/components/AddChannelDialog.tsx | 32 +- .../components/ConversationDeleteDialog.tsx | 19 +- .../components/ConversationRenameDialog.tsx | 18 +- src/pages/Home/components/DialogSurface.tsx | 7 +- .../Home/components/TaskOperationDialog.tsx | 44 +- src/pages/Home/index.tsx | 97 +- src/pages/Knowledge/copy.ts | 191 -- src/pages/Knowledge/index.tsx | 63 +- src/pages/Login/auth.ts | 9 +- src/pages/Models/copy.ts | 42 +- src/pages/Models/messages.ts | 314 ---- src/pages/Scripts/components/ScriptCard.tsx | 47 +- .../Scripts/components/ScriptDialogs.tsx | 83 +- src/pages/Scripts/index.tsx | 89 +- src/pages/Skills/copy.ts | 45 +- src/pages/Skills/messages.ts | 318 ---- src/router/index.tsx | 7 +- src/stores/chat.ts | 15 +- src/stores/task.ts | 29 +- 76 files changed, 4440 insertions(+), 3232 deletions(-) create mode 100644 src/i18n/locales/en/agents.json create mode 100644 src/i18n/locales/en/channels.json create mode 100644 src/i18n/locales/en/chat.json create mode 100644 src/i18n/locales/en/common.json create mode 100644 src/i18n/locales/en/cron.json create mode 100644 src/i18n/locales/en/dashboard.json create mode 100644 src/i18n/locales/en/knowledge.json create mode 100644 src/i18n/locales/en/login.json create mode 100644 src/i18n/locales/en/models.json create mode 100644 src/i18n/locales/en/scripts.json create mode 100644 src/i18n/locales/en/settings.json create mode 100644 src/i18n/locales/en/skills.json create mode 100644 src/i18n/locales/en/task.json create mode 100644 src/i18n/locales/ja/agents.json create mode 100644 src/i18n/locales/ja/channels.json create mode 100644 src/i18n/locales/ja/chat.json create mode 100644 src/i18n/locales/ja/common.json create mode 100644 src/i18n/locales/ja/cron.json create mode 100644 src/i18n/locales/ja/dashboard.json create mode 100644 src/i18n/locales/ja/knowledge.json create mode 100644 src/i18n/locales/ja/login.json create mode 100644 src/i18n/locales/ja/models.json create mode 100644 src/i18n/locales/ja/scripts.json create mode 100644 src/i18n/locales/ja/settings.json create mode 100644 src/i18n/locales/ja/skills.json create mode 100644 src/i18n/locales/ja/task.json create mode 100644 src/i18n/locales/zh/agents.json create mode 100644 src/i18n/locales/zh/channels.json create mode 100644 src/i18n/locales/zh/chat.json create mode 100644 src/i18n/locales/zh/common.json create mode 100644 src/i18n/locales/zh/cron.json create mode 100644 src/i18n/locales/zh/dashboard.json create mode 100644 src/i18n/locales/zh/knowledge.json create mode 100644 src/i18n/locales/zh/login.json create mode 100644 src/i18n/locales/zh/models.json create mode 100644 src/i18n/locales/zh/scripts.json create mode 100644 src/i18n/locales/zh/settings.json create mode 100644 src/i18n/locales/zh/skills.json create mode 100644 src/i18n/locales/zh/task.json delete mode 100644 src/i18n/messages.ts delete mode 100644 src/pages/Knowledge/copy.ts delete mode 100644 src/pages/Models/messages.ts delete mode 100644 src/pages/Skills/messages.ts diff --git a/dist-electron/main/main.js b/dist-electron/main/main.js index a2ff5f7..db11edf 100644 --- a/dist-electron/main/main.js +++ b/dist-electron/main/main.js @@ -1,6 +1,6 @@ "use strict"; require("electron"); -require("./main-Bm7LE7mJ.js"); +require("./main-EIYRjWjp.js"); require("electron-squirrel-startup"); require("electron-log"); require("bytenode"); diff --git a/src/components/channels/ChannelConfigFields.tsx b/src/components/channels/ChannelConfigFields.tsx index 79b9144..a734f98 100644 --- a/src/components/channels/ChannelConfigFields.tsx +++ b/src/components/channels/ChannelConfigFields.tsx @@ -1,4 +1,5 @@ import type { ReactNode } from 'react'; +import { useI18n } from '../../i18n'; import type { ChannelConfigFieldMeta, ChannelConfigFieldValueMap } from '../../lib/channel-types'; import ChannelTokenField from './ChannelTokenField'; @@ -145,10 +146,16 @@ export default function ChannelConfigFields({ showSecretLabel, hideSecretLabel, }: ChannelConfigFieldsProps) { + const { t } = useI18n(); + const resolvedEmptyStateText = emptyStateText ?? t('channels.modal.noAdditionalFields'); + const resolvedEnvVarLabel = envVarLabel ?? t('channels.modal.envVar'); + const resolvedShowSecretLabel = showSecretLabel ?? t('channels.modal.showSecret'); + const resolvedHideSecretLabel = hideSecretLabel ?? t('channels.modal.hideSecret'); + if (fields.length === 0) { return (
- {emptyStateText ?? 'No additional fields are required for this channel.'} + {resolvedEmptyStateText}
); } @@ -164,9 +171,9 @@ export default function ChannelConfigFields({ disabled, showSecretMap, onToggleSecret, - envVarLabel, - showSecretLabel, - hideSecretLabel, + envVarLabel: resolvedEnvVarLabel, + showSecretLabel: resolvedShowSecretLabel, + hideSecretLabel: resolvedHideSecretLabel, })} ))} diff --git a/src/components/channels/ChannelConfigModal.tsx b/src/components/channels/ChannelConfigModal.tsx index 7c5aa79..5daf431 100644 --- a/src/components/channels/ChannelConfigModal.tsx +++ b/src/components/channels/ChannelConfigModal.tsx @@ -1,8 +1,8 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { AlertCircle, CheckCircle2, X } from 'lucide-react'; import { useI18n } from '../../i18n'; -import type { ChannelConfigFieldMeta, ChannelConfigFieldValueMap } from '../../lib/channel-types'; -import { getChannelMeta, type ChannelMeta } from '../../lib/channel-meta'; +import type { ChannelConfigFieldValueMap } from '../../lib/channel-types'; +import { getChannelMeta, localizeChannelMeta } from '../../lib/channel-meta'; import ChannelConfigActions from './ChannelConfigActions'; import ChannelConfigFields from './ChannelConfigFields'; import ChannelInstructionsPanel from './ChannelInstructionsPanel'; @@ -51,44 +51,6 @@ export type ChannelConfigModalProps = { validatingLabel?: string; }; -function withTranslatedMeta( - meta: ChannelMeta, - translate: (path: string, fallback: string) => string, -): ChannelMeta { - return { - ...meta, - name: translate(`channels.meta.${meta.type}.name`, meta.name), - description: translate(`channels.meta.${meta.type}.description`, meta.description), - docsUrl: meta.docsUrl - ? translate(`channels.meta.${meta.type}.docsUrl`, meta.docsUrl) - : meta.docsUrl, - instructions: meta.instructions.map((instruction, index) => ( - translate(`channels.meta.${meta.type}.instructions.${index}`, instruction) - )), - configFields: meta.configFields.map((field) => withTranslatedField(meta.type, field, translate)), - }; -} - -function withTranslatedField( - channelType: string, - field: ChannelConfigFieldMeta, - translate: (path: string, fallback: string) => string, -): ChannelConfigFieldMeta { - return { - ...field, - label: translate(`channels.meta.${channelType}.fields.${field.key}.label`, field.label), - placeholder: field.placeholder - ? translate(`channels.meta.${channelType}.fields.${field.key}.placeholder`, field.placeholder) - : field.placeholder, - description: field.description - ? translate(`channels.meta.${channelType}.fields.${field.key}.description`, field.description) - : field.description, - docsUrl: field.docsUrl - ? translate(`channels.meta.${channelType}.fields.${field.key}.docsUrl`, field.docsUrl) - : field.docsUrl, - }; -} - export default function ChannelConfigModal({ open, selectedChannelType, @@ -107,16 +69,16 @@ export default function ChannelConfigModal({ validateLabel, validatingLabel, }: ChannelConfigModalProps) { - const { t, hasMessage } = useI18n(); + const { t } = useI18n(); const [showSecrets, setShowSecrets] = useState>({}); const firstFieldRef = useRef(null); const translate = useCallback((path: string, fallback: string) => ( - hasMessage(path) ? t(path) : fallback - ), [hasMessage, t]); + t(path, undefined, fallback) + ), [t]); const activeMeta = useMemo( - () => withTranslatedMeta(getChannelMeta(selectedChannelType), translate), + () => localizeChannelMeta(getChannelMeta(selectedChannelType), translate), [selectedChannelType, translate], ); const supportedFields = activeMeta.configFields.filter((field) => field.key !== 'accountId'); diff --git a/src/components/channels/ChannelTokenField.tsx b/src/components/channels/ChannelTokenField.tsx index c81b783..c5a1a51 100644 --- a/src/components/channels/ChannelTokenField.tsx +++ b/src/components/channels/ChannelTokenField.tsx @@ -1,4 +1,5 @@ import { Eye, EyeOff } from 'lucide-react'; +import { useI18n } from '../../i18n'; type ChannelTokenFieldProps = { label: string; @@ -27,7 +28,10 @@ export default function ChannelTokenField({ hideSecretLabel, required, }: ChannelTokenFieldProps) { + const { t } = useI18n(); const isSecretField = typeof showSecret === 'boolean' && typeof onToggleSecret === 'function'; + const resolvedShowSecretLabel = showSecretLabel ?? t('channels.modal.showSecret'); + const resolvedHideSecretLabel = hideSecretLabel ?? t('channels.modal.hideSecret'); return (
@@ -55,7 +59,7 @@ export default function ChannelTokenField({ onClick={onToggleSecret} disabled={disabled} className="inline-flex h-[56px] w-[56px] shrink-0 items-center justify-center rounded-[18px] border border-black/10 bg-[#fbf8f1] text-[#667085] transition-colors hover:bg-black/5 hover:text-[#171717] disabled:cursor-not-allowed disabled:opacity-60 dark:border-white/10 dark:bg-[#1a1a1e] dark:text-gray-400 dark:hover:bg-white/10 dark:hover:text-gray-100" - aria-label={showSecret ? (hideSecretLabel ?? 'Hide secret') : (showSecretLabel ?? 'Show secret')} + aria-label={showSecret ? resolvedHideSecretLabel : resolvedShowSecretLabel} > {showSecret ? : } diff --git a/src/components/channels/ChannelTypeSelector.tsx b/src/components/channels/ChannelTypeSelector.tsx index 6c06f00..aa9cd75 100644 --- a/src/components/channels/ChannelTypeSelector.tsx +++ b/src/components/channels/ChannelTypeSelector.tsx @@ -1,5 +1,7 @@ +import { useCallback, useMemo } from 'react'; import { BadgeCheck, ChevronDown, LayoutGrid, Plug, QrCode, Webhook } from 'lucide-react'; -import type { ChannelMeta } from '../../lib/channel-meta'; +import { useI18n } from '../../i18n'; +import { localizeChannelMeta, type ChannelMeta } from '../../lib/channel-meta'; type ChannelTypeSelectorProps = { label: string; @@ -31,9 +33,29 @@ export default function ChannelTypeSelector({ disabled, helpText, }: ChannelTypeSelectorProps) { - const selected = options.find((item) => item.type === value); + const { t } = useI18n(); + const translate = useCallback((path: string, fallback: string) => ( + t(path, undefined, fallback) + ), [t]); + + const localizedOptions = useMemo( + () => options.map((option) => localizeChannelMeta(option, translate)), + [options, translate], + ); + + const selected = localizedOptions.find((item) => item.type === value); const ConnectionIcon = getConnectionIcon(selected?.connectionType ?? helpText); + const connectionLabel = selected?.connectionType === 'qr' + ? t('channels.connectionType.qr') + : selected?.connectionType === 'webhook' + ? t('channels.connectionType.webhook') + : selected?.connectionType === 'plugin' + ? t('channels.page.pluginBadge') + : selected?.connectionType + ? t('channels.connectionType.token') + : helpText; + return (
@@ -44,7 +66,7 @@ export default function ChannelTypeSelector({
- {selected?.connectionType ?? helpText} + {connectionLabel}
@@ -56,7 +78,7 @@ export default function ChannelTypeSelector({ onChange={(event) => onChange(event.target.value)} className="h-[44px] w-full appearance-none rounded-[14px] border border-black/10 bg-white px-3 pr-10 text-[13px] text-foreground outline-none transition-colors focus:border-blue-500 disabled:cursor-not-allowed disabled:opacity-60 dark:border-white/10 dark:bg-[#101013] dark:text-gray-100" > - {options.map((option) => ( + {localizedOptions.map((option) => ( diff --git a/src/components/chat/ChatComposer.tsx b/src/components/chat/ChatComposer.tsx index 5b19570..726a7b1 100644 --- a/src/components/chat/ChatComposer.tsx +++ b/src/components/chat/ChatComposer.tsx @@ -1,5 +1,5 @@ import { useRef } from 'react'; -import { SendHorizontal, Square, X, Paperclip, FileText, Film, Music, FileArchive, File, Loader2, AtSign } from 'lucide-react'; +import { Paperclip, SendHorizontal, Square } from 'lucide-react'; import type { AttachedFileMeta } from '../../shared/chat-model'; import { useI18n } from '../../i18n'; @@ -63,7 +63,7 @@ export default function ChatComposer({ }} /> {attachments.length > 0 ? ( -
+
{attachments.map((attachment, index) => (
onRemoveAttachment(index)} + aria-label={t('conversation.composer.removeAttachment', { name: attachment.fileName })} > x @@ -81,7 +82,7 @@ export default function ChatComposer({ ))}
) : null} -
+
fileInputRef.current?.click()} + aria-label={t('conversation.composer.attachAria')} + title={t('conversation.composer.attachAria')} > @@ -106,6 +109,8 @@ export default function ChatComposer({ type="button" className="rounded-lg border border-[#E5E8EE] p-3 text-xs text-[#525866] transition-colors hover:border-[#2B7FFF] hover:text-[#2B7FFF] dark:border-[#2a2a2d] dark:text-gray-300" onClick={isSending ? onStop : onSend} + aria-label={isSending ? t('conversation.composer.stopAria') : t('conversation.composer.sendAria')} + title={isSending ? t('conversation.composer.stopAria') : t('conversation.composer.sendAria')} > {isSending ? : } diff --git a/src/components/chat/ChatHistoryPanel.tsx b/src/components/chat/ChatHistoryPanel.tsx index c767521..e56710c 100644 --- a/src/components/chat/ChatHistoryPanel.tsx +++ b/src/components/chat/ChatHistoryPanel.tsx @@ -1,17 +1,17 @@ import { memo, useEffect, useRef, useState } from 'react'; -import type { ChatHistoryBucket } from './types'; import { ChevronDown, LoaderCircle, - Plus, MoreHorizontal, PanelLeftClose, PanelLeftOpen, PencilLine, + Plus, Trash2, } from 'lucide-react'; - +import type { ChatHistoryBucket } from './types'; import blueLogo from '../../assets/images/login/blue_logo.png'; +import { useI18n } from '../../i18n'; type ChatHistoryPanelProps = { buckets: ChatHistoryBucket[]; @@ -40,6 +40,7 @@ function ChatHistoryPanel({ onRenameConversation, onDeleteConversation, }: ChatHistoryPanelProps) { + const { t } = useI18n(); const panelRef = useRef(null); const [collapsedBuckets, setCollapsedBuckets] = useState>({}); const [menuState, setMenuState] = useState(null); @@ -104,6 +105,9 @@ function ChatHistoryPanel({ }, [menuState]); const panelWidthClass = isCompact ? 'md:w-[70px] lg:w-[70px]' : 'md:w-[240px] lg:w-[252px]'; + const toggleSidebarLabel = isCompact + ? t('conversation.historyPanel.expandSidebar') + : t('conversation.historyPanel.collapseSidebar'); return (