From 7cefde6a1ddc4eab1dee7b76f9926ac529684a09 Mon Sep 17 00:00:00 2001 From: Dawnlck Date: Mon, 17 Nov 2025 14:55:32 +0800 Subject: [PATCH 1/7] Add i18n support with English and Chinese resources --- src/components/MainContent.jsx | 34 +++++++------- src/components/MobileNav.jsx | 19 +++++--- src/components/Sidebar.jsx | 44 +++++++++--------- src/i18n/en.json | 19 ++++++++ src/i18n/index.jsx | 85 ++++++++++++++++++++++++++++++++++ src/i18n/zh.json | 19 ++++++++ src/main.jsx | 5 +- 7 files changed, 181 insertions(+), 44 deletions(-) create mode 100644 src/i18n/en.json create mode 100644 src/i18n/index.jsx create mode 100644 src/i18n/zh.json diff --git a/src/components/MainContent.jsx b/src/components/MainContent.jsx index 58a8749e8..8a8167c8f 100644 --- a/src/components/MainContent.jsx +++ b/src/components/MainContent.jsx @@ -27,6 +27,7 @@ import Tooltip from './Tooltip'; import { useTaskMaster } from '../contexts/TaskMasterContext'; import { useTasksSettings } from '../contexts/TasksSettingsContext'; import { api } from '../utils/api'; +import { useTranslation } from '../i18n'; function MainContent({ selectedProject, @@ -71,10 +72,11 @@ function MainContent({ const [selectedPRD, setSelectedPRD] = useState(null); const [existingPRDs, setExistingPRDs] = useState([]); const [prdNotification, setPRDNotification] = useState(null); - + // TaskMaster context const { tasks, currentProject, refreshTasks, setCurrentProject } = useTaskMaster(); const { tasksEnabled, isTaskMasterInstalled, isTaskMasterReady } = useTasksSettings(); + const { t } = useTranslation(); // Only show tasks tab if TaskMaster is installed and enabled const shouldShowTasksTab = tasksEnabled && isTaskMasterInstalled; @@ -331,7 +333,7 @@ function MainContent({ ) : activeTab === 'chat' && !selectedSession ? (

- New Session + {t('sidebar.newSession')}

{selectedProject.displayName} @@ -340,10 +342,10 @@ function MainContent({ ) : (

- {activeTab === 'files' ? 'Project Files' : - activeTab === 'git' ? 'Source Control' : - (activeTab === 'tasks' && shouldShowTasksTab) ? 'TaskMaster' : - 'Project'} + {activeTab === 'files' ? t('navigation.projectFiles') : + activeTab === 'git' ? t('navigation.sourceControl') : + (activeTab === 'tasks' && shouldShowTasksTab) ? t('navigation.tasks') : + t('navigation.project')}

{selectedProject.displayName} @@ -357,7 +359,7 @@ function MainContent({ {/* Modern Tab Navigation - Right Side */}
- + - + - + - + {shouldShowTasksTab && ( - + diff --git a/src/components/MobileNav.jsx b/src/components/MobileNav.jsx index 9940b421b..d56bb0b9a 100644 --- a/src/components/MobileNav.jsx +++ b/src/components/MobileNav.jsx @@ -1,35 +1,42 @@ import React from 'react'; import { MessageSquare, Folder, Terminal, GitBranch, Globe, CheckSquare } from 'lucide-react'; import { useTasksSettings } from '../contexts/TasksSettingsContext'; +import { useTranslation } from '../i18n'; function MobileNav({ activeTab, setActiveTab, isInputFocused }) { const { tasksEnabled } = useTasksSettings(); + const { t } = useTranslation(); const navItems = [ { id: 'chat', icon: MessageSquare, - onClick: () => setActiveTab('chat') + onClick: () => setActiveTab('chat'), + label: t('navigation.chat') }, { id: 'shell', icon: Terminal, - onClick: () => setActiveTab('shell') + onClick: () => setActiveTab('shell'), + label: t('navigation.shell') }, { id: 'files', icon: Folder, - onClick: () => setActiveTab('files') + onClick: () => setActiveTab('files'), + label: t('navigation.files') }, { id: 'git', icon: GitBranch, - onClick: () => setActiveTab('git') + onClick: () => setActiveTab('git'), + label: t('navigation.sourceControl') }, // Conditionally add tasks tab if enabled ...(tasksEnabled ? [{ id: 'tasks', icon: CheckSquare, - onClick: () => setActiveTab('tasks') + onClick: () => setActiveTab('tasks'), + label: t('navigation.tasks') }] : []) ]; @@ -57,7 +64,7 @@ function MobileNav({ activeTab, setActiveTab, isInputFocused }) { ? 'text-blue-600 dark:text-blue-400' : 'text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white' }`} - aria-label={item.id} + aria-label={item.label || item.id} > {isActive && ( diff --git a/src/components/Sidebar.jsx b/src/components/Sidebar.jsx index 8a160ccd9..843369203 100644 --- a/src/components/Sidebar.jsx +++ b/src/components/Sidebar.jsx @@ -14,6 +14,7 @@ import ProjectCreationWizard from './ProjectCreationWizard'; import { api } from '../utils/api'; import { useTaskMaster } from '../contexts/TaskMasterContext'; import { useTasksSettings } from '../contexts/TasksSettingsContext'; +import { useTranslation } from '../i18n'; // Move formatTimeAgo outside component to avoid recreation on every render const formatTimeAgo = (dateString, currentTime) => { @@ -80,6 +81,7 @@ function Sidebar({ // TaskMaster context const { setCurrentProject, mcpServerStatus } = useTaskMaster(); const { tasksEnabled } = useTasksSettings(); + const { t } = useTranslation(); // Starred projects state - persisted in localStorage @@ -1021,7 +1023,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 || 'Untitled Session') : (session.summary || t('sidebar.newSession')); const sessionTime = isCursorSession ? session.createdAt : session.lastActivity; const messageCount = session.messageCount || 0; @@ -1213,7 +1215,7 @@ function Sidebar({ onClick={(e) => { e.stopPropagation(); setEditingSession(session.id); - setEditingSessionName(session.summary || 'New Session'); + setEditingSessionName(session.summary || t('sidebar.newSession')); }} title="Manually edit session name" > @@ -1252,12 +1254,12 @@ function Sidebar({ {loadingSessions[project.name] ? ( <>
- Loading... + {t('common.loading')} ) : ( <> - Show more sessions + {t('sidebar.showMoreSessions')} )} @@ -1267,25 +1269,25 @@ function Sidebar({
-
- - + {t('sidebar.newSession')} + +
+ +
)}
diff --git a/src/i18n/en.json b/src/i18n/en.json new file mode 100644 index 000000000..85f3d47cd --- /dev/null +++ b/src/i18n/en.json @@ -0,0 +1,19 @@ +{ + "navigation": { + "project": "Project", + "projectFiles": "Project Files", + "sourceControl": "Source Control", + "chat": "Chat", + "shell": "Shell", + "files": "Files", + "git": "Source Control", + "tasks": "TaskMaster" + }, + "sidebar": { + "newSession": "New Session", + "showMoreSessions": "Show more sessions" + }, + "common": { + "loading": "Loading..." + } +} diff --git a/src/i18n/index.jsx b/src/i18n/index.jsx new file mode 100644 index 000000000..a266e9c6b --- /dev/null +++ b/src/i18n/index.jsx @@ -0,0 +1,85 @@ +import React, { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react'; +import en from './en.json'; +import zh from './zh.json'; + +const resources = { + en, + zh +}; + +const I18nContext = createContext({ + t: (key) => key, + i18n: { + language: 'en', + changeLanguage: () => {} + } +}); + +const resolveKeyPath = (key, language) => { + const parts = key.split('.'); + let value = resources[language]; + + for (const part of parts) { + if (value && typeof value === 'object' && part in value) { + value = value[part]; + } else { + return undefined; + } + } + + return typeof value === 'string' ? value : undefined; +}; + +export function I18nProvider({ children, defaultLanguage = 'en' }) { + const [language, setLanguage] = useState(defaultLanguage); + + useEffect(() => { + const savedLanguage = localStorage.getItem('claudecodeui-language'); + if (savedLanguage && resources[savedLanguage]) { + setLanguage(savedLanguage); + return; + } + + const browserLanguage = navigator.language?.toLowerCase().startsWith('zh') ? 'zh' : 'en'; + if (resources[browserLanguage]) { + setLanguage(browserLanguage); + } + }, []); + + useEffect(() => { + localStorage.setItem('claudecodeui-language', language); + }, [language]); + + const t = useCallback( + (key, options = {}) => { + const { defaultValue } = options; + const translated = resolveKeyPath(key, language) ?? resolveKeyPath(key, 'en'); + return translated ?? defaultValue ?? key; + }, + [language] + ); + + const value = useMemo(() => ({ + t, + i18n: { + language, + changeLanguage: setLanguage + } + }), [t, language]); + + return ( + + {children} + + ); +} + +export function useTranslation() { + const context = useContext(I18nContext); + return { + t: context.t, + i18n: context.i18n + }; +} + +export { resources }; diff --git a/src/i18n/zh.json b/src/i18n/zh.json new file mode 100644 index 000000000..d3649455f --- /dev/null +++ b/src/i18n/zh.json @@ -0,0 +1,19 @@ +{ + "navigation": { + "project": "项目", + "projectFiles": "项目文件", + "sourceControl": "源代码管理", + "chat": "聊天", + "shell": "终端", + "files": "文件", + "git": "源代码管理", + "tasks": "任务管理" + }, + "sidebar": { + "newSession": "新会话", + "showMoreSessions": "显示更多会话" + }, + "common": { + "loading": "加载中..." + } +} diff --git a/src/main.jsx b/src/main.jsx index f80d47a7e..24790a065 100644 --- a/src/main.jsx +++ b/src/main.jsx @@ -3,6 +3,7 @@ import ReactDOM from 'react-dom/client' import App from './App.jsx' import './index.css' import 'katex/dist/katex.min.css' +import { I18nProvider } from './i18n' // Clean up stale service workers on app load to prevent caching issues after builds if ('serviceWorker' in navigator) { @@ -17,6 +18,8 @@ if ('serviceWorker' in navigator) { ReactDOM.createRoot(document.getElementById('root')).render( - + + + , ) From fae9ba5a166e3317b332ec54fc3ceeab0d87405a Mon Sep 17 00:00:00 2001 From: tata Date: Mon, 17 Nov 2025 16:22:37 +0800 Subject: [PATCH 2/7] Add Chinese README and improve documentation formatting Add Chinese translation of README (README.zh-CN.md) with link in main README. Clean up formatting throughout main README: remove extra blank lines, fix list numbering, standardize italic formatting for optional features, and improve code block consistency. --- README.md | 69 ++++++---- README.zh-CN.md | 334 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 376 insertions(+), 27 deletions(-) create mode 100644 README.zh-CN.md diff --git a/README.md b/README.md index 1f81fd0d3..3eed1bf99 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,7 @@

Claude Code UI

+[中文文档 README.zh-CN](./README.zh-CN.md) A desktop and mobile UI for [Claude Code](https://docs.anthropic.com/en/docs/claude-code), and [Cursor CLI](https://docs.cursor.com/en/cli/overview). You can use it locally or remotely to view your active projects and sessions in Claude Code or Cursor and make changes to them from everywhere (mobile or desktop). This gives you a proper interface that works everywhere. Supports models including **Claude Sonnet 4**, **Opus 4.1**, and **GPT-5** @@ -35,22 +36,19 @@ A desktop and mobile UI for [Claude Code](https://docs.anthropic.com/en/docs/cla - -
## Features -- **Responsive Design** - Works seamlessly across desktop, tablet, and mobile so you can also use Claude Code from mobile +- **Responsive Design** - Works seamlessly across desktop, tablet, and mobile so you can also use Claude Code from mobile - **Interactive Chat Interface** - Built-in chat interface for seamless communication with Claude Code or Cursor - **Integrated Shell Terminal** - Direct access to Claude Code or Cursor CLI through built-in shell functionality - **File Explorer** - Interactive file tree with syntax highlighting and live editing -- **Git Explorer** - View, stage and commit your changes. You can also switch branches +- **Git Explorer** - View, stage and commit your changes. You can also switch branches - **Session Management** - Resume conversations, manage multiple sessions, and track history -- **TaskMaster AI Integration** *(Optional)* - Advanced project management with AI-powered task planning, PRD parsing, and workflow automation +- **TaskMaster AI Integration** _(Optional)_ - Advanced project management with AI-powered task planning, PRD parsing, and workflow automation - **Model Compatibility** - Works with Claude Sonnet 4, Opus 4.1, and GPT-5 - ## Quick Start ### Prerequisites @@ -70,6 +68,7 @@ npx @siteboon/claude-code-ui The server will start and be accessible at `http://localhost:3001` (or your configured PORT). **To restart**: Simply run the same `npx` command again after stopping the server + ### Global Installation (For Regular Use) For frequent use, install globally once: @@ -84,7 +83,6 @@ Then start with a simple command: claude-code-ui ``` - **To restart**: Stop with Ctrl+C and run `claude-code-ui` again. ### CLI Commands @@ -107,13 +105,14 @@ cloudcli version ``` **The `cloudcli status` command shows you:** + - Installation directory location - Database location (where credentials are stored) - Current configuration (PORT, DATABASE_PATH, etc.) - Claude projects folder location - Configuration file location -``` +```` ### Run as Background Service (Recommended for Production) @@ -123,7 +122,7 @@ For production use, run Claude Code UI as a background service using PM2 (Proces ```bash npm install -g pm2 -``` +```` #### Start as Background Service @@ -135,7 +134,6 @@ pm2 start claude-code-ui --name "claude-code-ui" pm2 start cloudcli --name "claude-code-ui" ``` - #### Auto-Start on System Boot To make Claude Code UI start automatically when your system boots: @@ -148,32 +146,36 @@ pm2 startup pm2 save ``` - ### Local Development Installation 1. **Clone the repository:** + ```bash git clone https://github.com/siteboon/claudecodeui.git cd claudecodeui ``` 2. **Install dependencies:** + ```bash npm install ``` 3. **Configure environment:** + ```bash cp .env.example .env # Edit .env with your preferred settings ``` 4. **Start the application:** + ```bash # Development mode (with hot reload) npm run dev ``` + The application will start at the port you specified in your .env 5. **Open your browser:** @@ -188,50 +190,54 @@ The application will start at the port you specified in your .env To use Claude Code's full functionality, you'll need to manually enable tools: 1. **Open Tools Settings** - Click the gear icon in the sidebar -3. **Enable Selectively** - Turn on only the tools you need -4. **Apply Settings** - Your preferences are saved locally +2. **Enable Selectively** - Turn on only the tools you need +3. **Apply Settings** - Your preferences are saved locally
![Tools Settings Modal](public/screenshots/tools-modal.png) -*Tools Settings interface - enable only what you need* +_Tools Settings interface - enable only what you need_
**Recommended approach**: Start with basic tools enabled and add more as needed. You can always adjust these settings later. -## TaskMaster AI Integration *(Optional)* +## TaskMaster AI Integration _(Optional)_ Claude Code UI supports **[TaskMaster AI](https://github.com/eyaltoledano/claude-task-master)** (aka claude-task-master) integration for advanced project management and AI-powered task planning. It provides + - AI-powered task generation from PRDs (Product Requirements Documents) -- Smart task breakdown and dependency management +- Smart task breakdown and dependency management - Visual task boards and progress tracking **Setup & Documentation**: Visit the [TaskMaster AI GitHub repository](https://github.com/eyaltoledano/claude-task-master) for installation instructions, configuration guides, and usage examples. After installing it you should be able to enable it from the Settings - ## Usage Guide ### Core Features #### Project Management + The UI automatically discovers Claude Code projects from `~/.claude/projects/` and provides: + - **Visual Project Browser** - All available projects with metadata and session counts - **Project Actions** - Rename, delete, and organize projects - **Smart Navigation** - Quick access to recent projects and sessions -- **MCP support** - Add your own MCP servers through the UI +- **MCP support** - Add your own MCP servers through the UI #### Chat Interface -- **Use responsive chat or Claude Code/Cursor CLI** - You can either use the adapted chat interface or use the shell button to connect to your selected CLI. + +- **Use responsive chat or Claude Code/Cursor CLI** - You can either use the adapted chat interface or use the shell button to connect to your selected CLI. - **Real-time Communication** - Stream responses from Claude with WebSocket connection - **Session Management** - Resume previous conversations or start fresh sessions - **Message History** - Complete conversation history with timestamps and metadata - **Multi-format Support** - Text, code blocks, and file references #### File Explorer & Editor + - **Interactive File Tree** - Browse project structure with expand/collapse navigation - **Live File Editing** - Read, modify, and save files directly in the interface - **Syntax Highlighting** - Support for multiple programming languages @@ -239,19 +245,21 @@ The UI automatically discovers Claude Code projects from `~/.claude/projects/` a #### Git Explorer +#### TaskMaster AI Integration _(Optional)_ -#### TaskMaster AI Integration *(Optional)* - **Visual Task Board** - Kanban-style interface for managing development tasks - **PRD Parser** - Create Product Requirements Documents and parse them into structured tasks - **Progress Tracking** - Real-time status updates and completion tracking #### Session Management + - **Session Persistence** - All conversations automatically saved - **Session Organization** - Group sessions by project and timestamp - **Session Actions** - Rename, delete, and export conversation history - **Cross-device Sync** - Access sessions from any device ### Mobile App + - **Responsive Design** - Optimized for all screen sizes - **Touch-friendly Interface** - Swipe gestures and touch navigation - **Mobile Navigation** - Bottom tab bar for easy thumb navigation @@ -270,6 +278,7 @@ The UI automatically discovers Claude Code projects from `~/.claude/projects/` a ``` ### Backend (Node.js + Express) + - **Express Server** - RESTful API with static file serving - **WebSocket Server** - Communication for chats and project refresh - **CLI Integration (Claude Code / Cursor)** - Process spawning and management @@ -277,24 +286,23 @@ The UI automatically discovers Claude Code projects from `~/.claude/projects/` a - **File System API** - Exposing file browser for projects ### Frontend (React + Vite) + - **React 18** - Modern component architecture with hooks - **CodeMirror** - Advanced code editor with syntax highlighting - - - - ### Contributing We welcome contributions! Please follow these guidelines: #### Getting Started + 1. **Fork** the repository 2. **Clone** your fork: `git clone ` 3. **Install** dependencies: `npm install` 4. **Create** a feature branch: `git checkout -b feature/amazing-feature` #### Development Process + 1. **Make your changes** following the existing code style 2. **Test thoroughly** - ensure all features work correctly 3. **Run quality checks**: `npm run lint && npm run format` @@ -306,6 +314,7 @@ We welcome contributions! Please follow these guidelines: - Test results if applicable #### What to Contribute + - **Bug fixes** - Help us improve stability - **New features** - Enhance functionality (discuss in issues first) - **Documentation** - Improve guides and API docs @@ -316,23 +325,25 @@ We welcome contributions! Please follow these guidelines: ### Common Issues & Solutions - #### "No Claude projects found" + **Problem**: The UI shows no projects or empty project list **Solutions**: + - Ensure [Claude CLI](https://docs.anthropic.com/en/docs/claude-code) is properly installed - Run `claude` command in at least one project directory to initialize - Verify `~/.claude/projects/` directory exists and has proper permissions #### File Explorer Issues + **Problem**: Files not loading, permission errors, empty directories **Solutions**: + - Check project directory permissions (`ls -la` in terminal) - Verify the project path exists and is accessible - Review server console logs for detailed error messages - Ensure you're not trying to access system directories outside project scope - ## License GNU General Public License v3.0 - see [LICENSE](LICENSE) file for details. @@ -342,22 +353,26 @@ This project is open source and free to use, modify, and distribute under the GP ## Acknowledgments ### Built With + - **[Claude Code](https://docs.anthropic.com/en/docs/claude-code)** - Anthropic's official CLI - **[React](https://react.dev/)** - User interface library - **[Vite](https://vitejs.dev/)** - Fast build tool and dev server - **[Tailwind CSS](https://tailwindcss.com/)** - Utility-first CSS framework - **[CodeMirror](https://codemirror.net/)** - Advanced code editor -- **[TaskMaster AI](https://github.com/eyaltoledano/claude-task-master)** *(Optional)* - AI-powered project management and task planning +- **[TaskMaster AI](https://github.com/eyaltoledano/claude-task-master)** _(Optional)_ - AI-powered project management and task planning ## Support & Community ### Stay Updated + - **Star** this repository to show support - **Watch** for updates and new releases - **Follow** the project for announcements ### Sponsors + - [Siteboon - AI powered website builder](https://siteboon.ai) + ---
diff --git a/README.zh-CN.md b/README.zh-CN.md new file mode 100644 index 000000000..49c28070d --- /dev/null +++ b/README.zh-CN.md @@ -0,0 +1,334 @@ +
+ Claude Code UI +

Claude Code UI

+
+ +一个用于 [Claude Code](https://docs.anthropic.com/en/docs/claude-code) 和 [Cursor CLI](https://docs.cursor.com/en/cli/overview) 的桌面与移动端 Web 界面。你可以在本地或远程使用它,方便地查看 Claude Code / Cursor 中的活动项目与会话,并在任意设备(手机或电脑)上进行修改。支持 **Claude Sonnet 4**、**Opus 4.1**、**GPT-5** 等多种模型。 + +--- + +## 目录 + +- [功能特性](#功能特性) +- [快速开始](#快速开始) +- [安全与工具配置](#安全与工具配置) +- [TaskMaster AI 集成(可选)](#taskmaster-ai-集成可选) +- [使用指南](#使用指南) +- [移动端体验](#移动端体验) +- [架构概览](#架构概览) +- [贡献指南](#贡献指南) +- [故障排查](#故障排查) +- [许可证](#许可证) +- [致谢](#致谢) +- [社区与支持](#社区与支持) + +--- + +## 功能特性 + +- **自适应布局**:在桌面、平板和手机上都能正常工作,便于你在移动端使用 Claude Code +- **交互式聊天界面**:内置聊天界面,可与 Claude Code 或 Cursor 无缝交互 +- **集成 Shell 终端**:通过内置终端直接访问 Claude Code / Cursor CLI +- **文件浏览器**:交互式文件树,支持语法高亮和在线编辑 +- **Git 浏览器**:查看、暂存并提交修改,可切换分支 +- **会话管理**:恢复对话、管理多会话并查看历史记录 +- **TaskMaster AI 集成(可选)**:提供 AI 驱动的项目管理、PRD 解析与任务规划 +- **模型兼容性**:支持 Claude Sonnet 4、Opus 4.1 和 GPT-5 等模型 + +--- + +## 快速开始 + +### 环境要求 + +- [Node.js](https://nodejs.org/) v20 或更高版本 +- 已安装并配置好的 [Claude Code CLI](https://docs.anthropic.com/en/docs/claude-code) 和/或 +- 已安装并配置好的 [Cursor CLI](https://docs.cursor.com/en/cli/overview) + +### 一键运行(推荐) + +无需安装,直接运行: + +```bash +npx @siteboon/claude-code-ui +``` + +服务启动后默认可通过 `http://localhost:3001`(或你配置的 PORT)访问。 + +**重启方式**:停止服务后再次运行相同的 `npx` 命令即可。 + +### 全局安装(适合经常使用) + +```bash +npm install -g @siteboon/claude-code-ui +``` + +安装完成后: + +```bash +claude-code-ui +``` + +**重启方式**:使用 `Ctrl+C` 停止服务,然后再次运行 `claude-code-ui`。 + +### CLI 命令 + +全局安装后,你可以使用 `claude-code-ui` 和 `cloudcli` 两组命令: + +```bash +# 启动服务(默认命令) +claude-code-ui +cloudcli start + +# 查看配置与数据路径 +cloudcli status + +# 查看帮助 +cloudcli help + +# 查看版本 +cloudcli version +``` + +`cloudcli status` 会显示: + +- 安装目录位置 +- 数据库存储位置(凭据保存位置) +- 当前配置(PORT、DATABASE_PATH 等) +- Claude 项目目录位置 +- 配置文件路径 + +### 作为后台服务运行(生产环境推荐) + +使用 PM2 将 Claude Code UI 以后台服务方式运行: + +```bash +npm install -g pm2 +``` + +启动服务: + +```bash +pm2 start claude-code-ui --name "claude-code-ui" +# 或使用别名 +pm2 start cloudcli --name "claude-code-ui" +``` + +开机自启: + +```bash +pm2 startup +pm2 save +``` + +### 本地开发 + +1. 克隆仓库: + +```bash +git clone https://github.com/siteboon/claudecodeui.git +cd claudecodeui +``` + +2. 安装依赖: + +```bash +npm install +``` + +3. 配置环境变量: + +```bash +cp .env.example .env +# 根据需要编辑 .env +``` + +4. 启动开发服务器: + +```bash +npm run dev +``` + +应用将运行在 `.env` 中设置的端口上(默认 `3001`)。 + +5. 浏览器访问: + +- 开发环境:`http://localhost:3001` + +--- + +## 安全与工具配置 + +> **重要提示**:所有 Claude Code 工具默认处于禁用状态,以避免潜在危险操作被自动执行。 + +启用工具步骤: + +1. 打开工具设置(侧边栏齿轮图标) +2. 根据需要选择性启用工具 +3. 应用设置(偏好会保存在本地) + +建议:先只启用基础工具,后续按需逐步开启更多能力。 + +--- + +## TaskMaster AI 集成(可选) + +Claude Code UI 支持与 **[TaskMaster AI](https://github.com/eyaltoledano/claude-task-master)** 集成,用于更高级的项目管理与任务规划: + +- 从 PRD 自动生成任务 +- 智能拆解任务与依赖管理 +- 看板视图与进度追踪 + +安装与配置方法请参考其 GitHub 仓库文档,安装完成后可在本项目设置中启用。 + +--- + +## 使用指南 + +### 项目管理 + +UI 会自动从 `~/.claude/projects/` 读取 Claude Code 项目,并提供: + +- 可视化项目浏览 +- 项目重命名、删除与整理 +- 最近项目与会话快捷访问 +- MCP 服务器管理(在 UI 中添加你自己的 MCP) + +### 聊天界面 + +- 可在自适应聊天界面与 Claude Code / Cursor CLI 间切换 +- WebSocket 实时流式响应 +- 支持会话恢复与多会话管理 +- 完整消息历史与元数据 +- 支持文本、代码块与文件引用 + +### 文件浏览与编辑 + +- 交互式文件树 +- 在线编辑并保存 +- 多语言语法高亮 +- 基本文件操作(新建、重命名、删除) + +### Git 浏览 + +- 查看变更 +- 暂存与提交 +- 分支切换等(具体能力取决于配置) + +### 会话管理 + +- 自动保存所有会话 +- 按项目与时间组织 +- 重命名、删除、导出会话 +- 跨设备访问(通过统一服务端) + +--- + +## 移动端体验 + +- 自适应移动端布局 +- 触控优化(滑动、点击区域) +- 底部导航栏,便于单手操作 +- 可添加到主屏幕,以 PWA 方式运行 + +--- + +## 架构概览 + +整体架构示意: + +```text +┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ +│ 前端 │ │ 后端 │ │ Claude CLI │ +│ (React/Vite) │◄──►│ (Express/WS) │◄──►│ 集成 │ +└─────────────────┘ └─────────────────┘ └─────────────────┘ +``` + +### 后端(Node.js + Express) + +- Express 服务与 REST API +- WebSocket 服务(聊天与项目刷新) +- 与 Claude Code / Cursor 的 CLI 进程管理 +- 会话持久化(JSONL 等) +- 文件系统 API(提供项目文件浏览与编辑) + +### 前端(React + Vite) + +- 使用 React 18 组件化架构 +- 基于 CodeMirror 的代码编辑器 +- Tailwind CSS 构建界面样式 + +--- + +## 贡献指南 + +非常欢迎社区贡献!简单流程: + +1. Fork 仓库 +2. 克隆到本地:`git clone ` +3. 安装依赖:`npm install` +4. 新建分支:`git checkout -b feature/my-feature` +5. 按现有代码风格进行开发 +6. 运行校验:`npm run lint && npm run format` +7. 提交并推送:`git push origin feature/my-feature` +8. 提交 Pull Request,附上变更说明、必要截图与测试结果 + +--- + +## 故障排查 + +### “No Claude projects found” + +可能原因:没有检测到 Claude 项目。 + +排查建议: + +- 确认已正确安装 [Claude CLI](https://docs.anthropic.com/en/docs/claude-code) +- 在至少一个项目目录中运行一次 `claude` 命令 +- 检查 `~/.claude/projects/` 目录是否存在且有权限 + +### 文件浏览器问题 + +如果出现文件无法加载、权限错误或目录为空: + +- 检查项目目录权限(终端运行 `ls -la`) +- 确认项目路径存在且可访问 +- 查看服务器日志以获取详细错误信息 +- 确保没有尝试访问项目目录外的系统路径 + +--- + +## 许可证 + +本项目使用 **GNU General Public License v3.0**。 + +详情请参见仓库中的 [LICENSE](LICENSE) 文件。 + +你可以在 GPL v3 协议下自由使用、修改和分发本项目。 + +--- + +## 致谢 + +本项目基于以下关键技术构建: + +- **[Claude Code](https://docs.anthropic.com/en/docs/claude-code)** +- **[React](https://react.dev/)** +- **[Vite](https://vitejs.dev/)** +- **[Tailwind CSS](https://tailwindcss.com/)** +- **[CodeMirror](https://codemirror.net/)** +- **[TaskMaster AI](https://github.com/eyaltoledano/claude-task-master)**(可选) + +--- + +## 社区与支持 + +- 欢迎 Star 本仓库支持项目 +- 关注更新与新版本发布 +- 如有问题或建议,可通过 Issue 反馈 + +--- + +
+ 为 Claude Code 社区用心打造。 +
From 68e4b0fd1a4da929f6f5db178d3dd63e80a76a49 Mon Sep 17 00:00:00 2001 From: tata Date: Tue, 18 Nov 2025 08:36:51 +0800 Subject: [PATCH 3/7] feat: Add i18n translations to ChatInterface, CommandMenu, ProjectCreationWizard, QuickSettingsPanel and Settings components --- src/components/ChatInterface.jsx | 8 +- src/components/CommandMenu.jsx | 18 +- src/components/ProjectCreationWizard.jsx | 71 +-- src/components/QuickSettingsPanel.jsx | 44 +- src/components/Settings.jsx | 528 ++++++++++++----------- src/components/Sidebar.jsx | 26 +- src/i18n/en.json | 236 +++++++++- src/i18n/index.jsx | 20 +- src/i18n/zh.json | 236 +++++++++- 9 files changed, 850 insertions(+), 337 deletions(-) diff --git a/src/components/ChatInterface.jsx b/src/components/ChatInterface.jsx index a16e8698d..ce6edb5db 100644 --- a/src/components/ChatInterface.jsx +++ b/src/components/ChatInterface.jsx @@ -27,6 +27,7 @@ import ClaudeLogo from './ClaudeLogo.jsx'; import CursorLogo from './CursorLogo.jsx'; import NextTaskBanner from './NextTaskBanner.jsx'; import { useTasksSettings } from '../contexts/TasksSettingsContext'; +import { useTranslation } from '../i18n'; import ClaudeStatus from './ClaudeStatus'; import TokenUsagePie from './TokenUsagePie'; @@ -1528,7 +1529,7 @@ const MessageComponent = memo(({ message, index, prevMessage, createDiff, onFile {showThinking && message.reasoning && (
- 💭 Thinking... + 💭 {t('chat.thinking')}
@@ -1642,6 +1643,7 @@ const ImageAttachment = ({ file, onRemove, uploadProgress, error }) => { // This ensures uninterrupted chat experience by pausing sidebar refreshes during conversations. function ChatInterface({ selectedProject, selectedSession, ws, sendMessage, messages, onFileOpen, onInputFocusChange, onSessionActive, onSessionInactive, onSessionProcessing, onSessionNotProcessing, processingSessions, onReplaceTemporarySession, onNavigateToSession, onShowSettings, autoExpandTools, showRawParameters, showThinking, autoScrollToBottom, sendByCtrlEnter, externalMessageUpdate, onTaskClick, onShowAllTasks }) { const { tasksEnabled } = useTasksSettings(); + const { t } = useTranslation(); const [input, setInput] = useState(() => { if (typeof window !== 'undefined' && selectedProject) { return safeLocalStorage.getItem(`draft_input_${selectedProject.name}`) || ''; @@ -4419,7 +4421,7 @@ function ChatInterface({ selectedProject, selectedSession, ws, sendMessage, mess
- Thinking... + {t('chat.thinking')}
@@ -4699,7 +4701,7 @@ function ChatInterface({ selectedProject, selectedSession, ws, sendMessage, mess type="button" onClick={open} className="absolute left-2 top-1/2 transform -translate-y-1/2 p-2 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-lg transition-colors" - title="Attach images" + title={t('chat.attachImages')} > diff --git a/src/components/CommandMenu.jsx b/src/components/CommandMenu.jsx index 4420aed56..c86f6367f 100644 --- a/src/components/CommandMenu.jsx +++ b/src/components/CommandMenu.jsx @@ -1,4 +1,5 @@ import React, { useEffect, useRef } from 'react'; +import { useTranslation } from '../i18n'; /** * CommandMenu - Autocomplete dropdown for slash commands @@ -12,6 +13,7 @@ import React, { useEffect, useRef } from 'react'; * @param {Array} frequentCommands - Array of frequently used command objects */ const CommandMenu = ({ commands = [], selectedIndex = -1, onSelect, onClose, position = { top: 0, left: 0 }, isOpen = false, frequentCommands = [] }) => { + const { t } = useTranslation(); const menuRef = useRef(null); const selectedItemRef = useRef(null); @@ -103,7 +105,7 @@ const CommandMenu = ({ commands = [], selectedIndex = -1, onSelect, onClose, pos textAlign: 'center' }} > - No commands available + {t('commands.noCommands', { defaultValue: 'No commands available' })}
); } @@ -133,11 +135,11 @@ const CommandMenu = ({ commands = [], selectedIndex = -1, onSelect, onClose, pos const orderedNamespaces = namespaceOrder.filter(ns => groupedCommands[ns]); const namespaceLabels = { - frequent: '⭐ Frequently Used', - builtin: 'Built-in Commands', - project: 'Project Commands', - user: 'User Commands', - other: 'Other Commands' + frequent: `⭐ ${t('commands.frequentlyUsed', { defaultValue: 'Frequently Used' })}`, + builtin: t('commands.builtinCommands', { defaultValue: 'Built-in Commands' }), + project: t('commands.projectCommands', { defaultValue: 'Project Commands' }), + user: t('commands.userCommands', { defaultValue: 'User Commands' }), + other: t('commands.otherCommands', { defaultValue: 'Other Commands' }) }; // Calculate global index for each command @@ -277,7 +279,9 @@ const CommandMenu = ({ commands = [], selectedIndex = -1, onSelect, onClose, pos textOverflow: 'ellipsis' }} > - {command.description} + {command.namespace === 'builtin' && command.name.startsWith('/') + ? t(`commands.${command.name}`, { defaultValue: command.description }) + : command.description}
)} diff --git a/src/components/ProjectCreationWizard.jsx b/src/components/ProjectCreationWizard.jsx index 38564dc08..6b253749f 100644 --- a/src/components/ProjectCreationWizard.jsx +++ b/src/components/ProjectCreationWizard.jsx @@ -3,8 +3,11 @@ import { X, FolderPlus, GitBranch, Key, ChevronRight, ChevronLeft, Check, Loader import { Button } from './ui/button'; import { Input } from './ui/input'; import { api } from '../utils/api'; +import { useTranslation } from '../i18n'; const ProjectCreationWizard = ({ onClose, onProjectCreated }) => { + const { t } = useTranslation(); + // Wizard state const [step, setStep] = useState(1); // 1: Choose type, 2: Configure, 3: Confirm const [workspaceType, setWorkspaceType] = useState(null); // 'existing' or 'new' @@ -88,13 +91,13 @@ const ProjectCreationWizard = ({ onClose, onProjectCreated }) => { if (step === 1) { if (!workspaceType) { - setError('Please select whether you have an existing workspace or want to create a new one'); + setError(t('projectWizard.selectTypeError')); return; } setStep(2); } else if (step === 2) { if (!workspacePath.trim()) { - setError('Please provide a workspace path'); + setError(t('projectWizard.pathRequiredError')); return; } @@ -165,7 +168,7 @@ const ProjectCreationWizard = ({ onClose, onProjectCreated }) => {

- Create New Project + {t('projectWizard.title')}

{tokenMode === 'stored' ? (
setNewGithubToken(e.target.value)} - placeholder="ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + placeholder={t('projectWizard.newTokenPlaceholder')} className="w-full" />

@@ -472,17 +475,17 @@ const ProjectCreationWizard = ({ onClose, onProjectCreated }) => {

- Review Your Configuration + {t('projectWizard.confirmDetails')}

- Workspace Type: + {t('projectWizard.workspaceType')} - {workspaceType === 'existing' ? 'Existing Workspace' : 'New Workspace'} + {workspaceType === 'existing' ? t('projectWizard.existingWorkspace') : t('projectWizard.newWorkspace')}
- Path: + {t('projectWizard.path')} {workspacePath} @@ -490,19 +493,19 @@ const ProjectCreationWizard = ({ onClose, onProjectCreated }) => { {workspaceType === 'new' && githubUrl && ( <>
- Clone From: + {t('projectWizard.repository')} {githubUrl}
- Authentication: + {t('projectWizard.token')} {tokenMode === 'stored' && selectedGithubToken - ? `Using stored token: ${availableTokens.find(t => t.id.toString() === selectedGithubToken)?.credential_name || 'Unknown'}` + ? `${t('projectWizard.stored')}: ${availableTokens.find(t => t.id.toString() === selectedGithubToken)?.credential_name || 'Unknown'}` : tokenMode === 'new' && newGithubToken - ? 'Using provided token' - : 'No authentication'} + ? t('projectWizard.new') + : t('projectWizard.none')}
@@ -531,11 +534,11 @@ const ProjectCreationWizard = ({ onClose, onProjectCreated }) => { disabled={isCreating} > {step === 1 ? ( - 'Cancel' + t('common.cancel') ) : ( <> - Back + {t('projectWizard.back')} )} @@ -546,17 +549,17 @@ const ProjectCreationWizard = ({ onClose, onProjectCreated }) => { > {isCreating ? ( <> - - Creating... + + {t('projectWizard.creating')} ) : step === 3 ? ( <> - Create Project + {t('projectWizard.createProject')} ) : ( <> - Next + {t('projectWizard.next')} )} diff --git a/src/components/QuickSettingsPanel.jsx b/src/components/QuickSettingsPanel.jsx index 17dce0582..2daa58425 100644 --- a/src/components/QuickSettingsPanel.jsx +++ b/src/components/QuickSettingsPanel.jsx @@ -12,10 +12,12 @@ import { Brain, Sparkles, FileText, - Languages + Languages, + Globe } from 'lucide-react'; import DarkModeToggle from './DarkModeToggle'; import { useTheme } from '../contexts/ThemeContext'; +import { useTranslation } from '../i18n'; const QuickSettingsPanel = ({ isOpen, @@ -37,6 +39,7 @@ const QuickSettingsPanel = ({ return localStorage.getItem('whisperMode') || 'default'; }); const { isDarkMode } = useTheme(); + const { t, i18n } = useTranslation(); useEffect(() => { setLocalIsOpen(isOpen); @@ -80,7 +83,7 @@ const QuickSettingsPanel = ({

- Quick Settings + {t('quickSettings.title')}

@@ -88,25 +91,40 @@ const QuickSettingsPanel = ({
{/* Appearance Settings */}
-

Appearance

+

{t('quickSettings.appearance')}

{isDarkMode ? : } - Dark Mode + {t('quickSettings.darkMode')}
+ +
+ + + {t('quickSettings.language')} + + +
{/* Tool Display Settings */}
-

Tool Display

+

{t('quickSettings.toolDisplay')}

@@ -743,222 +745,242 @@ function Settings({ isOpen, onClose, projects = [], initialTab = 'tools' }) { {/* Appearance Tab */} {activeTab === 'appearance' && (
- {activeTab === 'appearance' && ( -
- {/* Theme Settings */} -
-
-
-
-
- Dark Mode -
-
- Toggle between light and dark themes -
-
- -
-
-
+ {/* Theme Settings */} +
+
+
+
+
+ {t('settings.appearance.darkMode')} +
+
+ {t('settings.appearance.darkModeDesc')} +
+
+ +
+
+
- {/* Project Sorting */} -
-
-
-
-
- Project Sorting -
-
- How projects are ordered in the sidebar -
-
- -
-
-
+ {/* Language Settings */} +
+
+
+
+
+ + {t('settings.appearance.language')} +
+
+ {t('settings.appearance.languageDesc')} +
+
+ +
+
+
- {/* Code Editor Settings */} -
-

Code Editor

+ {/* Project Sorting */} +
+
+
+
+
+ {t('settings.appearance.projectSorting')} +
+
+ {t('settings.appearance.projectSortingDesc')} +
+
+ +
+
+
- {/* Editor Theme */} -
-
-
-
- Editor Theme -
-
- Default theme for the code editor -
-
- -
-
+ {/* Code Editor Settings */} +
+

{t('settings.codeEditor.title')}

- {/* Word Wrap */} -
-
-
-
- Word Wrap -
-
- Enable word wrapping by default in the editor -
-
- -
-
+ {/* Editor Theme */} +
+
+
+
+ {t('settings.codeEditor.theme')} +
+
+ {t('settings.codeEditor.themeDesc')} +
+
+ +
+
- {/* Show Minimap */} -
-
-
-
- Show Minimap -
-
- Display a minimap for easier navigation in diff view -
-
- -
-
+ {/* Word Wrap */} +
+
+
+
+ {t('settings.codeEditor.wordWrap')} +
+
+ {t('settings.codeEditor.wordWrapDesc')} +
+
+ +
+
- {/* Show Line Numbers */} -
-
-
-
- Show Line Numbers -
-
- Display line numbers in the editor -
-
- -
-
+ {/* Show Minimap */} +
+
+
+
+ {t('settings.codeEditor.showMinimap')} +
+
+ {t('settings.codeEditor.showMinimapDesc')} +
+
+ +
+
- {/* Font Size */} -
-
-
-
- Font Size -
-
- Editor font size in pixels -
-
- -
-
-
-
-)} + {/* Show Line Numbers */} +
+
+
+
+ {t('settings.codeEditor.showLineNumbers')} +
+
+ {t('settings.codeEditor.showLineNumbersDesc')} +
+
+ +
+
+ {/* Font Size */} +
+
+
+
+ {t('settings.codeEditor.fontSize')} +
+
+ {t('settings.codeEditor.fontSizeDesc')} +
+
+ +
+
+
)} @@ -1007,7 +1029,7 @@ function Settings({ isOpen, onClose, projects = [], initialTab = 'tools' }) {

- Permission Settings + {t('settings.tools.permissionSettings')}

@@ -1020,10 +1042,10 @@ function Settings({ isOpen, onClose, projects = [], initialTab = 'tools' }) { />
- Skip permission prompts (use with caution) + {t('settings.tools.skipPermissions')}
- Equivalent to --dangerously-skip-permissions flag + {t('settings.tools.skipPermissionsDesc')}
@@ -1035,7 +1057,7 @@ function Settings({ isOpen, onClose, projects = [], initialTab = 'tools' }) {

- Authentication + {t('settings.tools.authentication')}

@@ -1054,7 +1076,7 @@ function Settings({ isOpen, onClose, projects = [], initialTab = 'tools' }) { size="sm" > - Login + {t('settings.tools.login')}
@@ -1065,11 +1087,11 @@ function Settings({ isOpen, onClose, projects = [], initialTab = 'tools' }) {

- Allowed Tools + {t('settings.tools.allowedTools')}

- Tools that are automatically allowed without prompting for permission + {t('settings.tools.allowedToolsDesc')}

@@ -1092,14 +1114,14 @@ function Settings({ isOpen, onClose, projects = [], initialTab = 'tools' }) { className="h-10 px-4 touch-manipulation" > - Add Tool + {t('common.addTool')}
{/* Common tools quick add */}

- Quick add common tools: + {t('common.quickAddCommon')}

{commonTools.map(tool => ( @@ -1135,7 +1157,7 @@ function Settings({ isOpen, onClose, projects = [], initialTab = 'tools' }) { ))} {allowedTools.length === 0 && (
- No allowed tools configured + {t('settings.tools.noAllowedTools')}
)}
@@ -1146,11 +1168,11 @@ function Settings({ isOpen, onClose, projects = [], initialTab = 'tools' }) {

- Disallowed Tools + {t('settings.tools.disallowedTools')}

- Tools that are automatically blocked without prompting for permission + {t('settings.tools.disallowedToolsDesc')}

@@ -1173,7 +1195,7 @@ function Settings({ isOpen, onClose, projects = [], initialTab = 'tools' }) { className="h-10 px-4 touch-manipulation" > - Add Tool + {t('common.addTool')}
@@ -1195,7 +1217,7 @@ function Settings({ isOpen, onClose, projects = [], initialTab = 'tools' }) { ))} {disallowedTools.length === 0 && (
- No disallowed tools configured + {t('settings.tools.noDisallowedTools')}
)}
@@ -1741,14 +1763,14 @@ function Settings({ isOpen, onClose, projects = [], initialTab = 'tools' }) {
@@ -1767,7 +1789,7 @@ function Settings({ isOpen, onClose, projects = [], initialTab = 'tools' }) {

- Cursor Permission Settings + {t('cursor.permissionSettings')}

@@ -1780,10 +1802,10 @@ function Settings({ isOpen, onClose, projects = [], initialTab = 'tools' }) { />
- Skip permission prompts (use with caution) + {t('cursor.skipPermissions')}
- Equivalent to -f flag in Cursor CLI + {t('cursor.skipPermissionsDesc')}
@@ -1795,17 +1817,17 @@ function Settings({ isOpen, onClose, projects = [], initialTab = 'tools' }) {

- Authentication + {t('cursor.authentication')}

- Cursor CLI Login + {t('cursor.cursorLogin')}
- Sign in to your Cursor account to enable AI features + {t('cursor.cursorLoginDesc')}
@@ -1825,18 +1847,18 @@ function Settings({ isOpen, onClose, projects = [], initialTab = 'tools' }) {

- Allowed Shell Commands + {t('cursor.allowedCommands')}

- Shell commands that are automatically allowed without prompting for permission + {t('cursor.allowedCommandsDesc')}

setNewCursorCommand(e.target.value)} - placeholder='e.g., "Shell(ls)" or "Shell(git status)"' + placeholder={t('cursor.commandPlaceholder')} onKeyPress={(e) => { if (e.key === 'Enter') { if (newCursorCommand && !cursorAllowedCommands.includes(newCursorCommand)) { @@ -1860,14 +1882,14 @@ function Settings({ isOpen, onClose, projects = [], initialTab = 'tools' }) { className="h-10 px-4 touch-manipulation" > - Add Command + {t('common.addCommand')}
{/* Common commands quick add */}

- Quick add common commands: + {t('common.quickAddCommonCommands')}

{commonCursorCommands.map(cmd => ( @@ -1907,7 +1929,7 @@ function Settings({ isOpen, onClose, projects = [], initialTab = 'tools' }) { ))} {cursorAllowedCommands.length === 0 && (
- No allowed shell commands configured + {t('cursor.noAllowedCommands')}
)}
@@ -1918,18 +1940,18 @@ function Settings({ isOpen, onClose, projects = [], initialTab = 'tools' }) {

- Disallowed Shell Commands + {t('cursor.disallowedCommands')}

- Shell commands that should always be denied + {t('cursor.disallowedCommandsDesc')}

setNewCursorDisallowedCommand(e.target.value)} - placeholder='e.g., "Shell(rm -rf)" or "Shell(sudo)"' + placeholder={t('cursor.disallowedCommandPlaceholder')} onKeyPress={(e) => { if (e.key === 'Enter') { if (newCursorDisallowedCommand && !cursorDisallowedCommands.includes(newCursorDisallowedCommand)) { @@ -1953,7 +1975,7 @@ function Settings({ isOpen, onClose, projects = [], initialTab = 'tools' }) { className="h-10 px-4 touch-manipulation" > - Add Command + {t('common.addCommand')}
@@ -1975,7 +1997,7 @@ function Settings({ isOpen, onClose, projects = [], initialTab = 'tools' }) { ))} {cursorDisallowedCommands.length === 0 && (
- No disallowed shell commands configured + {t('cursor.noDisallowedCommands')}
)}
@@ -2187,7 +2209,7 @@ function Settings({ isOpen, onClose, projects = [], initialTab = 'tools' }) { disabled={isSaving} className="flex-1 sm:flex-none h-10 touch-manipulation" > - Cancel + {t('common.cancel')}
diff --git a/src/components/Sidebar.jsx b/src/components/Sidebar.jsx index 843369203..328112ba3 100644 --- a/src/components/Sidebar.jsx +++ b/src/components/Sidebar.jsx @@ -543,7 +543,7 @@ function Sidebar({

Claude Code UI

-

Projects

+

{t('sidebar.title')}

) : ( @@ -553,7 +553,7 @@ function Sidebar({

Claude Code UI

-

Projects

+

{t('sidebar.title')}

)} @@ -590,7 +590,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" @@ -613,10 +613,10 @@ function Sidebar({ size="sm" className="flex-1 h-8 text-xs bg-primary hover:bg-primary/90 transition-all duration-200" onClick={() => setShowNewProject(true)} - title="Create new project (Ctrl+N)" + title={t('sidebar.createNewProject')} > - New Project + {t('sidebar.newProject')}
) : projects.length === 0 ? ( @@ -658,9 +658,9 @@ function Sidebar({
-

No projects found

+

{t('sidebar.noProjects')}

- Run Claude CLI in a project directory to get started + {t('sidebar.noProjectsDesc')}

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

No matching projects

+

{t('sidebar.noSearchResults')}

- Try adjusting your search term + {t('sidebar.noSearchResultsDesc')}

) : ( @@ -1356,7 +1356,7 @@ function Sidebar({
- Settings + {t('sidebar.settings')} @@ -1367,7 +1367,7 @@ function Sidebar({ onClick={onShowSettings} > - Settings + {t('sidebar.settings')} diff --git a/src/i18n/en.json b/src/i18n/en.json index 85f3d47cd..a5963dc04 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -10,10 +10,242 @@ "tasks": "TaskMaster" }, "sidebar": { + "title": "Projects", "newSession": "New Session", - "showMoreSessions": "Show more sessions" + "showMoreSessions": "Show more sessions", + "settings": "Settings", + "noProjects": "No projects yet", + "noProjectsDesc": "Run Claude CLI in a project directory to get started", + "noSearchResults": "No projects found", + "noSearchResultsDesc": "Try adjusting your search terms", + "searchPlaceholder": "Search projects...", + "newProject": "New Project", + "starred": "Starred", + "refresh": "Refresh", + "sessions": "sessions", + "session": "session", + "createNewProject": "Create new project (Ctrl+N)" + }, + "settings": { + "title": "Settings", + "tabs": { + "tools": "Tools", + "appearance": "Appearance", + "tasks": "Tasks", + "mcpServers": "MCP Servers", + "api": "API & Tokens" + }, + "appearance": { + "darkMode": "Dark Mode", + "darkModeDesc": "Toggle between light and dark themes", + "language": "Language", + "languageDesc": "Select your preferred language", + "projectSorting": "Project Sorting", + "projectSortingDesc": "How projects are ordered in the sidebar", + "alphabetical": "Alphabetical", + "recentActivity": "Recent Activity" + }, + "codeEditor": { + "title": "Code Editor", + "theme": "Editor Theme", + "themeDesc": "Default theme for the code editor", + "wordWrap": "Word Wrap", + "wordWrapDesc": "Enable word wrapping by default in the editor", + "showMinimap": "Show Minimap", + "showMinimapDesc": "Display a minimap for easier navigation in diff view", + "showLineNumbers": "Show Line Numbers", + "showLineNumbersDesc": "Display line numbers in the editor", + "fontSize": "Font Size", + "fontSizeDesc": "Editor font size in pixels" + }, + "tools": { + "permissionSettings": "Permission Settings", + "skipPermissions": "Skip permission prompts (use with caution)", + "skipPermissionsDesc": "Equivalent to --dangerously-skip-permissions flag", + "authentication": "Authentication", + "login": "Login", + "allowedTools": "Allowed Tools", + "allowedToolsDesc": "Tools that are automatically allowed without prompting for permission", + "disallowedTools": "Disallowed Tools", + "disallowedToolsDesc": "Tools that are always blocked", + "noAllowedTools": "No allowed tools configured", + "noDisallowedTools": "No disallowed tools configured" + }, + "cursor": { + "permissionSettings": "Cursor Permission Settings", + "skipPermissions": "Skip permission prompts (use with caution)", + "skipPermissionsDesc": "Equivalent to -f flag in Cursor CLI", + "authentication": "Authentication", + "login": "Login", + "cursorLogin": "Cursor CLI Login", + "cursorLoginDesc": "Sign in to your Cursor account to enable AI features", + "allowedCommands": "Allowed Shell Commands", + "allowedCommandsDesc": "Shell commands that are automatically allowed without prompting for permission", + "disallowedCommands": "Disallowed Shell Commands", + "disallowedCommandsDesc": "Shell commands that are always blocked", + "noAllowedCommands": "No allowed shell commands configured", + "noDisallowedCommands": "No disallowed shell commands configured", + "commandPlaceholder": "e.g., \"Shell(ls)\" or \"Shell(git status)\"", + "disallowedCommandPlaceholder": "e.g., \"Shell(rm -rf)\" or \"Shell(sudo)\"" + }, + "auth": { + "notLoggedIn": "Not logged in", + "loggedInAs": "Logged in as", + "loginDesc": "Authenticate with your provider to access projects" + }, + "mcpServers": { + "title": "MCP Servers", + "description": "Model Context Protocol servers provide additional tools and context", + "add": "Add MCP Server", + "addServer": "Add Server", + "updateServer": "Update Server", + "edit": "Edit", + "delete": "Delete", + "test": "Test Connection", + "discoverTools": "Discover Tools", + "name": "Name", + "type": "Type", + "scope": "Scope", + "command": "Command", + "args": "Arguments", + "user": "User", + "local": "Local", + "status": "Status", + "connected": "Connected", + "disconnected": "Disconnected", + "noServers": "No MCP servers configured" + }, + "tasks": { + "title": "TaskMaster", + "enable": "Enable TaskMaster", + "enableDesc": "Enable AI-powered task management and tracking", + "notInstalled": "TaskMaster is not installed", + "installing": "Installing...", + "install": "Install TaskMaster" + }, + "api": { + "title": "API Keys & Tokens", + "description": "Manage your API keys and authentication tokens", + "createKey": "Create API Key", + "keyName": "Key Name", + "apiKey": "API Key", + "created": "Created", + "copyKey": "Copy Key", + "copied": "Copied!", + "deleteKey": "Delete Key" + }, + "save": "Save Settings", + "saving": "Saving...", + "saved": "Settings saved successfully!", + "error": "Failed to save settings" + }, + "quickSettings": { + "title": "Quick Settings", + "appearance": "Appearance", + "darkMode": "Dark Mode", + "language": "Language", + "toolDisplay": "Tool Display", + "autoExpandTools": "Auto-expand tools", + "showRawParameters": "Show raw parameters", + "showThinking": "Show thinking", + "viewOptions": "View Options", + "autoScrollToBottom": "Auto-scroll to bottom", + "inputSettings": "Input Settings", + "sendByCtrlEnter": "Send by Ctrl+Enter", + "sendByCtrlEnterDesc": "When enabled, pressing Ctrl+Enter will send the message instead of just Enter. This is useful for IME users to avoid accidental sends." + }, + "chat": { + "thinking": "Thinking...", + "attachImages": "Attach images", + "sendMessage": "Send message", + "typeMessage": "Type a message...", + "stopGenerating": "Stop generating" + }, + "commands": { + "/help": "Show help documentation for Claude Code", + "/clear": "Clear the conversation history", + "/model": "Switch or view the current AI model", + "/cost": "Display token usage and cost information", + "/memory": "Open CLAUDE.md memory file for editing", + "/config": "Open settings and configuration", + "/status": "Show system status and version information", + "/rewind": "Rewind the conversation to a previous state", + "frequentlyUsed": "Frequently Used", + "builtinCommands": "Built-in Commands", + "projectCommands": "Project Commands", + "userCommands": "User Commands", + "otherCommands": "Other Commands", + "noCommands": "No commands available" + }, + "projectWizard": { + "title": "Create New Project", + "steps": { + "type": "Type", + "configure": "Configure", + "confirm": "Confirm" + }, + "chooseType": "Do you already have a workspace, or would you like to create a new one?", + "existingWorkspace": "Existing Workspace", + "existingWorkspaceDesc": "I already have a workspace on my server and just need to add it to the project list", + "newWorkspace": "New Workspace", + "newWorkspaceDesc": "Create a new workspace, optionally clone from a GitHub repository", + "workspacePath": "Workspace Path", + "workspacePathPlaceholder": "Enter workspace path (e.g., ~/projects/my-app)", + "githubUrl": "GitHub Repository URL (Optional)", + "githubUrlPlaceholder": "https://github.com/username/repo", + "githubToken": "GitHub Token", + "useStoredToken": "Use stored token", + "enterNewToken": "Enter new token", + "noToken": "No token (public repos only)", + "selectToken": "Select a token", + "newTokenPlaceholder": "ghp_...", + "confirmDetails": "Please confirm the project details:", + "workspaceType": "Workspace Type:", + "path": "Path:", + "repository": "Repository:", + "token": "Token:", + "none": "None", + "stored": "Stored", + "new": "New", + "back": "Back", + "next": "Next", + "createProject": "Create Project", + "creating": "Creating...", + "selectTypeError": "Please select whether you have an existing workspace or want to create a new one", + "pathRequiredError": "Please provide a workspace path" + }, + "mainContent": { + "selectProject": "Select a project to start chatting", + "noSessions": "No sessions yet", + "newSession": "Start a new session", + "deleteSession": "Delete session", + "renameSession": "Rename session", + "loadMore": "Load more" }, "common": { - "loading": "Loading..." + "loading": "Loading...", + "error": "Error", + "success": "Success", + "cancel": "Cancel", + "confirm": "Confirm", + "delete": "Delete", + "edit": "Edit", + "save": "Save", + "add": "Add", + "remove": "Remove", + "search": "Search", + "close": "Close", + "ok": "OK", + "yes": "Yes", + "no": "No", + "copy": "Copy", + "paste": "Paste", + "refresh": "Refresh", + "addTool": "Add Tool", + "addCommand": "Add Command", + "saving": "Saving...", + "quickAddCommon": "Quick add common tools:", + "quickAddCommonCommands": "Quick add common commands:", + "deleteConfirm": "Are you sure you want to delete this MCP server?" } } diff --git a/src/i18n/index.jsx b/src/i18n/index.jsx index a266e9c6b..6ce00c0d5 100644 --- a/src/i18n/index.jsx +++ b/src/i18n/index.jsx @@ -31,22 +31,22 @@ const resolveKeyPath = (key, language) => { }; export function I18nProvider({ children, defaultLanguage = 'en' }) { - const [language, setLanguage] = useState(defaultLanguage); - - useEffect(() => { + // Initialize language from localStorage first, to avoid overwriting saved preference + const [language, setLanguage] = useState(() => { const savedLanguage = localStorage.getItem('claudecodeui-language'); + console.log('[i18n] Initializing with saved language:', savedLanguage); if (savedLanguage && resources[savedLanguage]) { - setLanguage(savedLanguage); - return; + return savedLanguage; } - + const browserLanguage = navigator.language?.toLowerCase().startsWith('zh') ? 'zh' : 'en'; - if (resources[browserLanguage]) { - setLanguage(browserLanguage); - } - }, []); + console.log('[i18n] No saved language, using browser language:', browserLanguage); + return resources[browserLanguage] ? browserLanguage : defaultLanguage; + }); + // Save language to localStorage whenever it changes useEffect(() => { + console.log('[i18n] Saving language to localStorage:', language); localStorage.setItem('claudecodeui-language', language); }, [language]); diff --git a/src/i18n/zh.json b/src/i18n/zh.json index d3649455f..d6f0abdee 100644 --- a/src/i18n/zh.json +++ b/src/i18n/zh.json @@ -10,10 +10,242 @@ "tasks": "任务管理" }, "sidebar": { + "title": "项目", "newSession": "新会话", - "showMoreSessions": "显示更多会话" + "showMoreSessions": "显示更多会话", + "settings": "设置", + "noProjects": "暂无项目", + "noProjectsDesc": "在项目目录中运行 Claude CLI 以开始使用", + "noSearchResults": "未找到项目", + "noSearchResultsDesc": "尝试调整搜索条件", + "searchPlaceholder": "搜索项目...", + "newProject": "新建项目", + "starred": "已收藏", + "refresh": "刷新", + "sessions": "会话", + "session": "会话", + "createNewProject": "创建新项目 (Ctrl+N)" + }, + "settings": { + "title": "设置", + "tabs": { + "tools": "工具", + "appearance": "外观", + "tasks": "任务", + "mcpServers": "MCP 服务器", + "api": "API 与令牌" + }, + "appearance": { + "darkMode": "深色模式", + "darkModeDesc": "在浅色和深色主题之间切换", + "language": "语言", + "languageDesc": "选择您偏好的语言", + "projectSorting": "项目排序", + "projectSortingDesc": "侧边栏中项目的排序方式", + "alphabetical": "按字母顺序", + "recentActivity": "按最近活动" + }, + "codeEditor": { + "title": "代码编辑器", + "theme": "编辑器主题", + "themeDesc": "代码编辑器的默认主题", + "wordWrap": "自动换行", + "wordWrapDesc": "在编辑器中默认启用自动换行", + "showMinimap": "显示缩略图", + "showMinimapDesc": "在差异视图中显示缩略图以便于导航", + "showLineNumbers": "显示行号", + "showLineNumbersDesc": "在编辑器中显示行号", + "fontSize": "字体大小", + "fontSizeDesc": "编辑器字体大小(像素)" + }, + "tools": { + "permissionSettings": "权限设置", + "skipPermissions": "跳过权限提示(谨慎使用)", + "skipPermissionsDesc": "等同于 --dangerously-skip-permissions 标志", + "authentication": "身份验证", + "login": "登录", + "allowedTools": "允许的工具", + "allowedToolsDesc": "无需提示即可自动允许的工具", + "disallowedTools": "禁止的工具", + "disallowedToolsDesc": "始终被阻止的工具", + "noAllowedTools": "未配置允许的工具", + "noDisallowedTools": "未配置禁止的工具" + }, + "cursor": { + "permissionSettings": "Cursor 权限设置", + "skipPermissions": "跳过权限提示(谨慎使用)", + "skipPermissionsDesc": "等同于 Cursor CLI 中的 -f 标志", + "authentication": "身份验证", + "login": "登录", + "cursorLogin": "Cursor CLI 登录", + "cursorLoginDesc": "登录您的 Cursor 账户以启用 AI 功能", + "allowedCommands": "允许的 Shell 命令", + "allowedCommandsDesc": "无需提示即可自动允许的 Shell 命令", + "disallowedCommands": "禁止的 Shell 命令", + "disallowedCommandsDesc": "始终被阻止的 Shell 命令", + "noAllowedCommands": "未配置允许的 Shell 命令", + "noDisallowedCommands": "未配置禁止的 Shell 命令", + "commandPlaceholder": "例如:\"Shell(ls)\" 或 \"Shell(git status)\"", + "disallowedCommandPlaceholder": "例如:\"Shell(rm -rf)\" 或 \"Shell(sudo)\"" + }, + "auth": { + "notLoggedIn": "未登录", + "loggedInAs": "已登录为", + "loginDesc": "使用您的提供商进行身份验证以访问项目" + }, + "mcpServers": { + "title": "MCP 服务器", + "description": "模型上下文协议服务器提供额外的工具和上下文", + "add": "添加 MCP 服务器", + "addServer": "添加服务器", + "updateServer": "更新服务器", + "edit": "编辑", + "delete": "删除", + "test": "测试连接", + "discoverTools": "发现工具", + "name": "名称", + "type": "类型", + "scope": "范围", + "command": "命令", + "args": "参数", + "user": "用户", + "local": "本地", + "status": "状态", + "connected": "已连接", + "disconnected": "未连接", + "noServers": "未配置 MCP 服务器" + }, + "tasks": { + "title": "任务管理器", + "enable": "启用任务管理器", + "enableDesc": "启用 AI 驱动的任务管理和跟踪", + "notInstalled": "未安装任务管理器", + "installing": "安装中...", + "install": "安装任务管理器" + }, + "api": { + "title": "API 密钥与令牌", + "description": "管理您的 API 密钥和身份验证令牌", + "createKey": "创建 API 密钥", + "keyName": "密钥名称", + "apiKey": "API 密钥", + "created": "创建时间", + "copyKey": "复制密钥", + "copied": "已复制!", + "deleteKey": "删除密钥" + }, + "save": "保存设置", + "saving": "保存中...", + "saved": "设置保存成功!", + "error": "保存设置失败" + }, + "quickSettings": { + "title": "快速设置", + "appearance": "外观", + "darkMode": "深色模式", + "language": "语言", + "toolDisplay": "工具显示", + "autoExpandTools": "自动展开工具", + "showRawParameters": "显示原始参数", + "showThinking": "显示思考过程", + "viewOptions": "查看选项", + "autoScrollToBottom": "自动滚动到底部", + "inputSettings": "输入设置", + "sendByCtrlEnter": "通过 Ctrl+Enter 发送", + "sendByCtrlEnterDesc": "启用后,按 Ctrl+Enter 发送消息,而不是仅按 Enter。这对输入法用户很有用,可以避免意外发送。" + }, + "chat": { + "thinking": "思考中...", + "attachImages": "附加图片", + "sendMessage": "发送消息", + "typeMessage": "输入消息...", + "stopGenerating": "停止生成" + }, + "commands": { + "/help": "显示 Claude Code 帮助文档", + "/clear": "清除对话历史", + "/model": "切换或查看当前 AI 模型", + "/cost": "显示令牌使用和成本信息", + "/memory": "打开 CLAUDE.md 记忆文件进行编辑", + "/config": "打开设置和配置", + "/status": "显示系统状态和版本信息", + "/rewind": "将对话回退到之前的状态", + "frequentlyUsed": "常用命令", + "builtinCommands": "内置命令", + "projectCommands": "项目命令", + "userCommands": "用户命令", + "otherCommands": "其他命令", + "noCommands": "无可用命令" + }, + "projectWizard": { + "title": "创建新项目", + "steps": { + "type": "类型", + "configure": "配置", + "confirm": "确认" + }, + "chooseType": "您已经有工作区,还是想创建一个新的?", + "existingWorkspace": "现有工作区", + "existingWorkspaceDesc": "我已经在服务器上有一个工作区,只需将其添加到项目列表中", + "newWorkspace": "新建工作区", + "newWorkspaceDesc": "创建新工作区,可选择从 GitHub 仓库克隆", + "workspacePath": "工作区路径", + "workspacePathPlaceholder": "输入工作区路径(例如:~/projects/my-app)", + "githubUrl": "GitHub 仓库 URL(可选)", + "githubUrlPlaceholder": "https://github.com/username/repo", + "githubToken": "GitHub 令牌", + "useStoredToken": "使用已保存的令牌", + "enterNewToken": "输入新令牌", + "noToken": "无令牌(仅限公共仓库)", + "selectToken": "选择令牌", + "newTokenPlaceholder": "ghp_...", + "confirmDetails": "请确认项目详情:", + "workspaceType": "工作区类型:", + "path": "路径:", + "repository": "仓库:", + "token": "令牌:", + "none": "无", + "stored": "已保存", + "new": "新建", + "back": "返回", + "next": "下一步", + "createProject": "创建项目", + "creating": "创建中...", + "selectTypeError": "请选择您是否有现有工作区或想创建新工作区", + "pathRequiredError": "请提供工作区路径" + }, + "mainContent": { + "selectProject": "选择一个项目开始聊天", + "noSessions": "暂无会话", + "newSession": "开始新会话", + "deleteSession": "删除会话", + "renameSession": "重命名会话", + "loadMore": "加载更多" }, "common": { - "loading": "加载中..." + "loading": "加载中...", + "error": "错误", + "success": "成功", + "cancel": "取消", + "confirm": "确认", + "delete": "删除", + "edit": "编辑", + "save": "保存", + "add": "添加", + "remove": "移除", + "search": "搜索", + "close": "关闭", + "ok": "确定", + "yes": "是", + "no": "否", + "copy": "复制", + "paste": "粘贴", + "refresh": "刷新", + "addTool": "添加工具", + "addCommand": "添加命令", + "saving": "保存中...", + "quickAddCommon": "快速添加常用工具:", + "quickAddCommonCommands": "快速添加常用命令:", + "deleteConfirm": "确定要删除此 MCP 服务器吗?" } } From 7398f6cb97ec270d9fb7d32cc6e05e5429913393 Mon Sep 17 00:00:00 2001 From: tata Date: Tue, 18 Nov 2025 09:25:57 +0800 Subject: [PATCH 4/7] feat: Add i18n translations to GitSettings, LoginForm, MainContent, NextTaskBanner and ChatInterface components --- src/components/ChatInterface.jsx | 12 +- src/components/GitSettings.jsx | 22 ++-- src/components/LoginForm.jsx | 20 +-- src/components/MainContent.jsx | 6 +- src/components/NextTaskBanner.jsx | 102 ++++++++-------- src/components/Onboarding.jsx | 86 ++++++------- src/components/Settings.jsx | 35 +++--- src/components/SetupForm.jsx | 28 +++-- src/components/Shell.jsx | 6 +- src/i18n/en.json | 197 ++++++++++++++++++++++++++++-- src/i18n/zh.json | 197 ++++++++++++++++++++++++++++-- 11 files changed, 543 insertions(+), 168 deletions(-) diff --git a/src/components/ChatInterface.jsx b/src/components/ChatInterface.jsx index 09b484b20..4742846be 100644 --- a/src/components/ChatInterface.jsx +++ b/src/components/ChatInterface.jsx @@ -4195,16 +4195,16 @@ function ChatInterface({ selectedProject, selectedSession, ws, sendMessage, mess
-

Loading session messages...

+

{t('chatInterface.loadingMessages')}

) : chatMessages.length === 0 ? (
{!selectedSession && !currentSessionId && (
-

Choose Your AI Assistant

+

{t('chatInterface.chooseAssistant')}

- Select a provider to start a new conversation + {t('chatInterface.selectProvider')}

@@ -4296,10 +4296,10 @@ function ChatInterface({ selectedProject, selectedSession, ws, sendMessage, mess

{provider === 'claude' - ? 'Ready to use Claude AI. Start typing your message below.' + ? t('chatInterface.readyClaude') : provider === 'cursor' - ? `Ready to use Cursor with ${cursorModel}. Start typing your message below.` - : 'Select a provider above to begin' + ? t('chatInterface.readyCursor', { model: cursorModel }) + : t('chatInterface.selectProviderPrompt') }

diff --git a/src/components/GitSettings.jsx b/src/components/GitSettings.jsx index 91b0902a6..7b0242cda 100644 --- a/src/components/GitSettings.jsx +++ b/src/components/GitSettings.jsx @@ -3,8 +3,10 @@ import { Button } from './ui/button'; import { Input } from './ui/input'; import { GitBranch, Check } from 'lucide-react'; import { authenticatedFetch } from '../utils/api'; +import { useTranslation } from '../i18n'; function GitSettings() { + const { t } = useTranslation(); const [gitName, setGitName] = useState(''); const [gitEmail, setGitEmail] = useState(''); const [gitConfigLoading, setGitConfigLoading] = useState(false); @@ -61,47 +63,47 @@ function GitSettings() {
-

Git Configuration

+

{t('gitSettings.title')}

- Configure your git identity for commits. These settings will be applied globally via git config --global + {t('gitSettings.description')} git config --global

setGitName(e.target.value)} - placeholder="John Doe" + placeholder={t('gitSettings.gitNamePlaceholder')} disabled={gitConfigLoading} className="w-full" />

- Your name for git commits + {t('gitSettings.gitNameDesc')}

setGitEmail(e.target.value)} - placeholder="john@example.com" + placeholder={t('gitSettings.gitEmailPlaceholder')} disabled={gitConfigLoading} className="w-full" />

- Your email for git commits + {t('gitSettings.gitEmailDesc')}

@@ -110,13 +112,13 @@ function GitSettings() { onClick={saveGitConfig} disabled={gitConfigSaving || !gitName || !gitEmail} > - {gitConfigSaving ? 'Saving...' : 'Save Configuration'} + {gitConfigSaving ? t('common.saving') : t('gitSettings.saveConfig')} {saveStatus === 'success' && (
- Saved successfully + {t('gitSettings.savedSuccess')}
)}
diff --git a/src/components/LoginForm.jsx b/src/components/LoginForm.jsx index f2a490a13..147010b0f 100644 --- a/src/components/LoginForm.jsx +++ b/src/components/LoginForm.jsx @@ -1,8 +1,10 @@ import React, { useState } from 'react'; import { useAuth } from '../contexts/AuthContext'; import { MessageSquare } from 'lucide-react'; +import { useTranslation } from '../i18n'; const LoginForm = () => { + const { t } = useTranslation(); const [username, setUsername] = useState(''); const [password, setPassword] = useState(''); const [isLoading, setIsLoading] = useState(false); @@ -15,7 +17,7 @@ const LoginForm = () => { setError(''); if (!username || !password) { - setError('Please enter both username and password'); + setError(t('loginForm.errorBothRequired')); return; } @@ -41,9 +43,9 @@ const LoginForm = () => {
-

Welcome Back

+

{t('loginForm.welcomeBack')}

- Sign in to your Claude Code UI account + {t('loginForm.signInDesc')}

@@ -51,7 +53,7 @@ const LoginForm = () => {
{ 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('loginForm.enterUsername')} required disabled={isLoading} /> @@ -67,7 +69,7 @@ const LoginForm = () => {
{ 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('loginForm.enterPassword')} required disabled={isLoading} /> @@ -92,13 +94,13 @@ const LoginForm = () => { 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 ? 'Signing in...' : 'Sign In'} + {isLoading ? t('loginForm.signingIn') : t('loginForm.signIn')}

- Enter your credentials to access Claude Code UI + {t('loginForm.enterCredentials')}

diff --git a/src/components/MainContent.jsx b/src/components/MainContent.jsx index 8a8167c8f..f70a3d743 100644 --- a/src/components/MainContent.jsx +++ b/src/components/MainContent.jsx @@ -273,13 +273,13 @@ function MainContent({
-

Choose Your Project

+

{t('mainContent.chooseProject')}

- Select a project from the sidebar to start coding with Claude. Each project contains your chat sessions and file history. + {t('mainContent.chooseProjectDesc')}

- 💡 Tip: {isMobile ? 'Tap the menu button above to access projects' : 'Create a new project by clicking the folder icon in the sidebar'} + 💡 {t('mainContent.tip')} {isMobile ? t('mainContent.tipMobile') : t('mainContent.tipDesktop')}

diff --git a/src/components/NextTaskBanner.jsx b/src/components/NextTaskBanner.jsx index 2a55cb8cf..2abadf1ac 100644 --- a/src/components/NextTaskBanner.jsx +++ b/src/components/NextTaskBanner.jsx @@ -5,8 +5,10 @@ import { useTaskMaster } from '../contexts/TaskMasterContext'; import { api } from '../utils/api'; import Shell from './Shell'; import TaskDetail from './TaskDetail'; +import { useTranslation } from '../i18n'; const NextTaskBanner = ({ onShowAllTasks, onStartTask, className = '' }) => { + const { t } = useTranslation(); const { nextTask, tasks, currentProject, isLoadingTasks, projectTaskMaster, refreshTasks, refreshProjects } = useTaskMaster(); const [showDetails, setShowDetails] = useState(false); const [showTaskOptions, setShowTaskOptions] = useState(false); @@ -68,7 +70,7 @@ const NextTaskBanner = ({ onShowAllTasks, onStartTask, className = '' }) => {
- TaskMaster AI is not configured + {t('taskBanner.notConfigured')}
@@ -80,7 +82,7 @@ const NextTaskBanner = ({ onShowAllTasks, onStartTask, className = '' }) => { className="text-xs px-2 py-1 bg-blue-600 hover:bg-blue-700 text-white rounded transition-colors flex items-center gap-1" > - Initialize TaskMaster AI + {t('taskBanner.initializeButton')}
@@ -90,14 +92,14 @@ const NextTaskBanner = ({ onShowAllTasks, onStartTask, className = '' }) => { {!projectTaskMaster?.hasTaskmaster && (

- 🎯 What is TaskMaster? + 🎯 {t('taskBanner.whatIsTaskMaster')}

-

AI-Powered Task Management: Break complex projects into manageable subtasks

-

PRD Templates: Generate tasks from Product Requirements Documents

-

Dependency Tracking: Understand task relationships and execution order

-

Progress Visualization: Kanban boards and detailed task analytics

-

CLI Integration: Use taskmaster commands for advanced workflows

+

{t('taskBanner.aiPowered')} {t('taskBanner.aiPoweredDesc')}

+

{t('taskBanner.prdTemplates')} {t('taskBanner.prdTemplatesDesc')}

+

{t('taskBanner.dependencyTracking')} {t('taskBanner.dependencyTrackingDesc')}

+

{t('taskBanner.progressVisualization')} {t('taskBanner.progressVisualizationDesc')}

+

{t('taskBanner.cliIntegration')} {t('taskBanner.cliIntegrationDesc')}

)} @@ -108,12 +110,12 @@ const NextTaskBanner = ({ onShowAllTasks, onStartTask, className = '' }) => { onClick={() => setShowCLI(true)} > - Initialize TaskMaster + {t('taskBanner.initializeTaskMaster')} ) : ( <>
- Add more tasks: Create additional tasks manually or generate them from a PRD template + {t('taskBanner.addMoreTasks')} {t('taskBanner.addMoreTasksDesc')}
)} @@ -151,19 +153,19 @@ const NextTaskBanner = ({ onShowAllTasks, onStartTask, className = '' }) => {
- Task {nextTask.id} + {t('taskBanner.task')} {nextTask.id} {nextTask.priority === 'high' && ( -
+
)} {nextTask.priority === 'medium' && ( -
+
)} {nextTask.priority === 'low' && ( -
+
)} @@ -179,12 +181,12 @@ const NextTaskBanner = ({ onShowAllTasks, onStartTask, className = '' }) => { className="text-xs px-3 py-1.5 bg-blue-600 hover:bg-blue-700 text-white rounded-md font-medium transition-colors shadow-sm flex items-center gap-1" > - Start Task + {t('taskBanner.startTask')} @@ -192,7 +194,7 @@ const NextTaskBanner = ({ onShowAllTasks, onStartTask, className = '' }) => { @@ -216,7 +218,7 @@ const NextTaskBanner = ({ onShowAllTasks, onStartTask, className = '' }) => {
- {completedTasks === totalTasks ? "All done! 🎉" : "No pending tasks"} + {completedTasks === totalTasks ? t('taskBanner.allDone') : t('taskBanner.noPendingTasks')}
@@ -227,7 +229,7 @@ const NextTaskBanner = ({ onShowAllTasks, onStartTask, className = '' }) => { onClick={onShowAllTasks} className="text-xs px-2 py-1 bg-purple-600 hover:bg-purple-700 text-white rounded transition-colors" > - Review + {t('taskBanner.review')}
@@ -277,8 +279,8 @@ const NextTaskBanner = ({ onShowAllTasks, onStartTask, className = '' }) => {
-

TaskMaster Setup

-

Interactive CLI for {currentProject?.displayName}

+

{t('taskBanner.taskmasterSetup')}

+

{t('taskBanner.interactiveCLI')} {currentProject?.displayName}

@@ -336,6 +338,7 @@ const NextTaskBanner = ({ onShowAllTasks, onStartTask, className = '' }) => { // Simple Create Task Modal Component const CreateTaskModal = ({ currentProject, onClose, onTaskCreated }) => { + const { t } = useTranslation(); const [formData, setFormData] = useState({ title: '', description: '', @@ -376,7 +379,7 @@ const CreateTaskModal = ({ currentProject, onClose, onTaskCreated }) => {
-

Create New Task

+

{t('taskBanner.createNewTask')}

{formData.useAI ? (