Files
colibri/web/src/ErrorBoundary.tsx
T
ZacharyZcR e486574442 web: i18n support — English, 简体中文, 繁體中文, Italiano
Lightweight i18n without react-i18next: a LocaleProvider context +
useLocale() hook with {{var}} interpolation, auto-detect from
navigator.language, persisted to localStorage.

98 translation keys across 4 locale files (en/zh-CN/zh-TW/it).
All user-visible strings in App/Brain/Profiling/ErrorBoundary are
now t() calls. Language switcher added to the sidebar footer.

Build clean (tsc + vite), 18 tests pass.
2026-07-19 21:12:12 +08:00

29 lines
1.4 KiB
TypeScript

import { Component, type ReactNode } from "react"
import { useLocale } from "./i18n"
function ErrorFallback({ error, onRetry }: { error: Error; onRetry: () => void }) {
const { t } = useLocale()
return (
<div style={{ padding: "2rem", fontFamily: "ui-monospace, monospace", color: "#e5e7eb", background: "#0b0f10", minHeight: "100vh" }}>
<h2 style={{ color: "#4ed6a5" }}>{t("error.title")}</h2>
<p style={{ color: "#9ca3af" }}>{t("error.hint")}</p>
<pre style={{ whiteSpace: "pre-wrap", color: "#f87171" }}>{String(error)}</pre>
<button onClick={onRetry} style={{ marginTop: "1rem", padding: "0.5rem 1rem", background: "#1f2937", color: "#e5e7eb", border: "1px solid #374151", borderRadius: 8, cursor: "pointer" }}>{t("error.retry")}</button>
</div>
)
}
interface State { error: Error | null; stack: string }
export class ErrorBoundary extends Component<{ children: ReactNode }, State> {
state: State = { error: null, stack: "" }
static getDerivedStateFromError(error: Error): Partial<State> { return { error } }
componentDidCatch(error: Error, info: { componentStack?: string }) {
console.error("[colibrì] render crash:", error, info.componentStack)
this.setState({ stack: info.componentStack ?? "" })
}
render() {
if (!this.state.error) return this.props.children
return <ErrorFallback error={this.state.error} onRetry={() => this.setState({ error: null, stack: "" })} />
}
}