feat(app): i18n (#48)
This commit is contained in:
committed by
GitHub
Unverified
parent
505a64438e
commit
6e09a69f4f
42
src/App.tsx
42
src/App.tsx
@@ -6,6 +6,7 @@ import { Routes, Route, useNavigate, useLocation } from 'react-router-dom';
|
||||
import { Component, useEffect } from 'react';
|
||||
import type { ErrorInfo, ReactNode } from 'react';
|
||||
import { Toaster } from 'sonner';
|
||||
import i18n from './i18n';
|
||||
import { MainLayout } from './components/layout/MainLayout';
|
||||
import { Dashboard } from './pages/Dashboard';
|
||||
import { Chat } from './pages/Chat';
|
||||
@@ -17,6 +18,7 @@ import { Setup } from './pages/Setup';
|
||||
import { useSettingsStore } from './stores/settings';
|
||||
import { useGatewayStore } from './stores/gateway';
|
||||
|
||||
|
||||
/**
|
||||
* Error Boundary to catch and display React rendering errors
|
||||
*/
|
||||
@@ -40,16 +42,16 @@ class ErrorBoundary extends Component<
|
||||
render() {
|
||||
if (this.state.hasError) {
|
||||
return (
|
||||
<div style={{
|
||||
padding: '40px',
|
||||
color: '#f87171',
|
||||
background: '#0f172a',
|
||||
<div style={{
|
||||
padding: '40px',
|
||||
color: '#f87171',
|
||||
background: '#0f172a',
|
||||
minHeight: '100vh',
|
||||
fontFamily: 'monospace'
|
||||
}}>
|
||||
<h1 style={{ fontSize: '24px', marginBottom: '16px' }}>Something went wrong</h1>
|
||||
<pre style={{
|
||||
whiteSpace: 'pre-wrap',
|
||||
<pre style={{
|
||||
whiteSpace: 'pre-wrap',
|
||||
wordBreak: 'break-all',
|
||||
background: '#1e293b',
|
||||
padding: '16px',
|
||||
@@ -85,21 +87,29 @@ function App() {
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
const theme = useSettingsStore((state) => state.theme);
|
||||
const language = useSettingsStore((state) => state.language);
|
||||
const setupComplete = useSettingsStore((state) => state.setupComplete);
|
||||
const initGateway = useGatewayStore((state) => state.init);
|
||||
|
||||
|
||||
// Sync i18n language with persisted settings on mount
|
||||
useEffect(() => {
|
||||
if (language && language !== i18n.language) {
|
||||
i18n.changeLanguage(language);
|
||||
}
|
||||
}, [language]);
|
||||
|
||||
// Initialize Gateway connection on mount
|
||||
useEffect(() => {
|
||||
initGateway();
|
||||
}, [initGateway]);
|
||||
|
||||
|
||||
// Redirect to setup wizard if not complete
|
||||
useEffect(() => {
|
||||
if (!setupComplete && !location.pathname.startsWith('/setup')) {
|
||||
navigate('/setup');
|
||||
}
|
||||
}, [setupComplete, location.pathname, navigate]);
|
||||
|
||||
|
||||
// Listen for navigation events from main process
|
||||
useEffect(() => {
|
||||
const handleNavigate = (...args: unknown[]) => {
|
||||
@@ -108,21 +118,21 @@ function App() {
|
||||
navigate(path);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
const unsubscribe = window.electron.ipcRenderer.on('navigate', handleNavigate);
|
||||
|
||||
|
||||
return () => {
|
||||
if (typeof unsubscribe === 'function') {
|
||||
unsubscribe();
|
||||
}
|
||||
};
|
||||
}, [navigate]);
|
||||
|
||||
|
||||
// Apply theme
|
||||
useEffect(() => {
|
||||
const root = window.document.documentElement;
|
||||
root.classList.remove('light', 'dark');
|
||||
|
||||
|
||||
if (theme === 'system') {
|
||||
const systemTheme = window.matchMedia('(prefers-color-scheme: dark)').matches
|
||||
? 'dark'
|
||||
@@ -132,13 +142,13 @@ function App() {
|
||||
root.classList.add(theme);
|
||||
}
|
||||
}, [theme]);
|
||||
|
||||
|
||||
return (
|
||||
<ErrorBoundary>
|
||||
<Routes>
|
||||
{/* Setup wizard (shown on first launch) */}
|
||||
<Route path="/setup/*" element={<Setup />} />
|
||||
|
||||
|
||||
{/* Main application routes */}
|
||||
<Route element={<MainLayout />}>
|
||||
<Route path="/" element={<Chat />} />
|
||||
@@ -149,7 +159,7 @@ function App() {
|
||||
<Route path="/settings/*" element={<Settings />} />
|
||||
</Route>
|
||||
</Routes>
|
||||
|
||||
|
||||
{/* Global toast notifications */}
|
||||
<Toaster
|
||||
position="bottom-right"
|
||||
|
||||
Reference in New Issue
Block a user