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
This commit is contained in:
yangdx
2025-03-12 14:36:34 +08:00
parent e118cf6d92
commit fb0f8e11fd
5 changed files with 87 additions and 35 deletions

View File

@@ -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 (
<StrictMode>
<App />
</StrictMode>
)
}

View File

@@ -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;

View File

@@ -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<typeof i18n> => {
// 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

View File

@@ -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(
<StrictMode>
<App />
</StrictMode>
)
createRoot(document.getElementById('root')!).render(<Root />)

View File

@@ -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<SettingsState>()(
persist(
(set) => ({
theme: 'system',
language: 'zh',
...getInitialState(),
refreshLayout: () => {
const graphState = useGraphStore.getState();
const currentGraph = graphState.sigmaGraph;
@@ -116,10 +132,13 @@ const useSettingsStoreBase = create<SettingsState>()(
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) =>