From 8df5e68a001f6e6f7548c20924b1022a9a1486d9 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Thu, 28 Aug 2025 12:11:52 +0000 Subject: [PATCH 1/3] feat: Add Japanese translation This commit adds Japanese translation to the application. - A new `i18n.js` file has been created to store all the Japanese translation strings. - The following components have been modified to use the new translations: - `App.jsx` - `ChatInterface.jsx` - `LoginForm.jsx` - `SetupForm.jsx` - `Sidebar.jsx` - `ToolsSettings.jsx` --- src/App.jsx | 18 +- src/components/ChatInterface.jsx | 151 ++++++++-------- src/components/LoginForm.jsx | 20 ++- src/components/SetupForm.jsx | 28 +-- src/components/Sidebar.jsx | 154 ++++++++-------- src/components/ToolsSettings.jsx | 266 +++++++++++++-------------- src/lib/i18n.js | 298 +++++++++++++++++++++++++++++++ 7 files changed, 623 insertions(+), 312 deletions(-) create mode 100644 src/lib/i18n.js diff --git a/src/App.jsx b/src/App.jsx index 1cbd7eb8..8fc00cae 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -20,6 +20,7 @@ import React, { useState, useEffect } from 'react'; import { BrowserRouter as Router, Routes, Route, useNavigate, useParams } from 'react-router-dom'; +import { translations } from './lib/i18n.js'; import Sidebar from './components/Sidebar'; import MainContent from './components/MainContent'; import MobileNav from './components/MobileNav'; @@ -38,6 +39,7 @@ import { api, authenticatedFetch } from './utils/api'; function AppContent() { const navigate = useNavigate(); const { sessionId } = useParams(); + const t = (key) => translations[key] || key; const { updateAvailable, latestVersion, currentVersion } = useVersionCheck('siteboon', 'claudecodeui'); const [showVersionModal, setShowVersionModal] = useState(false); @@ -474,8 +476,8 @@ function AppContent() {
-

Update Available

-

A new version is ready

+

{t("Update Available")}

+

{t("A new version is ready")}

diff --git a/src/components/ChatInterface.jsx b/src/components/ChatInterface.jsx index 26820b23..b5dfe6e2 100644 --- a/src/components/ChatInterface.jsx +++ b/src/components/ChatInterface.jsx @@ -19,6 +19,7 @@ import React, { useState, useEffect, useRef, useMemo, useCallback, memo } from 'react'; import ReactMarkdown from 'react-markdown'; import { useDropzone } from 'react-dropzone'; +import { translations } from '../lib/i18n.js'; import TodoList from './TodoList'; import ClaudeLogo from './ClaudeLogo.jsx'; import CursorLogo from './CursorLogo.jsx'; @@ -155,6 +156,7 @@ const safeLocalStorage = { // Memoized message component to prevent unnecessary re-renders const MessageComponent = memo(({ message, index, prevMessage, createDiff, onFileOpen, onShowSettings, autoExpandTools, showRawParameters }) => { + const t = (key) => translations[key] || key; const isGrouped = prevMessage && prevMessage.type === message.type && prevMessage.type === 'assistant' && !prevMessage.isToolUse && !message.isToolUse; @@ -246,7 +248,7 @@ const MessageComponent = memo(({ message, index, prevMessage, createDiff, onFile )}
- {message.type === 'error' ? 'Error' : message.type === 'tool' ? 'Tool' : ((localStorage.getItem('selected-provider') || 'claude') === 'cursor' ? 'Cursor' : 'Claude')} + {message.type === 'error' ? t('Error') : message.type === 'tool' ? t('Tool') : ((localStorage.getItem('selected-provider') || 'claude') === 'cursor' ? t('Cursor') : t('Claude'))}
)} @@ -264,7 +266,7 @@ const MessageComponent = memo(({ message, index, prevMessage, createDiff, onFile - Using {message.toolName} + {t("Using {message.toolName}").replace("{message.toolName}", message.toolName)} {message.toolId} @@ -277,7 +279,7 @@ const MessageComponent = memo(({ message, index, prevMessage, createDiff, onFile onShowSettings(); }} className="p-1 rounded hover:bg-blue-200 dark:hover:bg-blue-800 transition-colors" - title="Tool Settings" + title={t("Tool Settings")} > @@ -296,7 +298,7 @@ const MessageComponent = memo(({ message, index, prevMessage, createDiff, onFile - 📝 View edit diff for + 📝 {t("View edit diff for")} - Diff + {t("Diff")}
@@ -351,7 +353,7 @@ const MessageComponent = memo(({ message, index, prevMessage, createDiff, onFile {showRawParameters && (
- View raw parameters + {t("View raw parameters")}
                                   {message.toolInput}
@@ -368,7 +370,7 @@ const MessageComponent = memo(({ message, index, prevMessage, createDiff, onFile
                   return (
                     
- View input parameters + {t("View input parameters")}
                         {message.toolInput}
@@ -398,7 +400,7 @@ const MessageComponent = memo(({ message, index, prevMessage, createDiff, onFile
                               
                                 
                               
-                              📄 Creating new file: 
+                              📄 {t("Creating new file:")}
                               
                                   
-                                    New File
+                                    {t("New File")}
                                   
                                 
@@ -453,7 +455,7 @@ const MessageComponent = memo(({ message, index, prevMessage, createDiff, onFile {showRawParameters && (
- View raw parameters + {t("View raw parameters")}
                                     {message.toolInput}
@@ -480,14 +482,14 @@ const MessageComponent = memo(({ message, index, prevMessage, createDiff, onFile
                               
                                 
                               
-                              Updating Todo List
+                              {t("Updating Todo List")}
                             
                             
{showRawParameters && (
- View raw parameters + {t("View raw parameters")}
                                     {message.toolInput}
@@ -513,7 +515,7 @@ const MessageComponent = memo(({ message, index, prevMessage, createDiff, onFile
                             
                               
                             
-                            Running command
+                            {t("Running command")}
                           
                           
@@ -521,7 +523,7 @@ const MessageComponent = memo(({ message, index, prevMessage, createDiff, onFile - Terminal + {t("Terminal")}
$ {input.command} @@ -535,7 +537,7 @@ const MessageComponent = memo(({ message, index, prevMessage, createDiff, onFile {showRawParameters && (
- View raw parameters + {t("View raw parameters")}
                                   {message.toolInput}
@@ -559,7 +561,7 @@ const MessageComponent = memo(({ message, index, prevMessage, createDiff, onFile
                         
                         return (
                           
- Read{' '} + {t("Read")}{' '}

- The file content is displayed in the diff view above + {t("The file content is displayed in the diff view above")}

); @@ -871,7 +873,7 @@ const MessageComponent = memo(({ message, index, prevMessage, createDiff, onFile - View file content + {t("View file content")}
@@ -889,7 +891,7 @@ const MessageComponent = memo(({ message, index, prevMessage, createDiff, onFile - View full output ({content.length} chars) + {t("View full output ({content.length} chars)").replace("{content.length}", content.length)}
{content} @@ -919,7 +921,7 @@ const MessageComponent = memo(({ message, index, prevMessage, createDiff, onFile

- Interactive Prompt + {t("Interactive Prompt")}

{(() => { const lines = message.content.split('\n').filter(line => line.trim()); @@ -979,10 +981,10 @@ const MessageComponent = memo(({ message, index, prevMessage, createDiff, onFile

- ⏳ Waiting for your response in the CLI + {t("⏳ Waiting for your response in the CLI")}

- Please select an option in your terminal where Claude is running. + {t("Please select an option in your terminal where Claude is running.")}

@@ -1000,7 +1002,7 @@ const MessageComponent = memo(({ message, index, prevMessage, createDiff, onFile const filename = input.file_path.split('/').pop(); return (
- 📖 Read{' '} + 📖 {t("Read")}{' '}

- Enter your credentials to access Claude Code UI + {t("Enter your credentials to access Claude Code UI")}

diff --git a/src/components/SetupForm.jsx b/src/components/SetupForm.jsx index f1aa497e..08b74b99 100644 --- a/src/components/SetupForm.jsx +++ b/src/components/SetupForm.jsx @@ -1,8 +1,10 @@ import React, { useState } from 'react'; import { useAuth } from '../contexts/AuthContext'; import ClaudeLogo from './ClaudeLogo'; +import { translations } from '../lib/i18n.js'; const SetupForm = () => { + const t = (key) => translations[key] || key; const [username, setUsername] = useState(''); const [password, setPassword] = useState(''); const [confirmPassword, setConfirmPassword] = useState(''); @@ -16,17 +18,17 @@ const SetupForm = () => { setError(''); if (password !== confirmPassword) { - setError('Passwords do not match'); + setError(t('Passwords do not match')); return; } if (username.length < 3) { - setError('Username must be at least 3 characters long'); + setError(t('Username must be at least 3 characters long')); return; } if (password.length < 6) { - setError('Password must be at least 6 characters long'); + setError(t('Password must be at least 6 characters long')); return; } @@ -50,9 +52,9 @@ const SetupForm = () => {
-

Welcome to Claude Code UI

+

{t("Welcome to Claude Code UI")}

- Set up your account to get started + {t("Set up your account to get started")}

@@ -60,7 +62,7 @@ const SetupForm = () => {
{ value={username} onChange={(e) => setUsername(e.target.value)} className="w-full px-3 py-2 border border-border rounded-md bg-background text-foreground focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent" - placeholder="Enter your username" + placeholder={t("Enter your username")} required disabled={isLoading} /> @@ -76,7 +78,7 @@ const SetupForm = () => {
{ value={password} onChange={(e) => setPassword(e.target.value)} className="w-full px-3 py-2 border border-border rounded-md bg-background text-foreground focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent" - placeholder="Enter your password" + placeholder={t("Enter your password")} required disabled={isLoading} /> @@ -92,7 +94,7 @@ const SetupForm = () => {
{ value={confirmPassword} onChange={(e) => setConfirmPassword(e.target.value)} className="w-full px-3 py-2 border border-border rounded-md bg-background text-foreground focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent" - placeholder="Confirm your password" + placeholder={t("Confirm your password")} required disabled={isLoading} /> @@ -117,13 +119,13 @@ const SetupForm = () => { disabled={isLoading} className="w-full bg-blue-600 hover:bg-blue-700 disabled:bg-blue-400 text-white font-medium py-2 px-4 rounded-md transition-colors duration-200" > - {isLoading ? 'Setting up...' : 'Create Account'} + {isLoading ? t('Setting up...') : t('Create Account')}

- This is a single-user system. Only one account can be created. + {t("This is a single-user system. Only one account can be created.")}

diff --git a/src/components/Sidebar.jsx b/src/components/Sidebar.jsx index 36f3bbfe..10f36762 100644 --- a/src/components/Sidebar.jsx +++ b/src/components/Sidebar.jsx @@ -1,6 +1,7 @@ import React, { useState, useEffect } from 'react'; import { ScrollArea } from './ui/scroll-area'; import { Button } from './ui/button'; +import { translations } from '../lib/i18n.js'; import { Badge } from './ui/badge'; import { Input } from './ui/input'; @@ -11,13 +12,13 @@ import CursorLogo from './CursorLogo.jsx'; import { api } from '../utils/api'; // 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('Unknown'); } const diffInMs = now - date; @@ -26,13 +27,13 @@ const formatTimeAgo = (dateString, currentTime) => { 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('Just now'); + if (diffInMinutes === 1) return t('1 min ago'); + if (diffInMinutes < 60) return `${diffInMinutes}${t(' mins ago')}`; + if (diffInHours === 1) return t('1 hour ago'); + if (diffInHours < 24) return `${diffInHours}${t(' hours ago')}`; + if (diffInDays === 1) return t('1 day ago'); + if (diffInDays < 7) return `${diffInDays}${t(' days ago')}`; return date.toLocaleDateString(); }; @@ -53,6 +54,7 @@ function Sidebar({ currentVersion, onShowVersionModal }) { + const t = (key) => translations[key] || key; const [expandedProjects, setExpandedProjects] = useState(new Set()); const [editingProject, setEditingProject] = useState(null); const [showNewProject, setShowNewProject] = useState(false); @@ -77,7 +79,7 @@ function Sidebar({ const saved = localStorage.getItem('starredProjects'); return saved ? new Set(JSON.parse(saved)) : new Set(); } catch (error) { - console.error('Error loading starred projects:', error); + console.error(t('Error loading starred projects:'), error); return new Set(); } }); @@ -140,7 +142,7 @@ function Sidebar({ setProjectSortOrder(settings.projectSortOrder || 'name'); } } catch (error) { - console.error('Error loading sort order:', error); + console.error(t('Error loading sort order:'), error); } }; @@ -193,7 +195,7 @@ function Sidebar({ try { localStorage.setItem('starredProjects', JSON.stringify([...newStarred])); } catch (error) { - console.error('Error saving starred projects:', error); + console.error(t('Error saving starred projects:'), error); } }; @@ -270,10 +272,10 @@ function Sidebar({ window.location.reload(); } } else { - console.error('Failed to rename project'); + console.error(t('Failed to rename project')); } } catch (error) { - console.error('Error renaming project:', error); + console.error(t('Error renaming project:'), error); } setEditingProject(null); @@ -281,7 +283,7 @@ function Sidebar({ }; const deleteSession = async (projectName, sessionId) => { - if (!confirm('Are you sure you want to delete this session? This action cannot be undone.')) { + if (!confirm(t('Are you sure you want to delete this session? This action cannot be undone.'))) { return; } @@ -294,17 +296,17 @@ function Sidebar({ onSessionDelete(sessionId); } } else { - console.error('Failed to delete session'); - alert('Failed to delete session. Please try again.'); + console.error(t('Failed to delete session')); + alert(t('Failed to delete session. Please try again.')); } } catch (error) { - console.error('Error deleting session:', error); - alert('Error deleting session. Please try again.'); + console.error(t('Error deleting session:'), error); + alert(t('Error deleting session. Please try again.')); } }; const deleteProject = async (projectName) => { - if (!confirm('Are you sure you want to delete this empty project? This action cannot be undone.')) { + if (!confirm(t('Are you sure you want to delete this empty project? This action cannot be undone.'))) { return; } @@ -318,18 +320,18 @@ function Sidebar({ } } else { const error = await response.json(); - console.error('Failed to delete project'); - alert(error.error || 'Failed to delete project. Please try again.'); + console.error(t('Failed to delete project')); + alert(error.error || t('Failed to delete project. Please try again.')); } } catch (error) { - console.error('Error deleting project:', error); - alert('Error deleting project. Please try again.'); + console.error(t('Error deleting project:'), error); + alert(t('Error deleting project. Please try again.')); } }; const createNewProject = async () => { if (!newProjectPath.trim()) { - alert('Please enter a project path'); + alert(t('Please enter a project path')); return; } @@ -351,11 +353,11 @@ function Sidebar({ } } else { const error = await response.json(); - alert(error.error || 'Failed to create project. Please try again.'); + alert(error.error || t('Failed to create project. Please try again.')); } } catch (error) { - console.error('Error creating project:', error); - alert('Error creating project. Please try again.'); + console.error(t('Error creating project:'), error); + alert(t('Error creating project. Please try again.')); } finally { setCreatingProject(false); } @@ -399,7 +401,7 @@ function Sidebar({ } } } catch (error) { - console.error('Error loading more sessions:', error); + console.error(t('Error loading more sessions:'), error); } finally { setLoadingSessions(prev => ({ ...prev, [project.name]: false })); } @@ -428,8 +430,8 @@ function Sidebar({
-

Claude Code UI

-

AI coding assistant interface

+

{t("Claude Code UI")}

+

{t("AI coding assistant interface")}

@@ -446,7 +448,7 @@ function Sidebar({ } }} disabled={isRefreshing} - title="Refresh projects and sessions (Ctrl+R)" + title={t("Refresh projects and sessions (Ctrl+R)")} > @@ -455,7 +457,7 @@ function Sidebar({ size="sm" className="h-9 w-9 px-0 bg-primary hover:bg-primary/90 transition-all duration-200 shadow-sm hover:shadow-md" onClick={() => setShowNewProject(true)} - title="Create new project (Ctrl+N)" + title={t("Create new project (Ctrl+N)")} > @@ -470,8 +472,8 @@ function Sidebar({
-

Claude Code UI

-

Projects

+

{t("Claude Code UI")}

+

{t("Projects")}

@@ -507,12 +509,12 @@ function Sidebar({
- Create New Project + {t("Create New Project")}
setNewProjectPath(e.target.value)} - placeholder="/path/to/project or relative/path" + placeholder={t("/path/to/project or relative/path")} className="text-sm focus:ring-2 focus:ring-primary/20" autoFocus onKeyDown={(e) => { @@ -527,7 +529,7 @@ function Sidebar({ disabled={!newProjectPath.trim() || creatingProject} className="flex-1 h-8 text-xs hover:bg-primary/90 transition-colors" > - {creatingProject ? 'Creating...' : 'Create Project'} + {creatingProject ? t('Creating...') : t('Create Project')}
@@ -550,7 +552,7 @@ function Sidebar({
-

New Project

+

{t("New Project")}

@@ -608,7 +610,7 @@ function Sidebar({ setSearchFilter(e.target.value)} className="pl-9 h-9 text-sm bg-muted/50 border-0 focus:bg-background focus:ring-1 focus:ring-primary/20" @@ -633,9 +635,9 @@ function Sidebar({
-

Loading projects...

+

{t("Loading projects...")}

- Fetching your Claude projects and sessions + {t("Fetching your Claude projects and sessions")}

) : projects.length === 0 ? ( @@ -643,9 +645,9 @@ function Sidebar({
-

No projects found

+

{t("No projects found")}

- Run Claude CLI in a project directory to get started + {t("Run Claude CLI in a project directory to get started")}

) : filteredProjects.length === 0 ? ( @@ -653,9 +655,9 @@ function Sidebar({
-

No matching projects

+

{t("No matching projects")}

- Try adjusting your search term + {t("Try adjusting your search term")}

) : ( @@ -701,7 +703,7 @@ function Sidebar({ value={editingName} onChange={(e) => setEditingName(e.target.value)} className="w-full px-3 py-2 text-sm border-2 border-primary/40 focus:border-primary rounded-lg bg-background text-foreground shadow-sm focus:shadow-md transition-all duration-200 focus:outline-none" - placeholder="Project name" + placeholder={t("Project name")} autoFocus autoComplete="off" onClick={(e) => e.stopPropagation()} @@ -725,7 +727,7 @@ function Sidebar({ const sessionCount = getAllSessions(project).length; const hasMore = project.sessionMeta?.hasMore !== false; const count = hasMore && sessionCount >= 5 ? `${sessionCount}+` : sessionCount; - return `${count} session${count === 1 ? '' : 's'}`; + return `${count} ${count === 1 ? t('session') : t('sessions')}`; })()}

@@ -769,7 +771,7 @@ function Sidebar({ toggleStarProject(project.name); }} onTouchEnd={handleTouchClick(() => toggleStarProject(project.name))} - title={isStarred ? "Remove from favorites" : "Add to favorites"} + title={isStarred ? t("Remove from favorites") : t("Add to favorites")} > setEditingName(e.target.value)} className="w-full px-2 py-1 text-sm border border-border rounded bg-background text-foreground focus:ring-2 focus:ring-primary/20" - placeholder="Project name" + placeholder={t("Project name")} autoFocus onKeyDown={(e) => { if (e.key === 'Enter') saveProjectName(project.name); @@ -919,7 +921,7 @@ function Sidebar({ e.stopPropagation(); toggleStarProject(project.name); }} - title={isStarred ? "Remove from favorites" : "Add to favorites"} + title={isStarred ? t("Remove from favorites") : t("Add to favorites")} > @@ -945,7 +947,7 @@ function Sidebar({ e.stopPropagation(); deleteProject(project.name); }} - title="Delete empty project (Delete)" + title={t("Delete empty project (Delete)")} > @@ -979,7 +981,7 @@ function Sidebar({ )) ) : getAllSessions(project).length === 0 && !loadingSessions[project.name] ? (
-

No sessions yet

+

{t("No sessions yet")}

) : ( getAllSessions(project).map((session) => { @@ -992,7 +994,7 @@ function Sidebar({ const isActive = diffInMinutes < 10; // Get session display values - const sessionName = isCursorSession ? (session.name || 'Untitled Session') : (session.summary || 'New Session'); + const sessionName = isCursorSession ? (session.name || t('Untitled Session')) : (session.summary || t('New Session')); const sessionTime = isCursorSession ? session.createdAt : session.lastActivity; const messageCount = session.messageCount || 0; @@ -1039,7 +1041,7 @@ function Sidebar({
- {formatTimeAgo(sessionTime, currentTime)} + {formatTimeAgo(sessionTime, currentTime, t)} {messageCount > 0 && ( @@ -1097,7 +1099,7 @@ function Sidebar({
- {formatTimeAgo(sessionTime, currentTime)} + {formatTimeAgo(sessionTime, currentTime, t)} {messageCount > 0 && ( @@ -1144,7 +1146,7 @@ function Sidebar({ e.stopPropagation(); updateSessionSummary(project.name, session.id, editingSessionName); }} - title="Save" + title={t("Save")} > @@ -1155,7 +1157,7 @@ function Sidebar({ setEditingSession(null); setEditingSessionName(''); }} - title="Cancel" + title={t("Cancel")} > @@ -1184,9 +1186,9 @@ function Sidebar({ onClick={(e) => { e.stopPropagation(); setEditingSession(session.id); - setEditingSessionName(session.summary || 'New Session'); + setEditingSessionName(session.summary || t('New Session')); }} - title="Manually edit session name" + title={t("Manually edit session name")} > @@ -1197,7 +1199,7 @@ function Sidebar({ e.stopPropagation(); deleteSession(project.name, session.id); }} - title="Delete this session permanently" + title={t("Delete this session permanently")} > @@ -1223,12 +1225,12 @@ function Sidebar({ {loadingSessions[project.name] ? ( <>
- Loading... + {t("Loading...")} ) : ( <> - Show more sessions + {t("Show more sessions")} )} @@ -1244,7 +1246,7 @@ function Sidebar({ }} > - New Session + {t("New Session")}
@@ -1255,7 +1257,7 @@ function Sidebar({ onClick={() => onNewSession(project)} > - New Session + {t("New Session")}
)} @@ -1283,8 +1285,8 @@ function Sidebar({
-
Update Available
-
Version {latestVersion} is ready
+
{t("Update Available")}
+
{t("Version {latestVersion} is ready").replace("{latestVersion}", latestVersion)}
@@ -1302,8 +1304,8 @@ function Sidebar({
-
Update Available
-
Version {latestVersion} is ready
+
{t("Update Available")}
+
{t("Version {latestVersion} is ready").replace("{latestVersion}", latestVersion)}
@@ -1321,7 +1323,7 @@ function Sidebar({
- Settings + {t("Settings")} @@ -1332,7 +1334,7 @@ function Sidebar({ onClick={onShowSettings} > - Tools Settings + {t("Tools Settings")} diff --git a/src/components/ToolsSettings.jsx b/src/components/ToolsSettings.jsx index b0939c37..b532f7a8 100644 --- a/src/components/ToolsSettings.jsx +++ b/src/components/ToolsSettings.jsx @@ -1,11 +1,13 @@ import { useState, useEffect } from 'react'; import { Button } from './ui/button'; import { Input } from './ui/input'; +import { translations } from '../lib/i18n.js'; import { Badge } from './ui/badge'; import { X, Plus, Settings, Shield, AlertTriangle, Moon, Sun, Server, Edit3, Trash2, Globe, Terminal, Zap, FolderOpen } from 'lucide-react'; import { useTheme } from '../contexts/ThemeContext'; function ToolsSettings({ isOpen, onClose, projects = [] }) { + const t = (key) => translations[key] || key; const { isDarkMode, toggleDarkMode } = useTheme(); const [allowedTools, setAllowedTools] = useState([]); const [disallowedTools, setDisallowedTools] = useState([]); @@ -99,10 +101,10 @@ function ToolsSettings({ isOpen, onClose, projects = [] }) { const data = await response.json(); setCursorMcpServers(data.servers || []); } else { - console.error('Failed to fetch Cursor MCP servers'); + console.error(t('Failed to fetch Cursor MCP servers')); } } catch (error) { - console.error('Error fetching Cursor MCP servers:', error); + console.error(t('Error fetching Cursor MCP servers:'), error); } }; @@ -172,10 +174,10 @@ function ToolsSettings({ isOpen, onClose, projects = [] }) { const data = await response.json(); setMcpServers(data.servers || []); } else { - console.error('Failed to fetch MCP servers'); + console.error(t('Failed to fetch MCP servers')); } } catch (error) { - console.error('Error fetching MCP servers:', error); + console.error(t('Error fetching MCP servers:'), error); } }; @@ -214,14 +216,14 @@ function ToolsSettings({ isOpen, onClose, projects = [] }) { await fetchMcpServers(); // Refresh the list return true; } else { - throw new Error(result.error || 'Failed to save server via Claude CLI'); + throw new Error(result.error || t('Failed to save server via Claude CLI')); } } else { const error = await response.json(); - throw new Error(error.error || 'Failed to save server'); + throw new Error(error.error || t('Failed to save server')); } } catch (error) { - console.error('Error saving MCP server:', error); + console.error(t('Error saving MCP server:'), error); throw error; } }; @@ -245,14 +247,14 @@ function ToolsSettings({ isOpen, onClose, projects = [] }) { await fetchMcpServers(); // Refresh the list return true; } else { - throw new Error(result.error || 'Failed to delete server via Claude CLI'); + throw new Error(result.error || t('Failed to delete server via Claude CLI')); } } else { const error = await response.json(); - throw new Error(error.error || 'Failed to delete server'); + throw new Error(error.error || t('Failed to delete server')); } } catch (error) { - console.error('Error deleting MCP server:', error); + console.error(t('Error deleting MCP server:'), error); throw error; } }; @@ -273,10 +275,10 @@ function ToolsSettings({ isOpen, onClose, projects = [] }) { return data.testResult; } else { const error = await response.json(); - throw new Error(error.error || 'Failed to test server'); + throw new Error(error.error || t('Failed to test server')); } } catch (error) { - console.error('Error testing MCP server:', error); + console.error(t('Error testing MCP server:'), error); throw error; } }; @@ -298,10 +300,10 @@ function ToolsSettings({ isOpen, onClose, projects = [] }) { return data.toolsResult; } else { const error = await response.json(); - throw new Error(error.error || 'Failed to discover tools'); + throw new Error(error.error || t('Failed to discover tools')); } } catch (error) { - console.error('Error discovering MCP tools:', error); + console.error(t('Error discovering MCP tools:'), error); throw error; } }; @@ -353,7 +355,7 @@ function ToolsSettings({ isOpen, onClose, projects = [] }) { // Load Cursor MCP servers await fetchCursorMcpServers(); } catch (error) { - console.error('Error loading tool settings:', error); + console.error(t('Error loading tool settings:'), error); // Set defaults on error setAllowedTools([]); setDisallowedTools([]); @@ -394,7 +396,7 @@ function ToolsSettings({ isOpen, onClose, projects = [] }) { onClose(); }, 1000); } catch (error) { - console.error('Error saving tool settings:', error); + console.error(t('Error saving tool settings:'), error); setSaveStatus('error'); } finally { setIsSaving(false); @@ -495,11 +497,11 @@ function ToolsSettings({ isOpen, onClose, projects = [] }) { resetMcpForm(); setSaveStatus('success'); } else { - throw new Error(result.error || 'Failed to add server via JSON'); + throw new Error(result.error || t('Failed to add server via JSON')); } } else { const error = await response.json(); - throw new Error(error.error || 'Failed to add server'); + throw new Error(error.error || t('Failed to add server')); } } else { // Use regular form-based save @@ -508,7 +510,7 @@ function ToolsSettings({ isOpen, onClose, projects = [] }) { setSaveStatus('success'); } } catch (error) { - alert(`Error: ${error.message}`); + alert(`${t("Error")}: ${error.message}`); setSaveStatus('error'); } finally { setMcpLoading(false); @@ -516,12 +518,12 @@ function ToolsSettings({ isOpen, onClose, projects = [] }) { }; const handleMcpDelete = async (serverId, scope) => { - if (confirm('Are you sure you want to delete this MCP server?')) { + if (confirm(t('Are you sure you want to delete this MCP server?'))) { try { await deleteMcpServer(serverId, scope); setSaveStatus('success'); } catch (error) { - alert(`Error: ${error.message}`); + alert(`${t("Error")}: ${error.message}`); setSaveStatus('error'); } } @@ -593,7 +595,7 @@ function ToolsSettings({ isOpen, onClose, projects = [] }) {

- Settings + {t("Settings")}

@@ -646,10 +648,10 @@ function ToolsSettings({ isOpen, onClose, projects = [] }) {
- Dark Mode + {t("Dark Mode")}
- Toggle between light and dark themes + {t("Toggle between light and dark themes")}
@@ -720,7 +722,7 @@ function ToolsSettings({ isOpen, onClose, projects = [] }) { : 'border-transparent text-muted-foreground hover:text-foreground' }`} > - Claude Tools + {t("Claude Tools")} @@ -744,7 +746,7 @@ function ToolsSettings({ isOpen, onClose, projects = [] }) {

- Permission Settings + {t("Permission Settings")}

@@ -757,10 +759,10 @@ function ToolsSettings({ isOpen, onClose, projects = [] }) { />
- Skip permission prompts (use with caution) + {t("Skip permission prompts (use with caution)")}
- Equivalent to --dangerously-skip-permissions flag + {t("Equivalent to --dangerously-skip-permissions flag")}
@@ -772,18 +774,18 @@ function ToolsSettings({ isOpen, onClose, projects = [] }) {

- Allowed Tools + {t("Allowed Tools")}

- Tools that are automatically allowed without prompting for permission + {t("Tools that are automatically allowed without prompting for permission")}

setNewAllowedTool(e.target.value)} - placeholder='e.g., "Bash(git log:*)" or "Write"' + placeholder={t('e.g., "Bash(git log:*)" or "Write"')} onKeyPress={(e) => { if (e.key === 'Enter') { addAllowedTool(newAllowedTool); @@ -799,14 +801,14 @@ function ToolsSettings({ isOpen, onClose, projects = [] }) { className="h-10 px-4 touch-manipulation" > - Add Tool + {t("Add Tool")}
{/* Common tools quick add */}

- Quick add common tools: + {t("Quick add common tools:")}

{commonTools.map(tool => ( @@ -842,7 +844,7 @@ function ToolsSettings({ isOpen, onClose, projects = [] }) { ))} {allowedTools.length === 0 && (
- No allowed tools configured + {t("No allowed tools configured")}
)}
@@ -853,18 +855,18 @@ function ToolsSettings({ isOpen, onClose, projects = [] }) {

- Disallowed Tools + {t("Disallowed Tools")}

- Tools that are automatically blocked without prompting for permission + {t("Tools that are automatically blocked without prompting for permission")}

setNewDisallowedTool(e.target.value)} - placeholder='e.g., "Bash(rm:*)" or "Write"' + placeholder={t('e.g., "Bash(rm:*)" or "Write"')} onKeyPress={(e) => { if (e.key === 'Enter') { addDisallowedTool(newDisallowedTool); @@ -880,7 +882,7 @@ function ToolsSettings({ isOpen, onClose, projects = [] }) { className="h-10 px-4 touch-manipulation" > - Add Tool + {t("Add Tool")}
@@ -902,7 +904,7 @@ function ToolsSettings({ isOpen, onClose, projects = [] }) { ))} {disallowedTools.length === 0 && (
- No disallowed tools configured + {t("No disallowed tools configured")}
)}
@@ -911,14 +913,14 @@ function ToolsSettings({ isOpen, onClose, projects = [] }) { {/* Help Section */}

- Tool Pattern Examples: + {t("Tool Pattern Examples:")}

    -
  • "Bash(git log:*)" - Allow all git log commands
  • -
  • "Bash(git diff:*)" - Allow all git diff commands
  • -
  • "Write" - Allow all Write tool usage
  • -
  • "Read" - Allow all Read tool usage
  • -
  • "Bash(rm:*)" - Block all rm commands (dangerous)
  • +
  • "Bash(git log:*)" - {t("Allow all git log commands")}
  • +
  • "Bash(git diff:*)" - {t("Allow all git diff commands")}
  • +
  • "Write" - {t("Allow all Write tool usage")}
  • +
  • "Read" - {t("Allow all Read tool usage")}
  • +
  • "Bash(rm:*)" - {t("Block all rm commands (dangerous)")}
@@ -927,12 +929,12 @@ function ToolsSettings({ isOpen, onClose, projects = [] }) {

- MCP Servers + {t("MCP Servers")}

- Model Context Protocol servers provide additional tools and data sources to Claude + {t("Model Context Protocol servers provide additional tools and data sources to Claude")}

@@ -943,7 +945,7 @@ function ToolsSettings({ isOpen, onClose, projects = [] }) { size="sm" > - Add MCP Server + {t("Add MCP Server")}
@@ -971,20 +973,20 @@ function ToolsSettings({ isOpen, onClose, projects = [] }) {
{server.type === 'stdio' && server.config.command && ( -
Command: {server.config.command}
+
{t("Command:")} {server.config.command}
)} {(server.type === 'sse' || server.type === 'http') && server.config.url && ( -
URL: {server.config.url}
+
{t("URL:")} {server.config.url}
)} {server.config.args && server.config.args.length > 0 && ( -
Args: {server.config.args.join(' ')}
+
{t("Args:")} {server.config.args.join(' ')}
)} {server.config.env && Object.keys(server.config.env).length > 0 && ( -
Environment: {Object.entries(server.config.env).map(([k, v]) => `${k}=${v}`).join(', ')}
+
{t("Environment:")} {Object.entries(server.config.env).map(([k, v]) => `${k}=${v}`).join(', ')}
)} {server.raw && (
- View full config + {t("View full config")}
                                 {JSON.stringify(server.raw, null, 2)}
                               
@@ -1013,11 +1015,11 @@ function ToolsSettings({ isOpen, onClose, projects = [] }) { {/* Tools Discovery Results */} {mcpServerTools[server.id] && (
-
Available Tools & Resources
+
{t("Available Tools & Resources")}
{mcpServerTools[server.id].tools && mcpServerTools[server.id].tools.length > 0 && (
-
Tools ({mcpServerTools[server.id].tools.length}):
+
{t("Tools")} ({mcpServerTools[server.id].tools.length}):
    {mcpServerTools[server.id].tools.map((tool, i) => (
  • @@ -1036,7 +1038,7 @@ function ToolsSettings({ isOpen, onClose, projects = [] }) { {mcpServerTools[server.id].resources && mcpServerTools[server.id].resources.length > 0 && (
    -
    Resources ({mcpServerTools[server.id].resources.length}):
    +
    {t("Resources")} ({mcpServerTools[server.id].resources.length}):
      {mcpServerTools[server.id].resources.map((resource, i) => (
    • @@ -1055,7 +1057,7 @@ function ToolsSettings({ isOpen, onClose, projects = [] }) { {mcpServerTools[server.id].prompts && mcpServerTools[server.id].prompts.length > 0 && (
      -
      Prompts ({mcpServerTools[server.id].prompts.length}):
      +
      {t("Prompts")} ({mcpServerTools[server.id].prompts.length}):
        {mcpServerTools[server.id].prompts.map((prompt, i) => (
      • @@ -1075,7 +1077,7 @@ function ToolsSettings({ isOpen, onClose, projects = [] }) { {(!mcpServerTools[server.id].tools || mcpServerTools[server.id].tools.length === 0) && (!mcpServerTools[server.id].resources || mcpServerTools[server.id].resources.length === 0) && (!mcpServerTools[server.id].prompts || mcpServerTools[server.id].prompts.length === 0) && ( -
        No tools, resources, or prompts discovered
        +
        {t("No tools, resources, or prompts discovered")}
        )}
      )} @@ -1087,7 +1089,7 @@ function ToolsSettings({ isOpen, onClose, projects = [] }) { variant="ghost" size="sm" className="text-gray-600 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-300" - title="Edit server" + title={t("Edit server")} > @@ -1096,7 +1098,7 @@ function ToolsSettings({ isOpen, onClose, projects = [] }) { variant="ghost" size="sm" className="text-red-600 hover:text-red-700 dark:text-red-400 dark:hover:text-red-300" - title="Delete server" + title={t("Delete server")} > @@ -1106,7 +1108,7 @@ function ToolsSettings({ isOpen, onClose, projects = [] }) { ))} {mcpServers.length === 0 && (
      - No MCP servers configured + {t("No MCP servers configured")}
      )}
    @@ -1118,7 +1120,7 @@ function ToolsSettings({ isOpen, onClose, projects = [] }) {

    - {editingMcpServer ? 'Edit MCP Server' : 'Add MCP Server'} + {editingMcpServer ? t('Edit MCP Server') : t('Add MCP Server')}

    )} @@ -1158,12 +1160,12 @@ function ToolsSettings({ isOpen, onClose, projects = [] }) { {mcpFormData.importMode === 'form' && editingMcpServer && (
    {mcpFormData.scope === 'user' ? : } - {mcpFormData.scope === 'user' ? 'User (Global)' : 'Project (Local)'} + {mcpFormData.scope === 'user' ? t('User (Global)') : t('Project (Local)')} {mcpFormData.scope === 'local' && mcpFormData.projectPath && ( @@ -1172,7 +1174,7 @@ function ToolsSettings({ isOpen, onClose, projects = [] }) { )}

    - Scope cannot be changed when editing an existing server + {t("Scope cannot be changed when editing an existing server")}

    )} @@ -1182,7 +1184,7 @@ function ToolsSettings({ isOpen, onClose, projects = [] }) {

    {mcpFormData.scope === 'user' - ? 'User scope: Available across all projects on your machine' - : 'Local scope: Only available in the selected project' + ? t('User scope: Available across all projects on your machine') + : t('Local scope: Only available in the selected project') }

    @@ -1226,7 +1228,7 @@ function ToolsSettings({ isOpen, onClose, projects = [] }) { {mcpFormData.scope === 'local' && !editingMcpServer && (
    {mcpFormData.projectPath && (

    - Path: {mcpFormData.projectPath} + {t("Path:")} {mcpFormData.projectPath}

    )}
    @@ -1255,14 +1257,14 @@ function ToolsSettings({ isOpen, onClose, projects = [] }) {
    { setMcpFormData(prev => ({...prev, name: e.target.value})); }} - placeholder="my-server" + placeholder={t("my-server")} required />
    @@ -1270,7 +1272,7 @@ function ToolsSettings({ isOpen, onClose, projects = [] }) { {mcpFormData.importMode === 'form' && (