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:
@@ -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="%"
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user