feat(dashboard): add i18n support with zh/en/ja/ko locales

- Add react-i18next + i18next + i18next-browser-languagedetector
- Create i18n config (singleton import) with zh/en/ja/ko JSON locale files
- Add language switcher Globe dropdown in Header topbar
- Replace all hardcoded Chinese strings in:
  - Layout (Header, Sidebar, NavItem, Layout, constants)
  - Settings (index, AppearanceTab, SecurityTab, OtherTab, AboutTab)
  - Auth page (auth.tsx)
  - Search dialog (searchItems via useMemo + t())
  - Restart overlay (getStatusConfig accepts t param)
  - Error boundary (ErrorFallback, ErrorDetails function components)
  - HTTP warning banner
- localStorage key: maibot-locale
- Compatible with Electron
This commit is contained in:
DrSmoothl
2026-03-03 20:50:06 +08:00
parent 5cc34f24c0
commit a65a40f85f
23 changed files with 7271 additions and 473 deletions

View File

@@ -1,4 +1,5 @@
import { Component } from 'react'
import { useTranslation } from 'react-i18next'
import type { ErrorInfo, ReactNode } from 'react'
import { AlertTriangle, RefreshCw, Home, ChevronDown, ChevronUp, Copy, Check, Bug } from 'lucide-react'
import { Button } from '@/components/ui/button'
@@ -65,6 +66,7 @@ function ErrorDetails({ error, errorInfo }: { error: Error; errorInfo: ErrorInfo
const [isStackOpen, setIsStackOpen] = useState(true)
const [isComponentStackOpen, setIsComponentStackOpen] = useState(false)
const [copied, setCopied] = useState(false)
const { t } = useTranslation()
const stackFrames = error.stack ? parseStackTrace(error.stack) : []
@@ -183,12 +185,12 @@ Time: ${new Date().toISOString()}
{copied ? (
<>
<Check className="mr-2 h-4 w-4 text-green-500" />
{t('errorBoundary.copiedToClipboard')}
</>
) : (
<>
<Copy className="mr-2 h-4 w-4" />
{t('errorBoundary.copyError')}
</>
)}
</Button>
@@ -204,6 +206,7 @@ function ErrorFallback({
error: Error
errorInfo: ErrorInfo | null
}) {
const { t } = useTranslation()
const handleGoHome = () => {
window.location.href = '/'
}
@@ -219,9 +222,9 @@ function ErrorFallback({
<div className="mx-auto flex h-16 w-16 items-center justify-center rounded-full bg-red-100 dark:bg-red-900/30 mb-4">
<AlertTriangle className="h-8 w-8 text-red-600 dark:text-red-400" />
</div>
<CardTitle className="text-2xl font-bold"></CardTitle>
<CardTitle className="text-2xl font-bold">{t('errorBoundary.title')}</CardTitle>
<CardDescription className="text-base mt-2">
{t('errorBoundary.description')}
</CardDescription>
</CardHeader>
@@ -232,17 +235,17 @@ function ErrorFallback({
<div className="flex flex-col sm:flex-row gap-2 pt-2">
<Button onClick={handleRefresh} className="flex-1">
<RefreshCw className="mr-2 h-4 w-4" />
{t('errorBoundary.refreshPage')}
</Button>
<Button onClick={handleGoHome} variant="outline" className="flex-1">
<Home className="mr-2 h-4 w-4" />
{t('errorBoundary.goHome')}
</Button>
</div>
{/* 提示信息 */}
<p className="text-xs text-center text-muted-foreground pt-2">
{t('errorBoundary.footer')}
</p>
</CardContent>
</Card>