diff --git a/src/web-ui/src/app/scenes/miniapps/MiniAppScene.tsx b/src/web-ui/src/app/scenes/miniapps/MiniAppScene.tsx index 57650def..068dc2a3 100644 --- a/src/web-ui/src/app/scenes/miniapps/MiniAppScene.tsx +++ b/src/web-ui/src/app/scenes/miniapps/MiniAppScene.tsx @@ -14,6 +14,7 @@ import { IconButton, Button } from '@/component-library'; import { useSceneManager } from '@/app/hooks/useSceneManager'; import type { SceneTabId } from '@/app/components/SceneBar/types'; import { useMiniAppStore } from './miniAppStore'; +import { useI18n } from '@/infrastructure/i18n'; import './MiniAppScene.scss'; const log = createLogger('MiniAppScene'); @@ -30,6 +31,7 @@ const MiniAppScene: React.FC = ({ appId }) => { const { themeType } = useTheme(); const { workspacePath } = useCurrentWorkspace(); const { closeScene } = useSceneManager(); + const { t } = useI18n('scenes/miniapp'); const [app, setApp] = useState(null); const [loading, setLoading] = useState(false); @@ -130,7 +132,7 @@ const MiniAppScene: React.FC = ({ appId }) => { size="small" onClick={handleReload} disabled={loading} - tooltip="重新加载" + tooltip={t('scene.reload')} > {loading ? ( @@ -144,15 +146,15 @@ const MiniAppScene: React.FC = ({ appId }) => { {loading && !app && (
- 加载中… + {t('scene.loading')}
)} {error && (
-

加载失败:{error}

+

{t('scene.loadFailed', { error })}

)} diff --git a/src/web-ui/src/app/scenes/miniapps/components/MiniAppCard.tsx b/src/web-ui/src/app/scenes/miniapps/components/MiniAppCard.tsx index adf913b3..6b216a3e 100644 --- a/src/web-ui/src/app/scenes/miniapps/components/MiniAppCard.tsx +++ b/src/web-ui/src/app/scenes/miniapps/components/MiniAppCard.tsx @@ -2,6 +2,7 @@ import React from 'react'; import { Play, Square, Trash2 } from 'lucide-react'; import type { MiniAppMeta } from '@/infrastructure/api/service-api/MiniAppAPI'; import { renderMiniAppIcon } from '../utils/miniAppIcons'; +import { useI18n } from '@/infrastructure/i18n'; import './MiniAppCard.scss'; interface MiniAppCardProps { @@ -23,6 +24,7 @@ const MiniAppCard: React.FC = ({ onDelete, onStop, }) => { + const { t } = useI18n('scenes/miniapp'); const handleDeleteClick = (e: React.MouseEvent) => { e.stopPropagation(); onDelete(app.id); @@ -72,8 +74,8 @@ const MiniAppCard: React.FC = ({ @@ -81,8 +83,8 @@ const MiniAppCard: React.FC = ({ @@ -90,8 +92,8 @@ const MiniAppCard: React.FC = ({ diff --git a/src/web-ui/src/app/scenes/miniapps/views/MiniAppGalleryView.tsx b/src/web-ui/src/app/scenes/miniapps/views/MiniAppGalleryView.tsx index 1fcefaab..a8724b6d 100644 --- a/src/web-ui/src/app/scenes/miniapps/views/MiniAppGalleryView.tsx +++ b/src/web-ui/src/app/scenes/miniapps/views/MiniAppGalleryView.tsx @@ -30,6 +30,7 @@ import type { SceneTabId } from '@/app/components/SceneBar/types'; import { getMiniAppIconGradient, renderMiniAppIcon } from '../utils/miniAppIcons'; import { useCurrentWorkspace } from '@/infrastructure/contexts/WorkspaceContext'; import { useMiniAppStore } from '../miniAppStore'; +import { useI18n } from '@/infrastructure/i18n'; import './MiniAppGalleryView.scss'; const log = createLogger('MiniAppGalleryView'); @@ -43,6 +44,7 @@ const MiniAppGalleryView: React.FC = () => { const markWorkerStopped = useMiniAppStore((state) => state.markWorkerStopped); const { workspacePath } = useCurrentWorkspace(); const { openScene, activateScene, closeScene, openTabs } = useSceneManager(); + const { t } = useI18n('scenes/miniapp'); const [search, setSearch] = useState(''); const [categoryFilter, setCategoryFilter] = useState('all'); @@ -147,7 +149,7 @@ const MiniAppGalleryView: React.FC = () => { const selected = await open({ directory: true, multiple: false, - title: '选择小应用目录(需包含 meta.json 与 source/)', + title: t('selectFolderTitle'), }); const path = Array.isArray(selected) ? selected[0] : selected; if (!path) return; @@ -177,8 +179,8 @@ const MiniAppGalleryView: React.FC = () => { : } message={apps.length === 0 - ? '边聊边生成,马上可用。和 AI 对话生成第一个小应用吧。' - : '没有匹配的应用。'} + ? t('empty.generate') + : t('empty.noMatch')} /> ); } @@ -203,17 +205,17 @@ const MiniAppGalleryView: React.FC = () => { return ( - + @@ -222,7 +224,7 @@ const MiniAppGalleryView: React.FC = () => { className="gallery-action-btn" onClick={handleRefresh} disabled={loading} - title="刷新列表" + title={t('refreshList')} > {
0 ? {runningApps.length} : null} > {runningApps.length > 0 ? ( @@ -255,13 +257,13 @@ const MiniAppGalleryView: React.FC = () => { ) : (
- 暂无运行中的应用 + {t('noRunningApps')}
)}
{categories.length > 1 ? ( @@ -278,12 +280,12 @@ const MiniAppGalleryView: React.FC = () => { .join(' ')} onClick={() => setCategoryFilter(category)} > - {category === 'all' ? '全部' : category} + {category === 'all' ? t('all') : category} ))}
) : null} - {filtered.length} 个 + {t('count', { count: filtered.length })} )} > @@ -305,16 +307,16 @@ const MiniAppGalleryView: React.FC = () => { {runningIdSet.has(selectedApp.id) ? ( ) : null} ) : null} @@ -335,12 +337,12 @@ const MiniAppGalleryView: React.FC = () => { isOpen={pendingDeleteId !== null} onClose={() => setPendingDeleteId(null)} onConfirm={handleDeleteConfirm} - title={`删除 "${apps.find((app) => app.id === pendingDeleteId)?.name ?? ''}"?`} - message="此操作不可撤销,应用及其所有数据将被永久删除。" + title={t('confirmDelete.title', { name: apps.find((app) => app.id === pendingDeleteId)?.name ?? '' })} + message={t('confirmDelete.message')} type="warning" confirmDanger - confirmText="删除" - cancelText="取消" + confirmText={t('confirmDelete.confirm')} + cancelText={t('confirmDelete.cancel')} />
); diff --git a/src/web-ui/src/infrastructure/i18n/core/I18nService.ts b/src/web-ui/src/infrastructure/i18n/core/I18nService.ts index cca3c0bf..8501d367 100644 --- a/src/web-ui/src/infrastructure/i18n/core/I18nService.ts +++ b/src/web-ui/src/infrastructure/i18n/core/I18nService.ts @@ -35,6 +35,7 @@ import zhCNScenesCapabilities from '../../../locales/zh-CN/scenes/capabilities.j import zhCNScenesAgents from '../../../locales/zh-CN/scenes/agents.json'; import zhCNScenesProfile from '../../../locales/zh-CN/scenes/profile.json'; import zhCNScenesSkills from '../../../locales/zh-CN/scenes/skills.json'; +import zhCNScenesMiniapp from '../../../locales/zh-CN/scenes/miniapp.json'; import zhCNPanelsFiles from '../../../locales/zh-CN/panels/files.json'; import zhCNPanelsGit from '../../../locales/zh-CN/panels/git.json'; import zhCNPanelsTerminal from '../../../locales/zh-CN/panels/terminal.json'; @@ -74,6 +75,7 @@ import enUSScenesCapabilities from '../../../locales/en-US/scenes/capabilities.j import enUSScenesAgents from '../../../locales/en-US/scenes/agents.json'; import enUSScenesProfile from '../../../locales/en-US/scenes/profile.json'; import enUSScenesSkills from '../../../locales/en-US/scenes/skills.json'; +import enUSScenesMiniapp from '../../../locales/en-US/scenes/miniapp.json'; import enUSPanelsFiles from '../../../locales/en-US/panels/files.json'; import enUSPanelsGit from '../../../locales/en-US/panels/git.json'; import enUSPanelsTerminal from '../../../locales/en-US/panels/terminal.json'; @@ -120,6 +122,7 @@ const resources = { 'scenes/agents': zhCNScenesAgents, 'scenes/profile': zhCNScenesProfile, 'scenes/skills': zhCNScenesSkills, + 'scenes/miniapp': zhCNScenesMiniapp, 'panels/files': zhCNPanelsFiles, 'panels/git': zhCNPanelsGit, 'panels/terminal': zhCNPanelsTerminal, @@ -160,6 +163,7 @@ const resources = { 'scenes/agents': enUSScenesAgents, 'scenes/profile': enUSScenesProfile, 'scenes/skills': enUSScenesSkills, + 'scenes/miniapp': enUSScenesMiniapp, 'panels/files': enUSPanelsFiles, 'panels/git': enUSPanelsGit, 'panels/terminal': enUSPanelsTerminal, @@ -223,6 +227,7 @@ export class I18nService { 'scenes/agents', 'scenes/profile', 'scenes/skills', + 'scenes/miniapp', 'panels/files', 'panels/git', 'panels/terminal', diff --git a/src/web-ui/src/infrastructure/i18n/types/index.ts b/src/web-ui/src/infrastructure/i18n/types/index.ts index 00243557..d6d47c9c 100644 --- a/src/web-ui/src/infrastructure/i18n/types/index.ts +++ b/src/web-ui/src/infrastructure/i18n/types/index.ts @@ -38,7 +38,12 @@ export type I18nNamespace = | 'panels/git' | 'panels/terminal' | 'mermaid-editor' - | 'settings/prompt-templates'; + | 'settings/prompt-templates' + | 'scenes/miniapp' + | 'scenes/capabilities' + | 'scenes/agents' + | 'scenes/profile' + | 'scenes/skills'; export interface I18nConfig { diff --git a/src/web-ui/src/locales/en-US/scenes/miniapp.json b/src/web-ui/src/locales/en-US/scenes/miniapp.json new file mode 100644 index 00000000..43bb408d --- /dev/null +++ b/src/web-ui/src/locales/en-US/scenes/miniapp.json @@ -0,0 +1,39 @@ +{ + "title": "Mini Apps", + "subtitle": "Instantly generated mini apps, ready to use right away. You can also continue to iterate on them.", + "searchPlaceholder": "Search mini apps...", + "importFromFolder": "Import from folder", + "refreshList": "Refresh list", + "running": "Running", + "allApps": "All Apps", + "noRunningApps": "No running apps", + "all": "All", + "count": "{{count}}", + "empty": { + "generate": "Generate as you chat, ready instantly. Start a conversation with AI to create your first mini app.", + "noMatch": "No matching apps." + }, + "selectFolderTitle": "Select mini app directory (must contain meta.json and source/)", + "card": { + "start": "Start", + "stop": "Stop", + "delete": "Delete" + }, + "detail": { + "stop": "Stop", + "delete": "Delete", + "open": "Open" + }, + "confirmDelete": { + "title": "Delete \"{{name}}\"?", + "message": "This action cannot be undone. The app and all its data will be permanently deleted.", + "confirm": "Delete", + "cancel": "Cancel" + }, + "scene": { + "loading": "Loading...", + "loadFailed": "Load failed: {{error}}", + "retry": "Retry", + "reload": "Reload" + } +} diff --git a/src/web-ui/src/locales/zh-CN/scenes/miniapp.json b/src/web-ui/src/locales/zh-CN/scenes/miniapp.json new file mode 100644 index 00000000..1003f83c --- /dev/null +++ b/src/web-ui/src/locales/zh-CN/scenes/miniapp.json @@ -0,0 +1,39 @@ +{ + "title": "小应用", + "subtitle": "即时生成的小应用,打开就能用,也能继续迭代。", + "searchPlaceholder": "搜索小应用...", + "importFromFolder": "从文件夹导入", + "refreshList": "刷新列表", + "running": "已启动", + "allApps": "全部应用", + "noRunningApps": "暂无运行中的应用", + "all": "全部", + "count": "{{count}} 个", + "empty": { + "generate": "边聊边生成,马上可用。和 AI 对话生成第一个小应用吧。", + "noMatch": "没有匹配的应用。" + }, + "selectFolderTitle": "选择小应用目录(需包含 meta.json 与 source/)", + "card": { + "start": "启动", + "stop": "停止", + "delete": "删除" + }, + "detail": { + "stop": "停止", + "delete": "删除", + "open": "打开" + }, + "confirmDelete": { + "title": "删除 \"{{name}}\"?", + "message": "此操作不可撤销,应用及其所有数据将被永久删除。", + "confirm": "删除", + "cancel": "取消" + }, + "scene": { + "loading": "加载中…", + "loadFailed": "加载失败:{{error}}", + "retry": "重试", + "reload": "重新加载" + } +}