diff --git a/src/components/Layout/SettingLayout.tsx b/src/components/Layout/SettingLayout.tsx index 627aaca..476dd90 100644 --- a/src/components/Layout/SettingLayout.tsx +++ b/src/components/Layout/SettingLayout.tsx @@ -4,6 +4,8 @@ import { Outlet, useLocation, useNavigate } from "react-router-dom"; import { BaseBreadcrumbs, type BreadcrumbItem } from '@/components/Breadcrumbs'; import SettingSidebar from './SettingSidebar'; import { Home as HomeIcon } from '@mui/icons-material'; +import { useTranslation } from 'react-i18next'; +import LanguageSwitcher from '../LanguageSwitcher'; const LayoutContainer = styled(Box)({ display: 'flex', @@ -21,6 +23,9 @@ const HeaderContainer = styled(Box)(({ theme }) => ({ padding: theme.spacing(2, 3), backgroundColor: theme.palette.background.paper, borderBottom: `1px solid ${theme.palette.divider}`, + display: 'flex', + justifyContent: 'space-between', + alignItems: 'center', })); const MainContent = styled(Box)({ @@ -29,23 +34,28 @@ const MainContent = styled(Box)({ padding: '24px', }); -// 设置页面路径映射 -const settingPathMap: Record = { - '/setting/profile': '个人资料', - '/setting/models': '模型配置', - '/setting/system': '系统设置', - '/setting/teams': '团队管理', - '/setting/mcp': 'MCP配置', -}; function SettingHeader() { const location = useLocation(); const navigate = useNavigate(); + const { t } = useTranslation(); + + + // 设置页面路径映射 + const settingPathMap: Record = { + '/setting/profile': t('setting.profile'), + '/setting/models': t('setting.model'), + '/setting/system': t('setting.system'), + '/setting/teams': t('setting.team'), + '/setting/mcp': t('setting.mcp'), + }; + + // 生成面包屑导航 const breadcrumbItems: BreadcrumbItem[] = [ { - label: '首页', + label: t('header.home'), path: '/', onClick: () => navigate('/'), }, @@ -65,6 +75,7 @@ function SettingHeader() { items={breadcrumbItems} sx={{ mb: 0 }} /> + ); } diff --git a/src/components/Layout/SettingSidebar.tsx b/src/components/Layout/SettingSidebar.tsx index eb4564a..1b3c2d1 100644 --- a/src/components/Layout/SettingSidebar.tsx +++ b/src/components/Layout/SettingSidebar.tsx @@ -8,6 +8,8 @@ import { SmartToy as SmartToyIcon, Extension as ExtensionIcon, } from '@mui/icons-material'; +import { useTranslation } from 'react-i18next'; + interface SettingMenuItem { key: string; @@ -16,40 +18,43 @@ interface SettingMenuItem { path: string; } -const settingMenuItems: SettingMenuItem[] = [ - { - key: 'profile', - label: '个人资料', - icon: PersonIcon, - path: '/setting/profile', - }, - { - key: 'models', - label: '模型配置', - icon: SmartToyIcon, - path: '/setting/models', - }, - { - key: 'system', - label: '系统设置', - icon: ComputerIcon, - path: '/setting/system', - }, - { - key: 'teams', - label: '团队管理', - icon: GroupIcon, - path: '/setting/teams', - }, - { - key: 'mcp', - label: 'MCP配置', - icon: ExtensionIcon, - path: '/setting/mcp', - }, -]; - const SettingSidebar: React.FC = () => { + + const { t } = useTranslation(); + const settingMenuItems: SettingMenuItem[] = [ + { + key: 'profile', + label: t('setting.profile'), + icon: PersonIcon, + path: '/setting/profile', + }, + { + key: 'models', + label: t('setting.model'), + icon: SmartToyIcon, + path: '/setting/models', + }, + { + key: 'system', + label: t('setting.system'), + icon: ComputerIcon, + path: '/setting/system', + }, + { + key: 'teams', + label: t('setting.team'), + icon: GroupIcon, + path: '/setting/teams', + }, + { + key: 'mcp', + label: t('setting.mcp'), + icon: ExtensionIcon, + path: '/setting/mcp', + }, + ]; + + const navigate = useNavigate(); const location = useLocation(); @@ -79,14 +84,14 @@ const SettingSidebar: React.FC = () => { letterSpacing: '0.5px', }} > - 设置 - - + {t('header.setting')} + + {settingMenuItems.map((item) => { const IconComponent = item.icon; const isActive = location.pathname === item.path; - + return ( { }, }} > - ); })} - + - You are a helpful assistant, an AI assistant specialized in problem-solving for the user. - If a specific domain is provided, adapt your expertise to that domain; otherwise, operate as a generalist. - - - 1. Understand the user’s request. - 2. Decompose it into logical subtasks. - 3. Execute each subtask step by step, reasoning transparently. - 4. Validate accuracy and consistency. - 5. Summarize the final result clearly. - `, - singleLineText: 'Single-line text', - multimodalModels: 'Multimodal Models', - textOnlyModels: 'Text-only Models', - allModels: 'All Models', - codeExecDescription: 'Write your custom Python or Javascript logic.', - stringTransformDescription: - 'Modifies text content. Currently supports: Splitting or concatenating text.', - foundation: 'Foundation', - tools: 'Tools', - dataManipulation: 'Data Manipulation', - flow: 'Flow', - dialog: 'Dialogue', - cite: 'Cite', - citeTip: 'citeTip', - name: 'Name', - nameMessage: 'Please input name', - description: 'Description', - descriptionMessage: 'This is an agent for a specific task.', - examples: 'Examples', - to: 'To', - msg: 'Messages', - msgTip: - 'Output the variable content of the upstream component or the text entered by yourself.', - messagePlaceholder: `Please enter your message content, use '/' to quickly insert variables.`, - messageMsg: 'Please input message or delete this field.', - addField: 'Add option', - addMessage: 'Add message', - loop: 'Loop', - loopTip: - 'Loop is the upper limit of the number of loops of the current component, when the number of loops exceeds the value of loop, it means that the component can not complete the current task, please re-optimize agent', - yes: 'Yes', - no: 'No', - key: 'Key', - componentId: 'Component ID', - add: 'Add', - operation: 'operation', - run: 'Run', - save: 'Save', - title: 'ID:', - beginDescription: 'This is where the flow begins.', - answerDescription: `A component that serves as the interface between human and bot, receiving user inputs and displaying the agent's responses.`, - retrievalDescription: `A component that retrieves information from specified knowledge bases (datasets). Ensure that the knowledge bases you select use the same embedding model.`, - generateDescription: `A component that prompts the LLM to generate responses. Ensure the prompt is set correctly.`, - categorizeDescription: `A component that uses the LLM to classify user inputs into predefined categories. Ensure you specify the name, description, and examples for each category, along with the corresponding next component.`, - relevantDescription: `A component that uses the LLM to assess whether the upstream output is relevant to the user's latest query. Ensure you specify the next component for each judge result.`, - rewriteQuestionDescription: `A component that rewrites a user query from the Interact component, based on the context of previous dialogues.`, - messageDescription: - 'This component returns the final data output of the workflow along with predefined message content. ', - keywordDescription: `A component that retrieves top N search results from user's input. Ensure the TopN value is set properly before use.`, - switchDescription: `A component that evaluates conditions based on the output of previous components and directs the flow of execution accordingly. It allows for complex branching logic by defining cases and specifying actions for each case or default action if no conditions are met.`, - wikipediaDescription: `A component that searches from wikipedia.org, using TopN to specify the number of search results. It supplements the existing knowledge bases.`, - promptText: `Please summarize the following paragraphs. Be careful with the numbers, do not make things up. Paragraphs as following: - {input} - The above is the content you need to summarize.`, - createGraph: 'Create agent', - createFromTemplates: 'Create from template', - retrieval: 'Retrieval', - generate: 'Generate', - answer: 'Interact', - categorize: 'Categorize', - relevant: 'Relevant', - rewriteQuestion: 'Rewrite', - rewrite: 'Rewrite', - begin: 'Begin', - message: 'Message', - blank: 'Blank', - createFromNothing: 'Create your agent from scratch', - addItem: 'Add Item', - addSubItem: 'Add Sub Item', - nameRequiredMsg: 'Name is required', - nameRepeatedMsg: 'The name cannot be repeated', - keywordExtract: 'Keyword', - keywordExtractDescription: `A component that extracts keywords from a user query, with Top N specifying the number of keywords to extract.`, - baidu: 'Baidu', - baiduDescription: `A component that searches from baidu.com, using TopN to specify the number of search results. It supplements the existing knowledge bases.`, - duckDuckGo: 'DuckDuckGo', - duckDuckGoDescription: - 'A component that searches from duckduckgo.com, allowing you to specify the number of search results using TopN. It supplements the existing knowledge bases.', - searXNG: 'SearXNG', - searXNGDescription: - 'A component that searches via your provided SearXNG instance URL. Specify TopN and the instance URL.', - channel: 'Channel', - channelTip: `Perform text search or news search on the component's input`, - text: 'Text', - news: 'News', - messageHistoryWindowSize: 'Message window size', - messageHistoryWindowSizeTip: - 'The window size of conversation history visible to the LLM. Larger is better, but be mindful of the maximum token limit of LLM.', - wikipedia: 'Wikipedia', - pubMed: 'PubMed', - pubMedDescription: - 'A component that searches from https://pubmed.ncbi.nlm.nih.gov/, allowing you to specify the number of search results using TopN. It supplements the existing knowledge bases.', - email: 'Email', - emailTip: - 'E-mail is a required field. You must input an E-mail address here.', - arXiv: 'ArXiv', - arXivDescription: - 'A component that searches from https://arxiv.org/, allowing you to specify the number of search results using TopN. It supplements the existing knowledge bases.', - sortBy: 'Sort by', - submittedDate: 'Submitted date', - lastUpdatedDate: 'Last updated date', - relevance: 'Relevance', - google: 'Google', - googleDescription: - 'A component that searches from https://www.google.com/, allowing you to specify the number of search results using TopN. It supplements the existing knowledge bases. Please note that this requires an API key from serpapi.com.', - bing: 'Bing', - bingDescription: - 'A component that searches from https://www.bing.com/, allowing you to specify the number of search results using TopN. It supplements the existing knowledge bases. Please note that this requires an API key from microsoft.com.', - apiKey: 'API KEY', - country: 'Country & Region', - language: 'Language', - googleScholar: 'Google Scholar', - googleScholarDescription: - 'A component that searches https://scholar.google.com/. You can use Top N to specify the number of search results.', - yearLow: 'Year low', - yearHigh: 'Year high', - patents: 'Patents', - data: 'Data', - deepL: 'DeepL', - deepLDescription: - 'A component that gets more specialized translations from https://www.deepl.com/.', - authKey: 'Auth key', - sourceLang: 'Source language', - targetLang: 'Target language', - gitHub: 'GitHub', - gitHubDescription: - 'A component that searches for repositories from https://github.com/. You can use Top N to specify the number of search results.', - baiduFanyi: 'BaiduFanyi', - baiduFanyiDescription: - 'A component that gets specialized translations from https://fanyi.baidu.com/.', - appid: 'App ID', - secretKey: 'Secret key', - domain: 'Domain', - transType: 'Translation type', - baiduSecretKeyOptions: { - translate: 'General translation', - fieldtranslate: 'Field translation', - }, - baiduDomainOptions: { - it: 'Information technology', - finance: 'Financial and economics', - machinery: 'Machinery manufacturing', - senimed: 'Biomedicine', - novel: 'Online literature', - academic: 'Academic paper', - aerospace: 'Aerospace', - wiki: 'Humanities and social sciences', - news: 'News and information', - law: 'Laws and regulations', - contract: 'Contract', - }, - baiduSourceLangOptions: { - auto: 'Auto detect', - zh: 'Chinese', - en: 'English', - yue: 'Cantonese', - wyw: 'Classical Chinese', - jp: 'Japanese', - kor: 'Korean', - fra: 'French', - spa: 'Spanish', - th: 'Thai', - ara: 'Arabic', - ru: 'Russian', - pt: 'Portuguese', - de: 'German', - it: 'Italian', - el: 'Greek', - nl: 'Dutch', - pl: 'Polish', - bul: 'Bulgarian', - est: 'Estonian', - dan: 'Danish', - fin: 'Finnish', - cs: 'Czech', - rom: 'Romanian', - slo: 'Slovenian', - swe: 'Swedish', - hu: 'Hungarian', - cht: 'Traditional Chinese', - vie: 'Vietnamese', - }, - qWeather: 'QWeather', - qWeatherDescription: - 'A component that retrieves weather information, such as temperature and air quality, from https://www.qweather.com/.', - lang: 'Language', - type: 'Type', - webApiKey: 'Web API key', - userType: 'User type', - timePeriod: 'Time period', - qWeatherLangOptions: { - zh: 'Simplified Chinese', - 'zh-hant': 'Traditional Chinese', - en: 'English', - de: 'German', - es: 'Spanish', - fr: 'French', - it: 'Italian', - ja: 'Japanese', - ko: 'Korean', - ru: 'Russian', - hi: 'Hindi', - th: 'Thai', - ar: 'Arabic', - pt: 'Portuguese', - bn: 'Bengali', - ms: 'Malay', - nl: 'Dutch', - el: 'Greek', - la: 'Latin', - sv: 'Swedish', - id: 'Indonesian', - pl: 'Polish', - tr: 'Turkish', - cs: 'Czech', - et: 'Estonian', - vi: 'Vietnamese', - fil: 'Filipino', - fi: 'Finnish', - he: 'Hebrew', - is: 'Icelandic', - nb: 'Norwegian', - }, - qWeatherTypeOptions: { - weather: 'Weather forecast', - indices: 'Weather life index', - airquality: 'Air quality', - }, - qWeatherUserTypeOptions: { - free: 'Free subscriber', - paid: 'Paid subscriber', - }, - qWeatherTimePeriodOptions: { - now: 'Now', - '3d': '3 days', - '7d': '7 days', - '10d': '10 days', - '15d': '12 days', - '30d': '30 days', - }, - publish: 'API', - exeSQL: 'Execute SQL', - exeSQLDescription: - 'A component that performs SQL queries on a relational database, supporting querying from MySQL, PostgreSQL, or MariaDB.', - dbType: 'Database Type', - database: 'Database', - username: 'Username', - host: 'Host', - port: 'Port', - password: 'Password', - switch: 'Switch', - logicalOperator: 'Logical operator', - switchOperatorOptions: { - equal: 'Equals', - notEqual: 'Not equal', - gt: 'Greater than', - ge: 'Greater equal', - lt: 'Less than', - le: 'Less equal', - contains: 'Contains', - notContains: 'Not contains', - startWith: 'Starts with', - endWith: 'Ends with', - empty: 'Is empty', - notEmpty: 'Not empty', - }, - switchLogicOperatorOptions: { - and: 'AND', - or: 'OR', - }, - operator: 'Operator', - value: 'Value', - useTemplate: 'Use', - wenCai: 'WenCai', - queryType: 'Query type', - wenCaiDescription: - 'A component that obtains financial information, including stock prices and funding news, from a wide range of financial websites.', - wenCaiQueryTypeOptions: { - stock: 'stock', - zhishu: 'index', - fund: 'fund', - hkstock: 'Hong Kong shares', - usstock: 'US stock market', - threeboard: 'New OTC Market', - conbond: 'Convertible Bond', - insurance: 'insurance', - futures: 'futures', - lccp: 'Financing', - foreign_exchange: 'Foreign currency', - }, - akShare: 'AkShare', - akShareDescription: - 'A component that obtains news about stocks from https://www.eastmoney.com/.', - yahooFinance: 'YahooFinance', - yahooFinanceDescription: - 'A component that queries information about a publicly traded company using its ticker symbol.', - crawler: 'Web Crawler', - crawlerDescription: - 'A component that crawls HTML source code from a specified URL.', - proxy: 'Proxy', - crawlerResultOptions: { - html: 'Html', - markdown: 'Markdown', - content: 'Content', - }, - extractType: 'Extract type', - info: 'Info', - history: 'History', - financials: 'Financials', - balanceSheet: 'Balance sheet', - cashFlowStatement: 'Cash flow statement', - jin10: 'Jin10', - jin10Description: - 'A component that retrieves financial information from the Jin10 Open Platform, including news updates, calendars, quotes, and references.', - flashType: 'Flash type', - filter: 'Filter', - contain: 'Contain', - calendarType: 'Calendar type', - calendarDatashape: 'Calendar datashape', - symbolsDatatype: 'Symbols datatype', - symbolsType: 'Symbols type', - jin10TypeOptions: { - flash: 'Quick News', - calendar: 'Calendar', - symbols: 'quotes', - news: 'reference', - }, - jin10FlashTypeOptions: { - '1': 'Market News', - '2': ' Futures News', - '3': 'US-Hong Kong News', - '4': 'A-Share News', - '5': 'Commodities & Forex News', - }, - jin10CalendarTypeOptions: { - cj: 'Macroeconomic Data Calendar', - qh: ' Futures Calendar', - hk: 'Hong Kong Stock Market Calendar', - us: 'US Stock Market Calendar', - }, - jin10CalendarDatashapeOptions: { - data: 'Data', - event: ' Event', - holiday: 'Holiday', - }, - jin10SymbolsTypeOptions: { - GOODS: 'Commodity Quotes', - FOREX: ' Forex Quotes', - FUTURE: 'International Market Quotes', - CRYPTO: 'Cryptocurrency Quotes', - }, - jin10SymbolsDatatypeOptions: { - symbols: 'Commodity List', - quotes: ' Latest Market Quotes', - }, - concentrator: 'Concentrator', - concentratorDescription: - 'A component that receives the output from the upstream component and passes it on as input to the downstream components.', - tuShare: 'TuShare', - tuShareDescription: - 'A component that obtains financial news briefs from mainstream financial websites, aiding industry and quantitative research.', - tuShareSrcOptions: { - sina: 'Sina', - wallstreetcn: 'wallstreetcn', - '10jqka': 'Straight flush', - eastmoney: 'Eastmoney', - yuncaijing: 'YUNCAIJING', - fenghuang: 'FENGHUANG', - jinrongjie: 'JRJ', - }, - token: 'Token', - src: 'Source', - startDate: 'Start date', - endDate: 'End date', - keyword: 'Keyword', - note: 'Note', - noteDescription: 'Note', - notePlaceholder: 'Please enter a note', - invoke: 'HTTP Request', - invokeDescription: `A component capable of calling remote services, using other components' outputs or constants as inputs.`, - url: 'Url', - method: 'Method', - timeout: 'Timeout', - headers: 'Headers', - cleanHtml: 'Clean HTML', - cleanHtmlTip: - 'If the response is HTML formatted and only the primary content wanted, please toggle it on.', - reference: 'Reference', - input: 'Input', - output: 'Output', - parameter: 'Parameter', - howUseId: 'How to use agent ID?', - content: 'Content', - operationResults: 'Operation Results', - autosaved: 'Autosaved', - optional: 'Optional', - pasteFileLink: 'Paste file link', - testRun: 'Test Run', - template: 'Template', - templateDescription: - 'A component that formats the output of other components.1. Supports Jinja2 templates, will first convert the input to an object and then render the template, 2. Simultaneously retains the original method of using {parameter} string replacement', - emailComponent: 'Email', - emailDescription: 'Send an email to a specified address.', - smtpServer: 'SMTP Server', - smtpPort: 'SMTP Port', - senderEmail: 'Sender Email', - authCode: 'Authorization Code', - senderName: 'Sender Name', - toEmail: 'Recipient Email', - ccEmail: 'CC Email', - emailSubject: 'Subject', - emailContent: 'Content', - smtpServerRequired: 'Please input SMTP server address', - senderEmailRequired: 'Please input sender email', - authCodeRequired: 'Please input authorization code', - toEmailRequired: 'Please input recipient email', - emailContentRequired: 'Please input email content', - emailSentSuccess: 'Email sent successfully', - emailSentFailed: 'Failed to send email', - dynamicParameters: 'Dynamic Parameters', - jsonFormatTip: - 'Upstream component should provide JSON string in following format:', - toEmailTip: 'to_email: Recipient email (Required)', - ccEmailTip: 'cc_email: CC email (Optional)', - subjectTip: 'subject: Email subject (Optional)', - contentTip: 'content: Email content (Optional)', - jsonUploadTypeErrorMessage: 'Please upload json file', - jsonUploadContentErrorMessage: 'json file error', - iteration: 'Iteration', - iterationDescription: `A looping component that iterates over an input array and executes a defined logic for each item.`, - delimiterTip: ` -This delimiter is used to split the input text into several text pieces echo of which will be performed as input item of each iteration.`, - delimiterOptions: { - comma: 'Comma', - lineBreak: 'Line break', - tab: 'Tab', - underline: 'Underline', - diagonal: 'Forward slash', - minus: 'Dash', - semicolon: 'Semicolon', - }, - addVariable: 'Add variable', - variableSettings: 'Variable settings', - globalVariables: 'Global variables', - systemPrompt: 'System prompt', - userPrompt: 'User prompt', - addCategory: 'Add category', - categoryName: 'Category name', - nextStep: 'Next step', - variableExtractDescription: - 'Extract user information into global variable throughout the conversation', - variableExtract: 'Variables', - variables: 'Variables', - variablesTip: `Set the clear json key variable with a value of empty. e.g. - { - "UserCode":"", - "NumberPhone":"" - }`, - datatype: 'MINE type of the HTTP request', - insertVariableTip: `Enter / Insert variables`, - historyversion: 'Version history', - filename: 'File name', - version: { - created: 'Created', - details: 'Version details', - dsl: 'DSL', - download: 'Download', - version: 'Version', - select: 'No version selected', - }, - setting: 'Settings', - settings: { - agentSetting: 'Agent settings', - title: 'title', - description: 'description', - upload: 'Upload', - photo: 'Photo', - permissions: 'Permissions', - permissionsTip: 'You can set the permissions of the team members here.', - me: 'me', - team: 'Team', - }, - noMoreData: 'No more data', - searchAgentPlaceholder: 'Search agent', - footer: { - profile: 'All rights reserved @ React', - }, - layout: { - file: 'file', - knowledge: 'knowledge', - chat: 'chat', - }, - prompt: 'Prompt', - promptTip: - 'Use the system prompt to describe the task for the LLM, specify how it should respond, and outline other miscellaneous requirements. The system prompt is often used in conjunction with keys (variables), which serve as various data inputs for the LLM. Use a forward slash `/` or the (x) button to show the keys to use.', - promptMessage: 'Prompt is required', - infor: 'Information run', - knowledgeBasesTip: - 'Select the knowledge bases to associate with this chat assistant, or choose variables containing knowledge base IDs below.', - knowledgeBaseVars: 'Knowledge base variables', - code: 'Code', - codeDescription: 'It allows developers to write custom Python logic.', - inputVariables: 'Input variables', - runningHintText: 'is running...🕞', - openingSwitch: 'Opening switch', - openingCopy: 'Opening greeting', - openingSwitchTip: - 'Your users will see this welcome message at the beginning.', - modeTip: 'The mode defines how the workflow is initiated.', - mode: 'Mode', - conversational: 'conversational', - task: 'task', - beginInputTip: - 'By defining input parameters, this content can be accessed by other components in subsequent processes.', - query: 'Query variables', - queryTip: 'Select the variable you want to use', - agent: 'Agent', - addAgent: 'Add Agent', - agentDescription: - 'Builds agent components equipped with reasoning, tool usage, and multi-agent collaboration. ', - maxRecords: 'Max records', - createAgent: 'Agent flow', - stringTransform: 'Text Processing', - userFillUp: 'Await Response', - userFillUpDescription: `Pauses the workflow and waits for the user's message before continuing.`, - codeExec: 'Code', - tavilySearch: 'Tavily Search', - tavilySearchDescription: 'Search results via Tavily service.', - tavilyExtract: 'Tavily Extract', - tavilyExtractDescription: 'Tavily Extract', - log: 'Log', - management: 'Management', - import: 'Import', - export: 'Export', - seconds: 'Seconds', - subject: 'Subject', - tag: 'Tag', - tagPlaceholder: 'Please enter tag', - descriptionPlaceholder: 'Please enter description', - line: 'Single-line text', - paragraph: 'Paragraph text', - options: 'Dropdown options', - file: 'File upload', - integer: 'Number', - boolean: 'Boolean', - - logTimeline: { - begin: 'Ready to begin', - agent: 'Agent is thinking', - userFillUp: 'Waiting for you', - retrieval: 'Looking up knowledge', - message: 'Agent says', - awaitResponse: 'Waiting for you', - switch: 'Choosing the best path', - iteration: 'Batch processing', - categorize: 'Categorising info', - code: 'Running a quick script', - textProcessing: 'Tidying up text', - tavilySearch: 'Searching the web', - tavilyExtract: 'Reading the page', - exeSQL: 'Querying database', - google: 'Searching the web', - wikipedia: 'Searching Wikipedia', - googleScholar: 'Academic search', - gitHub: 'Searching GitHub', - email: 'Sending email', - httpRequest: 'Calling an API', - wenCai: 'Querying financial data', - }, - goto: 'Fail Branch', - comment: 'Default Value', - sqlStatement: 'SQL Statement', - sqlStatementTip: - 'Write your SQL query here. You can use variables, raw SQL, or mix both using variable syntax.', - frameworkPrompts: 'Framework', - release: 'Publish', - createFromBlank: 'Create from blank', - createFromTemplate: 'Create from template', - importJsonFile: 'Import JSON file', - ceateAgent: 'Agent flow', - createPipeline: 'Data pipeline', - chooseAgentType: 'Choose Agent Type', - }, llmTools: { bad_calculator: { name: 'Calculator', @@ -1618,6 +942,27 @@ This delimiter is used to split the input text into several text pieces echo of toolsAvailable: 'tools available', mcpServers: 'MCP Servers', customizeTheListOfMcpServers: 'Customize the list of MCP servers', + searchPlaceholder: 'Search MCP servers...', + deleteSelected: 'Delete', + exportSelected: 'Export', + type: 'Type', + updateTime: 'Update Time', + totalItems: 'Total {{count}} items', + importTitle: 'Import MCP Servers', + importDescription: 'Paste your MCP servers JSON configuration below. The format should match the Mock.json structure.', + jsonConfiguration: 'JSON Configuration', + deleteSuccess: 'Delete successful', + deleteFailed: 'Delete failed', + batchDeleteSuccess: 'Batch delete successful', + batchDeleteFailed: 'Batch delete failed', + exportSuccess: 'Export successful', + exportFailed: 'Export failed', + serverIdRequired: 'Server ID cannot be empty', + mcpServerUpdateSuccess: 'MCP server updated successfully', + mcpServerCreateSuccess: 'MCP server created successfully', + operationFailed: 'Operation failed', + testSuccess: 'Test successful', + jsonFormatError: 'JSON format error', }, search: { searchApps: 'Search Apps', diff --git a/src/locales/zh.ts b/src/locales/zh.ts index a572831..733fd62 100644 --- a/src/locales/zh.ts +++ b/src/locales/zh.ts @@ -47,6 +47,8 @@ export default { noDataFound: '没有找到数据。', noData: '暂无数据', promptPlaceholder: '请输入或使用 / 快速插入变量。', + update: '更新', + configure: '配置', }, login: { login: '登录', @@ -499,172 +501,85 @@ General:实体和关系提取提示来自 GitHub - microsoft/graphrag:基于 disable: '禁用', delete: '删除', }, - chat: { - messagePlaceholder: '请输入消息...', - exit: '退出', - multipleModels: '多模型', - applyModelConfigs: '应用模型配置', - conversations: '会话', - chatApps: '聊天', - createChat: '创建聊天', - newConversation: '新会话', - createAssistant: '新建助理', - assistantSetting: '助理设置', - promptEngine: '提示引擎', - modelSetting: '模型设置', - chat: '聊天', - newChat: '新建聊天', - send: '发送', - sendPlaceholder: '给助理发送消息...', - chatConfiguration: '聊天配置', - chatConfigurationDescription: '为你的知识库配置专属聊天助手! 💕', - assistantName: '助理姓名', - assistantNameMessage: '助理姓名是必填项', - namePlaceholder: '例如 贾维斯简历', - assistantAvatar: '助理头像', - language: '语言', - emptyResponse: '空回复', - emptyResponseTip: `如果在知识库中没有检索到用户的问题,它将使用它作为答案。 如果您希望 LLM 在未检索到任何内容时提出自己的意见,请将此留空。`, - emptyResponseMessage: `当知识库中未检索到任何相关信息时,将触发空响应。由于未选择任何知识库,因此请清除“空响应”。`, - setAnOpener: '设置开场白', - setAnOpenerInitial: `你好! 我是你的助理,有什么可以帮到你的吗?`, - setAnOpenerTip: '您想如何欢迎您的客户?', - knowledgeBases: '知识库', - knowledgeBasesMessage: '请选择', - knowledgeBasesTip: - '选择关联的知识库。新建或空知识库不会在下拉菜单中显示。', - system: '系统提示词', - systemInitialValue: `你是一个智能助手,请总结知识库的内容来回答问题,请列举知识库中的数据详细回答。当所有知识库内容都与问题无关时,你的回答必须包括“知识库中未找到您要的答案!”这句话。回答需要考虑聊天历史。 - 以下是知识库: - {knowledge} - 以上是知识库。`, - systemMessage: '请输入', - systemTip: - '当LLM回答问题时,你需要LLM遵循的说明,比如角色设计、答案长度和答案语言等。如果您的模型原生支持在问答中推理,可以通过 //no_thinking 关闭自动推理。', - topN: 'Top N', - topNTip: `并非所有相似度得分高于“相似度阈值”的块都会被提供给大语言模型。 LLM 只能看到这些“Top N”块。`, - variable: '变量', - variableTip: `你可以通过对话 API,并配合变量设置来动态调整大模型的系统提示词。 - {knowledge}为系统预留变量,代表从指定知识库召回的文本块。 - “系统提示词”中的所有变量都必须用大括号{}括起来。详见 https://ragflow.io/docs/dev/set_chat_variables。`, - add: '新增', - key: '关键字', - optional: '可选的', - operation: '操作', - model: '模型', - modelTip: '大语言聊天模型', - modelMessage: '请选择', - modelEnabledTools: '可用的工具', - modelEnabledToolsTip: - '请选择一个或多个可供该模型所使用的工具。仅对支持工具调用的模型生效。', - freedom: '自由度', - improvise: '即兴创作', - precise: '精确', - balance: '平衡', - custom: '自定义', - freedomTip: `“精确”意味着大语言模型会保守并谨慎地回答你的问题。 “即兴发挥”意味着你希望大语言模型能够自由地畅所欲言。 “平衡”是谨慎与自由之间的平衡。`, - temperature: '温度', - temperatureMessage: '温度是必填项', - temperatureTip: - '该参数控制模型预测的随机性。 较低的温度使模型对其响应更有信心,而较高的温度则使其更具创造性和多样性。', - topP: 'Top P', - topPMessage: 'Top P 是必填项', - topPTip: - '该参数也称为“核心采样”,它设置一个阈值来选择较小的单词集进行采样。 它专注于最可能的单词,剔除不太可能的单词。', - presencePenalty: '存在处罚', - presencePenaltyMessage: '存在处罚是必填项', - presencePenaltyTip: - '这会通过惩罚对话中已经出现的单词来阻止模型重复相同的信息。', - frequencyPenalty: '频率惩罚', - frequencyPenaltyMessage: '频率惩罚是必填项', - frequencyPenaltyTip: - '与存在惩罚类似,这减少了模型频繁重复相同单词的倾向。', - maxTokens: '最大token数', - maxTokensMessage: '最大token数是必填项', - maxTokensTip: - '这设置了模型输出的最大长度,以标记(单词或单词片段)的数量来衡量。', - maxTokensInvalidMessage: '请输入有效的最大令牌数。', - maxTokensMinMessage: '最大令牌数不能小于 0。', - quote: '显示引文', - quoteTip: '是否应该显示原文出处?', - selfRag: 'Self-RAG', - selfRagTip: '请参考: https://huggingface.co/papers/2310.11511', - overview: '聊天 ID', - pv: '消息数', - uv: '活跃用户数', - speed: 'Token 输出速度', - tokens: '消耗Token数', - round: '会话互动数', - thumbUp: '用户满意度', - preview: '预览', - embedded: '嵌入', - serviceApiEndpoint: '服务API端点', - apiKey: 'API KEY', - apiReference: 'API 文档', - dateRange: '日期范围:', - backendServiceApi: 'API 服务器', - createNewKey: '创建新密钥', - created: '创建于', - action: '操作', - embedModalTitle: '嵌入网站', - comingSoon: '即将推出', - fullScreenTitle: '全屏嵌入', - fullScreenDescription: '将以下iframe嵌入您的网站处于所需位置', - partialTitle: '部分嵌入', - extensionTitle: 'Chrome 插件', - tokenError: '请先创建 API Token!', - betaError: '请先在系统设置中申请API密钥。', - searching: '搜索中', - parsing: '解析中', - uploading: '上传中', - uploadFailed: '上传失败', - regenerate: '重新生成', - read: '朗读内容', - tts: '文本转语音', - ttsTip: '是否用语音转换播放语音,请先在设置里面选择TTS(语音转换模型)。', - relatedQuestion: '相关问题', - answerTitle: '智能回答', - multiTurn: '多轮对话优化', - multiTurnTip: - '在多轮对话时,对查询问题根据上下文进行优化。会调用大模型额外消耗 token。', - howUseId: '如何使用聊天ID?', - description: '助理描述', - descriptionPlaceholder: - '例如 你是一个专业的简历助手,只能回答简历的问题。', - useKnowledgeGraph: '使用知识图谱', - useKnowledgeGraphTip: - '是否检索与所选知识库对应的知识图谱相关文本块,以处理复杂的多跳问题?这一过程将涉及对实体、关系和社区报告文本块的多次检索,会显著延长检索时间。', - keyword: '关键词分析', - keywordTip: `应用 LLM 分析用户的问题,提取在相关性计算中要强调的关键词。对长查询效果较好,但会延长响应时间。`, - reasoning: '推理', - reasoningTip: - '在问答过程中是否启用推理工作流,例如Deepseek-R1或OpenAI o1等模型所采用的方式。启用后,该功能允许模型访问外部知识,并借助思维链推理等技术逐步解决复杂问题。通过将问题分解为可处理的步骤,这种方法增强了模型提供准确回答的能力,从而在需要逻辑推理和多步思考的任务上表现更优。', - tavilyApiKeyTip: - '如果 API 密钥设置正确,它将利用 Tavily 进行网络搜索作为知识库的补充。', - tavilyApiKeyMessage: '请输入你的 Tavily API Key', - tavilyApiKeyHelp: '如何获取?', - crossLanguage: '跨语言搜索', - crossLanguageTip: `选择一种或多种语言进行跨语言搜索。如果未选择任何语言,系统将使用原始查询进行搜索。`, - metadata: '元数据', - metadataTip: - '元数据过滤是使用元数据属性(例如标签、类别或访问权限)来优化和控制系统内相关信息检索的过程。', - conditions: '条件', - addCondition: '增加条件', - meta: { - disabled: '禁用', - automatic: '自动', - manual: '手动', - }, - cancel: '取消', - chatSetting: '聊天设置', - avatarHidden: '隐藏头像', - locale: '地区', - }, setting: { profile: '概要', - avatar: '头像', avatarTip: '这会在你的个人主页展示', profileDescription: '在此更新您的照片和个人详细信息。', + accountSecurity: '账户安全', + passwordUpdateTip: '定期更新密码有助于保护您的账户安全', + modelSettings: '模型设置', + modelSettingsDescription: '管理您的 LLM 模型工厂和个人模型配置', + setDefaultModel: '设置默认模型', + myLlmModels: '我的 LLM 模型', + noModelsConfigured: '您还没有配置任何 LLM 模型。请在下方的模型工厂中进行配置。', + llmModelFactories: 'LLM 模型工厂', + confirmDelete: '确认删除', + confirmDeleteModel: '是否确认删除模型 {{modelName}}?', + confirmDeleteFactory: '是否确认删除模型工厂 {{factoryName}}?', + edit: '编辑', + delete: '删除', + docEngine: 'Doc Engine', + objectStorage: 'Object Storage', + redis: 'Redis', + database: 'Database', + elasticsearch: 'Elasticsearch', + taskExecutor: 'Task Executor', + systemStatus: '系统状态', + systemStatusDescription: '查看系统各个组件的运行状态和性能指标', + noSystemStatusData: '暂无系统状态数据', + // 密码修改对话框 + currentPasswordRequired: '请输入当前密码', + newPasswordRequired: '请输入新密码', + passwordMinLength: '新密码长度至少6位', + confirmPasswordRequired: '请确认新密码', + passwordMismatch: '两次输入的密码不一致', + newPasswordSameAsCurrent: '新密码不能与当前密码相同', + passwordChangeSuccess: '密码修改成功', + currentPasswordIncorrect: '当前密码不正确', + passwordChangeError: '修改密码失败,请稍后重试', + passwordSecurityTip: '为了您的账户安全,请设置一个强密码。密码长度至少6位,建议包含字母、数字和特殊字符。', + currentPassword: '当前密码', + newPassword: '新密码', + confirmNewPassword: '确认新密码', + cancel: '取消', + changing: '修改中...', + confirmChange: '确认修改', + // 个人资料表单 + personalProfile: '个人资料', + pleaseSelectImageFile: '请选择图片文件', + imageSizeLimit: '图片大小不能超过2MB', + usernameRequired: '用户名不能为空', + profileUpdateSuccess: '个人信息更新成功', + updateFailed: '更新失败,请重试', + avatar: '头像', + uploadAvatar: '上传头像', + avatarFormatTip: '支持 JPG、PNG 格式,文件大小不超过 2MB', + username: '用户名', + email: '邮箱', + emailNotEditable: '邮箱地址不可修改', + // API Key Dialog + apiKeyRequired: 'API Key 是必填项', + baseUrlOptional: '可选,自定义 API 端点', + minimaxGroupId: 'Minimax 专用的 Group ID', + addModel: '添加模型', + nameRequired: '名称不能为空', + urlRequired: 'URL不能为空', + urlFormatInvalid: 'URL格式不正确', + serverTypeRequired: '请选择服务器类型', + testConnection: '测试连接', + testing: '测试中...', + connectionSuccess: '连接成功!发现 {count} 个工具', + availableTools: '可用工具', + connectionFailed: '连接失败', + testFailed: '测试失败', + testConnectionError: '测试连接时发生错误', + saving: '保存中...', + authTokenOptional: '可选:用于身份验证的令牌', + fillUrlFirst: '请先填写 URL', + language: '语言', + timezone: '时区', + save: '保存', + // LLM 工厂卡片 maxTokens: '最大token数', maxTokensMessage: '最大token数是必填项', maxTokensTip: @@ -679,7 +594,6 @@ General:实体和关系提取提示来自 GitHub - microsoft/graphrag:基于 system: '系统', logout: '登出', api: 'API', - username: '用户名', usernameMessage: '请输入用户名', photo: '头像', photoDescription: '这将显示在您的个人资料上。', @@ -688,21 +602,16 @@ General:实体和关系提取提示来自 GitHub - microsoft/graphrag:基于 colorSchemaPlaceholder: '请选择您的主题!', bright: '明亮', dark: '暗色', - timezone: '时区', timezoneMessage: '请选择时区', timezonePlaceholder: '请选择时区', - email: '邮箱地址', emailDescription: '一旦注册,电子邮件将无法更改。', - currentPassword: '当前密码', currentPasswordMessage: '请输入当前密码', - newPassword: '新密码', changePassword: '修改密码', newPasswordMessage: '请输入新密码', newPasswordDescription: '您的新密码必须超过 8 个字符。', confirmPassword: '确认新密码', confirmPasswordMessage: '请确认新密码', confirmPasswordNonMatchMessage: '您输入的新密码不匹配!', - cancel: '取消', addedModels: '添加了的模型', modelsToBeAdded: '待添加的模型', addTheModel: '添加模型', @@ -711,12 +620,83 @@ General:实体和关系提取提示来自 GitHub - microsoft/graphrag:基于 apiKeyTip: 'API key可以通过注册相应的LLM供应商来获取。', showMoreModels: '展示更多模型', hideModels: '隐藏模型', - baseUrl: 'Base-Url', baseUrlTip: '如果您的 API 密钥来自 OpenAI,请忽略它。 任何其他中间提供商都会提供带有 API 密钥的基本 URL。', tongyiBaseUrlTip: '对于中国用户,不需要填写或使用 https://dashscope.aliyuncs.com/compatible-mode/v1。对于国际用户,使用 https://dashscope-intl.aliyuncs.com/compatible-mode/v1。', tongyiBaseUrlPlaceholder: '(仅国际用户需要)', + // Dialog translations + addLlm: '添加 LLM', + editLlm: '编辑 LLM', + configureLlm: '配置 LLM', + howToIntegrate: '如何集成', + // 模型配置翻译 + modelType: '模型类型', + modelName: '模型名称', + modelNameHelperText: '请输入模型名称', + modelNamePlaceholder: '请输入模型名称', + ollamaModelNamePlaceholder: '例如: llama2, mistral', + baseUrl: '基础 URL', + baseUrlHelperText: '基础 URL', + baseUrlValidationMessage: '基础 URL 必须是有效的 URL', + azureOpenAIEndpointHelperText: 'Azure OpenAI 服务的端点 URL', + azureAPIVersionHelperText: 'Azure OpenAI API 版本', + apiVersionRequired: 'API Version 是必填项', + apiKeyHelperText: '输入api key(如果是本地部署的模型,请忽略)', + apiKeyOptional: 'API Key (可选)', + apiKeyOptionalPlaceholder: '如果需要认证,请输入 API Key', + apiKeyPlaceholder: '请输入 API KEY', + secretKeyPlaceholder: '请输入 SECRET KEY', + accessKeyPlaceholder: '请输入 ACCESS KEY', + maxTokensPlaceholder: '设置了模型输出的最大长度,以token(单词片段)的数量表示', + maxTokensHelperText: '设置了模型输出的最大长度,以token(单词片段)的数量表示', + maxTokensSupportedHelperText: '模型支持的最大 Token 数', + maxTokensMaxMessage: '最大token数不能超过100000', + // 百度一言 + baiduYiYanAPIKey: '一言 API KEY', + baiduYiYanSecretKey: '一言 Secret KEY', + // Fish Audio + fishAudioRefIdPlaceholder: '请输入 Refrence ID', + // Google Cloud + googleProjectIdPlaceholder: '请输入 Project ID', + googleCloudRegion: 'Google Cloud 区域', + googleCloudRegionPlaceholder: '请输入 Google Cloud 区域', + googleCloudRegionHelperText: 'Google Cloud 区域', + googleServiceAccountKeyPlaceholder: '请输入 Google Cloud Service Account Key', + // 腾讯云 + tencentSecretId: '腾讯云 Secret ID', + tencentSecretKey: '腾讯云 Secret KEY', + secretIdPlaceholder: '请输入 Secret ID', + tencentSecretIdHelperText: '腾讯云 Secret ID', + tencentSecretKeyHelperText: '腾讯云 Secret KEY', + // 腾讯混元 + hunyuanSecretId: '混元 Secret ID', + hunyuanSecretKey: '混元 Secret KEY', + hunyuanSecretIdHelperText: '混元 Secret ID', + hunyuanSecretKeyHelperText: '混元 Secret KEY', + // 讯飞星火 + xunfeiSparkAPIPassword: '讯飞星火 API Password', + apiPasswordPlaceholder: '请输入 API Password', + xunfeiSparkAPIPasswordHelperText: '讯飞星火 API Password', + // 火山引擎 + modelEndpointId: '模型 EndpointID', + endpointIdPlaceholder: '请输入 EndpointID', + modelEndpointIdHelperText: '模型 EndpointID', + volcEngineARKAPIKey: '火山 ARK_API_KEY', + arkApiKeyPlaceholder: '请输入 ARK_API_KEY', + modelARKAPIKeyHelperText: '模型 ARK_API_KEY', + accessKey: 'ACCESS KEY', + secretKey: 'SECRET KEY', + awsRegion: 'AWS Region', + maxTokensValidation: '请输入有效数字', + maxTokensMin: '最大token数必须大于0', + documentationLink: '查看文档', + confirm: '确定', + required: '此项为必填项', + baseUrlPlaceholder: '请输入基础 URL', + systemModelName: '系统模型名称', + systemModelNamePlaceholder: '请输入系统模型名称', + submitFailed: '提交失败', modify: '修改', systemModelSettings: '设置默认模型', chatModel: '聊天模型', @@ -740,10 +720,8 @@ General:实体和关系提取提示来自 GitHub - microsoft/graphrag:基于 addLlmTitle: '添加 LLM', editLlmTitle: '编辑 {{name}} 模型', editModel: '编辑模型', - modelName: '模型名称', modelID: '模型ID', modelUid: '模型UID', - modelType: '模型类型', addLlmBaseUrl: '基础 Url', vision: '是否支持 Vision', modelNameMessage: '请输入模型名称!', @@ -821,7 +799,8 @@ General:实体和关系提取提示来自 GitHub - microsoft/graphrag:基于 sureDelete: '您确定要删除该成员吗?', quit: '退出', sureQuit: '确定退出加入的团队吗?', - secretKey: '密钥', + inviting: '邀请中...', + emailPlaceholder: '请输入邀请用户的邮箱地址', publicKey: '公钥', secretKeyMessage: '请输入私钥', publicKeyMessage: '请输入公钥', @@ -893,615 +872,6 @@ General:实体和关系提取提示来自 GitHub - microsoft/graphrag:基于 destinationFolder: '目标文件夹', pleaseUploadAtLeastOneFile: '请上传至少一个文件', }, - flow: { - recommended: '推荐', - customerSupport: '客户支持', - marketing: '营销', - consumerApp: '消费者应用', - other: '其他', - agents: '智能体', - beginInput: '开始输入', - seconds: '秒', - ref: '引用变量', - stockCode: '股票代码', - apiKeyPlaceholder: '您的API密钥(从https://serpapi.com获取)', - flowStart: '开始', - flowNum: '编号', - test: '测试', - extractDepth: '深度提取', - format: '格式', - basic: '基本', - advanced: '高级', - general: '通用', - searchDepth: '深度搜索', - tavilyTopic: 'Tavily话题', - maxResults: '最大结果数', - includeAnswer: '包含答案', - includeRawContent: '包含原始内容', - includeImages: '包含图片', - includeImageDescriptions: '包含图片描述', - includeDomains: '包含域名', - ExcludeDomains: '排除域名', - days: '天数', - comma: '逗号', - semicolon: '分号', - period: '句点', - linebreak: '换行符', - tab: '制表符', - space: '空格', - delimiters: '分隔符', - merge: '合并', - split: '拆分', - script: '脚本', - iterationItemDescription: - '它是迭代过程中的当前元素,可以被后续流程引用和操作。', - guidingQuestion: '引导问题', - onFailure: '异常时', - userPromptDefaultValue: - 'This is the order you need to send to the agent.', - descriptionMessage: '这是一个用于特定任务的代理。', - search: '搜索', - communication: '通信', - developer: '开发者', - typeCommandOrsearch: '输入命令或或搜索...', - builtIn: '内置', - goto: '异常分支', - comment: '默认值', - ExceptionDefaultValue: '异常处理默认值', - exceptionMethod: '异常处理方法', - maxRounds: '最大反思轮数', - delayEfterError: '错误后延迟', - maxRetries: '最大反思轮数', - advancedSettings: '高级设置', - addTools: '添加工具', - sysPromptDefultValue: ` - - 你是一名乐于助人的助手,一名专注于为用户解决问题的 AI 助手。 - 如果用户指定了特定领域,你需要在该领域展现专业性;如果没有,则以通用助手的方式工作。 - - - 1. 理解用户请求。 - 2. 将其分解为逻辑子任务。 - 3. 逐步执行每个子任务,并清晰地进行推理。 - 4. 验证准确性和一致性。 - 5. 清晰地总结最终结果。 -`, - line: '单行文本', - paragraph: '段落文字', - options: '选项', - file: '文件', - integer: '数字', - boolean: '布尔值', - name: '名称', - singleLineText: '单行文本', - variableSettings: '变量设置', - multimodalModels: '多模态模型', - textOnlyModels: '仅文本模型', - allModels: '所有模型', - codeExecDescription: '用 Python 或者 Javascript 编写自定义逻辑', - stringTransformDescription: - '修改文本内容,目前支持文本分割、文本拼接操作', - foundation: '基础', - tools: '工具', - dataManipulation: '数据操控', - dialog: '对话', - flow: '工作流', - noMoreData: '没有更多数据了', - historyversion: '历史版本', - version: { - details: '版本详情', - download: '下载', - }, - cite: '引用', - citeTip: '引用', - nameMessage: '请输入名称', - description: '描述', - examples: '示例', - to: '下一步', - msg: '消息', - msgTip: '输出上游组件的变量内容或者自己输入的文本。', - messagePlaceholder: '请输入您的消息内容,使用‘/’快速插入变量。', - messageMsg: '请输入消息或删除此字段。', - addField: '新增字段', - addMessage: '新增消息', - loop: '循环上限', - loopTip: - 'loop为当前组件循环次数上限,当循环次数超过loop的值时,说明组件不能完成当前任务,请重新优化agent', - yes: '是', - no: '否', - key: '键', - componentId: '组件ID', - add: '新增', - operation: '操作', - run: '运行', - save: '保存', - title: 'ID:', - beginDescription: '这是流程开始的地方', - answerDescription: `该组件用作机器人与人类之间的接口。它接收用户的输入并显示机器人的计算结果。`, - retrievalDescription: `此组件用于从知识库中检索相关信息。选择知识库。如果没有检索到任何内容,将返回“空响应”。`, - generateDescription: `此组件用于调用LLM生成文本,请注意提示的设置。`, - categorizeDescription: `此组件用于对文本进行分类。请指定类别的名称、描述和示例。每个类别都指向不同的下游组件。`, - relevantDescription: `该组件用来判断upstream的输出是否与用户最新的问题相关,‘是’代表相关,‘否’代表不相关。`, - rewriteQuestionDescription: `此组件用于细化用户的提问。通常,当用户的原始提问无法从知识库中检索到相关信息时,此组件可帮助您将问题更改为更符合知识库表达方式的适当问题。`, - messageDescription: - '该组件用来返回工作流最后产生的数据内容和原先设置的文本内容。', - keywordDescription: `该组件用于从用户的问题中提取关键词。Top N指定需要提取的关键词数量。`, - switchDescription: `该组件用于根据前面组件的输出评估条件,并相应地引导执行流程。通过定义各种情况并指定操作,或在不满足条件时采取默认操作,实现复杂的分支逻辑。`, - wikipediaDescription: `此组件用于从 https://www.wikipedia.org/ 获取搜索结果。通常,它作为知识库的补充。Top N 指定您需要调整的搜索结果数量。`, - promptText: `请总结以下段落。注意数字,不要胡编乱造。段落如下: -{input} -以上就是你需要总结的内容。`, - createGraph: '创建智能体', - createFromTemplates: '从模板创建', - retrieval: '知识检索', - generate: '生成回答', - answer: '对话', - categorize: '问题分类', - relevant: '是否相关', - rewriteQuestion: '问题优化', - begin: '开始', - message: '回复消息', - blank: '空', - createFromNothing: '从无到有', - addItem: '新增', - addSubItem: '新增子项', - nameRequiredMsg: '名称不能为空', - nameRepeatedMsg: '名称不能重复', - keywordExtract: '关键词', - keywordExtractDescription: `该组件用于从用户的问题中提取关键词。Top N指定需要提取的关键词数量。`, - baidu: '百度', - baiduDescription: `此组件用于从 www.baidu.com 获取搜索结果。通常,它作为知识库的补充。Top N 指定您需要调整的搜索结果数量。`, - duckDuckGo: 'DuckDuckGo', - duckDuckGoDescription: - '此元件用於從 www.duckduckgo.com 取得搜尋結果。通常,它作為知識庫的補充。 Top N 指定您需要調整的搜尋結果數。', - searXNG: 'SearXNG', - searXNGDescription: - '该组件通过您提供的 SearXNG 实例地址进行搜索。请设置 Top N 和实例 URL。', - channel: '频道', - channelTip: '针对该组件的输入进行文本搜索或新闻搜索', - text: '文本', - news: '新闻', - messageHistoryWindowSize: '历史消息窗口大小', - messageHistoryWindowSizeTip: - 'LLM 需要查看的对话历史窗口大小。越大越好。但要注意 LLM 的最大 Token 数。', - wikipedia: '维基百科', - emailTip: - '此组件用于从 https://pubmed.ncbi.nlm.nih.gov/ 获取搜索结果。通常,它作为知识库的补充。Top N 指定您需要调整的搜索结果数。电子邮件是必填字段。', - email: '邮箱', - pubMed: 'PubMed', - pubMedDescription: - '此组件用于从 https://pubmed.ncbi.nlm.nih.gov/ 获取搜索结果。通常,它作为知识库的补充。Top N 指定您需要调整的搜索结果数。电子邮件是必填字段。', - arXiv: 'ArXiv', - arXivDescription: - '此组件用于从 https://arxiv.org/ 获取搜索结果。通常,它作为知识库的补充。Top N 指定您需要调整的搜索结果数量。', - sortBy: '排序方式', - submittedDate: '提交日期', - lastUpdatedDate: '最后更新日期', - relevance: '关联', - google: 'Google', - googleDescription: - '此组件用于从https://www.google.com/获取搜索结果。通常,它作为知识库的补充。Top N 和 SerpApi API 密钥指定您需要调整的搜索结果数量。', - bing: 'Bing', - bingDescription: - '此组件用于从 https://www.bing.com/ 获取搜索结果。通常,它作为知识库的补充。Top N 和 Bing Subscription-Key 指定您需要调整的搜索结果数量。', - apiKey: 'API密钥', - country: '国家和地区', - language: '语言', - googleScholar: '谷歌学术', - googleScholarDescription: `此组件用于从 https://scholar.google.com/ 获取搜索结果。通常,它作为知识库的补充。Top N 指定您需要调整的搜索结果数量。`, - yearLow: '开始年份', - yearHigh: '结束年份', - patents: '专利', - data: '数据', - deepL: 'DeepL', - deepLDescription: - '该组件用于从 https://www.deepl.com/ 获取翻译。通常,它提供更专业的翻译结果。', - authKey: '授权键', - sourceLang: '源语言', - targetLang: '目标语言', - gitHub: 'GitHub', - gitHubDescription: - '该组件用于从 https://github.com/ 搜索仓库。Top N 指定需要调整的搜索结果数量。', - baiduFanyi: '百度翻译', - baiduFanyiDescription: - '该组件用于从 https://fanyi.baidu.com/ 获取翻译。通常,它提供更专业的翻译结果', - appid: 'App id', - secretKey: '秘钥', - domain: '领域', - transType: '翻译类型', - baiduSecretKeyOptions: { - translate: '通用翻译', - fieldtranslate: '领域翻译', - }, - baiduDomainOptions: { - it: '信息技术领域', - finance: '金融财经领域', - machinery: '机械制造领域', - senimed: '生物医药领域', - novel: '网络文学领域', - academic: '学术论文领域', - aerospace: '航空航天领域', - wiki: '人文社科领域', - news: '新闻资讯领域', - law: '法律法规领域', - contract: '合同领域', - }, - baiduSourceLangOptions: { - auto: '自动检测', - zh: '中文', - en: '英语', - yue: '粤语', - wyw: '文言文', - jp: '日语', - kor: '韩语', - fra: '法语', - spa: '西班牙语', - th: '泰语', - ara: '阿拉伯语', - ru: '俄语', - pt: '葡萄牙语', - de: '德语', - it: '意大利语', - el: '希腊语', - nl: '荷兰语', - pl: '波兰语', - bul: '保加利亚语', - est: '爱沙尼亚语', - dan: '丹麦语', - fin: '芬兰语', - cs: '捷克语', - rom: '罗马尼亚语', - slo: '斯洛文尼亚语', - swe: '瑞典语', - hu: '匈牙利语', - cht: '繁体中文', - vie: '越南语', - }, - qWeather: '和风天气', - qWeatherDescription: - '该组件用于从 https://www.qweather.com/ 获取天气相关信息。您可以获取天气、指数、空气质量。', - lang: '语言', - type: '类型', - webApiKey: 'Web API 密钥', - userType: '用户类型', - timePeriod: '时间段', - qWeatherLangOptions: { - zh: '简体中文', - 'zh-hant': '繁体中文', - en: '英文', - de: '德语', - es: '西班牙语', - fr: '法语', - it: '意大利语', - ja: '日语', - ko: '韩语', - ru: '俄语', - hi: '印地语', - th: '泰语', - ar: '阿拉伯语', - pt: '葡萄牙语', - bn: '孟加拉语', - ms: '马来语', - nl: '荷兰语', - el: '希腊语', - la: '拉丁语', - sv: '瑞典语', - id: '印尼语', - pl: '波兰语', - tr: '土耳其语', - cs: '捷克语', - et: '爱沙尼亚语', - vi: '越南语', - fil: '菲律宾语', - fi: '芬兰语', - he: '希伯来语', - is: '冰岛语', - nb: '挪威语', - }, - qWeatherTypeOptions: { - weather: '天气预报', - indices: '天气生活指数', - airquality: '空气质量', - }, - qWeatherUserTypeOptions: { - free: '免费订阅用户', - paid: '付费订阅用户', - }, - qWeatherTimePeriodOptions: { - now: '现在', - '3d': '3天', - '7d': '7天', - '10d': '10天', - '15d': '12天', - '30d': '30天', - }, - publish: 'API', - exeSQL: '执行 SQL', - exeSQLDescription: - '该组件通过SQL语句从相应的关系数据库中查询结果。支持MySQL,PostgreSQL,MariaDB。', - dbType: '数据库类型', - database: '数据库', - username: '用户名', - host: '主机', - port: '端口', - password: '密码', - switch: '条件', - logicalOperator: '操作符', - switchOperatorOptions: { - equal: '等于', - notEqual: '不等于', - gt: '大于', - ge: '大于等于', - lt: '小于', - le: '小于等于', - contains: '包含', - notContains: '不包含', - startWith: '开始是', - endWith: '结束是', - empty: '为空', - notEmpty: '不为空', - }, - switchLogicOperatorOptions: { - and: '与', - or: '或', - }, - operator: '操作符', - value: '值', - useTemplate: '使用', - wenCai: '问财', - queryType: '查询类型', - wenCaiDescription: - '该组件可用于获取广泛金融领域的信息,包括但不限于股票、基金等...', - wenCaiQueryTypeOptions: { - stock: '股票', - zhishu: '指数', - fund: '基金', - hkstock: '港股', - usstock: '美股', - threeboard: '新三板', - conbond: '可转债', - insurance: '保险', - futures: '期货', - lccp: '理财', - foreign_exchange: '外汇', - }, - akShare: 'AkShare', - akShareDescription: '该组件可用于从东方财富网站获取相应股票的新闻信息。', - yahooFinance: '雅虎财经', - yahooFinanceDescription: '该组件根据提供的股票代码查询有关公司的信息。', - crawler: '网页爬虫', - crawlerDescription: '该组件可用于从指定url爬取html源码。', - proxy: '代理', - crawlerResultOptions: { - html: 'Html', - markdown: 'Markdown', - content: '文本', - }, - extractType: '提取类型', - info: '信息', - history: '历史', - financials: '财务', - balanceSheet: '资产负债表', - cashFlowStatement: '现金流量表', - jin10: '金十', - jin10Description: - '该组件可用于从金十开放平台获取金融领域的信息,包括快讯、日历、行情、参考。', - flashType: '闪光类型', - filter: '筛选', - contain: '包含', - calendarType: '日历类型', - calendarDatashape: '日历数据形状', - symbolsDatatype: '符号数据类型', - symbolsType: '符号类型', - jin10TypeOptions: { - flash: '快讯', - calendar: '日历', - symbols: '行情', - news: '参考', - }, - jin10FlashTypeOptions: { - '1': '市场快讯', - '2': '期货快讯', - '3': '美港快讯', - '4': 'A股快讯', - '5': '商品外汇快讯', - }, - jin10CalendarTypeOptions: { - cj: '宏观数据日历', - qh: '期货日历', - hk: '港股日历', - us: '美股日历', - }, - jin10CalendarDatashapeOptions: { - data: '数据', - event: ' 事件', - holiday: '假期', - }, - jin10SymbolsTypeOptions: { - GOODS: '商品行情', - FOREX: '外汇行情', - FUTURE: '国际行情', - CRYPTO: '加密货币行情', - }, - jin10SymbolsDatatypeOptions: { - symbols: '品种列表', - quotes: '最新行情', - }, - concentrator: '集线器', - concentratorDescription: - '该组件可用于连接多个下游组件。它接收来自上游组件的输入并将其传递给每个下游组件。', - tuShare: 'TuShare', - tuShareDescription: - '该组件可用于从主流金融网站获取金融新闻简报,辅助行业和量化研究。', - tuShareSrcOptions: { - sina: '新浪财经', - wallstreetcn: '华尔街见闻', - '10jqka': '同花顺', - eastmoney: '东方财富', - yuncaijing: '云财经', - fenghuang: '凤凰新闻', - jinrongjie: '金融界', - }, - token: 'Token', - src: '源', - startDate: '开始日期', - endDate: '结束日期', - keyword: '关键字', - note: '注释', - noteDescription: '注释', - notePlaceholder: '请输入注释', - invoke: 'HTTP 请求', - invokeDescription: - '该组件可以调用远程端点调用。将其他组件的输出作为参数或设置常量参数来调用远程函数。', - url: 'Url', - method: '方法', - timeout: '超时', - headers: '请求头', - cleanHtml: '清除 HTML', - cleanHtmlTip: '如果响应是 HTML 格式且只需要主要内容,请将其打开。', - reference: '引用', - input: '输入', - output: '输出', - parameter: '参数', - howUseId: '如何使用Agent ID?', - content: '内容', - operationResults: '运行结果', - autosaved: '已自动保存', - optional: '可选项', - pasteFileLink: '粘贴文件链接', - testRun: '试运行', - template: '模板转换', - templateDescription: - '该组件用于排版各种组件的输出。1、支持Jinja2模板,会先将输入转为对象后进行模版渲染2、同时保留原使用{参数}字符串替换的方式', - emailComponent: '邮件', - emailDescription: '发送邮件到指定邮箱', - smtpServer: 'SMTP服务器', - smtpPort: 'SMTP端口', - senderEmail: '发件人邮箱', - authCode: '授权码', - senderName: '发件人名称', - toEmail: '收件人邮箱', - ccEmail: '抄送邮箱', - emailSubject: '邮件主题', - emailContent: '邮件内容', - smtpServerRequired: '请输入SMTP服务器地址', - senderEmailRequired: '请输入发件人邮箱', - authCodeRequired: '请输入授权码', - toEmailRequired: '请输入收件人邮箱', - emailContentRequired: '请输入邮件内容', - emailSentSuccess: '邮件发送成功', - emailSentFailed: '邮件发送失败', - dynamicParameters: '动态参数说明', - jsonFormatTip: '上游组件需要传入以下格式的JSON字符串:', - toEmailTip: 'to_email: 收件人邮箱(必填)', - ccEmailTip: 'cc_email: 抄送邮箱(可选)', - subjectTip: 'subject: 邮件主题(可选)', - contentTip: 'content: 邮件内容(可选)', - jsonUploadTypeErrorMessage: '请上传json文件', - jsonUploadContentErrorMessage: 'json 文件错误', - iteration: '循环', - iterationDescription: `该组件负责迭代生成新的内容,对列表对象执行多次步骤直至输出所有结果。`, - delimiterTip: `该分隔符用于将输入文本分割成几个文本片段,每个文本片段的回显将作为每次迭代的输入项。`, - delimiterOptions: { - comma: '逗号', - lineBreak: '换行', - tab: '制表符', - underline: '下划线', - diagonal: '斜线', - minus: '连字符', - semicolon: '分号', - }, - addCategory: '新增分类', - categoryName: '分类名称', - nextStep: '下一步', - insertVariableTip: `输入 / 插入变量`, - setting: '设置', - settings: { - agentSetting: 'Agent设置', - title: '标题', - description: '描述', - upload: '上传', - photo: '照片', - permissions: '权限', - permissionsTip: '你可以在这里设置团队访问权限。', - me: '仅限自己', - team: '团队', - }, - systemPrompt: '系统提示词', - userPrompt: '用户提示词', - prompt: '提示词', - promptMessage: '提示词是必填项', - promptTip: - '系统提示为大模型提供任务描述、规定回复方式,以及设置其他各种要求。系统提示通常与 key (变量)合用,通过变量设置大模型的输入数据。你可以通过斜杠或者 (x) 按钮显示可用的 key。', - knowledgeBasesTip: '选择关联的知识库,或者在下方选择包含知识库ID的变量。', - knowledgeBaseVars: '知识库变量', - code: '代码', - codeDescription: '它允许开发人员编写自定义 Python 逻辑。', - inputVariables: '输入变量', - addVariable: '新增变量', - runningHintText: '正在运行中...🕞', - openingSwitch: '开场白开关', - openingCopy: '开场白文案', - openingSwitchTip: '您的用户将在开始时看到此欢迎消息。', - modeTip: '模式定义了工作流的启动方式。', - mode: '模式', - conversational: '对话式', - task: '任务', - beginInputTip: '通过定义输入参数,此内容可以被后续流程中的其他组件访问。', - query: '查询变量', - queryTip: '选择您想要使用的变量', - agent: '智能体', - addAgent: '添加智能体', - agentDescription: '构建具备推理、工具调用和多智能体协同的智能体组件。', - maxRecords: '最大记录数', - createAgent: '智能体流程', - stringTransform: '文本处理', - userFillUp: '等待输入', - userFillUpDescription: `此组件会暂停当前的流程并等待用户发送消息,接收到消息之后再进行之后的流程。`, - - codeExec: '代码', - tavilySearch: 'Tavily 搜索', - tavilySearchDescription: '通过 Tavily 服务搜索结果', - tavilyExtract: 'Tavily 提取', - tavilyExtractDescription: 'Tavily 提取', - log: '日志', - management: '管理', - import: '导入', - export: '导出', - subject: '主题', - logTimeline: { - begin: '准备开始', - userFillUp: '等你输入', - agent: '智能体正在思考', - retrieval: '查找知识', - message: '回复', - awaitResponse: '等你输入', - switch: '选择最佳路线', - iteration: '批量处理', - categorize: '信息归类', - code: '运行小段代码', - textProcessing: '整理文字', - tavilySearch: '正在网上搜索', - tavilyExtract: '读取网页内容', - exeSQL: '查询数据库', - google: '正在网上搜索', - wikipedia: '搜索维基百科', - googleScholar: '学术检索', - gitHub: '搜索', - email: '发送邮件', - httpRequest: '请求接口', - wenCai: '查询财务数据', - }, - sqlStatement: 'SQL 语句', - sqlStatementTip: - '在此处编写您的 SQL 查询。您可以使用变量、原始 SQL,或使用变量语法混合使用两者。', - frameworkPrompts: '框架', - release: '发布', - createFromBlank: '从空白创建', - createFromTemplate: '从模板创建', - importJsonFile: '导入 JSON 文件', - chooseAgentType: '选择智能体类型', - }, footer: { profile: 'All rights reserved @ React', }, @@ -1534,6 +904,27 @@ General:实体和关系提取提示来自 GitHub - microsoft/graphrag:基于 toolsAvailable: '可用的工具', mcpServers: 'MCP 服务器', customizeTheListOfMcpServers: '自定义 MCP 服务器列表', + searchPlaceholder: '搜索 MCP 服务器...', + deleteSelected: '删除', + exportSelected: '导出', + type: '类型', + updateTime: '更新时间', + totalItems: '共 {{count}} 条', + importTitle: '导入 MCP 服务器', + importDescription: '请在下方粘贴您的 MCP 服务器 JSON 配置。格式应与 Mock.json 结构匹配。', + jsonConfiguration: 'JSON 配置', + deleteSuccess: '删除成功', + deleteFailed: '删除失败', + batchDeleteSuccess: '批量删除成功', + batchDeleteFailed: '批量删除失败', + exportSuccess: '导出成功', + exportFailed: '导出失败', + serverIdRequired: 'server id 不能为空', + mcpServerUpdateSuccess: 'MCP 服务器更新成功', + mcpServerCreateSuccess: 'MCP 服务器创建成功', + operationFailed: '操作失败', + testSuccess: '测试成功', + jsonFormatError: 'JSON 格式错误', }, search: { searchApps: '搜索', diff --git a/src/pages/setting/components/ChangePasswordDialog.tsx b/src/pages/setting/components/ChangePasswordDialog.tsx index ffd192f..e32d2af 100644 --- a/src/pages/setting/components/ChangePasswordDialog.tsx +++ b/src/pages/setting/components/ChangePasswordDialog.tsx @@ -13,6 +13,7 @@ import { Alert } from '@mui/material'; import { Visibility, VisibilityOff, Close } from '@mui/icons-material'; +import { useTranslation } from 'react-i18next'; import { useSnackbar } from '@/hooks/useSnackbar'; import logger from '@/utils/logger'; @@ -32,6 +33,7 @@ interface PasswordFormData { * 修改密码对话框 */ function ChangePasswordDialog({ open, onClose, changeUserPassword }: ChangePasswordDialogProps) { + const { t } = useTranslation(); const { showMessage } = useSnackbar(); const [formData, setFormData] = useState({ @@ -100,23 +102,23 @@ function ChangePasswordDialog({ open, onClose, changeUserPassword }: ChangePassw const newErrors: Partial = {}; if (!formData.currentPassword.trim()) { - newErrors.currentPassword = '请输入当前密码'; + newErrors.currentPassword = t('setting.currentPasswordRequired'); } if (!formData.newPassword.trim()) { - newErrors.newPassword = '请输入新密码'; + newErrors.newPassword = t('setting.newPasswordRequired'); } else if (formData.newPassword.length < 6) { - newErrors.newPassword = '新密码长度至少6位'; + newErrors.newPassword = t('setting.passwordMinLength'); } if (!formData.confirmPassword.trim()) { - newErrors.confirmPassword = '请确认新密码'; + newErrors.confirmPassword = t('setting.confirmPasswordRequired'); } else if (formData.newPassword !== formData.confirmPassword) { - newErrors.confirmPassword = '两次输入的密码不一致'; + newErrors.confirmPassword = t('setting.passwordMismatch'); } if (formData.currentPassword === formData.newPassword) { - newErrors.newPassword = '新密码不能与当前密码相同'; + newErrors.newPassword = t('setting.newPasswordSameAsCurrent'); } setErrors(newErrors); @@ -136,10 +138,15 @@ function ChangePasswordDialog({ open, onClose, changeUserPassword }: ChangePassw new_password: formData.newPassword }); - showMessage.success('密码修改成功'); + showMessage.success(t('setting.passwordChangeSuccess')); handleClose(); } catch (error: any) { logger.error('修改密码失败:', error); + if (error.response?.status === 400) { + showMessage.error(t('setting.currentPasswordIncorrect')); + } else { + showMessage.error(t('setting.passwordChangeError')); + } } finally { setLoading(false); } @@ -157,7 +164,7 @@ function ChangePasswordDialog({ open, onClose, changeUserPassword }: ChangePassw > - 修改密码 + {t('setting.changePassword')} - 为了您的账户安全,请设置一个强密码。密码长度至少6位,建议包含字母、数字和特殊字符。 + {t('setting.passwordSecurityTip')} + + {t('setting.passwordUpdateTip')} + + {/* 当前密码 */} - 取消 + {t('setting.cancel')} diff --git a/src/pages/setting/components/Dialog/ApiKeyDialog.tsx b/src/pages/setting/components/Dialog/ApiKeyDialog.tsx index 03167ee..f2716c0 100644 --- a/src/pages/setting/components/Dialog/ApiKeyDialog.tsx +++ b/src/pages/setting/components/Dialog/ApiKeyDialog.tsx @@ -14,6 +14,7 @@ import { } from '@mui/material'; import { Visibility, VisibilityOff } from '@mui/icons-material'; import { Controller, useForm } from 'react-hook-form'; +import { useTranslation } from 'react-i18next'; // 表单数据接口 export interface ApiKeyFormData { @@ -45,6 +46,7 @@ function ApiKeyDialog({ initialData, editMode = false, }: ApiKeyDialogProps) { + const { t } = useTranslation(); const [showApiKey, setShowApiKey] = React.useState(false); const { @@ -81,14 +83,14 @@ function ApiKeyDialog({ return ( - {editMode ? '编辑' : '配置'} {factoryName} API Key + {editMode ? t('common.edit') : t('common.configure')} {factoryName} API Key ( )} /> @@ -142,7 +144,7 @@ function ApiKeyDialog({ fullWidth label="Group ID" margin="normal" - helperText="Minimax 专用的 Group ID" + helperText={t('setting.minimaxGroupId')} /> )} /> @@ -151,7 +153,7 @@ function ApiKeyDialog({ diff --git a/src/pages/setting/components/Dialog/AzureOpenAIDialog.tsx b/src/pages/setting/components/Dialog/AzureOpenAIDialog.tsx deleted file mode 100644 index 1dbd98e..0000000 --- a/src/pages/setting/components/Dialog/AzureOpenAIDialog.tsx +++ /dev/null @@ -1,266 +0,0 @@ -import React, { useEffect } from 'react'; -import { - Dialog, - DialogTitle, - DialogContent, - DialogActions, - Button, - TextField, - Box, - Typography, - IconButton, - InputAdornment, - CircularProgress, - MenuItem, - Select, - FormControl, - InputLabel, - FormHelperText, - Link, -} from '@mui/material'; -import { Visibility, VisibilityOff } from '@mui/icons-material'; -import { Controller, useForm } from 'react-hook-form'; -import type { IAddLlmRequestBody } from '@/interfaces/request/llm'; - -// 模型类型选项 -const MODEL_TYPE_OPTIONS = [ - { value: 'chat', label: 'Chat' }, - { value: 'embedding', label: 'Embedding' }, - { value: 'image2text', label: 'Image2Text' }, -]; - -// 表单数据接口 -export interface AzureOpenAIFormData extends IAddLlmRequestBody { - api_version: string; -} - -// 对话框 Props 接口 -export interface AzureOpenAIDialogProps { - open: boolean; - onClose: () => void; - onSubmit: (data: AzureOpenAIFormData) => void; - loading: boolean; - initialData?: AzureOpenAIFormData; - editMode?: boolean; -} - -/** - * Azure OpenAI 配置对话框 - */ -function AzureOpenAIDialog({ - open, - onClose, - onSubmit, - loading, - initialData, - editMode = false, -}: AzureOpenAIDialogProps) { - const [showApiKey, setShowApiKey] = React.useState(false); - - const { - control, - handleSubmit, - reset, - formState: { errors }, - } = useForm({ - defaultValues: { - model_type: 'embedding', - llm_name: 'gpt-3.5-turbo', - api_base: '', - api_key: '', - api_version: '2024-02-01', - max_tokens: 4096, - llm_factory: 'Azure-OpenAI', - }, - }); - - // 当对话框打开或初始数据变化时重置表单 - useEffect(() => { - if (open) { - reset({ - model_type: 'embedding', - llm_name: 'gpt-3.5-turbo', - api_base: '', - api_key: '', - api_version: '2024-02-01', - max_tokens: 4096, - llm_factory: initialData?.llm_factory || 'Azure-OpenAI', - }); - } - }, [open, initialData, reset]); - - const handleFormSubmit = (data: AzureOpenAIFormData) => { - onSubmit(data); - }; - - const toggleShowApiKey = () => { - setShowApiKey(!showApiKey); - }; - - return ( - - - {editMode ? '编辑' : '配置'} Azure OpenAI - - - - {/* 模型类型选择 */} - ( - - 模型类型 - - {errors.model_type && ( - {errors.model_type.message} - )} - - )} - /> - - {/* 模型名称 */} - ( - - )} - /> - - {/* 基础 URL */} - ( - - )} - /> - - {/* API Key */} - ( - - - {showApiKey ? : } - - - ), - }} - /> - )} - /> - - {/* API Version */} - ( - - )} - /> - - {/* 最大token数 */} - ( - field.onChange(parseInt(e.target.value) || 0)} - /> - )} - /> - - - - {/* 右侧按钮组 */} - - - - - - - ); -}; - -export default AzureOpenAIDialog; \ No newline at end of file diff --git a/src/pages/setting/components/Dialog/BedrockDialog.tsx b/src/pages/setting/components/Dialog/BedrockDialog.tsx deleted file mode 100644 index 6a66ceb..0000000 --- a/src/pages/setting/components/Dialog/BedrockDialog.tsx +++ /dev/null @@ -1,336 +0,0 @@ -import React, { useEffect } from 'react'; -import { - Dialog, - DialogTitle, - DialogContent, - DialogActions, - Button, - TextField, - Box, - Typography, - IconButton, - InputAdornment, - FormControl, - InputLabel, - Select, - MenuItem, - CircularProgress, - FormHelperText, - Link, -} from '@mui/material'; -import { Visibility, VisibilityOff } from '@mui/icons-material'; -import { Controller, useForm } from 'react-hook-form'; -import type { IAddLlmRequestBody } from '@/interfaces/request/llm'; - -// AWS Bedrock 支持的区域列表 -export const BEDROCK_REGIONS = [ - 'us-east-2', - 'us-east-1', - 'us-west-1', - 'us-west-2', - 'af-south-1', - 'ap-east-1', - 'ap-south-2', - 'ap-southeast-3', - 'ap-southeast-5', - 'ap-southeast-4', - 'ap-south-1', - 'ap-northeast-3', - 'ap-northeast-2', - 'ap-southeast-1', - 'ap-southeast-2', - 'ap-east-2', - 'ap-southeast-7', - 'ap-northeast-1', - 'ca-central-1', - 'ca-west-1', - 'eu-central-1', - 'eu-west-1', - 'eu-west-2', - 'eu-south-1', - 'eu-west-3', - 'eu-south-2', - 'eu-north-1', - 'eu-central-2', - 'il-central-1', - 'mx-central-1', - 'me-south-1', - 'me-central-1', - 'sa-east-1', - 'us-gov-east-1', - 'us-gov-west-1', -]; - -// 模型类型选项 -const MODEL_TYPE_OPTIONS = [ - { value: 'chat', label: 'Chat' }, - { value: 'embedding', label: 'Embedding' }, -]; - -// 表单数据接口 -export interface BedrockFormData extends IAddLlmRequestBody { - bedrock_ak: string; - bedrock_sk: string; - bedrock_region: string; -} - -// 对话框 Props 接口 -export interface BedrockDialogProps { - open: boolean; - onClose: () => void; - onSubmit: (data: BedrockFormData) => void; - loading: boolean; - initialData?: BedrockFormData; - editMode?: boolean; -} - -/** - * AWS Bedrock 配置对话框 - */ -function BedrockDialog ({ - open, - onClose, - onSubmit, - loading, - initialData, - editMode = false, -}: BedrockDialogProps) { - const [showAccessKey, setShowAccessKey] = React.useState(false); - const [showSecretKey, setShowSecretKey] = React.useState(false); - - const { - control, - handleSubmit, - reset, - formState: { errors }, - } = useForm({ - defaultValues: { - model_type: 'chat', - llm_name: '', - bedrock_ak: '', - bedrock_sk: '', - bedrock_region: 'us-east-1', - max_tokens: 4096, - llm_factory: 'Bedrock', - }, - }); - - // 当对话框打开或初始数据变化时重置表单 - useEffect(() => { - if (open) { - reset({ - model_type: 'chat', - llm_name: '', - bedrock_ak: '', - bedrock_sk: '', - bedrock_region: 'us-east-1', - max_tokens: 4096, - llm_factory: initialData?.llm_factory || 'Bedrock', - }); - } - }, [open, initialData, reset]); - - const handleFormSubmit = (data: BedrockFormData) => { - onSubmit(data); - }; - - const toggleShowAccessKey = () => { - setShowAccessKey(!showAccessKey); - }; - - const toggleShowSecretKey = () => { - setShowSecretKey(!showSecretKey); - }; - - const docInfo = { - url: 'https://console.aws.amazon.com/', - text: '如何集成 Bedrock', - }; - - return ( - - - {editMode ? '编辑' : '添加'} LLM - - - - {/* 模型类型 */} - ( - - * 模型类型 - - {errors.model_type && ( - {errors.model_type.message} - )} - - )} - /> - - {/* 模型名称 */} - ( - - )} - /> - - {/* ACCESS KEY */} - ( - - - {showAccessKey ? : } - - - ), - }} - /> - )} - /> - - {/* SECRET KEY */} - ( - - - {showSecretKey ? : } - - - ), - }} - /> - )} - /> - - {/* AWS Region */} - ( - - * AWS Region - - {errors.bedrock_region && ( - {errors.bedrock_region.message} - )} - - )} - /> - - {/* 最大token数 */} - ( - field.onChange(Number(e.target.value))} - /> - )} - /> - - - - - - {docInfo.text} - - - - - - - - - ); -}; - -export default BedrockDialog; \ No newline at end of file diff --git a/src/pages/setting/components/Dialog/ConfigurationDialog.tsx b/src/pages/setting/components/Dialog/ConfigurationDialog.tsx index 2cc427d..db16cc9 100644 --- a/src/pages/setting/components/Dialog/ConfigurationDialog.tsx +++ b/src/pages/setting/components/Dialog/ConfigurationDialog.tsx @@ -19,6 +19,7 @@ import { } from '@mui/material'; import { Visibility, VisibilityOff } from '@mui/icons-material'; import { Controller, useForm } from 'react-hook-form'; +import { useTranslation } from 'react-i18next'; import type { IAddLlmRequestBody } from '@/interfaces/request/llm'; // 表单项配置接口 @@ -79,6 +80,7 @@ function ConfigurationDialog({ docLink, editMode = false, }: ConfigurationDialogProps) { + const { t } = useTranslation(); const [passwordVisibility, setPasswordVisibility] = React.useState>({}); // 构建默认值 @@ -138,7 +140,7 @@ function ConfigurationDialog({ // 构建验证规则 const rules: any = {}; if (item.required) { - rules.required = `${item.label}是必填项`; + rules.required = t('setting.fieldRequired', { field: item.label }); } if (item.validation) { Object.assign(rules, item.validation); @@ -266,7 +268,7 @@ function ConfigurationDialog({ return ( - {editMode ? '编辑' : ''} {title} + {editMode ? t('setting.edit') : ''} {title} @@ -290,7 +292,7 @@ function ConfigurationDialog({ {/* 右侧按钮组 */} diff --git a/src/pages/setting/components/Dialog/OllamaDialog.tsx b/src/pages/setting/components/Dialog/OllamaDialog.tsx index 62c9eae..91f8cc3 100644 --- a/src/pages/setting/components/Dialog/OllamaDialog.tsx +++ b/src/pages/setting/components/Dialog/OllamaDialog.tsx @@ -17,8 +17,10 @@ import { Link, } from '@mui/material'; import { Controller, useForm } from 'react-hook-form'; +import { useTranslation } from 'react-i18next'; import logger from '@/utils/logger'; import { LLM_FACTORY_LIST, type LLMFactory } from '@/constants/llm'; +import i18n from '@/locales'; // 表单数据接口 export interface OllamaFormData { @@ -64,7 +66,7 @@ const llmFactoryToUrlMap: { [x: string]: string } = { function getURLByFactory(factory: LLMFactory) { const url = llmFactoryToUrlMap[factory]; return { - textTip: `如何集成 ${factory}`, + textTip: `${i18n.t('setting.howToIntegrate')} ${factory}`, url, } } @@ -91,6 +93,7 @@ function OllamaDialog({ initialData, editMode = false, }: OllamaDialogProps) { + const { t } = useTranslation(); const { control, @@ -163,7 +166,7 @@ function OllamaDialog({ return ( - {editMode ? `编辑 ${initialData?.llm_factory || LLM_FACTORY_LIST.Ollama}` : `配置 ${initialData?.llm_factory || LLM_FACTORY_LIST.Ollama}`} + {editMode ? t('setting.edit') : t('setting.configure')} {initialData?.llm_factory || LLM_FACTORY_LIST.Ollama} @@ -171,13 +174,13 @@ function OllamaDialog({ ( - 模型类型 * + {t('setting.modelType')} * + {t('setting.chatModel')} + + {t('setting.embeddingModel')} + + {t('setting.img2txtModel')} + + {t('setting.speech2txtModel')} + + {t('setting.rerankModel')} + + {t('setting.ttsModel')} + SSE Streamable HTTP @@ -276,21 +278,21 @@ const McpForm: React.FC = ({ disabled={disabled} fullWidth placeholder="e.g. eyJhbGciOiJIUzI1Ni..." - helperText="可选:用于身份验证的令牌" + helperText={t('setting.authTokenOptional')} /> {/* 测试连接部分 */} {onTest && ( - 测试连接 + {t('setting.testConnection')} @@ -299,16 +301,16 @@ const McpForm: React.FC = ({ {testResult.success ? ( - 连接成功!发现 {testResult.tools?.length || 0} 个工具 + {t('setting.connectionSuccess', { count: testResult.tools?.length || 0 })} {testResult.tools && testResult.tools.length > 0 && ( }> - 可用工具 + {t('setting.availableTools')} @@ -344,7 +346,7 @@ const McpForm: React.FC = ({ ) : ( - {testResult.error || '连接失败'} + {testResult.error || t('setting.connectionFailed')} )} @@ -360,7 +362,7 @@ const McpForm: React.FC = ({ disabled={loading || disabled || !testResult?.success} startIcon={loading ? : undefined} > - {loading ? '保存中...' : '保存'} + {loading ? t('setting.saving') : t('common.save')} diff --git a/src/pages/setting/components/ModelDialogs.tsx b/src/pages/setting/components/ModelDialogs.tsx index 4f242e5..aeb57b6 100644 --- a/src/pages/setting/components/ModelDialogs.tsx +++ b/src/pages/setting/components/ModelDialogs.tsx @@ -2,8 +2,6 @@ import React from 'react'; // 导入独立的对话框组件 import ApiKeyDialog, { type ApiKeyFormData, type ApiKeyDialogProps } from './Dialog/ApiKeyDialog'; -import AzureOpenAIDialog, { type AzureOpenAIFormData, type AzureOpenAIDialogProps } from './Dialog/AzureOpenAIDialog'; -import BedrockDialog, { type BedrockFormData, type BedrockDialogProps, BEDROCK_REGIONS } from './Dialog/BedrockDialog'; import OllamaDialog, { type OllamaFormData, type OllamaDialogProps } from './Dialog/OllamaDialog'; import SystemModelDialog, { type SystemModelFormData, type SystemModelDialogProps, type ModelOption, type ModelGroup } from './Dialog/SystemModelDialog'; import ConfigurationDialog, { type ConfigurationFormData, type ConfigurationDialogProps, type ConfigFormItem, type DocLinkConfig } from './Dialog/ConfigurationDialog'; @@ -19,14 +17,13 @@ export interface BaseDialogProps { } // 导出所有表单数据接口 -export type { ApiKeyFormData, AzureOpenAIFormData, BedrockFormData, OllamaFormData, SystemModelFormData, ConfigurationFormData }; +export type { ApiKeyFormData, OllamaFormData, SystemModelFormData, ConfigurationFormData }; // 导出所有对话框 Props 接口 -export type { ApiKeyDialogProps, AzureOpenAIDialogProps, BedrockDialogProps, OllamaDialogProps, SystemModelDialogProps, ConfigurationDialogProps }; +export type { ApiKeyDialogProps, OllamaDialogProps, SystemModelDialogProps, ConfigurationDialogProps }; // 导出其他相关接口和常量 export type { ModelOption, ModelGroup }; -export { BEDROCK_REGIONS }; // 模型对话框整合组件的 Props 接口 export interface ModelDialogsProps { @@ -39,22 +36,6 @@ export interface ModelDialogsProps { initialData?: ApiKeyFormData; editMode?: boolean; }; - azureDialog: { - open: boolean; - closeDialog: () => void; - submitAzureOpenAI: (data: AzureOpenAIFormData) => void; - loading: boolean; - initialData?: AzureOpenAIFormData; - editMode?: boolean; - }; - bedrockDialog: { - open: boolean; - closeDialog: () => void; - submitBedrock: (data: BedrockFormData) => void; - loading: boolean; - initialData?: BedrockFormData; - editMode?: boolean; - }; ollamaDialog: { open: boolean; closeDialog: () => void; @@ -93,8 +74,6 @@ export interface ModelDialogsProps { */ export const ModelDialogs: React.FC = ({ apiKeyDialog, - azureDialog, - bedrockDialog, ollamaDialog, configurationDialog, systemDialog, @@ -112,26 +91,6 @@ export const ModelDialogs: React.FC = ({ editMode={apiKeyDialog!.editMode} /> - {/* Azure OpenAI 对话框 */} - - - {/* AWS Bedrock 对话框 */} - - {/* Ollama 对话框 */} (null); @@ -91,13 +93,13 @@ function ProfileForm({ userInfo, onSubmit }: ProfileFormProps) { if (file) { // 检查文件类型 if (!file.type.startsWith('image/')) { - showMessage.error('请选择图片文件'); + showMessage.error(t('setting.pleaseSelectImageFile')); return; } // 检查文件大小 (限制为2MB) if (file.size > 2 * 1024 * 1024) { - showMessage.error('图片大小不能超过2MB'); + showMessage.error(t('setting.imageSizeLimit')); return; } @@ -123,7 +125,7 @@ function ProfileForm({ userInfo, onSubmit }: ProfileFormProps) { const handleSave = async () => { try { if (!formData.nickname?.trim()) { - showMessage.error('用户名不能为空'); + showMessage.error(t('setting.usernameRequired')); return; } @@ -136,17 +138,17 @@ function ProfileForm({ userInfo, onSubmit }: ProfileFormProps) { }; await onSubmit(updateData); - showMessage.success('个人信息更新成功'); + showMessage.success(t('setting.profileUpdateSuccess')); } catch (error) { console.error('更新用户信息失败:', error); - showMessage.error('更新失败,请重试'); + showMessage.error(t('setting.updateFailed')); } }; return ( - 个人资料 + {t('setting.personalProfile')} @@ -166,9 +168,9 @@ function ProfileForm({ userInfo, onSubmit }: ProfileFormProps) { - 头像 + {t('setting.avatar')} - + - 支持 JPG、PNG 格式,文件大小不超过 2MB + {t('setting.avatarFormatTip')} @@ -202,7 +204,7 @@ function ProfileForm({ userInfo, onSubmit }: ProfileFormProps) { {/* 语言 */} - 语言 + {t('setting.language')} {timezoneOptions.map((option) => ( @@ -266,7 +268,7 @@ function ProfileForm({ userInfo, onSubmit }: ProfileFormProps) { onClick={handleSave} sx={{ minWidth: 120 }} > - 保存 + {t('setting.save')} diff --git a/src/pages/setting/mcp.tsx b/src/pages/setting/mcp.tsx index 2071f91..cb46734 100644 --- a/src/pages/setting/mcp.tsx +++ b/src/pages/setting/mcp.tsx @@ -35,6 +35,7 @@ import McpDialog from '@/pages/setting/components/McpDialog'; import type { IMcpServer } from '@/interfaces/database/mcp'; import type { IImportMcpServersRequestBody, ICreateMcpServerRequestBody, ITestMcpRequestBody } from '@/interfaces/request/mcp'; import { useMessage } from '@/hooks/useSnackbar'; +import { useTranslation } from 'react-i18next'; import dayjs from 'dayjs'; const PageContainer = styled(Box)(({ theme }) => ({ @@ -69,6 +70,7 @@ const SearchContainer = styled(Box)(({ theme }) => ({ export default function McpSettingPage() { + const { t } = useTranslation(); const handleRefreshServer = async (initial?: boolean) => { if (initial) { @@ -187,9 +189,9 @@ export default function McpSettingPage() { if (selectedServerId) { const result = await deleteMcpServer(selectedServerId); if (result.success) { - showMessage.success('删除成功'); + showMessage.success(t('mcp.deleteSuccess')); } else { - showMessage.error(result.error || '删除失败'); + showMessage.error(t('mcp.deleteFailed')); } } handleMenuClose(); @@ -198,19 +200,19 @@ export default function McpSettingPage() { const handleBulkDelete = async () => { const result = await batchDeleteMcpServers(selectedServers); if (result.success) { - showMessage.success('批量删除成功'); + showMessage.success(t('mcp.batchDeleteSuccess')); setSelectedServers([]); } else { - showMessage.error(result.error || '批量删除失败'); + showMessage.error(t('mcp.batchDeleteFailed')); } }; const handleExport = async () => { const result = await exportMcpServers(selectedServers); if (result.success) { - showMessage.success('导出成功'); + showMessage.success(t('mcp.exportSuccess')); } else { - showMessage.error(result.error || '导出失败'); + showMessage.error(t('mcp.exportFailed')); } }; @@ -218,12 +220,12 @@ export default function McpSettingPage() { try { if (editingServer) { if (!editingServer.id) { - showMessage.error('server id 不能为空'); + showMessage.error(t('mcp.serverIdRequired')); return { success: false, error: 'server id 不能为空' }; } const result = await updateMcpServer({ ...data, mcp_id: editingServer.id ?? '' }); if (result.success) { - showMessage.success('MCP 服务器更新成功'); + showMessage.success(t('mcp.mcpServerUpdateSuccess')); setMcpDialogOpen(false); setEditingServer(null); return result; @@ -233,7 +235,7 @@ export default function McpSettingPage() { } else { const result = await createMcpServer(data); if (result.success) { - showMessage.success('MCP 服务器创建成功'); + showMessage.success(t('mcp.mcpServerCreateSuccess')); setMcpDialogOpen(false); setEditingServer(null); return result; @@ -251,7 +253,7 @@ export default function McpSettingPage() { try { const result = await testMcpServer(data); if (result.success) { - showMessage.success('测试成功'); + showMessage.success(t('mcp.testSuccess')); return result; } else { return result; @@ -283,7 +285,7 @@ export default function McpSettingPage() { setImportJson(''); } } else { - showMessage.error('JSON 格式错误'); + showMessage.error(t('mcp.jsonFormatError')); } } catch (error) { showMessage.error('JSON 格式错误'); @@ -294,11 +296,11 @@ export default function McpSettingPage() { - - MCP Servers + + {t('mcp.mcpServers')} - - Customize the list of MCP servers + + {t('mcp.customizeTheListOfMcpServers')} @@ -307,14 +309,14 @@ export default function McpSettingPage() { startIcon={} onClick={() => setImportDialogOpen(true)} > - Import + {t('mcp.import')} @@ -323,7 +325,7 @@ export default function McpSettingPage() { } onClick={handleBulkDelete} - color="error" + disabled={selectedServers.length === 0} > - Delete ({selectedServers.length}) + {t('mcp.deleteSelected')} )} @@ -389,10 +392,10 @@ export default function McpSettingPage() { {server.name} - 类型: {server.server_type} + {t('mcp.type')}: {server.server_type} - 更新时间: {dayjs(server.update_date).format('YYYY-MM-DD HH:mm:ss')} + {t('mcp.updateTime')}: {dayjs(server.update_date).format('YYYY-MM-DD HH:mm:ss')} @@ -409,7 +412,7 @@ export default function McpSettingPage() { color="primary" /> - 共 {total} 条 + {t('mcp.totalItems', { count: total })} @@ -423,11 +426,11 @@ export default function McpSettingPage() { > - Edit + {t('common.edit')} - Delete + {t('common.delete')} @@ -446,16 +449,16 @@ export default function McpSettingPage() { {/* 导入对话框 */} setImportDialogOpen(false)} maxWidth="md" fullWidth> - Import MCP Servers + {t('mcp.importTitle')} - Paste your MCP servers JSON configuration below. The format should match the Mock.json structure. + {t('mcp.importDescription')} setImportJson(e.target.value)} placeholder={`{ @@ -472,9 +475,9 @@ export default function McpSettingPage() { /> - + diff --git a/src/pages/setting/models.tsx b/src/pages/setting/models.tsx index 71f5211..935ec29 100644 --- a/src/pages/setting/models.tsx +++ b/src/pages/setting/models.tsx @@ -23,6 +23,7 @@ import { Edit as EditIcon, Delete as DeleteIcon, } from '@mui/icons-material'; +import { useTranslation } from 'react-i18next'; import { useLlmModelSetting } from '@/hooks/setting-hooks'; import { useModelDialogs } from './hooks/useModelDialogs'; import type { IFactory, IMyLlmModel, ILlmItem } from '@/interfaces/database/llm'; @@ -66,6 +67,7 @@ function MyLlmGridItem({ model, onDelete }: { model: ILlmItem, onDelete: (model: // 主页面组件 function ModelsPage() { + const { t } = useTranslation(); const { llmFactory, myLlm, refreshLlmModel } = useLlmModelSetting(); const modelDialogs = useModelDialogs(refreshLlmModel); @@ -106,12 +108,15 @@ function ModelsPage() { LLM_FACTORY_LIST.VolcEngine, ] if (LocalLlmFactories.includes(factoryName)) { + // local llm modelDialogs.ollamaDialog.openDialog({ llm_factory: factory.name, }); } else if (configurationFactories.includes(factoryName)) { + // custom configuration llm modelDialogs.configurationDialog.openConfigurationDialog(factory.name); } else { + // llm set api modelDialogs.apiKeyDialog.openApiKeyDialog(factoryName); } logger.debug('handleConfigureFactory', factory); @@ -129,26 +134,26 @@ function ModelsPage() { // 处理删除单个模型 const handleDeleteModel = useCallback(async (factoryName: string, modelName: string) => { dialog.confirm({ - title: '确认删除', - content: `是否确认删除模型 ${modelName}?`, + title: t('setting.confirmDelete'), + content: t('setting.confirmDeleteModel', { modelName }), showCancel: true, onConfirm: async () => { await modelDialogs.deleteOps.deleteLlm(factoryName, modelName); }, }); - }, [dialog, modelDialogs.deleteOps]); + }, [dialog, modelDialogs.deleteOps, t]); // 处理删除模型工厂 const handleDeleteFactory = useCallback(async (factoryName: string) => { dialog.confirm({ - title: '确认删除', - content: `是否确认删除模型工厂 ${factoryName}?`, + title: t('setting.confirmDelete'), + content: t('setting.confirmDeleteFactory', { factoryName }), showCancel: true, onConfirm: async () => { await modelDialogs.deleteOps.deleteFactory(factoryName); }, }); - }, [dialog, modelDialogs.deleteOps]); + }, [dialog, modelDialogs.deleteOps, t]); if (!llmFactory || !myLlm) { return ( @@ -168,28 +173,28 @@ function ModelsPage() { }}> - 模型设置 + {t('setting.modelSettings')} - 管理您的 LLM 模型工厂和个人模型配置 + {t('setting.modelSettingsDescription')} {/* 设置默认模型 */} {/* My LLM 部分 */} {!myLlm || Object.keys(myLlm).length === 0 ? ( - 您还没有配置任何 LLM 模型。请在下方的模型工厂中进行配置。 + {t('setting.noModelsConfigured')} ) : ( }> - 我的 LLM 模型 + {t('setting.myLlmModels')} @@ -242,13 +247,13 @@ function ModelsPage() { variant='contained' color='primary' startIcon={} onClick={() => handleEditLlmFactory(factoryName)} > - 编辑 + {t('setting.edit')} @@ -281,7 +286,7 @@ function ModelsPage() { }> - LLM 模型工厂 + {t('setting.llmModelFactories')} diff --git a/src/pages/setting/profile.tsx b/src/pages/setting/profile.tsx index 76d0ddb..9bd1836 100644 --- a/src/pages/setting/profile.tsx +++ b/src/pages/setting/profile.tsx @@ -1,12 +1,14 @@ import React, { useState } from "react"; import { Box, Button, Divider, Typography } from "@mui/material"; import { Lock } from "@mui/icons-material"; +import { useTranslation } from 'react-i18next'; import ProfileForm from "./components/ProfileForm"; import ChangePasswordDialog from "./components/ChangePasswordDialog"; import { useProfileSetting } from "@/hooks/setting-hooks"; import logger from "@/utils/logger"; function ProfileSetting() { + const { t } = useTranslation(); const { userInfo, updateUserInfo: updateUserInfoFunc, changeUserPassword: changeUserPasswordFunc } = useProfileSetting(); const [passwordDialogOpen, setPasswordDialogOpen] = useState(false); @@ -31,10 +33,10 @@ function ProfileSetting() { {/* 密码修改部分 */} - 账户安全 + {t('setting.accountSecurity')} - 定期更新密码有助于保护您的账户安全 + {t('setting.passwordUpdateTip')} diff --git a/src/pages/setting/system.tsx b/src/pages/setting/system.tsx index 20fa1a1..5db3a31 100644 --- a/src/pages/setting/system.tsx +++ b/src/pages/setting/system.tsx @@ -11,6 +11,7 @@ import { Divider, Paper, } from '@mui/material'; +import { useTranslation } from 'react-i18next'; import { Storage as StorageIcon, Memory as RedisIcon, @@ -39,6 +40,7 @@ const TITLE_MAP = { task_executor_heartbeat: 'Task Executor', }; + // 图标映射 const ICON_MAP = { es: SearchIcon, @@ -49,7 +51,16 @@ const ICON_MAP = { }; function SystemSetting() { + const { t } = useTranslation(); const { systemStatus, loading, error, fetchSystemStatus } = useSystemStatus(); + const componentTitleMap: Record = { + 'doc_engine': t('setting.docEngine'), + 'object_storage': t('setting.objectStorage'), + 'redis': t('setting.redis'), + 'database': t('setting.database'), + 'elasticsearch': t('setting.elasticsearch'), + 'task_executor': t('setting.taskExecutor'), + }; useEffect(() => { fetchSystemStatus(); @@ -60,7 +71,7 @@ function SystemSetting() { if (key.startsWith('task_executor_heartbeat')) { return null; } - + const IconComponent = ICON_MAP[key as keyof typeof ICON_MAP] || DefaultIcon; const title = TITLE_MAP[key as keyof typeof TITLE_MAP] || key; const status = info?.status || 'unknown'; @@ -68,7 +79,7 @@ function SystemSetting() { return ( - - + - + {Object.keys(info) .filter((x) => x !== 'status') .map((x) => ( - + + {x.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase())}: + + - - {x.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase())}: - - - {typeof info[x] === 'number' - ? info[x].toFixed(2) - : (info[x] != null ? String(info[x]) : 'N/A') - } - {x === 'elapsed' && ' ms'} - - - ))} - + {typeof info[x] === 'number' + ? info[x].toFixed(2) + : (info[x] != null ? String(info[x]) : 'N/A') + } + {x === 'elapsed' && ' ms'} + + + ))} + @@ -170,23 +181,23 @@ function SystemSetting() { - 系统状态 + {t('setting.systemStatus')} - + - 查看系统各个组件的运行状态和性能指标 + {t('setting.systemStatusDescription')} {systemStatus ? ( - {Object.keys(systemStatus).map((key) => + {Object.keys(systemStatus).map((key) => renderSystemInfo(key, systemStatus[key as keyof ISystemStatus]) )} ) : ( - 暂无系统状态数据 + {t('setting.noSystemStatusData')} )} diff --git a/src/pages/setting/teams.tsx b/src/pages/setting/teams.tsx index 77d7854..139ff7b 100644 --- a/src/pages/setting/teams.tsx +++ b/src/pages/setting/teams.tsx @@ -270,7 +270,7 @@ function TeamsSetting() { variant="outlined" value={inviteEmail} onChange={(e) => setInviteEmail(e.target.value)} - placeholder={t('setting.emailPlaceholder') || '请输入邀请用户的邮箱地址'} + placeholder={t('setting.emailPlaceholder')} /> @@ -282,7 +282,7 @@ function TeamsSetting() { variant="contained" disabled={loading || !inviteEmail.trim()} > - {loading ? t('setting.inviting', 'inviting') : t('setting.invite')} + {loading ? t('setting.inviting') : t('setting.invite')} diff --git a/src/utils/request.ts b/src/utils/request.ts index 89d9c5a..88c8e44 100644 --- a/src/utils/request.ts +++ b/src/utils/request.ts @@ -139,8 +139,7 @@ request.interceptors.response.use( redirectToLogin(); } else if (data?.code !== 0) { // 处理其他业务错误 - logger.info('请求出现错误:', data?.message); - const error = new CustomError(data?.message || '请求出现错误'); + const error = new CustomError(data?.message || i18n.t('message.requestError')); error.code = data?.code || -1; error.response = data; snackbar.warning(error.message);