From 2796cbb42de60638fd7cb9cc0763efe0e91be80c Mon Sep 17 00:00:00 2001 From: beabigegg Date: Thu, 8 Jan 2026 23:38:21 +0800 Subject: [PATCH] feat: complete i18n translation for remaining components MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Translate pages: Projects, ProjectHealthPage, ProjectSettings - Translate components: TaskDetailModal, KanbanBoard, Comments, SubtaskList, CalendarView, BlockerDialog - Add translation keys for tasks namespace: kanban, calendar, subtasks.error, comments.error, blockers (full translation) - Add common.labels.task translation key - Fix task creation: use original_estimate instead of time_estimate Translation coverage: - 10 locale files updated (zh-TW & en) - 6 page/component files translated - ~100 new translation keys added 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- frontend/public/locales/en/common.json | 3 +- frontend/public/locales/en/health.json | 33 ++++++++-- frontend/public/locales/en/projects.json | 24 +++++++- frontend/public/locales/en/settings.json | 6 +- frontend/public/locales/en/tasks.json | 67 ++++++++++++++++++--- frontend/public/locales/zh-TW/common.json | 3 +- frontend/public/locales/zh-TW/health.json | 33 ++++++++-- frontend/public/locales/zh-TW/projects.json | 22 ++++++- frontend/public/locales/zh-TW/settings.json | 6 +- frontend/public/locales/zh-TW/tasks.json | 67 ++++++++++++++++++--- frontend/src/components/BlockerDialog.tsx | 36 +++++------ frontend/src/components/CalendarView.tsx | 40 ++++++------ frontend/src/components/Comments.tsx | 42 +++++++------ frontend/src/components/KanbanBoard.tsx | 8 ++- frontend/src/components/SubtaskList.tsx | 24 ++++---- frontend/src/components/TaskDetailModal.tsx | 54 +++++++++-------- frontend/src/pages/ProjectHealthPage.tsx | 50 +++++++-------- frontend/src/pages/ProjectSettings.tsx | 32 +++++----- frontend/src/pages/Projects.tsx | 46 +++++++------- frontend/src/pages/Tasks.tsx | 2 +- 20 files changed, 401 insertions(+), 197 deletions(-) diff --git a/frontend/public/locales/en/common.json b/frontend/public/locales/en/common.json index 042321a..3f28c5b 100644 --- a/frontend/public/locales/en/common.json +++ b/frontend/public/locales/en/common.json @@ -52,7 +52,8 @@ "selectAssignee": "Select assignee...", "searchUsers": "Search users...", "noUsersFound": "No users found", - "typeToSearch": "Type to search users" + "typeToSearch": "Type to search users", + "task": "Task" }, "messages": { "success": "Operation successful", diff --git a/frontend/public/locales/en/health.json b/frontend/public/locales/en/health.json index e9d06e4..bd632e2 100644 --- a/frontend/public/locales/en/health.json +++ b/frontend/public/locales/en/health.json @@ -1,12 +1,29 @@ { - "title": "Project Health", - "subtitle": "Monitor the overall health of projects", + "title": "Project Health Dashboard", + "subtitle": "Monitor project health status and risk levels across all projects", "overall": { "title": "Overall Health", "healthy": "Healthy", "atRisk": "At Risk", "critical": "Critical" }, + "summary": { + "totalProjects": "Total Projects", + "healthy": "Healthy", + "atRisk": "At Risk", + "critical": "Critical", + "avgHealth": "Avg. Health", + "withBlockers": "With Blockers", + "delayed": "Delayed" + }, + "sort": { + "label": "Sort by", + "riskHigh": "Risk: High to Low", + "riskLow": "Risk: Low to High", + "healthHigh": "Health: High to Low", + "healthLow": "Health: Low to High", + "name": "Name: A to Z" + }, "metrics": { "schedule": "Schedule", "budget": "Budget", @@ -34,15 +51,21 @@ "high": "High Risk", "medium": "Medium Risk", "low": "Low Risk", + "critical": "Critical", "mitigated": "Mitigated" }, "actions": { "viewDetails": "View Details", "exportReport": "Export Report", - "setAlert": "Set Alert" + "setAlert": "Set Alert", + "retry": "Retry" }, + "projectCount": "{{count}} project(s)", "empty": { - "title": "No Health Data", - "description": "The project needs more data to display health metrics" + "title": "No projects found", + "description": "Create a project to start tracking health status" + }, + "error": { + "loadFailed": "Failed to load project health data. Please try again." } } diff --git a/frontend/public/locales/en/projects.json b/frontend/public/locales/en/projects.json index 34f9f37..1e2d85b 100644 --- a/frontend/public/locales/en/projects.json +++ b/frontend/public/locales/en/projects.json @@ -4,16 +4,26 @@ "editProject": "Edit Project", "deleteProject": "Delete Project", "projectSettings": "Project Settings", + "newProject": "New Project", "fields": { "name": "Name", "namePlaceholder": "Enter project name", + "title": "Project Title", + "titlePlaceholder": "Enter project title", "description": "Description", - "descriptionPlaceholder": "Enter project description", + "descriptionPlaceholder": "Description (optional)", "status": "Status", "startDate": "Start Date", "endDate": "End Date", "owner": "Project Owner", - "space": "Space" + "space": "Space", + "securityLevel": "Security Level" + }, + "securityLevel": { + "label": "Security Level", + "public": "Public - All users", + "department": "Department - Same department only", + "confidential": "Confidential - Owner only" }, "status": { "planning": "Planning", @@ -37,10 +47,18 @@ "overdue": "Overdue", "progress": "Overall Progress" }, + "card": { + "tasks": "{{count}} tasks", + "owner": "Owner", + "noDescription": "No description", + "unknown": "Unknown" + }, "messages": { - "created": "Project created", + "created": "Project created successfully", "updated": "Project updated", "deleted": "Project deleted", + "loadFailed": "Failed to load projects", + "createFailed": "Failed to create project", "confirmDelete": "Are you sure you want to delete this project? This will delete all related tasks." }, "empty": { diff --git a/frontend/public/locales/en/settings.json b/frontend/public/locales/en/settings.json index 87a1d74..a663abe 100644 --- a/frontend/public/locales/en/settings.json +++ b/frontend/public/locales/en/settings.json @@ -1,6 +1,7 @@ { "title": "Settings", "projectSettings": "Project Settings", + "backToTasks": "Back to Tasks", "tabs": { "general": "General", "members": "Members", @@ -13,10 +14,13 @@ "title": "General Settings", "projectName": "Project Name", "description": "Description", + "noDescription": "No description", + "securityLevel": "Security Level", "status": "Status", "visibility": "Visibility", "public": "Public", - "private": "Private" + "private": "Private", + "helpText": "To edit project details, contact the project owner." }, "members": { "title": "Member Management", diff --git a/frontend/public/locales/en/tasks.json b/frontend/public/locales/en/tasks.json index a027b8b..b09f9e6 100644 --- a/frontend/public/locales/en/tasks.json +++ b/frontend/public/locales/en/tasks.json @@ -23,7 +23,8 @@ "attachments": "Attachments", "comments": "Comments", "watchers": "Watchers", - "blockers": "Blockers" + "blockers": "Blockers", + "hours": "{{count}} hours" }, "status": { "todo": "To Do", @@ -31,7 +32,11 @@ "review": "In Review", "done": "Done", "cancelled": "Cancelled", - "blocked": "Blocked" + "blocked": "Blocked", + "noStatus": "No Status", + "unassigned": "Unassigned", + "noDueDate": "No due date", + "notEstimated": "Not estimated" }, "priority": { "low": "Low", @@ -86,17 +91,32 @@ "add": "Add Subtask", "placeholder": "Enter subtask title", "completed": "{{count}} / {{total}} completed", - "empty": "No subtasks" + "count": "{{count}} subtask(s)", + "empty": "No subtasks", + "adding": "Adding...", + "error": { + "load": "Failed to load subtasks", + "create": "Failed to create subtask" + } }, "comments": { "title": "Comments", - "add": "Add Comment", - "placeholder": "Write your comment...", + "add": "Post Comment", + "placeholder": "Add a comment... Use @name to mention someone", "edited": "edited", "delete": "Delete Comment", - "confirmDelete": "Are you sure you want to delete this comment?", + "confirmDelete": "Are you sure you want to delete this comment? This action cannot be undone.", "empty": "No comments yet", - "reply": "Reply" + "reply": "Reply", + "mentioned": "Mentioned", + "replyingTo": "Replying to comment", + "posting": "Posting...", + "error": { + "load": "Failed to load comments", + "post": "Failed to post comment", + "update": "Failed to update comment", + "delete": "Failed to delete comment" + } }, "attachments": { "title": "Attachments", @@ -113,7 +133,26 @@ "blockedBy": "Blocked by", "blocking": "Blocking", "remove": "Remove blocker", - "empty": "No blockers" + "empty": "No blockers", + "taskBlocked": "Task Blocked", + "markAsBlocked": "Mark as Blocked", + "activeBlocker": "Active Blocker", + "reportedBy": "Reported by", + "on": "on", + "resolutionNote": "Resolution Note", + "resolutionPlaceholder": "Describe how the blocker was resolved...", + "resolving": "Resolving...", + "resolveBlocker": "Resolve Blocker", + "blockerReason": "Blocker Reason", + "reasonPlaceholder": "Describe what is blocking this task...", + "history": "Blocker History", + "reported": "Reported", + "resolved": "Resolved", + "error": { + "load": "Failed to load blockers", + "create": "Failed to create blocker", + "resolve": "Failed to resolve blocker" + } }, "messages": { "created": "Task created", @@ -126,6 +165,18 @@ "attachmentUploaded": "Attachment uploaded", "confirmDelete": "Are you sure you want to delete this task? This action cannot be undone." }, + "kanban": { + "dropHere": "Drop tasks here" + }, + "calendar": { + "allAssignees": "All Assignees", + "allPriorities": "All Priorities", + "activeOnly": "Active Only", + "completedOnly": "Completed Only", + "clearFilters": "Clear Filters", + "overdue": "Overdue", + "completed": "Completed" + }, "empty": { "title": "No Tasks", "description": "There are no tasks yet. Create your first task to get started!", diff --git a/frontend/public/locales/zh-TW/common.json b/frontend/public/locales/zh-TW/common.json index 1d49df9..ba7ee3a 100644 --- a/frontend/public/locales/zh-TW/common.json +++ b/frontend/public/locales/zh-TW/common.json @@ -52,7 +52,8 @@ "selectAssignee": "選擇負責人...", "searchUsers": "搜尋使用者...", "noUsersFound": "找不到使用者", - "typeToSearch": "輸入以搜尋使用者" + "typeToSearch": "輸入以搜尋使用者", + "task": "任務" }, "messages": { "success": "操作成功", diff --git a/frontend/public/locales/zh-TW/health.json b/frontend/public/locales/zh-TW/health.json index 5b3eafb..6d2d459 100644 --- a/frontend/public/locales/zh-TW/health.json +++ b/frontend/public/locales/zh-TW/health.json @@ -1,12 +1,29 @@ { - "title": "專案健康度", - "subtitle": "監控專案的整體健康狀況", + "title": "專案健康度儀表板", + "subtitle": "監控所有專案的健康狀況與風險等級", "overall": { "title": "整體健康度", "healthy": "健康", "atRisk": "風險中", "critical": "危急" }, + "summary": { + "totalProjects": "專案總數", + "healthy": "健康", + "atRisk": "風險中", + "critical": "危急", + "avgHealth": "平均健康度", + "withBlockers": "有阻擋問題", + "delayed": "延遲" + }, + "sort": { + "label": "排序方式", + "riskHigh": "風險:高到低", + "riskLow": "風險:低到高", + "healthHigh": "健康度:高到低", + "healthLow": "健康度:低到高", + "name": "名稱:A 到 Z" + }, "metrics": { "schedule": "進度", "budget": "預算", @@ -34,15 +51,21 @@ "high": "高風險", "medium": "中風險", "low": "低風險", + "critical": "危急", "mitigated": "已緩解" }, "actions": { "viewDetails": "查看詳情", "exportReport": "匯出報告", - "setAlert": "設定警示" + "setAlert": "設定警示", + "retry": "重試" }, + "projectCount": "{{count}} 個專案", "empty": { - "title": "沒有健康度資料", - "description": "專案需要更多資料才能顯示健康度指標" + "title": "沒有專案", + "description": "建立專案以開始追蹤健康狀態" + }, + "error": { + "loadFailed": "載入專案健康度資料失敗,請重試。" } } diff --git a/frontend/public/locales/zh-TW/projects.json b/frontend/public/locales/zh-TW/projects.json index 07bf497..b340450 100644 --- a/frontend/public/locales/zh-TW/projects.json +++ b/frontend/public/locales/zh-TW/projects.json @@ -4,16 +4,26 @@ "editProject": "編輯專案", "deleteProject": "刪除專案", "projectSettings": "專案設定", + "newProject": "新增專案", "fields": { "name": "名稱", "namePlaceholder": "輸入專案名稱", + "title": "專案標題", + "titlePlaceholder": "輸入專案標題", "description": "描述", - "descriptionPlaceholder": "輸入專案描述", + "descriptionPlaceholder": "輸入專案描述(選填)", "status": "狀態", "startDate": "開始日期", "endDate": "結束日期", "owner": "專案負責人", - "space": "工作空間" + "space": "工作空間", + "securityLevel": "安全等級" + }, + "securityLevel": { + "label": "安全等級", + "public": "公開 - 所有使用者", + "department": "部門 - 僅同部門", + "confidential": "機密 - 僅負責人" }, "status": { "planning": "規劃中", @@ -37,10 +47,18 @@ "overdue": "逾期", "progress": "整體進度" }, + "card": { + "tasks": "{{count}} 個任務", + "owner": "負責人", + "noDescription": "沒有描述", + "unknown": "未知" + }, "messages": { "created": "專案已建立", "updated": "專案已更新", "deleted": "專案已刪除", + "loadFailed": "載入專案失敗", + "createFailed": "建立專案失敗", "confirmDelete": "確定要刪除此專案嗎?此操作將刪除所有相關任務。" }, "empty": { diff --git a/frontend/public/locales/zh-TW/settings.json b/frontend/public/locales/zh-TW/settings.json index cc9af24..fa3c6fe 100644 --- a/frontend/public/locales/zh-TW/settings.json +++ b/frontend/public/locales/zh-TW/settings.json @@ -1,6 +1,7 @@ { "title": "設定", "projectSettings": "專案設定", + "backToTasks": "返回任務", "tabs": { "general": "一般", "members": "成員", @@ -13,10 +14,13 @@ "title": "一般設定", "projectName": "專案名稱", "description": "描述", + "noDescription": "沒有描述", + "securityLevel": "安全等級", "status": "狀態", "visibility": "可見性", "public": "公開", - "private": "私人" + "private": "私人", + "helpText": "如需編輯專案詳情,請聯繫專案負責人。" }, "members": { "title": "成員管理", diff --git a/frontend/public/locales/zh-TW/tasks.json b/frontend/public/locales/zh-TW/tasks.json index 6eece02..a74beed 100644 --- a/frontend/public/locales/zh-TW/tasks.json +++ b/frontend/public/locales/zh-TW/tasks.json @@ -23,7 +23,8 @@ "attachments": "附件", "comments": "留言", "watchers": "關注者", - "blockers": "阻擋項目" + "blockers": "阻擋項目", + "hours": "{{count}} 小時" }, "status": { "todo": "待處理", @@ -31,7 +32,11 @@ "review": "審核中", "done": "已完成", "cancelled": "已取消", - "blocked": "被阻擋" + "blocked": "被阻擋", + "noStatus": "無狀態", + "unassigned": "未指派", + "noDueDate": "無截止日期", + "notEstimated": "未預估" }, "priority": { "low": "低", @@ -86,17 +91,32 @@ "add": "新增子任務", "placeholder": "輸入子任務標題", "completed": "已完成 {{count}} / {{total}}", - "empty": "沒有子任務" + "count": "{{count}} 個子任務", + "empty": "沒有子任務", + "adding": "新增中...", + "error": { + "load": "載入子任務失敗", + "create": "建立子任務失敗" + } }, "comments": { "title": "留言", - "add": "新增留言", - "placeholder": "輸入您的留言...", + "add": "發表留言", + "placeholder": "新增留言... 使用 @name 來提及某人", "edited": "已編輯", "delete": "刪除留言", - "confirmDelete": "確定要刪除此留言嗎?", + "confirmDelete": "確定要刪除此留言嗎?此操作無法復原。", "empty": "還沒有留言", - "reply": "回覆" + "reply": "回覆", + "mentioned": "提及", + "replyingTo": "回覆留言中", + "posting": "發送中...", + "error": { + "load": "載入留言失敗", + "post": "發表留言失敗", + "update": "更新留言失敗", + "delete": "刪除留言失敗" + } }, "attachments": { "title": "附件", @@ -113,7 +133,26 @@ "blockedBy": "被以下任務阻擋", "blocking": "正在阻擋以下任務", "remove": "移除阻擋關係", - "empty": "沒有阻擋項目" + "empty": "沒有阻擋項目", + "taskBlocked": "任務被阻擋", + "markAsBlocked": "標記為阻擋", + "activeBlocker": "進行中的阻擋", + "reportedBy": "回報者", + "on": "於", + "resolutionNote": "解決說明", + "resolutionPlaceholder": "描述如何解決阻擋問題...", + "resolving": "解決中...", + "resolveBlocker": "解決阻擋", + "blockerReason": "阻擋原因", + "reasonPlaceholder": "描述阻擋此任務的原因...", + "history": "阻擋歷史", + "reported": "回報", + "resolved": "已解決", + "error": { + "load": "載入阻擋項目失敗", + "create": "建立阻擋項目失敗", + "resolve": "解決阻擋項目失敗" + } }, "messages": { "created": "任務已建立", @@ -126,6 +165,18 @@ "attachmentUploaded": "附件已上傳", "confirmDelete": "確定要刪除此任務嗎?此操作無法復原。" }, + "kanban": { + "dropHere": "將任務拖放至此" + }, + "calendar": { + "allAssignees": "所有負責人", + "allPriorities": "所有優先順序", + "activeOnly": "僅進行中", + "completedOnly": "僅已完成", + "clearFilters": "清除篩選", + "overdue": "逾期", + "completed": "已完成" + }, "empty": { "title": "沒有任務", "description": "目前沒有任務。建立您的第一個任務開始吧!", diff --git a/frontend/src/components/BlockerDialog.tsx b/frontend/src/components/BlockerDialog.tsx index 51a0a0f..d6a6007 100644 --- a/frontend/src/components/BlockerDialog.tsx +++ b/frontend/src/components/BlockerDialog.tsx @@ -1,4 +1,5 @@ import { useState, useEffect } from 'react' +import { useTranslation } from 'react-i18next' import { blockersApi, Blocker } from '../services/collaboration' import { SkeletonList } from './Skeleton' @@ -17,6 +18,7 @@ export function BlockerDialog({ onClose, onBlockerChange, }: BlockerDialogProps) { + const { t } = useTranslation('tasks') const [blockers, setBlockers] = useState([]) const [loading, setLoading] = useState(true) const [reason, setReason] = useState('') @@ -34,7 +36,7 @@ export function BlockerDialog({ const active = response.blockers.find(b => !b.resolved_at) setActiveBlocker(active || null) } catch (err) { - setError('Failed to load blockers') + setError(t('blockers.error.load')) } finally { setLoading(false) } @@ -55,7 +57,7 @@ export function BlockerDialog({ setError(null) onBlockerChange() } catch (err) { - setError('Failed to create blocker') + setError(t('blockers.error.create')) } finally { setSubmitting(false) } @@ -74,7 +76,7 @@ export function BlockerDialog({ setError(null) onBlockerChange() } catch (err) { - setError('Failed to resolve blocker') + setError(t('blockers.error.resolve')) } finally { setSubmitting(false) } @@ -85,7 +87,7 @@ export function BlockerDialog({

- {isBlocked ? 'Task Blocked' : 'Mark as Blocked'} + {isBlocked ? t('blockers.taskBlocked') : t('blockers.markAsBlocked')}