From fb0f8e11fdbe8632a9104470f82dfdc77d314049 Mon Sep 17 00:00:00 2001 From: yangdx Date: Wed, 12 Mar 2025 14:36:34 +0800 Subject: [PATCH] Resolve the language setting persistence issue - Move i18n initialization to async function - Sync i18n with settings store language - Add Root component for i18n loading state - Convert i18n.js to TypeScript --- lightrag_webui/src/components/Root.tsx | 24 +++++++++++++++++ lightrag_webui/src/i18n.js | 21 --------------- lightrag_webui/src/i18n.ts | 37 ++++++++++++++++++++++++++ lightrag_webui/src/main.tsx | 11 ++------ lightrag_webui/src/stores/settings.ts | 29 ++++++++++++++++---- 5 files changed, 87 insertions(+), 35 deletions(-) create mode 100644 lightrag_webui/src/components/Root.tsx delete mode 100644 lightrag_webui/src/i18n.js create mode 100644 lightrag_webui/src/i18n.ts diff --git a/lightrag_webui/src/components/Root.tsx b/lightrag_webui/src/components/Root.tsx new file mode 100644 index 00000000..6191ace8 --- /dev/null +++ b/lightrag_webui/src/components/Root.tsx @@ -0,0 +1,24 @@ +import { StrictMode, useEffect, useState } from 'react' +import { initializeI18n } from '@/i18n' +import App from '@/App' + +export const Root = () => { + const [isI18nInitialized, setIsI18nInitialized] = useState(false) + + useEffect(() => { + // Initialize i18n immediately with persisted language + initializeI18n().then(() => { + setIsI18nInitialized(true) + }) + }, []) + + if (!isI18nInitialized) { + return null // or a loading spinner + } + + return ( + + + + ) +} diff --git a/lightrag_webui/src/i18n.js b/lightrag_webui/src/i18n.js deleted file mode 100644 index 428cba1e..00000000 --- a/lightrag_webui/src/i18n.js +++ /dev/null @@ -1,21 +0,0 @@ -import i18n from 'i18next'; -import { initReactI18next } from 'react-i18next'; - -import en from './locales/en.json'; -import zh from './locales/zh.json'; - -i18n - .use(initReactI18next) - .init({ - resources: { - en: { translation: en }, - zh: { translation: zh } - }, - lng: 'en', // default - fallbackLng: 'en', - interpolation: { - escapeValue: false - } - }); - -export default i18n; diff --git a/lightrag_webui/src/i18n.ts b/lightrag_webui/src/i18n.ts new file mode 100644 index 00000000..85ed13e5 --- /dev/null +++ b/lightrag_webui/src/i18n.ts @@ -0,0 +1,37 @@ +import i18n from 'i18next' +import { initReactI18next } from 'react-i18next' +import { useSettingsStore } from '@/stores/settings' + +import en from './locales/en.json' +import zh from './locales/zh.json' + +// Function to sync i18n with store state +export const initializeI18n = async (): Promise => { + // Get initial language from store + const initialLanguage = useSettingsStore.getState().language + + // Initialize with store language + await i18n.use(initReactI18next).init({ + resources: { + en: { translation: en }, + zh: { translation: zh } + }, + lng: initialLanguage, + fallbackLng: 'en', + interpolation: { + escapeValue: false + } + }) + + // Subscribe to language changes + useSettingsStore.subscribe((state) => { + const currentLanguage = state.language + if (i18n.language !== currentLanguage) { + i18n.changeLanguage(currentLanguage) + } + }) + + return i18n +} + +export default i18n diff --git a/lightrag_webui/src/main.tsx b/lightrag_webui/src/main.tsx index 6cf8a1e5..fd8f90e1 100644 --- a/lightrag_webui/src/main.tsx +++ b/lightrag_webui/src/main.tsx @@ -1,12 +1,5 @@ -import { StrictMode } from 'react' import { createRoot } from 'react-dom/client' import './index.css' -import App from './App.tsx' -import "./i18n"; +import { Root } from '@/components/Root' - -createRoot(document.getElementById('root')!).render( - - - -) +createRoot(document.getElementById('root')!).render() diff --git a/lightrag_webui/src/stores/settings.ts b/lightrag_webui/src/stores/settings.ts index 6ff61f1a..bbacd1ad 100644 --- a/lightrag_webui/src/stores/settings.ts +++ b/lightrag_webui/src/stores/settings.ts @@ -59,11 +59,27 @@ interface SettingsState { setCurrentTab: (tab: Tab) => void } +// Helper to get initial state from localStorage +const getInitialState = () => { + try { + const stored = localStorage.getItem('settings-storage') + if (stored) { + const { state } = JSON.parse(stored) + return { + theme: state?.theme || 'system', + language: state?.language || 'zh' + } + } + } catch (e) { + console.error('Failed to parse settings from localStorage:', e) + } + return { theme: 'system', language: 'zh' } +} + const useSettingsStoreBase = create()( persist( (set) => ({ - theme: 'system', - language: 'zh', + ...getInitialState(), refreshLayout: () => { const graphState = useGraphStore.getState(); const currentGraph = graphState.sigmaGraph; @@ -116,10 +132,13 @@ const useSettingsStoreBase = create()( setTheme: (theme: Theme) => set({ theme }), setLanguage: (language: Language) => { + set({ language }) + // Update i18n after state is updated import('i18next').then(({ default: i18n }) => { - i18n.changeLanguage(language); - }); - set({ language }); + if (i18n.language !== language) { + i18n.changeLanguage(language) + } + }) }, setGraphLayoutMaxIterations: (iterations: number) =>