feat: add i18n internationalization support

- Add react-i18next, i18next with browser language detection
- Support Traditional Chinese (zh-TW) and English (en)
- Default language: zh-TW, stored in localStorage
- Create 10 translation namespaces (common, auth, dashboard, tasks, etc.)
- Add LanguageSwitcher component in header
- Translate pages: Login, Dashboard, Tasks, Spaces, Workload, Audit

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
beabigegg
2026-01-08 23:18:41 +08:00
parent 1e31def7ba
commit 4bc3c24360
32 changed files with 1741 additions and 104 deletions

View File

@@ -1,4 +1,5 @@
import { useEffect, useState, useCallback } from 'react'
import { useTranslation } from 'react-i18next'
import { useAuth } from '../contexts/AuthContext'
import { dashboardApi, DashboardResponse } from '../services/dashboard'
import {
@@ -10,6 +11,7 @@ import {
import { Skeleton } from '../components/Skeleton'
export default function Dashboard() {
const { t } = useTranslation('dashboard')
const { user } = useAuth()
const [data, setData] = useState<DashboardResponse | null>(null)
const [loading, setLoading] = useState(true)
@@ -22,7 +24,7 @@ export default function Dashboard() {
const response = await dashboardApi.getDashboard()
setData(response)
} catch (err) {
setError('Failed to load dashboard data. Please try again.')
setError(t('common:messages.networkError'))
console.error('Dashboard fetch error:', err)
} finally {
setLoading(false)
@@ -93,19 +95,19 @@ export default function Dashboard() {
return (
<div style={styles.container}>
<div style={styles.welcomeSection}>
<h1 style={styles.welcomeTitle}>Welcome, {user?.name}!</h1>
<h1 style={styles.welcomeTitle}>{t('welcome', { name: user?.name })}</h1>
</div>
<div style={styles.errorCard}>
<div style={styles.errorIcon}>!</div>
<h3 style={styles.errorTitle}>Unable to Load Dashboard</h3>
<h3 style={styles.errorTitle}>{t('common:messages.error')}</h3>
<p style={styles.errorMessage}>{error}</p>
<button
style={styles.retryButton}
onClick={fetchDashboard}
type="button"
>
Try Again
{t('common:buttons.refresh')}
</button>
</div>
</div>
@@ -117,9 +119,9 @@ export default function Dashboard() {
<div style={styles.container}>
{/* Welcome Section */}
<div style={styles.welcomeSection}>
<h1 style={styles.welcomeTitle}>Welcome, {user?.name}!</h1>
<h1 style={styles.welcomeTitle}>{t('welcome', { name: user?.name })}</h1>
<p style={styles.welcomeSubtitle}>
Here is your work overview for today
{t('sections.projectOverview')}
</p>
</div>
@@ -130,27 +132,27 @@ export default function Dashboard() {
<StatisticsCard
icon="✓"
value={data.task_stats.assigned_count}
label="My Tasks"
label={t('stats.myTasks')}
color="#2196f3"
/>
<StatisticsCard
icon="⏰"
value={data.task_stats.due_this_week}
label="Due This Week"
label={t('deadlines.thisWeek')}
color="#ff9800"
highlight={data.task_stats.due_this_week > 0}
/>
<StatisticsCard
icon="⚠"
value={data.task_stats.overdue_count}
label="Overdue"
label={t('deadlines.overdue')}
color="#f44336"
highlight={data.task_stats.overdue_count > 0}
/>
<StatisticsCard
icon="✅"
value={data.task_stats.completion_rate}
label="Completion Rate"
label={t('stats.completedTasks')}
color="#4caf50"
suffix="%"
/>