import { createSlice, PayloadAction } from '@reduxjs/toolkit';
import languagesData from '@/lib/student/languages.json';
// Language interface
export interface Language {
name: string;
code: string;
flag: string;
isRtl: boolean;
}
// Language state interface
interface LanguageState {
// Current selected language code
currentLanguage: string;
// Current language name for display
currentLanguageName: string;
// Available languages list
languages: Language[];
// Translations object - key-value pairs for translated strings
translations: Record<string, string>;
// Loading state for translations
loading: boolean;
// Error state if translation loading fails
error: string | null;
}
// LocalStorage key for language persistence
const LANGUAGE_STORAGE_KEY = 'student-language';
// Available languages configuration from JSON
const availableLanguages: Language[] = languagesData;
// Default language
const defaultLanguage: string = availableLanguages[0]?.code || 'en';
// Helper function to load language from localStorage
// This should only be called on the client side
export const loadLanguageFromLocalStorage = (): string => {
if (typeof window !== 'undefined') {
try {
const saved = localStorage.getItem(LANGUAGE_STORAGE_KEY);
const isAvailable = availableLanguages.some((lang) => lang.code === saved);
if (saved && isAvailable) {
return saved;
}
} catch {
// If there's an error reading from localStorage, use default
localStorage.removeItem(LANGUAGE_STORAGE_KEY);
}
}
return defaultLanguage;
};
// Helper function to save language to localStorage
const saveLanguageToLocalStorage = (lang: string) => {
if (typeof window !== 'undefined') {
try {
localStorage.setItem(LANGUAGE_STORAGE_KEY, lang);
} catch (error) {
// Silently fail if localStorage is not available
console.error('Failed to save language to localStorage:', error);
}
}
};
// Get language name from code
const getLanguageName = (code: string): string => {
return (
availableLanguages.find((lang) => lang.code === code)?.name || 'English'
);
};
// Initial state
// Start with default language to prevent hydration mismatch
// Language will be rehydrated from localStorage after client mount
const initialState: LanguageState = {
currentLanguage: defaultLanguage,
currentLanguageName: getLanguageName(defaultLanguage),
languages: availableLanguages,
translations: {},
loading: false,
error: null,
};
// Create the language slice
const languageSlice = createSlice({
name: 'language',
initialState,
reducers: {
// Rehydrate language from localStorage after client mount
// This action should be dispatched once after the app mounts on the client
// It prevents hydration mismatch by loading persisted state after initial render
rehydrateLanguage: (state) => {
const savedLanguage = loadLanguageFromLocalStorage();
state.currentLanguage = savedLanguage;
state.currentLanguageName = getLanguageName(savedLanguage);
},
// Set current language
setLanguage: (state, action: PayloadAction<string>) => {
state.currentLanguage = action.payload;
state.currentLanguageName = getLanguageName(action.payload);
state.error = null;
// Save language to localStorage for persistence
saveLanguageToLocalStorage(action.payload);
},
// Set translations object
setTranslations: (state, action: PayloadAction<Record<string, string>>) => {
state.translations = action.payload;
state.loading = false;
state.error = null;
},
// Set loading state
setLoading: (state, action: PayloadAction<boolean>) => {
state.loading = action.payload;
},
// Set error message
setError: (state, action: PayloadAction<string | null>) => {
state.error = action.payload;
state.loading = false;
},
// Clear error
clearError: (state) => {
state.error = null;
},
},
});
// Export types
export type LanguageCode = string;
export type LanguageName = string;
// Export actions
export const {
rehydrateLanguage,
setLanguage,
setTranslations,
setLoading,
setError,
clearError,
} = languageSlice.actions;
// Export reducer
export default languageSlice.reducer;