From 25db9ed1f313ff313140bfb2a0eac69ff3925fdd Mon Sep 17 00:00:00 2001 From: YoVinchen Date: Sat, 11 Oct 2025 14:55:34 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=B0=B8=E4=B9=85=E5=AD=98?= =?UTF-8?q?=E5=82=A8=E8=AE=B0=E5=BD=95=E4=BF=A1=E6=81=AF=20=E5=AE=8C?= =?UTF-8?q?=E5=96=84=20i18n?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.tsx | 29 ++--- src/components/AgentExecution.tsx | 22 ++-- src/components/AgentRunOutputViewer.tsx | 43 +++--- src/components/AgentRunView.tsx | 18 +-- src/components/CCAgents.tsx | 2 +- src/components/ClaudeCodeSession.tsx | 6 +- src/components/ErrorBoundary.tsx | 9 +- src/components/NFOCredits.tsx | 6 +- src/components/ProjectList.tsx | 37 +++++- src/components/ProjectSettings.tsx | 20 +-- src/components/RunningClaudeSessions.tsx | 10 +- src/components/SessionList.tsx | 8 +- src/components/SessionOutputViewer.tsx | 42 +++--- src/components/Settings.tsx | 42 ++++-- src/components/StorageTab.tsx | 115 ++++++++--------- src/components/TabManager.tsx | 13 +- src/components/TimelineNavigator.tsx | 42 +++--- src/components/ToolWidgets.tsx | 7 +- src/components/Topbar.tsx | 2 +- src/components/WebviewPreview.tsx | 4 +- .../claude-code-session/SessionHeader.tsx | 8 +- src/components/widgets/BashWidget.tsx | 8 +- src/locales/en/common.json | 122 +++++++++++++++++- src/locales/zh/common.json | 120 ++++++++++++++++- 24 files changed, 502 insertions(+), 233 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 0195b3a..e829551 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,6 +1,6 @@ import { useState, useEffect, lazy, Suspense } from "react"; import { motion, AnimatePresence } from "framer-motion"; -import { Plus, Loader2, ArrowLeft } from "lucide-react"; +import { Loader2, ArrowLeft } from "lucide-react"; import { api, type Project, type Session, type ClaudeMdFile } from "@/lib/api"; import { OutputCacheProvider } from "@/lib/outputCache"; import { TabProvider } from "@/contexts/TabContext"; @@ -97,6 +97,13 @@ function AppContent() { initializeBackendLanguage(); }, []); // Run once on app startup + + // Update document title based on current language + useEffect(() => { + try { + document.title = `${t('app.name')} - ${t('app.tagline')}`; + } catch {} + }, [t]); // Track when user reaches different journey stages useEffect(() => { @@ -427,32 +434,16 @@ function AppContent() { exit={{ opacity: 0, x: 20 }} transition={{ duration: 0.3 }} > - {/* New session button at the top */} - - - - {/* Running Claude Sessions */} - {/* Project list */} + {/* Project list with integrated new session button */} {projects.length > 0 ? ( diff --git a/src/components/AgentExecution.tsx b/src/components/AgentExecution.tsx index 2439112..9128d53 100644 --- a/src/components/AgentExecution.tsx +++ b/src/components/AgentExecution.tsx @@ -274,7 +274,7 @@ export const AgentExecution: React.FC = ({ const selected = await open({ directory: true, multiple: false, - title: "Select Project Directory" + title: t('webview.selectProjectDirectory') }); if (selected) { @@ -576,7 +576,7 @@ export const AgentExecution: React.FC = ({

{t('agents.execute')}: {agent.name}

- {model === 'opus' ? 'Claude 4.1 Opus' : 'Claude 4 Sonnet'} + {model === 'opus' ? t('agents.opusName') : t('agents.sonnetName')}

@@ -669,7 +669,7 @@ export const AgentExecution: React.FC = ({
)}
- Claude 4 Sonnet + {t('agents.sonnetName')} @@ -695,7 +695,7 @@ export const AgentExecution: React.FC = ({
)}
- Claude 4.1 Opus + {t('agents.opusName')} @@ -772,7 +772,7 @@ export const AgentExecution: React.FC = ({
- Initializing agent... + {t('agents.initializing') || 'Initializing agent...'}
)} @@ -826,11 +826,11 @@ export const AgentExecution: React.FC = ({
{renderIcon()} -

{agent.name} - Output

+

{agent.name} - {t('app.output')}

{isRunning && (
- Running + {t('agents.statusRunning')}
)}
@@ -878,7 +878,7 @@ export const AgentExecution: React.FC = ({ className="flex items-center gap-2" > - Close + {t('app.close')}
@@ -914,7 +914,7 @@ export const AgentExecution: React.FC = ({
- Initializing agent... + {t('agents.initializing') || 'Initializing agent...'}
)} @@ -967,8 +967,8 @@ export const AgentExecution: React.FC = ({ - Project Settings - Local Settings + {t('agents.projectSettings') || 'Project Settings'} + {t('agents.localSettings') || 'Local Settings'} diff --git a/src/components/AgentRunOutputViewer.tsx b/src/components/AgentRunOutputViewer.tsx index 64189f0..3c4c38d 100644 --- a/src/components/AgentRunOutputViewer.tsx +++ b/src/components/AgentRunOutputViewer.tsx @@ -237,7 +237,7 @@ export function AgentRunOutputViewer({ } } catch (error) { console.error('Failed to load agent output:', error); - setToast({ message: 'Failed to load agent output', type: 'error' }); + setToast({ message: t('app.failedToLoadSessionOutput'), type: 'error' }); } finally { setLoading(false); } @@ -286,12 +286,12 @@ export function AgentRunOutputViewer({ }); const completeUnlisten = await listen(`agent-complete:${run!.id}`, () => { - setToast({ message: 'Agent execution completed', type: 'success' }); + setToast({ message: t('app.agentExecutionCompleted'), type: 'success' }); // Don't set status here as the parent component should handle it }); const cancelUnlisten = await listen(`agent-cancelled:${run!.id}`, () => { - setToast({ message: 'Agent execution was cancelled', type: 'error' }); + setToast({ message: t('app.agentExecutionCancelled'), type: 'error' }); }); unlistenRefs.current = [outputUnlisten, errorUnlisten, completeUnlisten, cancelUnlisten]; @@ -305,7 +305,7 @@ export function AgentRunOutputViewer({ const jsonl = rawJsonlOutput.join('\n'); await navigator.clipboard.writeText(jsonl); setCopyPopoverOpen(false); - setToast({ message: 'Output copied as JSONL', type: 'success' }); + setToast({ message: t('webview.sessionOutputCopiedJsonl'), type: 'success' }); }; const handleCopyAsMarkdown = async () => { @@ -363,7 +363,7 @@ export function AgentRunOutputViewer({ await navigator.clipboard.writeText(markdown); setCopyPopoverOpen(false); - setToast({ message: 'Output copied as Markdown', type: 'success' }); + setToast({ message: t('webview.sessionOutputCopiedMarkdown'), type: 'success' }); }; const handleRefresh = async () => { @@ -383,7 +383,7 @@ export function AgentRunOutputViewer({ const success = await api.killAgentSession(run.id); if (success) { - setToast({ message: 'Agent execution stopped', type: 'success' }); + setToast({ message: t('agentRun.executionStopped'), type: 'success' }); // Clean up listeners unlistenRefs.current.forEach(unlisten => unlisten()); @@ -410,14 +410,11 @@ export function AgentRunOutputViewer({ // Refresh the output to get updated status await loadOutput(true); } else { - setToast({ message: 'Failed to stop agent - it may have already finished', type: 'error' }); + setToast({ message: t('agentRun.stopFailed'), type: 'error' }); } } catch (err) { console.error('[AgentRunOutputViewer] Failed to stop agent:', err); - setToast({ - message: `Failed to stop execution: ${err instanceof Error ? err.message : 'Unknown error'}`, - type: 'error' - }); + setToast({ message: t('agentRun.stopFailed'), type: 'error' }); } }; @@ -515,7 +512,7 @@ export function AgentRunOutputViewer({
-

Loading agent run...

+

{t('app.loadingAgentRun')}

); @@ -537,7 +534,7 @@ export function AgentRunOutputViewer({ {run.status === 'running' && (
- Running + {t('agents.statusRunning')}
)} @@ -546,7 +543,7 @@ export function AgentRunOutputViewer({

- {run.model === 'opus' ? 'Claude 4.1 Opus' : 'Claude 4 Sonnet'} + {run.model === 'opus' ? t('agents.opusName') : t('agents.sonnetName')}
@@ -579,7 +576,7 @@ export function AgentRunOutputViewer({ className="h-8 px-2" > - Copy + {t('app.copyOutput')} } @@ -611,7 +608,7 @@ export function AgentRunOutputViewer({ variant="ghost" size="sm" onClick={() => setIsFullscreen(!isFullscreen)} - title={isFullscreen ? "Exit fullscreen" : "Enter fullscreen"} + title={isFullscreen ? t('webview.exitFullScreen') : t('webview.enterFullScreen')} className="h-8 px-2" > {isFullscreen ? ( @@ -625,7 +622,7 @@ export function AgentRunOutputViewer({ size="sm" onClick={handleRefresh} disabled={refreshing} - title="Refresh output" + title={t('app.refresh')} className="h-8 px-2" > @@ -636,7 +633,7 @@ export function AgentRunOutputViewer({ size="sm" onClick={handleStop} disabled={refreshing} - title="Stop execution" + title={t('agents.stop')} className="h-8 px-2 text-destructive hover:text-destructive" > @@ -650,12 +647,12 @@ export function AgentRunOutputViewer({
- Loading output... + {t('app.loadingOutput')}
) : messages.length === 0 ? (
-

No output available yet

+

{t('app.noOutput')}

) : (
- Stop + {t('agents.stop')} )}
@@ -766,7 +763,7 @@ export function AgentRunOutputViewer({
{messages.length === 0 ? (
- No output available yet + {t('app.noOutput')}
) : ( <> diff --git a/src/components/AgentRunView.tsx b/src/components/AgentRunView.tsx index 093c1d3..6e8f29b 100644 --- a/src/components/AgentRunView.tsx +++ b/src/components/AgentRunView.tsx @@ -103,7 +103,7 @@ export const AgentRunView: React.FC = ({ } } catch (err) { console.error("Failed to load run:", err); - setError("Failed to load execution details"); + setError(t('agentRun.loadFailed')); } finally { setLoading(false); } @@ -198,7 +198,7 @@ export const AgentRunView: React.FC = ({ type: "result", subtype: "error", is_error: true, - result: "Execution stopped by user", + result: t('agentRun.executionStopped'), duration_ms: 0, usage: { input_tokens: 0, @@ -235,8 +235,8 @@ export const AgentRunView: React.FC = ({ if (error || !run) { return (
-

{error || "Run not found"}

- +

{error || t('agentRun.runNotFound')}

+
); } @@ -264,7 +264,7 @@ export const AgentRunView: React.FC = ({ {renderIcon(run.agent_icon)}

{run.agent_name}

-

Execution History

+

{t('agents.executionHistory')}

@@ -278,7 +278,7 @@ export const AgentRunView: React.FC = ({ className="text-destructive hover:text-destructive" > - Stop + {t('agents.stop')} )} @@ -326,10 +326,10 @@ export const AgentRunView: React.FC = ({
-

Task:

+

{t('app.task')}:

{run.task}

- {run.model === 'opus' ? 'Claude 4.1 Opus' : 'Claude 4 Sonnet'} + {run.model === 'opus' ? t('agents.opusName') : t('agents.sonnetName')}
@@ -349,7 +349,7 @@ export const AgentRunView: React.FC = ({ {run.metrics?.total_tokens && (
- {run.metrics.total_tokens} tokens + {run.metrics.total_tokens} {t('usage.tokens')}
)} diff --git a/src/components/CCAgents.tsx b/src/components/CCAgents.tsx index 9c4cfd0..6a36d92 100644 --- a/src/components/CCAgents.tsx +++ b/src/components/CCAgents.tsx @@ -442,7 +442,7 @@ export const CCAgents: React.FC = ({ onBack, className }) => { variant="ghost" onClick={() => handleExportAgent(agent)} className="flex items-center gap-1" - title="Export agent to .claudia.json" + title={t('agents.exportToFile')} > {t('agents.export')} diff --git a/src/components/ClaudeCodeSession.tsx b/src/components/ClaudeCodeSession.tsx index bf76956..8352ac1 100644 --- a/src/components/ClaudeCodeSession.tsx +++ b/src/components/ClaudeCodeSession.tsx @@ -634,7 +634,7 @@ export const ClaudeCodeSession: React.FC = ({ const selected = await open({ directory: true, multiple: false, - title: "Select Project Directory" + title: t('webview.selectProjectDirectory') }); if (selected) { @@ -644,7 +644,7 @@ export const ClaudeCodeSession: React.FC = ({ } catch (err) { console.error("Failed to select directory:", err); const errorMessage = err instanceof Error ? err.message : String(err); - setError(`Failed to select directory: ${errorMessage}`); + setError(t('app.selectDirectoryFailed', { message: errorMessage })); } }; @@ -652,7 +652,7 @@ export const ClaudeCodeSession: React.FC = ({ console.log('[ClaudeCodeSession] handleSendPrompt called with:', { prompt, model, projectPath, claudeSessionId, effectiveSession }); if (!projectPath) { - setError("Please select a project directory first"); + setError(t('app.selectProjectFirst')); return; } diff --git a/src/components/ErrorBoundary.tsx b/src/components/ErrorBoundary.tsx index 591c99e..7aa6adc 100644 --- a/src/components/ErrorBoundary.tsx +++ b/src/components/ErrorBoundary.tsx @@ -2,6 +2,7 @@ import React, { Component, ReactNode } from "react"; import { AlertCircle } from "lucide-react"; import { Button } from "@/components/ui/button"; import { Card, CardContent } from "@/components/ui/card"; +import i18n from "@/lib/i18n"; interface ErrorBoundaryProps { children: ReactNode; @@ -51,14 +52,14 @@ export class ErrorBoundary extends Component
-

Something went wrong

+

{i18n.t('errorBoundary.somethingWentWrong')}

- An error occurred while rendering this component. + {i18n.t('errorBoundary.errorOccurred')}

{this.state.error.message && (
- Error details + {i18n.t('errorBoundary.errorDetails')}
                         {this.state.error.message}
@@ -70,7 +71,7 @@ export class ErrorBoundary extends Component
-                    Try again
+                    {i18n.t('errorBoundary.tryAgain')}
                   
                 
diff --git a/src/components/NFOCredits.tsx b/src/components/NFOCredits.tsx index 80e35ed..a6df28c 100644 --- a/src/components/NFOCredits.tsx +++ b/src/components/NFOCredits.tsx @@ -6,6 +6,7 @@ import { Button } from "@/components/ui/button"; import { openUrl } from "@tauri-apps/plugin-opener"; import asteriskLogo from "@/assets/nfo/asterisk-logo.png"; import keygennMusic from "@/assets/nfo/claudia-nfo.ogg"; +import { useTranslation } from "@/hooks/useTranslation"; interface NFOCreditsProps { /** @@ -22,6 +23,7 @@ interface NFOCreditsProps { * setShowNFO(false)} /> */ export const NFOCredits: React.FC = ({ onClose }) => { + const { t } = useTranslation(); const audioRef = useRef(null); const scrollRef = useRef(null); const [isMuted, setIsMuted] = useState(false); @@ -159,10 +161,10 @@ export const NFOCredits: React.FC = ({ onClose }) => { await openUrl("https://github.com/getAsterisk/claudia/issues/new"); }} className="flex items-center gap-1 h-auto px-2 py-1" - title="File a bug" + title={t('app.fileABug')} > - File a bug + {t('app.fileABug')} + )} + + {/* Search and results info */} +
+
setSearchQuery(e.target.value)} - className="pl-9 pr-9" + className="pl-9 pr-9 h-10" /> {searchQuery && (
- {/* Results info */} -
+ {/* Results info */} +
{searchQuery ? ( {t('showingResults')}: {filteredAndSortedProjects.length} / {projects.length} @@ -141,6 +163,7 @@ export const ProjectList: React.FC = ({ {t('totalProjects')}: {projects.length} )} +
diff --git a/src/components/ProjectSettings.tsx b/src/components/ProjectSettings.tsx index 352b3d0..d91b9f0 100644 --- a/src/components/ProjectSettings.tsx +++ b/src/components/ProjectSettings.tsx @@ -3,6 +3,7 @@ */ import React, { useState, useEffect } from 'react'; +import { useTranslation } from 'react-i18next'; import { HooksEditor } from '@/components/HooksEditor'; import { SlashCommandsManager } from '@/components/SlashCommandsManager'; import { api } from '@/lib/api'; @@ -33,6 +34,7 @@ export const ProjectSettings: React.FC = ({ onBack, className }) => { + const { t } = useTranslation(); const [activeTab, setActiveTab] = useState('commands'); const [toast, setToast] = useState<{ message: string; type: 'success' | 'error' } | null>(null); @@ -89,7 +91,7 @@ export const ProjectSettings: React.FC = ({
-

Project Settings

+

{t('agents.projectSettings')}

@@ -109,15 +111,15 @@ export const ProjectSettings: React.FC = ({ - Slash Commands + {t('slashCommands.slashCommands')} - Project Hooks + {t('hooks.projectHooks', 'Project Hooks')} - Local Hooks + {t('hooks.localHooks', 'Local Hooks')} @@ -125,7 +127,7 @@ export const ProjectSettings: React.FC = ({
-

Project Slash Commands

+

{t('slashCommands.projectSlashCommands')}

Custom commands that are specific to this project. These commands are stored in .claude/slash-commands/ @@ -145,7 +147,7 @@ export const ProjectSettings: React.FC = ({

-

Project Hooks

+

{t('hooks.projectHooks', 'Project Hooks')}

These hooks apply to all users working on this project. They are stored in .claude/settings.json @@ -165,7 +167,7 @@ export const ProjectSettings: React.FC = ({

-

Local Hooks

+

{t('hooks.localHooks', 'Local Hooks')}

These hooks only apply to your machine. They are stored in .claude/settings.local.json @@ -177,7 +179,7 @@ export const ProjectSettings: React.FC = ({

- Local settings file is not in .gitignore + {t('projectSettings.gitignoreLocalWarning')}

)} diff --git a/src/components/RunningClaudeSessions.tsx b/src/components/RunningClaudeSessions.tsx index c0a1d96..df4039f 100644 --- a/src/components/RunningClaudeSessions.tsx +++ b/src/components/RunningClaudeSessions.tsx @@ -46,7 +46,7 @@ export const RunningClaudeSessions: React.FC = ({ setError(null); } catch (err) { console.error("Failed to load running sessions:", err); - setError("Failed to load running sessions"); + setError(t('runningSessions.loadFailed')); } finally { setLoading(false); } @@ -101,10 +101,10 @@ export const RunningClaudeSessions: React.FC = ({
-

Active Claude Sessions

+

{t('runningSessions.title')}

- ({runningSessions.length} running) + {t('runningSessions.countRunning', { count: runningSessions.length })}
@@ -137,7 +137,7 @@ export const RunningClaudeSessions: React.FC = ({ {sessionId.substring(0, 20)}...

- Running + {t('runningSessions.running')}
@@ -163,7 +163,7 @@ export const RunningClaudeSessions: React.FC = ({ className="flex-shrink-0" > - Resume + {t('runningSessions.resume')}
diff --git a/src/components/SessionList.tsx b/src/components/SessionList.tsx index 7b0a282..016ecd4 100644 --- a/src/components/SessionList.tsx +++ b/src/components/SessionList.tsx @@ -8,6 +8,7 @@ import { ClaudeMemoriesDropdown } from "@/components/ClaudeMemoriesDropdown"; import { cn } from "@/lib/utils"; import { formatUnixTimestamp, formatISOTimestamp, truncateText, getFirstLine } from "@/lib/date-utils"; import type { Session, ClaudeMdFile } from "@/lib/api"; +import { useTranslation } from "@/hooks/useTranslation"; interface SessionListProps { /** @@ -57,6 +58,7 @@ export const SessionList: React.FC = ({ onEditClaudeFile, className, }) => { + const { t } = useTranslation(); const [currentPage, setCurrentPage] = useState(1); // Calculate pagination @@ -89,7 +91,7 @@ export const SessionList: React.FC = ({

{projectPath}

- {sessions.length} session{sessions.length !== 1 ? 's' : ''} + {t('projects.sessionCount', { count: sessions.length })}

@@ -149,7 +151,7 @@ export const SessionList: React.FC = ({
- First message: + {t('sessions.firstMessage')}

{truncateText(getFirstLine(session.first_message), 100)} @@ -173,7 +175,7 @@ export const SessionList: React.FC = ({ {session.todo_data && (

- Has todo + {t('sessions.hasTodo')}
)}
diff --git a/src/components/SessionOutputViewer.tsx b/src/components/SessionOutputViewer.tsx index 440eadf..4454333 100644 --- a/src/components/SessionOutputViewer.tsx +++ b/src/components/SessionOutputViewer.tsx @@ -189,7 +189,7 @@ export function SessionOutputViewer({ session, onClose, className }: SessionOutp } } catch (error) { console.error('Failed to load session output:', error); - setToast({ message: 'Failed to load session output', type: 'error' }); + setToast({ message: t('app.failedToLoadSessionOutput'), type: 'error' }); } finally { setLoading(false); } @@ -223,12 +223,12 @@ export function SessionOutputViewer({ session, onClose, className }: SessionOutp }); const completeUnlisten = await listen(`agent-complete:${session.id}`, () => { - setToast({ message: 'Agent execution completed', type: 'success' }); + setToast({ message: t('app.agentExecutionCompleted'), type: 'success' }); // Don't set status here as the parent component should handle it }); const cancelUnlisten = await listen(`agent-cancelled:${session.id}`, () => { - setToast({ message: 'Agent execution was cancelled', type: 'error' }); + setToast({ message: t('app.agentExecutionCancelled'), type: 'error' }); }); unlistenRefs.current = [outputUnlisten, errorUnlisten, completeUnlisten, cancelUnlisten]; @@ -242,7 +242,7 @@ export function SessionOutputViewer({ session, onClose, className }: SessionOutp const jsonl = rawJsonlOutput.join('\n'); await navigator.clipboard.writeText(jsonl); setCopyPopoverOpen(false); - setToast({ message: 'Output copied as JSONL', type: 'success' }); + setToast({ message: t('webview.sessionOutputCopiedJsonl'), type: 'success' }); }; const handleCopyAsMarkdown = async () => { @@ -297,7 +297,7 @@ export function SessionOutputViewer({ session, onClose, className }: SessionOutp await navigator.clipboard.writeText(markdown); setCopyPopoverOpen(false); - setToast({ message: 'Output copied as Markdown', type: 'success' }); + setToast({ message: t('webview.sessionOutputCopiedMarkdown'), type: 'success' }); }; @@ -305,10 +305,10 @@ export function SessionOutputViewer({ session, onClose, className }: SessionOutp setRefreshing(true); try { await loadOutput(true); // Skip cache when manually refreshing - setToast({ message: 'Output refreshed', type: 'success' }); + setToast({ message: t('app.outputRefreshed'), type: 'success' }); } catch (error) { console.error('Failed to refresh output:', error); - setToast({ message: 'Failed to refresh output', type: 'error' }); + setToast({ message: t('app.failedToRefreshOutput'), type: 'error' }); } finally { setRefreshing(false); } @@ -388,7 +388,7 @@ export function SessionOutputViewer({ session, onClose, className }: SessionOutp
{session.agent_icon}
- {session.agent_name} - Output + {session.agent_name} - {t('app.output')}
{session.status} @@ -396,7 +396,7 @@ export function SessionOutputViewer({ session, onClose, className }: SessionOutp {session.status === 'running' && (
- Live + {t('agentRun.live')}
)} @@ -459,7 +459,7 @@ export function SessionOutputViewer({ session, onClose, className }: SessionOutp size="sm" onClick={refreshOutput} disabled={refreshing} - title="Refresh output" + title={t('app.refresh')} > @@ -474,7 +474,7 @@ export function SessionOutputViewer({ session, onClose, className }: SessionOutp
- Loading output... + {t('app.loadingOutput')}
) : ( @@ -498,14 +498,14 @@ export function SessionOutputViewer({ session, onClose, className }: SessionOutp {session.status === 'running' ? ( <> -

Waiting for output...

+

{t('app.waitingForOutput')}

- Agent is running but no output received yet + {t('app.agentRunningNoOutput')}

) : ( <> -

No output available

+

{t('app.noOutput')}

)} @@ -551,11 +551,11 @@ export function SessionOutputViewer({ session, onClose, className }: SessionOutp
{session.agent_icon}
-

{session.agent_name} - Output

+

{session.agent_name} - {t('app.output')}

{session.status === 'running' && (
- Running + {t('agents.statusRunning')}
)}
@@ -605,7 +605,7 @@ export function SessionOutputViewer({ session, onClose, className }: SessionOutp className="flex items-center gap-2" > - Close + {t('app.close')}
@@ -632,14 +632,14 @@ export function SessionOutputViewer({ session, onClose, className }: SessionOutp {session.status === 'running' ? ( <> -

Waiting for output...

+

{t('app.waitingForOutput')}

- Agent is running but no output received yet + {t('app.agentRunningNoOutput')}

) : ( <> -

No output available

+

{t('app.noOutput')}

)}
diff --git a/src/components/Settings.tsx b/src/components/Settings.tsx index c0dac16..fced1dd 100644 --- a/src/components/Settings.tsx +++ b/src/components/Settings.tsx @@ -658,19 +658,37 @@ export const Settings: React.FC = ({ {/* Cleanup Period */}
- { - const value = e.target.value ? parseInt(e.target.value) : undefined; - updateSetting("cleanupPeriodDays", value); - }} - /> +
+ { + const value = e.target.value ? parseInt(e.target.value) : undefined; + updateSetting("cleanupPeriodDays", value); + }} + disabled={settings?.cleanupPeriodDays === -1} + className="flex-1" + /> +
+ { + updateSetting("cleanupPeriodDays", checked ? -1 : undefined); + }} + /> + +
+

- {t('settings.generalOptions.chatRetentionDesc')} + {settings?.cleanupPeriodDays === -1 + ? t('settings.generalOptions.chatRetentionPermanent') + : t('settings.generalOptions.chatRetentionDesc')}

diff --git a/src/components/StorageTab.tsx b/src/components/StorageTab.tsx index 71bd84b..bb808a6 100644 --- a/src/components/StorageTab.tsx +++ b/src/components/StorageTab.tsx @@ -133,7 +133,7 @@ export const StorageTab: React.FC = () => { } } catch (err) { console.error("Failed to load tables:", err); - setError("Failed to load tables"); + setError(t('storageTab.loadTablesFailed')); } finally { setLoading(false); } @@ -158,7 +158,7 @@ export const StorageTab: React.FC = () => { setCurrentPage(page); } catch (err) { console.error("Failed to load table data:", err); - setError("Failed to load table data"); + setError(t('storageTab.loadTableDataFailed')); } finally { setLoading(false); } @@ -205,7 +205,7 @@ export const StorageTab: React.FC = () => { setEditingRow(null); } catch (err) { console.error("Failed to update row:", err); - setError("Failed to update row"); + setError(t('storageTab.updateRowFailed')); } finally { setLoading(false); } @@ -225,7 +225,7 @@ export const StorageTab: React.FC = () => { setDeletingRow(null); } catch (err) { console.error("Failed to delete row:", err); - setError("Failed to delete row"); + setError(t('storageTab.deleteRowFailed')); } finally { setLoading(false); } @@ -244,7 +244,7 @@ export const StorageTab: React.FC = () => { setNewRow(null); } catch (err) { console.error("Failed to insert row:", err); - setError("Failed to insert row"); + setError(t('storageTab.insertRowFailed')); } finally { setLoading(false); } @@ -269,7 +269,7 @@ export const StorageTab: React.FC = () => { } } catch (err) { console.error("Failed to execute SQL:", err); - setSqlError(err instanceof Error ? err.message : "Failed to execute SQL"); + setSqlError(err instanceof Error ? err.message : t('storageTab.executeSqlFailed')); } finally { setLoading(false); } @@ -287,14 +287,14 @@ export const StorageTab: React.FC = () => { setTableData(null); setShowResetConfirm(false); setToast({ - message: "Database Reset Complete: The database has been restored to its default state with empty tables (agents, agent_runs, app_settings).", + message: t('storageTab.resetSuccess'), type: "success", }); } catch (err) { console.error("Failed to reset database:", err); - setError("Failed to reset database"); + setError(t('storageTab.resetDatabaseFailed')); setToast({ - message: "Reset Failed: Failed to reset the database. Please try again.", + message: t('storageTab.resetFailed'), type: "error", }); } finally { @@ -337,7 +337,7 @@ export const StorageTab: React.FC = () => {
-

Database Storage

+

{t('storageTab.title')}

@@ -365,7 +365,7 @@ export const StorageTab: React.FC = () => {
handleSearch(e.target.value)} className="pl-8 h-8 text-xs" @@ -406,7 +406,7 @@ export const StorageTab: React.FC = () => { className="gap-2 h-8 text-xs" > - New Row + {t('storageTab.newRow')} )}
@@ -437,7 +437,7 @@ export const StorageTab: React.FC = () => { ))} - Actions + {t('storageTab.actions')} @@ -520,9 +520,11 @@ export const StorageTab: React.FC = () => { {tableData.total_pages > 1 && (
- Showing {(currentPage - 1) * pageSize + 1} to{" "} - {Math.min(currentPage * pageSize, tableData.total_rows)} of{" "} - {tableData.total_rows} rows + {t('storageTab.pagination.showing', { + from: (currentPage - 1) * pageSize + 1, + to: Math.min(currentPage * pageSize, tableData.total_rows), + total: tableData.total_rows + })}
- Page {currentPage} of {tableData.total_pages} + {t('storageTab.pagination.pageOf', { page: currentPage, total: tableData.total_pages })}
@@ -575,9 +577,9 @@ export const StorageTab: React.FC = () => { setEditingRow(null)}> - Edit Row + {t('storageTab.editRow')} - Update the values for this row in the {selectedTable} table. + {t('storageTab.editRowDesc', { table: selectedTable })} {editingRow && tableData && ( @@ -588,7 +590,7 @@ export const StorageTab: React.FC = () => { {column.name} {column.pk && ( - (Primary Key) + ({t('storageTab.primaryKey')}) )} @@ -622,9 +624,9 @@ export const StorageTab: React.FC = () => { /> )}

- Type: {column.type_name} - {column.notnull && ", NOT NULL"} - {column.dflt_value && `, Default: ${column.dflt_value}`} + {t('storageTab.type')}: {column.type_name} + {column.notnull && `, ${t('storageTab.notNull')}`} + {column.dflt_value && `, ${t('storageTab.default')}: ${column.dflt_value}`}

))} @@ -632,7 +634,7 @@ export const StorageTab: React.FC = () => { )} @@ -652,9 +654,9 @@ export const StorageTab: React.FC = () => { setNewRow(null)}> - New Row + {t('storageTab.newRow')} - Add a new row to the {selectedTable} table. + {t('storageTab.newRowDesc', { table: selectedTable })} {newRow && tableData && ( @@ -665,7 +667,7 @@ export const StorageTab: React.FC = () => { {column.name} {column.notnull && ( - (Required) + ({t('validation.required')}) )} @@ -697,8 +699,8 @@ export const StorageTab: React.FC = () => { /> )}

- Type: {column.type_name} - {column.dflt_value && `, Default: ${column.dflt_value}`} + {t('storageTab.type')}: {column.type_name} + {column.dflt_value && `, ${t('storageTab.default')}: ${column.dflt_value}`}

))} @@ -706,7 +708,7 @@ export const StorageTab: React.FC = () => { )} @@ -726,10 +728,9 @@ export const StorageTab: React.FC = () => { setDeletingRow(null)}> - Delete Row + {t('storageTab.deleteRow')} - Are you sure you want to delete this row? This action cannot be - undone. + {t('storageTab.deleteRowConfirm')} {deletingRow && ( @@ -752,7 +753,7 @@ export const StorageTab: React.FC = () => { )} @@ -773,18 +774,15 @@ export const StorageTab: React.FC = () => { - Reset Database + {t('storageTab.resetDatabaseTitle')} - This will delete all data and recreate the database with its default structure - (empty tables for agents, agent_runs, and app_settings). The database will be - restored to the same state as when you first installed the app. This action - cannot be undone. + {t('storageTab.resetDatabaseDesc')}
- All your agents, runs, and settings will be permanently deleted! + {t('storageTab.resetWarning')}
@@ -792,7 +790,7 @@ export const StorageTab: React.FC = () => { variant="outline" onClick={() => setShowResetConfirm(false)} > - Cancel + {t('app.cancel')} @@ -813,19 +811,19 @@ export const StorageTab: React.FC = () => { - SQL Query Editor + {t('storageTab.sqlEditorTitle')} - Execute raw SQL queries on the database. Use with caution. + {t('storageTab.sqlEditorDesc')}
- +