diff --git a/dist-electron/main/main.js b/dist-electron/main/main.js
index a8c04dd..86f81e5 100644
--- a/dist-electron/main/main.js
+++ b/dist-electron/main/main.js
@@ -1,2 +1,7 @@
-require('bytenode')
-require('./main.jsc')
\ No newline at end of file
+"use strict";
+require("electron");
+require("./main-B0AKNiSn.js");
+require("electron-squirrel-startup");
+require("electron-log");
+require("bytenode");
+require("axios");
diff --git a/dist-electron/preload/preload.js b/dist-electron/preload/preload.js
index 956f473..c4f6639 100644
--- a/dist-electron/preload/preload.js
+++ b/dist-electron/preload/preload.js
@@ -1 +1,137 @@
-"use strict";const r=require("electron");var i=(e=>(e.EXTERNAL_OPEN="external-open",e.APP_SET_FRAMELESS="app:set-frameless",e.APP_LOAD_PAGE="app:load-page",e.TAB_CREATE="tab:create",e.TAB_LIST="tab:list",e.TAB_NAVIGATE="tab:navigate",e.TAB_RELOAD="tab:reload",e.TAB_BACK="tab:back",e.TAB_FORWARD="tab:forward",e.TAB_SWITCH="tab:switch",e.TAB_CLOSE="tab:close",e.LOG_TO_MAIN="log-to-main",e.READ_FILE="read-file",e.INVOKE="ipc:invoke",e.INVOKE_ASYNC="ipc:invokeAsync",e.APP_MINIMIZE="app:minimize",e.APP_MAXIMIZE="app:maximize",e.APP_QUIT="app:quit",e.FILE_READ="file:read",e.FILE_WRITE="file:write",e.GET_WINDOW_ID="get-window-id",e.CUSTOM_EVENT="custom:event",e.TIME_UPDATE="time:update",e.RENDERER_IS_READY="renderer-ready",e.SHOW_CONTEXT_MENU="show-context-menu",e.START_A_DIALOGUE="start-a-dialogue",e.OPEN_WINDOW="open-window",e.LOG_DEBUG="log-debug",e.LOG_INFO="log-info",e.LOG_WARN="log-warn",e.LOG_ERROR="log-error",e.CONFIG_UPDATED="config-updated",e.SET_CONFIG="set-config",e.GET_CONFIG="get-config",e.UPDATE_CONFIG="update-config",e.SET_THEME_MODE="set-theme-mode",e.GET_THEME_MODE="get-theme-mode",e.IS_DARK_THEME="is-dark-theme",e.THEME_MODE_UPDATED="theme-mode-updated",e.EXECUTE_SCRIPT="execute-script",e.TASK_PROGRESS="task:progress",e.TASK_STARTED="task:started",e.TASK_COMPLETED="task:completed",e.OPEN_CHANNEL="open-channel",e.SCRIPT_LIST="script:list",e.SCRIPT_GET="script:get",e.SCRIPT_SAVE="script:save",e.SCRIPT_DELETE="script:delete",e.SCRIPT_TOGGLE="script:toggle",e.SCRIPT_RUN="script:run",e.SCRIPT_RECORD_START="script:record-start",e.SCRIPT_RECORD_STOP="script:record-stop",e.SCRIPT_CODEGEN="script:codegen",e.GATEWAY_RPC="gateway:rpc",e.GATEWAY_EVENT="gateway:event",e.UPDATE_CHECK="update:check",e.UPDATE_DOWNLOAD="update:download",e.UPDATE_INSTALL="update:install",e.UPDATE_VERSION="update:version",e.UPDATE_STATUS_CHANGED="update:status-changed",e))(i||{});const d={versions:process.versions,external:{open:e=>r.ipcRenderer.invoke("external-open",e)},platform:process.platform,windowMinimize:()=>r.ipcRenderer.invoke("window:minimize"),windowMaximize:()=>r.ipcRenderer.invoke("window:maximize"),windowClose:()=>r.ipcRenderer.invoke("window:close"),windowIsMaximized:()=>r.ipcRenderer.invoke("window:isMaximized"),viewIsReady:()=>r.ipcRenderer.send(i.RENDERER_IS_READY),app:{setFrameless:e=>r.ipcRenderer.invoke(i.APP_SET_FRAMELESS,e),loadPage:e=>r.ipcRenderer.invoke(i.APP_LOAD_PAGE,e)},readFile:e=>r.ipcRenderer.invoke(i.READ_FILE,e),invoke:(e,...n)=>r.ipcRenderer.invoke(e,...n),invokeAsync:(e,...n)=>r.ipcRenderer.invoke(e,...n),on:(e,n)=>{const t=(o,...R)=>n(...R);return r.ipcRenderer.on(e,t),()=>r.ipcRenderer.removeListener(e,t)},send:(e,...n)=>r.ipcRenderer.send(e,...n),getCurrentWindowId:()=>r.ipcRenderer.sendSync(i.GET_WINDOW_ID),logger:{debug:(e,...n)=>r.ipcRenderer.send(i.LOG_DEBUG,e,...n),info:(e,...n)=>r.ipcRenderer.send(i.LOG_INFO,e,...n),warn:(e,...n)=>r.ipcRenderer.send(i.LOG_WARN,e,...n),error:(e,...n)=>r.ipcRenderer.send(i.LOG_ERROR,e,...n)},executeScript:e=>r.ipcRenderer.invoke(i.EXECUTE_SCRIPT,e),onTaskProgress:e=>{const n=(t,o)=>e(o);return r.ipcRenderer.on(i.TASK_PROGRESS,n),()=>r.ipcRenderer.removeListener(i.TASK_PROGRESS,n)},onTaskStarted:e=>{const n=(t,o)=>e(o);return r.ipcRenderer.on(i.TASK_STARTED,n),()=>r.ipcRenderer.removeListener(i.TASK_STARTED,n)},onTaskCompleted:e=>{const n=(t,o)=>e(o);return r.ipcRenderer.on(i.TASK_COMPLETED,n),()=>r.ipcRenderer.removeListener(i.TASK_COMPLETED,n)},openChannel:e=>r.ipcRenderer.invoke(i.OPEN_CHANNEL,e),scriptApi:{list:()=>r.ipcRenderer.invoke(i.SCRIPT_LIST),get:e=>r.ipcRenderer.invoke(i.SCRIPT_GET,e),save:e=>r.ipcRenderer.invoke(i.SCRIPT_SAVE,e),delete:e=>r.ipcRenderer.invoke(i.SCRIPT_DELETE,e),toggle:(e,n)=>r.ipcRenderer.invoke(i.SCRIPT_TOGGLE,e,n),run:e=>r.ipcRenderer.invoke(i.SCRIPT_RUN,e),startRecording:e=>r.ipcRenderer.invoke(i.SCRIPT_RECORD_START,e),stopRecording:()=>r.ipcRenderer.invoke(i.SCRIPT_RECORD_STOP),codegen:(e,n)=>r.ipcRenderer.invoke(i.SCRIPT_CODEGEN,e,n)}};r.contextBridge.exposeInMainWorld("api",d);
+"use strict";
+const electron = require("electron");
+var IPC_EVENTS = /* @__PURE__ */ ((IPC_EVENTS2) => {
+ IPC_EVENTS2["EXTERNAL_OPEN"] = "external-open";
+ IPC_EVENTS2["APP_SET_FRAMELESS"] = "app:set-frameless";
+ IPC_EVENTS2["APP_LOAD_PAGE"] = "app:load-page";
+ IPC_EVENTS2["TAB_CREATE"] = "tab:create";
+ IPC_EVENTS2["TAB_LIST"] = "tab:list";
+ IPC_EVENTS2["TAB_NAVIGATE"] = "tab:navigate";
+ IPC_EVENTS2["TAB_RELOAD"] = "tab:reload";
+ IPC_EVENTS2["TAB_BACK"] = "tab:back";
+ IPC_EVENTS2["TAB_FORWARD"] = "tab:forward";
+ IPC_EVENTS2["TAB_SWITCH"] = "tab:switch";
+ IPC_EVENTS2["TAB_CLOSE"] = "tab:close";
+ IPC_EVENTS2["LOG_TO_MAIN"] = "log-to-main";
+ IPC_EVENTS2["READ_FILE"] = "read-file";
+ IPC_EVENTS2["INVOKE"] = "ipc:invoke";
+ IPC_EVENTS2["INVOKE_ASYNC"] = "ipc:invokeAsync";
+ IPC_EVENTS2["APP_MINIMIZE"] = "app:minimize";
+ IPC_EVENTS2["APP_MAXIMIZE"] = "app:maximize";
+ IPC_EVENTS2["APP_QUIT"] = "app:quit";
+ IPC_EVENTS2["FILE_READ"] = "file:read";
+ IPC_EVENTS2["FILE_WRITE"] = "file:write";
+ IPC_EVENTS2["GET_WINDOW_ID"] = "get-window-id";
+ IPC_EVENTS2["CUSTOM_EVENT"] = "custom:event";
+ IPC_EVENTS2["TIME_UPDATE"] = "time:update";
+ IPC_EVENTS2["RENDERER_IS_READY"] = "renderer-ready";
+ IPC_EVENTS2["SHOW_CONTEXT_MENU"] = "show-context-menu";
+ IPC_EVENTS2["START_A_DIALOGUE"] = "start-a-dialogue";
+ IPC_EVENTS2["OPEN_WINDOW"] = "open-window";
+ IPC_EVENTS2["LOG_DEBUG"] = "log-debug";
+ IPC_EVENTS2["LOG_INFO"] = "log-info";
+ IPC_EVENTS2["LOG_WARN"] = "log-warn";
+ IPC_EVENTS2["LOG_ERROR"] = "log-error";
+ IPC_EVENTS2["CONFIG_UPDATED"] = "config-updated";
+ IPC_EVENTS2["SET_CONFIG"] = "set-config";
+ IPC_EVENTS2["GET_CONFIG"] = "get-config";
+ IPC_EVENTS2["UPDATE_CONFIG"] = "update-config";
+ IPC_EVENTS2["SET_THEME_MODE"] = "set-theme-mode";
+ IPC_EVENTS2["GET_THEME_MODE"] = "get-theme-mode";
+ IPC_EVENTS2["IS_DARK_THEME"] = "is-dark-theme";
+ IPC_EVENTS2["THEME_MODE_UPDATED"] = "theme-mode-updated";
+ IPC_EVENTS2["EXECUTE_SCRIPT"] = "execute-script";
+ IPC_EVENTS2["TASK_PROGRESS"] = "task:progress";
+ IPC_EVENTS2["TASK_STARTED"] = "task:started";
+ IPC_EVENTS2["TASK_COMPLETED"] = "task:completed";
+ IPC_EVENTS2["OPEN_CHANNEL"] = "open-channel";
+ IPC_EVENTS2["SCRIPT_LIST"] = "script:list";
+ IPC_EVENTS2["SCRIPT_GET"] = "script:get";
+ IPC_EVENTS2["SCRIPT_SAVE"] = "script:save";
+ IPC_EVENTS2["SCRIPT_DELETE"] = "script:delete";
+ IPC_EVENTS2["SCRIPT_TOGGLE"] = "script:toggle";
+ IPC_EVENTS2["SCRIPT_RUN"] = "script:run";
+ IPC_EVENTS2["SCRIPT_RECORD_START"] = "script:record-start";
+ IPC_EVENTS2["SCRIPT_RECORD_STOP"] = "script:record-stop";
+ IPC_EVENTS2["SCRIPT_CODEGEN"] = "script:codegen";
+ IPC_EVENTS2["GATEWAY_RPC"] = "gateway:rpc";
+ IPC_EVENTS2["GATEWAY_EVENT"] = "gateway:event";
+ IPC_EVENTS2["UPDATE_CHECK"] = "update:check";
+ IPC_EVENTS2["UPDATE_DOWNLOAD"] = "update:download";
+ IPC_EVENTS2["UPDATE_INSTALL"] = "update:install";
+ IPC_EVENTS2["UPDATE_VERSION"] = "update:version";
+ IPC_EVENTS2["UPDATE_STATUS_CHANGED"] = "update:status-changed";
+ return IPC_EVENTS2;
+})(IPC_EVENTS || {});
+const api = {
+ versions: process.versions,
+ external: {
+ open: (url) => electron.ipcRenderer.invoke("external-open", url)
+ },
+ platform: process.platform,
+ windowMinimize: () => electron.ipcRenderer.invoke("window:minimize"),
+ windowMaximize: () => electron.ipcRenderer.invoke("window:maximize"),
+ windowClose: () => electron.ipcRenderer.invoke("window:close"),
+ windowIsMaximized: () => electron.ipcRenderer.invoke("window:isMaximized"),
+ viewIsReady: () => electron.ipcRenderer.send(IPC_EVENTS.RENDERER_IS_READY),
+ app: {
+ setFrameless: (route) => electron.ipcRenderer.invoke(IPC_EVENTS.APP_SET_FRAMELESS, route),
+ loadPage: (page) => electron.ipcRenderer.invoke(IPC_EVENTS.APP_LOAD_PAGE, page)
+ },
+ // 通过 IPC 调用主进程
+ readFile: (filePath) => electron.ipcRenderer.invoke(IPC_EVENTS.READ_FILE, filePath),
+ // 异步调用(映射为 electron 的 invoke)
+ invoke: (channel, ...args) => electron.ipcRenderer.invoke(channel, ...args),
+ // 异步调用(为了兼容老代码)
+ invokeAsync: (channel, ...args) => electron.ipcRenderer.invoke(channel, ...args),
+ // 监听主进程消息
+ on: (event, callback) => {
+ const subscription = (_event, ...args) => callback(...args);
+ electron.ipcRenderer.on(event, subscription);
+ return () => electron.ipcRenderer.removeListener(event, subscription);
+ },
+ // 发送消息到主进程
+ send: (channel, ...args) => electron.ipcRenderer.send(channel, ...args),
+ // 获取窗口ID
+ getCurrentWindowId: () => electron.ipcRenderer.sendSync(IPC_EVENTS.GET_WINDOW_ID),
+ // 发送日志
+ logger: {
+ debug: (message, ...meta) => electron.ipcRenderer.send(IPC_EVENTS.LOG_DEBUG, message, ...meta),
+ info: (message, ...meta) => electron.ipcRenderer.send(IPC_EVENTS.LOG_INFO, message, ...meta),
+ warn: (message, ...meta) => electron.ipcRenderer.send(IPC_EVENTS.LOG_WARN, message, ...meta),
+ error: (message, ...meta) => electron.ipcRenderer.send(IPC_EVENTS.LOG_ERROR, message, ...meta)
+ },
+ // 执行脚本
+ executeScript: (params) => electron.ipcRenderer.invoke(IPC_EVENTS.EXECUTE_SCRIPT, params),
+ // 任务事件
+ onTaskProgress: (cb) => {
+ const subscription = (_event, payload) => cb(payload);
+ electron.ipcRenderer.on(IPC_EVENTS.TASK_PROGRESS, subscription);
+ return () => electron.ipcRenderer.removeListener(IPC_EVENTS.TASK_PROGRESS, subscription);
+ },
+ onTaskStarted: (cb) => {
+ const subscription = (_event, payload) => cb(payload);
+ electron.ipcRenderer.on(IPC_EVENTS.TASK_STARTED, subscription);
+ return () => electron.ipcRenderer.removeListener(IPC_EVENTS.TASK_STARTED, subscription);
+ },
+ onTaskCompleted: (cb) => {
+ const subscription = (_event, payload) => cb(payload);
+ electron.ipcRenderer.on(IPC_EVENTS.TASK_COMPLETED, subscription);
+ return () => electron.ipcRenderer.removeListener(IPC_EVENTS.TASK_COMPLETED, subscription);
+ },
+ // 打开渠道
+ openChannel: (channels) => electron.ipcRenderer.invoke(IPC_EVENTS.OPEN_CHANNEL, channels),
+ // 脚本管理
+ scriptApi: {
+ list: () => electron.ipcRenderer.invoke(IPC_EVENTS.SCRIPT_LIST),
+ get: (id) => electron.ipcRenderer.invoke(IPC_EVENTS.SCRIPT_GET, id),
+ save: (input) => electron.ipcRenderer.invoke(IPC_EVENTS.SCRIPT_SAVE, input),
+ delete: (id) => electron.ipcRenderer.invoke(IPC_EVENTS.SCRIPT_DELETE, id),
+ toggle: (id, enabled) => electron.ipcRenderer.invoke(IPC_EVENTS.SCRIPT_TOGGLE, id, enabled),
+ run: (id) => electron.ipcRenderer.invoke(IPC_EVENTS.SCRIPT_RUN, id),
+ startRecording: (url) => electron.ipcRenderer.invoke(IPC_EVENTS.SCRIPT_RECORD_START, url),
+ stopRecording: () => electron.ipcRenderer.invoke(IPC_EVENTS.SCRIPT_RECORD_STOP),
+ codegen: (id, url) => electron.ipcRenderer.invoke(IPC_EVENTS.SCRIPT_CODEGEN, id, url)
+ }
+};
+electron.contextBridge.exposeInMainWorld("api", api);
diff --git a/dist/index.html b/dist/index.html
index 96432eb..a7a7ccb 100644
--- a/dist/index.html
+++ b/dist/index.html
@@ -8,7 +8,7 @@
http-equiv="Content-Security-Policy"
content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: http://8.138.234.141 https://one-feel-bucket.oss-cn-guangzhou.aliyuncs.com; connect-src 'self' http://8.138.234.141 https://api.iconify.design wss://onefeel.brother7.cn"
/>
-
+
diff --git a/src/App.tsx b/src/App.tsx
index ed0af8a..99fd971 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -1,9 +1,12 @@
import { useEffect } from 'react';
import { HashRouter } from 'react-router-dom';
+import { useLocale } from './i18n';
import { AppRouter } from './router';
import { initSettingsStore } from './stores';
export default function App() {
+ useLocale();
+
useEffect(() => {
void initSettingsStore();
}, []);
diff --git a/src/components/layout/Sidebar.tsx b/src/components/layout/Sidebar.tsx
index 506c643..df70ea6 100644
--- a/src/components/layout/Sidebar.tsx
+++ b/src/components/layout/Sidebar.tsx
@@ -1,5 +1,6 @@
import { useLocation, useNavigate } from 'react-router-dom';
import { Book, Clock, Code, Cpu, House, Puzzle, Settings } from 'lucide-react';
+import { useI18n } from '../../i18n';
import { NAV_ITEMS, normalizeWorkspacePath } from '../../router/routes';
const MENU_MARKS: Record = {
@@ -15,6 +16,7 @@ const MENU_MARKS: Record = {
export default function Sidebar() {
const location = useLocation();
const navigate = useNavigate();
+ const { t } = useI18n();
const currentId = normalizeWorkspacePath(location.pathname);
return (
@@ -53,7 +55,7 @@ export default function Sidebar() {
className="mt-[4px] mb-[8px] text-[14px] hover:text-[#2B7FFF]"
style={{ color: active ? '#2B7FFF' : '#525866' }}
>
- {item.label}
+ {t(item.labelKey)}
diff --git a/src/components/layout/TitleBar.tsx b/src/components/layout/TitleBar.tsx
index 5a220d3..36d5b7f 100644
--- a/src/components/layout/TitleBar.tsx
+++ b/src/components/layout/TitleBar.tsx
@@ -1,8 +1,11 @@
+import { useI18n } from '../../i18n';
+
type TitleBarProps = {
variant?: 'default' | 'light';
};
export default function TitleBar({ variant = 'default' }: TitleBarProps) {
+ const { t } = useI18n();
const platform = (window as any).api?.platform ?? '';
if (platform === 'linux') return null;
@@ -34,7 +37,7 @@ export default function TitleBar({ variant = 'default' }: TitleBarProps) {
className={['flex h-full w-11 items-center justify-center hover:bg-[#999] hover:text-white transition-colors', iconColorClass].join(
' ',
)}
- title="Minimize"
+ title={t('window.minimize')}
onClick={() => {
(window as any).api?.windowMinimize?.();
}}
@@ -46,7 +49,7 @@ export default function TitleBar({ variant = 'default' }: TitleBarProps) {
className={['flex h-full w-11 items-center justify-center hover:bg-[#999] hover:text-white transition-colors', iconColorClass].join(
' ',
)}
- title="Maximize"
+ title={t('window.maximize')}
onClick={() => {
(window as any).api?.windowMaximize?.();
}}
@@ -58,7 +61,7 @@ export default function TitleBar({ variant = 'default' }: TitleBarProps) {
className={['flex h-full w-11 items-center justify-center hover:bg-[#ff0000] hover:text-white transition-colors', iconColorClass].join(
' ',
)}
- title="Close"
+ title={t('window.close')}
onClick={() => {
(window as any).api?.windowClose?.();
}}
diff --git a/src/i18n/constants.ts b/src/i18n/constants.ts
index 7df4514..2268c51 100644
--- a/src/i18n/constants.ts
+++ b/src/i18n/constants.ts
@@ -10,6 +10,6 @@ export const SUPPORTED_LANGUAGES = [
{ code: 'ja', label: '日本語' },
] as const;
-export type Namespace = 'common' | 'conversation' | 'setting' | 'window';
+export type Namespace = 'common' | 'conversation' | 'setting' | 'window' | 'sidebar' | 'login';
-export const NAMESPACES = ['common', 'conversation', 'setting', 'window'] as const satisfies readonly Namespace[];
+export const NAMESPACES = ['common', 'conversation', 'setting', 'window', 'sidebar', 'login'] as const satisfies readonly Namespace[];
diff --git a/src/i18n/index.ts b/src/i18n/index.ts
index e2beefc..59a3cc9 100644
--- a/src/i18n/index.ts
+++ b/src/i18n/index.ts
@@ -1,3 +1,4 @@
+import { useSyncExternalStore } from 'react';
import { DEFAULT_LANGUAGE } from '../lib/constants';
import type { LanguageCode } from '../types/runtime';
import type { Namespace } from './constants';
@@ -103,5 +104,25 @@ export function getMessages(locale?: LanguageCode): MessageTree {
return i18n.getMessages(locale);
}
+function subscribeToLocale(listener: () => void): () => void {
+ return i18n.subscribe(() => {
+ listener();
+ });
+}
+
+export function useLocale(): LanguageCode {
+ return useSyncExternalStore(subscribeToLocale, getLocale, getLocale);
+}
+
+export function useI18n() {
+ const locale = useLocale();
+
+ return {
+ locale,
+ t,
+ hasMessage,
+ };
+}
+
export { SUPPORTED_LANGUAGE_CODES, SUPPORTED_LANGUAGES, NAMESPACES } from './constants';
export type { LanguageCode, Namespace };
diff --git a/src/i18n/messages.ts b/src/i18n/messages.ts
index aa3b25c..812aaae 100644
--- a/src/i18n/messages.ts
+++ b/src/i18n/messages.ts
@@ -40,6 +40,72 @@ export const messages: I18nMessages = {
newConversation: 'New conversation',
emptyState: 'No messages yet',
},
+ sidebar: {
+ home: 'Home',
+ knowledge: 'Knowledge',
+ models: 'Models',
+ skills: 'Skills',
+ cron: 'Cron',
+ scripts: 'Scripts',
+ settings: 'Settings',
+ },
+ settings: {
+ menu: {
+ systemSettings: 'System Settings',
+ account: 'Account',
+ general: 'General',
+ },
+ account: {
+ title: 'Account Settings',
+ description: 'Manage your account details and sign-in security.',
+ accountLabel: 'Account',
+ passwordLabel: 'Login Password',
+ passwordHelp: 'Used for investor login operations, last login time: {time}',
+ configured: 'Configured',
+ changePassword: 'Change Password',
+ },
+ general: {
+ title: 'Basic Settings',
+ description: 'Customize the look and feel of the application.',
+ themeSection: 'Theme Settings',
+ languageSection: 'Language',
+ updatesTitle: 'Updates',
+ currentVersion: 'Current Version',
+ checkForUpdates: 'Check for Updates',
+ checkingForUpdates: 'Checking for updates...',
+ latestVersion: 'You have the latest version',
+ newVersionAvailable: 'New version available: v{version}',
+ downloadingVersion: 'Downloading new version... {percent}%',
+ downloadComplete: 'Download complete, ready to install',
+ updateError: 'Update error: {error}',
+ updateHint: 'Check for updates to get the latest features.',
+ downloadUpdate: 'Download Update',
+ restartAndInstall: 'Restart and Install',
+ autoCheckTitle: 'Auto check for updates',
+ autoCheckDescription: 'Check for updates on startup',
+ autoDownloadTitle: 'Auto download updates',
+ autoDownloadDescription: 'Automatically download and install updates',
+ autoUpdateHint: 'When auto-update is enabled, updates will be downloaded and installed automatically.',
+ },
+ },
+ login: {
+ title: 'Welcome Back',
+ subtitle: 'A digital teammate on duty 24/7, always ready to help.',
+ username: 'Username',
+ password: 'Password',
+ verificationCode: 'Verification Code',
+ usernamePlaceholder: 'Enter username',
+ passwordPlaceholder: 'Enter password',
+ verificationCodePlaceholder: 'Enter verification code',
+ usernameRequired: 'Please enter a username',
+ passwordRequired: 'Please enter a password',
+ codeRequired: 'Please enter the verification code',
+ submitFailed: 'Login failed, please try again later',
+ submit: 'Sign In',
+ submitting: 'Signing In...',
+ loadingCaptcha: 'Loading...',
+ captchaAlt: 'Verification code',
+ },
},
zh: {
app: {
@@ -74,6 +140,72 @@ export const messages: I18nMessages = {
newConversation: '新建对话',
emptyState: '暂无消息',
},
+ sidebar: {
+ home: '首页',
+ knowledge: '知识库',
+ models: '模型',
+ skills: '技能',
+ cron: '定时任务',
+ scripts: '脚本',
+ settings: '设置',
+ },
+ settings: {
+ menu: {
+ systemSettings: '系统设置',
+ account: '账号',
+ general: '通用',
+ },
+ account: {
+ title: '账号设置',
+ description: '管理账号信息与登录安全。',
+ accountLabel: '账号',
+ passwordLabel: '登录密码',
+ passwordHelp: '用于投资人登录操作,最近登录时间:{time}',
+ configured: '已配置',
+ changePassword: '修改密码',
+ },
+ general: {
+ title: '基础设置',
+ description: '自定义应用的外观与使用体验。',
+ themeSection: '主题设置',
+ languageSection: '语言',
+ updatesTitle: '版本更新',
+ currentVersion: '当前版本',
+ checkForUpdates: '检查更新',
+ checkingForUpdates: '正在检查更新...',
+ latestVersion: '当前已是最新版本',
+ newVersionAvailable: '发现新版本:v{version}',
+ downloadingVersion: '正在下载新版本... {percent}%',
+ downloadComplete: '下载完成,可立即安装',
+ updateError: '更新失败:{error}',
+ updateHint: '检查更新以获取最新功能。',
+ downloadUpdate: '下载更新',
+ restartAndInstall: '重启并安装',
+ autoCheckTitle: '自动检查更新',
+ autoCheckDescription: '应用启动时自动检查更新',
+ autoDownloadTitle: '自动下载更新',
+ autoDownloadDescription: '自动下载并安装更新',
+ autoUpdateHint: '开启自动更新后,更新包会自动下载并安装。',
+ },
+ },
+ login: {
+ title: '欢迎回来',
+ subtitle: '24 小时在岗,随时待命的数字员工。',
+ username: '账号',
+ password: '密码',
+ verificationCode: '验证码',
+ usernamePlaceholder: '请输入账号',
+ passwordPlaceholder: '请输入密码',
+ verificationCodePlaceholder: '请输入验证码',
+ usernameRequired: '请输入用户名',
+ passwordRequired: '请输入密码',
+ codeRequired: '请输入验证码',
+ submitFailed: '登录失败,请稍后重试',
+ submit: '登录',
+ submitting: '登录中...',
+ loadingCaptcha: '加载中...',
+ captchaAlt: '验证码',
+ },
},
ja: {
app: {
@@ -82,7 +214,7 @@ export const messages: I18nMessages = {
window: {
minimize: '最小化',
maximize: '最大化',
- restore: '復元',
+ restore: '元に戻す',
close: '閉じる',
},
dialog: {
@@ -92,7 +224,7 @@ export const messages: I18nMessages = {
theme: {
light: 'ライト',
dark: 'ダーク',
- system: 'システムに従う',
+ system: 'システム',
},
language: {
zh: '中国語',
@@ -106,7 +238,73 @@ export const messages: I18nMessages = {
},
conversation: {
newConversation: '新しい会話',
- emptyState: 'メッセージはまだありません',
+ emptyState: 'まだメッセージがありません',
+ },
+ sidebar: {
+ home: 'ホーム',
+ knowledge: 'ナレッジ',
+ models: 'モデル',
+ skills: 'スキル',
+ cron: '定時タスク',
+ scripts: 'スクリプト',
+ settings: '設定',
+ },
+ settings: {
+ menu: {
+ systemSettings: 'システム設定',
+ account: 'アカウント',
+ general: '一般',
+ },
+ account: {
+ title: 'アカウント設定',
+ description: 'アカウント情報とサインインの安全性を管理します。',
+ accountLabel: 'アカウント',
+ passwordLabel: 'ログインパスワード',
+ passwordHelp: '投資家ログインに使用します。最終ログイン時刻:{time}',
+ configured: '設定済み',
+ changePassword: 'パスワードを変更',
+ },
+ general: {
+ title: '基本設定',
+ description: 'アプリの見た目と操作感をカスタマイズします。',
+ themeSection: 'テーマ設定',
+ languageSection: '言語',
+ updatesTitle: 'アップデート',
+ currentVersion: '現在のバージョン',
+ checkForUpdates: '更新を確認',
+ checkingForUpdates: '更新を確認しています...',
+ latestVersion: '最新バージョンを使用しています',
+ newVersionAvailable: '新しいバージョンがあります:v{version}',
+ downloadingVersion: '新しいバージョンをダウンロード中... {percent}%',
+ downloadComplete: 'ダウンロードが完了しました。インストールできます',
+ updateError: 'アップデートエラー:{error}',
+ updateHint: '最新機能を利用するには更新を確認してください。',
+ downloadUpdate: '更新をダウンロード',
+ restartAndInstall: '再起動してインストール',
+ autoCheckTitle: '更新を自動確認',
+ autoCheckDescription: '起動時に更新を確認します',
+ autoDownloadTitle: '更新を自動ダウンロード',
+ autoDownloadDescription: '更新を自動でダウンロードしてインストールします',
+ autoUpdateHint: '自動更新を有効にすると、更新が自動でダウンロードされてインストールされます。',
+ },
+ },
+ login: {
+ title: 'おかえりなさい',
+ subtitle: '24時間待機し、いつでも支援できるデジタルチームメイトです。',
+ username: 'アカウント',
+ password: 'パスワード',
+ verificationCode: '認証コード',
+ usernamePlaceholder: 'アカウントを入力してください',
+ passwordPlaceholder: 'パスワードを入力してください',
+ verificationCodePlaceholder: '認証コードを入力してください',
+ usernameRequired: 'ユーザー名を入力してください',
+ passwordRequired: 'パスワードを入力してください',
+ codeRequired: '認証コードを入力してください',
+ submitFailed: 'ログインに失敗しました。しばらくしてからもう一度お試しください',
+ submit: 'ログイン',
+ submitting: 'ログイン中...',
+ loadingCaptcha: '読み込み中...',
+ captchaAlt: '認証コード',
},
},
};
diff --git a/src/pages/Knowledge/copy.ts b/src/pages/Knowledge/copy.ts
index a0b5d8b..29f960e 100644
--- a/src/pages/Knowledge/copy.ts
+++ b/src/pages/Knowledge/copy.ts
@@ -1,5 +1,4 @@
-import { useEffect, useState } from 'react';
-import { getLocale, i18n } from '../../i18n';
+import { useLocale } from '../../i18n';
import type { LanguageCode } from '../../types/runtime';
type Primitive = string | number;
@@ -216,9 +215,7 @@ function createKnowledgeTranslate(locale: LanguageCode): KnowledgeTranslate {
}
export function useKnowledgeCopy(): KnowledgeTranslate {
- const [locale, setLocale] = useState(getLocale());
-
- useEffect(() => i18n.subscribe(setLocale), []);
+ const locale = useLocale();
return createKnowledgeTranslate(locale);
}
diff --git a/src/pages/Login/index.tsx b/src/pages/Login/index.tsx
index d128dcc..060c68c 100644
--- a/src/pages/Login/index.tsx
+++ b/src/pages/Login/index.tsx
@@ -1,12 +1,13 @@
import { type ChangeEvent, type FormEvent, useEffect, useMemo, useState } from 'react';
-import { useLocation, useNavigate } from 'react-router-dom';
import { Lock, User } from 'lucide-react';
+import { useLocation, useNavigate } from 'react-router-dom';
import blueLogo from '../../assets/images/login/blue_logo.png';
import loginBackground from '../../assets/images/login/login_bg.png';
import loginIllustration from '../../assets/images/login/logo.png';
import userIcon from '../../assets/images/login/user_icon.png';
+import { useI18n } from '../../i18n';
import TitleBar from '../../components/layout/TitleBar';
import { resolvePostLoginPath } from '../../router/auth';
import {
@@ -35,6 +36,7 @@ const credentialFieldInputClass =
'h-full min-w-0 flex-1 border-0 bg-transparent pr-4 text-[14px] text-gray-800 outline-none placeholder:text-[#99A0AE] disabled:cursor-not-allowed dark:text-gray-100 dark:placeholder:text-gray-500';
export default function LoginPage() {
+ const { t } = useI18n();
const navigate = useNavigate();
const location = useLocation();
const platform = (window as any).api?.platform ?? '';
@@ -72,9 +74,9 @@ export default function LoginPage() {
function validate(values: LoginFormValues): FormErrors {
const nextErrors: FormErrors = {};
- if (!values.username.trim()) nextErrors.username = '请输入用户名';
- if (!values.password.trim()) nextErrors.password = '请输入密码';
- if (!values.code.trim()) nextErrors.code = '请输入验证码';
+ if (!values.username.trim()) nextErrors.username = t('login.usernameRequired');
+ if (!values.password.trim()) nextErrors.password = t('login.passwordRequired');
+ if (!values.code.trim()) nextErrors.code = t('login.codeRequired');
return nextErrors;
}
@@ -104,7 +106,7 @@ export default function LoginPage() {
navigate(resolvePostLoginPath(location.state as { from?: string } | null), { replace: true });
} catch (error) {
setErrors({
- submit: error instanceof Error ? error.message : '登录失败,请稍后重试',
+ submit: error instanceof Error ? error.message : t('login.submitFailed'),
});
refreshCaptcha(false);
} finally {
@@ -121,7 +123,7 @@ export default function LoginPage() {
return (
-
+
-

+
- 欢迎回到登录
+ {t('login.title')}
- 24小时在岗,从不打烊的数字员工
+ {t('login.subtitle')}