- Workspace Type:
+ {t('projectWizard.step3.workspaceType')}
- {workspaceType === 'existing' ? 'Existing Workspace' : 'New Workspace'}
+ {workspaceType === 'existing' ? t('projectWizard.step3.existingWorkspace') : t('projectWizard.step3.newWorkspace')}
-
Path:
+
{t('projectWizard.step3.path')}
{workspacePath}
@@ -490,19 +492,19 @@ const ProjectCreationWizard = ({ onClose, onProjectCreated }) => {
{workspaceType === 'new' && githubUrl && (
<>
- Clone From:
+ {t('projectWizard.step3.cloneFrom')}
{githubUrl}
- Authentication:
+ {t('projectWizard.step3.authentication')}
{tokenMode === 'stored' && selectedGithubToken
- ? `Using stored token: ${availableTokens.find(t => t.id.toString() === selectedGithubToken)?.credential_name || 'Unknown'}`
+ ? `${t('projectWizard.step3.usingStoredToken')} ${availableTokens.find(t => t.id.toString() === selectedGithubToken)?.credential_name || 'Unknown'}`
: tokenMode === 'new' && newGithubToken
- ? 'Using provided token'
- : 'No authentication'}
+ ? t('projectWizard.step3.usingProvidedToken')
+ : t('projectWizard.step3.noAuthentication')}
>
@@ -513,10 +515,10 @@ const ProjectCreationWizard = ({ onClose, onProjectCreated }) => {
{workspaceType === 'existing'
- ? 'The workspace will be added to your project list and will be available for Claude/Cursor sessions.'
+ ? t('projectWizard.step3.existingInfo')
: githubUrl
- ? 'A new workspace will be created and the repository will be cloned from GitHub.'
- : 'An empty workspace directory will be created at the specified path.'}
+ ? t('projectWizard.step3.newWithClone')
+ : t('projectWizard.step3.newEmpty')}
@@ -531,11 +533,11 @@ const ProjectCreationWizard = ({ onClose, onProjectCreated }) => {
disabled={isCreating}
>
{step === 1 ? (
- 'Cancel'
+ t('projectWizard.buttons.cancel')
) : (
<>
- Back
+ {t('projectWizard.buttons.back')}
>
)}
@@ -547,16 +549,16 @@ const ProjectCreationWizard = ({ onClose, onProjectCreated }) => {
{isCreating ? (
<>
- Creating...
+ {t('projectWizard.buttons.creating')}
>
) : step === 3 ? (
<>
- Create Project
+ {t('projectWizard.buttons.createProject')}
>
) : (
<>
- Next
+ {t('projectWizard.buttons.next')}
>
)}
diff --git a/src/components/QuickSettingsPanel.jsx b/src/components/QuickSettingsPanel.jsx
index 75f8f24..93b7d07 100644
--- a/src/components/QuickSettingsPanel.jsx
+++ b/src/components/QuickSettingsPanel.jsx
@@ -15,8 +15,10 @@ import {
Languages,
GripVertical
} from 'lucide-react';
+import { useTranslation } from 'react-i18next';
import DarkModeToggle from './DarkModeToggle';
import { useTheme } from '../contexts/ThemeContext';
+import LanguageSelector from './LanguageSelector';
const QuickSettingsPanel = ({
isOpen,
@@ -33,6 +35,7 @@ const QuickSettingsPanel = ({
onSendByCtrlEnterChange,
isMobile
}) => {
+ const { t } = useTranslation('settings');
const [localIsOpen, setLocalIsOpen] = useState(isOpen);
const [whisperMode, setWhisperMode] = useState(() => {
return localStorage.getItem('whisperMode') || 'default';
@@ -253,7 +256,7 @@ const QuickSettingsPanel = ({
- Quick Settings
+ {t('quickSettings.title')}
@@ -261,25 +264,30 @@ const QuickSettingsPanel = ({
{/* Appearance Settings */}
-
Appearance
-
+
{t('quickSettings.sections.appearance')}
+
{isDarkMode ? : }
- Dark Mode
+ {t('quickSettings.darkMode')}
+
+ {/* Language Selector */}
+
+
+
{/* Tool Display Settings */}
-
Tool Display
-
+
{t('quickSettings.sections.toolDisplay')}
+
- Auto-expand tools
+ {t('quickSettings.autoExpandTools')}
- Show raw parameters
+ {t('quickSettings.showRawParameters')}
- Show thinking
+ {t('quickSettings.showThinking')}
{/* View Options */}
{/* Whisper Dictation Settings - HIDDEN */}
-
Whisper Dictation
+
{t('quickSettings.sections.whisperDictation')}
diff --git a/src/components/Settings.jsx b/src/components/Settings.jsx
index a19abbe..cc8809f 100644
--- a/src/components/Settings.jsx
+++ b/src/components/Settings.jsx
@@ -4,6 +4,7 @@ import { Input } from './ui/input';
import { Badge } from './ui/badge';
import { X, Plus, Settings as SettingsIcon, Shield, AlertTriangle, Moon, Sun, Server, Edit3, Trash2, Globe, Terminal, Zap, FolderOpen, LogIn, Key, GitBranch, Check } from 'lucide-react';
import { useTheme } from '../contexts/ThemeContext';
+import { useTranslation } from 'react-i18next';
import ClaudeLogo from './ClaudeLogo';
import CursorLogo from './CursorLogo';
import CodexLogo from './CodexLogo';
@@ -18,9 +19,11 @@ import AgentListItem from './settings/AgentListItem';
import AccountContent from './settings/AccountContent';
import PermissionsContent from './settings/PermissionsContent';
import McpServersContent from './settings/McpServersContent';
+import LanguageSelector from './LanguageSelector';
function Settings({ isOpen, onClose, projects = [], initialTab = 'agents' }) {
const { isDarkMode, toggleDarkMode } = useTheme();
+ const { t } = useTranslation('settings');
const [allowedTools, setAllowedTools] = useState([]);
const [disallowedTools, setDisallowedTools] = useState([]);
const [newAllowedTool, setNewAllowedTool] = useState('');
@@ -1062,6 +1065,11 @@ function Settings({ isOpen, onClose, projects = [], initialTab = 'agents' }) {
+ {/* Language Selector */}
+
+
+
+
{/* Project Sorting */}
@@ -1313,7 +1321,7 @@ function Settings({ isOpen, onClose, projects = [], initialTab = 'agents' }) {
: 'border-transparent text-muted-foreground hover:text-foreground'
}`}
>
- Account
+ {t('tabs.account')}
setSelectedCategory('permissions')}
@@ -1323,7 +1331,7 @@ function Settings({ isOpen, onClose, projects = [], initialTab = 'agents' }) {
: 'border-transparent text-muted-foreground hover:text-foreground'
}`}
>
- Permissions
+ {t('tabs.permissions')}
setSelectedCategory('mcp')}
@@ -1333,7 +1341,7 @@ function Settings({ isOpen, onClose, projects = [], initialTab = 'agents' }) {
: 'border-transparent text-muted-foreground hover:text-foreground'
}`}
>
- MCP Servers
+ {t('tabs.mcpServers')}
diff --git a/src/components/Sidebar.jsx b/src/components/Sidebar.jsx
index b06f56e..55f5ba5 100644
--- a/src/components/Sidebar.jsx
+++ b/src/components/Sidebar.jsx
@@ -4,6 +4,7 @@ import { ScrollArea } from './ui/scroll-area';
import { Button } from './ui/button';
import { Badge } from './ui/badge';
import { Input } from './ui/input';
+import { useTranslation } from 'react-i18next';
import { FolderOpen, Folder, Plus, MessageSquare, Clock, ChevronDown, ChevronRight, Edit3, Check, X, Trash2, Settings, FolderPlus, RefreshCw, Sparkles, Edit2, Star, Search } from 'lucide-react';
import { cn } from '../lib/utils';
@@ -17,28 +18,28 @@ import { useTaskMaster } from '../contexts/TaskMasterContext';
import { useTasksSettings } from '../contexts/TasksSettingsContext';
// Move formatTimeAgo outside component to avoid recreation on every render
-const formatTimeAgo = (dateString, currentTime) => {
+const formatTimeAgo = (dateString, currentTime, t) => {
const date = new Date(dateString);
const now = currentTime;
-
+
// Check if date is valid
if (isNaN(date.getTime())) {
- return 'Unknown';
+ return t ? t('status.unknown') : 'Unknown';
}
-
+
const diffInMs = now - date;
const diffInSeconds = Math.floor(diffInMs / 1000);
const diffInMinutes = Math.floor(diffInMs / (1000 * 60));
const diffInHours = Math.floor(diffInMs / (1000 * 60 * 60));
const diffInDays = Math.floor(diffInMs / (1000 * 60 * 60 * 24));
-
- if (diffInSeconds < 60) return 'Just now';
- if (diffInMinutes === 1) return '1 min ago';
- if (diffInMinutes < 60) return `${diffInMinutes} mins ago`;
- if (diffInHours === 1) return '1 hour ago';
- if (diffInHours < 24) return `${diffInHours} hours ago`;
- if (diffInDays === 1) return '1 day ago';
- if (diffInDays < 7) return `${diffInDays} days ago`;
+
+ if (diffInSeconds < 60) return t ? t('time.justNow') : 'Just now';
+ if (diffInMinutes === 1) return t ? t('time.oneMinuteAgo') : '1 min ago';
+ if (diffInMinutes < 60) return t ? t('time.minutesAgo', { count: diffInMinutes }) : `${diffInMinutes} mins ago`;
+ if (diffInHours === 1) return t ? t('time.oneHourAgo') : '1 hour ago';
+ if (diffInHours < 24) return t ? t('time.hoursAgo', { count: diffInHours }) : `${diffInHours} hours ago`;
+ if (diffInDays === 1) return t ? t('time.oneDayAgo') : '1 day ago';
+ if (diffInDays < 7) return t ? t('time.daysAgo', { count: diffInDays }) : `${diffInDays} days ago`;
return date.toLocaleDateString();
};
@@ -63,6 +64,7 @@ function Sidebar({
isMobile,
onToggleSidebar
}) {
+ const { t } = useTranslation('sidebar');
const [expandedProjects, setExpandedProjects] = useState(new Set());
const [editingProject, setEditingProject] = useState(null);
const [showNewProject, setShowNewProject] = useState(false);
@@ -304,7 +306,7 @@ function Sidebar({
};
const deleteSession = async (projectName, sessionId, provider = 'claude') => {
- if (!confirm('Are you sure you want to delete this session? This action cannot be undone.')) {
+ if (!confirm(t('messages.deleteSessionConfirm'))) {
return;
}
@@ -332,16 +334,16 @@ function Sidebar({
} else {
const errorText = await response.text();
console.error('[Sidebar] Failed to delete session:', { status: response.status, error: errorText });
- alert('Failed to delete session. Please try again.');
+ alert(t('messages.deleteSessionFailed'));
}
} catch (error) {
console.error('[Sidebar] Error deleting session:', error);
- alert('Error deleting session. Please try again.');
+ alert(t('messages.deleteSessionError'));
}
};
const deleteProject = async (projectName) => {
- if (!confirm('Are you sure you want to delete this empty project? This action cannot be undone.')) {
+ if (!confirm(t('messages.deleteProjectConfirm'))) {
return;
}
@@ -356,34 +358,34 @@ function Sidebar({
} else {
const error = await response.json();
console.error('Failed to delete project');
- alert(error.error || 'Failed to delete project. Please try again.');
+ alert(error.error || t('messages.deleteProjectFailed'));
}
} catch (error) {
console.error('Error deleting project:', error);
- alert('Error deleting project. Please try again.');
+ alert(t('messages.deleteProjectError'));
}
};
const createNewProject = async () => {
if (!newProjectPath.trim()) {
- alert('Please enter a project path');
+ alert(t('messages.enterProjectPath'));
return;
}
setCreatingProject(true);
-
+
try {
const response = await api.createProject(newProjectPath.trim());
if (response.ok) {
const result = await response.json();
-
+
// Save the path to recent paths before clearing
saveToRecentPaths(newProjectPath.trim());
-
+
setShowNewProject(false);
setNewProjectPath('');
-
+
// Refresh projects to show the new one
if (window.refreshProjects) {
window.refreshProjects();
@@ -392,11 +394,11 @@ function Sidebar({
}
} else {
const error = await response.json();
- alert(error.error || 'Failed to create project. Please try again.');
+ alert(error.error || t('messages.createProjectFailed'));
}
} catch (error) {
console.error('Error creating project:', error);
- alert('Error creating project. Please try again.');
+ alert(t('messages.createProjectError'));
} finally {
setCreatingProject(false);
}
@@ -497,14 +499,14 @@ function Sidebar({
-
Claude Code UI
-
AI coding assistant interface
+
{t('app.title')}
+
{t('app.subtitle')}
) : (
@@ -513,8 +515,8 @@ function Sidebar({
-
Claude Code UI
-
AI coding assistant interface
+
{t('app.title')}
+
{t('app.subtitle')}
)}
@@ -524,7 +526,7 @@ function Sidebar({
size="sm"
className="h-8 w-8 px-0 hover:bg-accent transition-colors duration-200"
onClick={onToggleSidebar}
- title="Hide sidebar"
+ title={t('tooltips.hideSidebar')}
>
-
Claude Code UI
-
Projects
+
{t('app.title')}
+
{t('projects.title')}
) : (
@@ -564,8 +566,8 @@ function Sidebar({