diff --git a/lightrag_webui/src/App.tsx b/lightrag_webui/src/App.tsx index 0a2ec50f..80dd57a5 100644 --- a/lightrag_webui/src/App.tsx +++ b/lightrag_webui/src/App.tsx @@ -1,5 +1,4 @@ import { useState, useCallback } from 'react' -import ThemeProvider from '@/components/ThemeProvider' import MessageAlert from '@/components/MessageAlert' import ApiKeyAlert from '@/components/ApiKeyAlert' import StatusIndicator from '@/components/graph/StatusIndicator' @@ -52,7 +51,6 @@ function App() { }, [message, setApiKeyInvalid]) return ( - } {apiKeyInvalid && } - ) } diff --git a/lightrag_webui/src/AppRouter.tsx b/lightrag_webui/src/AppRouter.tsx index a28b57c9..0dfc9918 100644 --- a/lightrag_webui/src/AppRouter.tsx +++ b/lightrag_webui/src/AppRouter.tsx @@ -3,6 +3,7 @@ import { BrowserRouter, Routes, Route, Navigate } from 'react-router-dom' import { Toaster } from 'sonner' import App from './App' import LoginPage from '@/features/LoginPage' +import ThemeProvider from '@/components/ThemeProvider' interface ProtectedRouteProps { children: React.ReactNode @@ -20,20 +21,22 @@ const ProtectedRoute = ({ children }: ProtectedRouteProps) => { const AppRouter = () => { return ( - - - } /> - - - - } - /> - - - + + + + } /> + + + + } + /> + + + + ) } diff --git a/lightrag_webui/src/components/LanguageToggle.tsx b/lightrag_webui/src/components/LanguageToggle.tsx new file mode 100644 index 00000000..0eab780e --- /dev/null +++ b/lightrag_webui/src/components/LanguageToggle.tsx @@ -0,0 +1,49 @@ +import Button from '@/components/ui/Button' +import { useCallback } from 'react' +import { controlButtonVariant } from '@/lib/constants' +import { useTranslation } from 'react-i18next' +import { useSettingsStore } from '@/stores/settings' + +/** + * Component that toggles the language between English and Chinese. + */ +export default function LanguageToggle() { + const { i18n } = useTranslation() + const currentLanguage = i18n.language + const setLanguage = useSettingsStore.use.setLanguage() + + const setEnglish = useCallback(() => { + i18n.changeLanguage('en') + setLanguage('en') + }, [i18n, setLanguage]) + + const setChinese = useCallback(() => { + i18n.changeLanguage('zh') + setLanguage('zh') + }, [i18n, setLanguage]) + + if (currentLanguage === 'zh') { + return ( + + 中 + + ) + } + return ( + + EN + + ) +} diff --git a/lightrag_webui/src/features/LoginPage.tsx b/lightrag_webui/src/features/LoginPage.tsx index ad0e6227..f72bafd2 100644 --- a/lightrag_webui/src/features/LoginPage.tsx +++ b/lightrag_webui/src/features/LoginPage.tsx @@ -3,15 +3,19 @@ import { useNavigate } from 'react-router-dom' import { useAuthStore } from '@/stores/state' import { loginToServer } from '@/api/lightrag' import { toast } from 'sonner' +import { useTranslation } from 'react-i18next' import { Card, CardContent, CardHeader } from '@/components/ui/Card' import Input from '@/components/ui/Input' import Button from '@/components/ui/Button' import { ZapIcon } from 'lucide-react' +import ThemeToggle from '@/components/ThemeToggle' +import LanguageToggle from '@/components/LanguageToggle' const LoginPage = () => { const navigate = useNavigate() const { login } = useAuthStore() + const { t } = useTranslation() const [loading, setLoading] = useState(false) const [username, setUsername] = useState('') const [password, setPassword] = useState('') @@ -19,7 +23,7 @@ const LoginPage = () => { const handleSubmit = async (e: React.FormEvent) => { e.preventDefault() if (!username || !password) { - toast.error('Please enter your username and password') + toast.error(t('login.errorEmptyFields')) return } @@ -28,10 +32,10 @@ const LoginPage = () => { const response = await loginToServer(username, password) login(response.access_token) navigate('/') - toast.success('Login succeeded') + toast.success(t('login.successMessage')) } catch (error) { console.error('Login failed...', error) - toast.error('Login failed, please check username and password') + toast.error(t('login.errorInvalidCredentials')) } finally { setLoading(false) } @@ -39,6 +43,10 @@ const LoginPage = () => { return ( + + + + @@ -49,7 +57,7 @@ const LoginPage = () => { LightRAG - Please enter your account and password to log in to the system + {t('login.description')} @@ -58,11 +66,11 @@ const LoginPage = () => { - username + {t('login.username')} setUsername(e.target.value)} required @@ -71,12 +79,12 @@ const LoginPage = () => { - password + {t('login.password')} setPassword(e.target.value)} required @@ -88,7 +96,7 @@ const LoginPage = () => { className="w-full h-11 text-base font-medium mt-2" disabled={loading} > - {loading ? 'Logging in...' : 'Login'} + {loading ? t('login.loggingIn') : t('login.loginButton')} diff --git a/lightrag_webui/src/features/SiteHeader.tsx b/lightrag_webui/src/features/SiteHeader.tsx index f85d6251..121c43af 100644 --- a/lightrag_webui/src/features/SiteHeader.tsx +++ b/lightrag_webui/src/features/SiteHeader.tsx @@ -1,6 +1,7 @@ import Button from '@/components/ui/Button' import { SiteInfo } from '@/lib/constants' import ThemeToggle from '@/components/ThemeToggle' +import LanguageToggle from '@/components/LanguageToggle' import { TabsList, TabsTrigger } from '@/components/ui/Tabs' import { useSettingsStore } from '@/stores/settings' import { useAuthStore } from '@/stores/state' @@ -82,6 +83,7 @@ export default function SiteHeader() { + { + try { + const settingsString = localStorage.getItem('settings-storage'); + if (settingsString) { + const settings = JSON.parse(settingsString); + return settings.state?.language || 'en'; + } + } catch (e) { + console.error('Failed to get stored language:', e); + } + return 'en'; +}; + i18n .use(initReactI18next) .init({ @@ -11,7 +25,7 @@ i18n en: { translation: en }, zh: { translation: zh } }, - lng: "en", // default + lng: getStoredLanguage(), // 使用存储的语言设置 fallbackLng: "en", interpolation: { escapeValue: false diff --git a/lightrag_webui/src/locales/en.json b/lightrag_webui/src/locales/en.json index 31df1fe4..5e5bfd1c 100644 --- a/lightrag_webui/src/locales/en.json +++ b/lightrag_webui/src/locales/en.json @@ -10,6 +10,18 @@ "switchToDark": "Switch to dark theme" } }, + "login": { + "description": "Please enter your account and password to log in to the system", + "username": "Username", + "usernamePlaceholder": "Please input a username", + "password": "Password", + "passwordPlaceholder": "Please input a password", + "loginButton": "Login", + "loggingIn": "Logging in...", + "successMessage": "Login succeeded", + "errorEmptyFields": "Please enter your username and password", + "errorInvalidCredentials": "Login failed, please check username and password" + }, "documentPanel": { "clearDocuments": { "button": "Clear", diff --git a/lightrag_webui/src/locales/zh.json b/lightrag_webui/src/locales/zh.json index e9a98240..698fe9a8 100644 --- a/lightrag_webui/src/locales/zh.json +++ b/lightrag_webui/src/locales/zh.json @@ -10,6 +10,18 @@ "switchToDark": "切换到暗色主题" } }, + "login": { + "description": "请输入您的账号和密码登录系统", + "username": "用户名", + "usernamePlaceholder": "请输入用户名", + "password": "密码", + "passwordPlaceholder": "请输入密码", + "loginButton": "登录", + "loggingIn": "登录中...", + "successMessage": "登录成功", + "errorEmptyFields": "请输入您的用户名和密码", + "errorInvalidCredentials": "登录失败,请检查用户名和密码" + }, "documentPanel": { "clearDocuments": { "button": "清除", diff --git a/lightrag_webui/src/stores/settings.ts b/lightrag_webui/src/stores/settings.ts index 3c99ca40..361f3257 100644 --- a/lightrag_webui/src/stores/settings.ts +++ b/lightrag_webui/src/stores/settings.ts @@ -6,6 +6,7 @@ import { Message, QueryRequest } from '@/api/lightrag' type Theme = 'dark' | 'light' | 'system' type Tab = 'documents' | 'knowledge-graph' | 'retrieval' | 'api' +type Language = 'en' | 'zh' interface SettingsState { // Graph viewer settings @@ -46,6 +47,9 @@ interface SettingsState { theme: Theme setTheme: (theme: Theme) => void + language: Language + setLanguage: (language: Language) => void + enableHealthCheck: boolean setEnableHealthCheck: (enable: boolean) => void @@ -57,6 +61,7 @@ const useSettingsStoreBase = create()( persist( (set) => ({ theme: 'system', + language: 'en', showPropertyPanel: true, showNodeSearchBar: true, @@ -99,6 +104,8 @@ const useSettingsStoreBase = create()( setTheme: (theme: Theme) => set({ theme }), + setLanguage: (language: Language) => set({ language }), + setGraphLayoutMaxIterations: (iterations: number) => set({ graphLayoutMaxIterations: iterations
- Please enter your account and password to log in to the system + {t('login.description')}