File "languageSlice.ts"

Full Path: /home/trinadezambia/public_html/student_panel/src/components/store/slices/languageSlice.ts
File size: 4.23 KB
MIME-type: text/x-java
Charset: utf-8

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;