feat: implement lazy loading for languages in i18n, enhance LanguageSwitcher to handle language changes asynchronously, and update available languages management

This commit is contained in:
sebseb7
2025-07-20 15:44:50 +02:00
parent 2b64719758
commit 5202ff6e3e
3 changed files with 246 additions and 362 deletions

View File

@@ -24,10 +24,14 @@ class LanguageSwitcher extends Component {
this.setState({ anchorEl: null });
};
handleLanguageChange = (language) => {
handleLanguageChange = async (language) => {
const { languageContext } = this.props;
if (languageContext) {
languageContext.changeLanguage(language);
try {
await languageContext.changeLanguage(language);
} catch (error) {
console.error('Failed to change language:', error);
}
}
this.handleClose();
};
@@ -126,8 +130,8 @@ class LanguageSwitcher extends Component {
const { languageContext } = this.props;
if (anchorEl && !prevState.anchorEl && languageContext) {
// Menu just opened, lazy load all flags
languageContext.availableLanguages.forEach(lang => {
// Menu just opened, lazy load flags for all languages (not just available ones)
languageContext.allLanguages.forEach(lang => {
if (!this.state.loadedFlags[lang]) {
this.loadFlagComponent(lang);
}
@@ -197,7 +201,7 @@ class LanguageSwitcher extends Component {
return null;
}
const { currentLanguage, availableLanguages } = languageContext;
const { currentLanguage, allLanguages } = languageContext;
const open = Boolean(anchorEl);
return (
@@ -237,29 +241,31 @@ class LanguageSwitcher extends Component {
horizontal: 'right',
}}
>
{availableLanguages.map((language) => (
<MenuItem
key={language}
onClick={() => this.handleLanguageChange(language)}
selected={language === currentLanguage}
sx={{
minWidth: 160,
display: 'flex',
justifyContent: 'space-between',
gap: 2
}}
>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
{this.getLanguageFlag(language)}
<Typography variant="body2">
{this.getLanguageName(language)}
{allLanguages.map((language) => {
return (
<MenuItem
key={language}
onClick={() => this.handleLanguageChange(language)}
selected={language === currentLanguage}
sx={{
minWidth: 160,
display: 'flex',
justifyContent: 'space-between',
gap: 2
}}
>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
{this.getLanguageFlag(language)}
<Typography variant="body2">
{this.getLanguageName(language)}
</Typography>
</Box>
<Typography variant="caption" color="text.secondary" sx={{ ml: 'auto' }}>
{this.getLanguageLabel(language)}
</Typography>
</Box>
<Typography variant="caption" color="text.secondary" sx={{ ml: 'auto' }}>
{this.getLanguageLabel(language)}
</Typography>
</MenuItem>
))}
</MenuItem>
);
})}
</Menu>
</Box>
);

View File

@@ -1,178 +1,128 @@
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import LanguageDetector from 'i18next-browser-languagedetector';
// Note: LanguageDetector not used - we have custom detector
// Import all translation files
// Only import German translations by default
import translationDE from './locales/de/index.js';
import translationEN from './locales/en/index.js';
import translationAR from './locales/ar/translation.js';
import translationBG from './locales/bg/translation.js';
import translationCS from './locales/cs/translation.js';
import translationEL from './locales/el/translation.js';
import translationES from './locales/es/translation.js';
import translationFR from './locales/fr/translation.js';
import translationHR from './locales/hr/translation.js';
import translationHU from './locales/hu/translation.js';
import translationIT from './locales/it/translation.js';
import translationPL from './locales/pl/translation.js';
import translationRO from './locales/ro/translation.js';
import translationRU from './locales/ru/translation.js';
import translationSK from './locales/sk/translation.js';
import translationSL from './locales/sl/translation.js';
import translationSR from './locales/sr/translation.js';
import translationSV from './locales/sv/translation.js';
import translationTR from './locales/tr/translation.js';
import translationUK from './locales/uk/translation.js';
import translationZH from './locales/zh/translation.js';
// Import legal translations for all languages
// German
import legalAgbDE from './locales/de/legal-agb.js';
import legalDatenschutzDE from './locales/de/legal-datenschutz.js';
import legalImpressumDE from './locales/de/legal-impressum.js';
import legalWiderrufDE from './locales/de/legal-widerruf.js';
import legalBatterieDE from './locales/de/legal-batterie.js';
// English
import legalAgbEN from './locales/en/legal-agb.js';
import legalDatenschutzEN from './locales/en/legal-datenschutz.js';
import legalImpressumEN from './locales/en/legal-impressum.js';
import legalWiderrufEN from './locales/en/legal-widerruf.js';
import legalBatterieEN from './locales/en/legal-batterie.js';
// Language loading cache to prevent duplicate loads
const languageCache = new Set(['de']);
const loadingPromises = new Map();
// Arabic
import legalAgbAR from './locales/ar/legal-agb.js';
import legalDatenschutzAR from './locales/ar/legal-datenschutz.js';
import legalImpressumAR from './locales/ar/legal-impressum.js';
import legalWiderrufAR from './locales/ar/legal-widerruf.js';
import legalBatterieAR from './locales/ar/legal-batterie.js';
// Lazy loading function for languages
const loadLanguage = async (language) => {
if (languageCache.has(language)) {
return; // Already loaded
}
// Bulgarian
import legalAgbBG from './locales/bg/legal-agb.js';
import legalDatenschutzBG from './locales/bg/legal-datenschutz.js';
import legalImpressumBG from './locales/bg/legal-impressum.js';
import legalWiderrufBG from './locales/bg/legal-widerruf.js';
import legalBatterieBG from './locales/bg/legal-batterie.js';
if (loadingPromises.has(language)) {
return loadingPromises.get(language); // Already loading
}
// Czech
import legalAgbCS from './locales/cs/legal-agb.js';
import legalDatenschutzCS from './locales/cs/legal-datenschutz.js';
import legalImpressumCS from './locales/cs/legal-impressum.js';
import legalWiderrufCS from './locales/cs/legal-widerruf.js';
import legalBatterieCS from './locales/cs/legal-batterie.js';
const loadingPromise = (async () => {
try {
console.log(`🌍 Lazy loading language: ${language}`);
// Dynamic imports for lazy loading
const [
translation,
legalAgb,
legalDatenschutz,
legalImpressum,
legalWiderruf,
legalBatterie
] = await Promise.all([
import(`./locales/${language}/index.js`),
import(`./locales/${language}/legal-agb.js`),
import(`./locales/${language}/legal-datenschutz.js`),
import(`./locales/${language}/legal-impressum.js`),
import(`./locales/${language}/legal-widerruf.js`),
import(`./locales/${language}/legal-batterie.js`)
]);
// Greek
import legalAgbEL from './locales/el/legal-agb.js';
import legalDatenschutzEL from './locales/el/legal-datenschutz.js';
import legalImpressumEL from './locales/el/legal-impressum.js';
import legalWiderrufEL from './locales/el/legal-widerruf.js';
import legalBatterieEL from './locales/el/legal-batterie.js';
// Add the loaded resources to i18n
i18n.addResourceBundle(language, 'translation', translation.default);
i18n.addResourceBundle(language, 'legal-agb', legalAgb.default);
i18n.addResourceBundle(language, 'legal-datenschutz', legalDatenschutz.default);
i18n.addResourceBundle(language, 'legal-impressum', legalImpressum.default);
i18n.addResourceBundle(language, 'legal-widerruf', legalWiderruf.default);
i18n.addResourceBundle(language, 'legal-batterie', legalBatterie.default);
// Spanish
import legalAgbES from './locales/es/legal-agb.js';
import legalDatenschutzES from './locales/es/legal-datenschutz.js';
import legalImpressumES from './locales/es/legal-impressum.js';
import legalWiderrufES from './locales/es/legal-widerruf.js';
import legalBatterieES from './locales/es/legal-batterie.js';
languageCache.add(language);
console.log(`✅ Language ${language} loaded successfully`);
} catch (error) {
console.error(`❌ Failed to load language ${language}:`, error);
throw error;
} finally {
loadingPromises.delete(language);
}
})();
// French
import legalAgbFR from './locales/fr/legal-agb.js';
import legalDatenschutzFR from './locales/fr/legal-datenschutz.js';
import legalImpressumFR from './locales/fr/legal-impressum.js';
import legalWiderrufFR from './locales/fr/legal-widerruf.js';
import legalBatterieFR from './locales/fr/legal-batterie.js';
loadingPromises.set(language, loadingPromise);
return loadingPromise;
};
// Croatian
import legalAgbHR from './locales/hr/legal-agb.js';
import legalDatenschutzHR from './locales/hr/legal-datenschutz.js';
import legalImpressumHR from './locales/hr/legal-impressum.js';
import legalWiderrufHR from './locales/hr/legal-widerruf.js';
import legalBatterieHR from './locales/hr/legal-batterie.js';
// Custom language detector that prioritizes session storage and defaults to German
const customDetector = {
name: 'customDetector',
lookup() {
// Only try storage in browser environment
if (typeof window === 'undefined') {
return 'de';
}
// Hungarian
import legalAgbHU from './locales/hu/legal-agb.js';
import legalDatenschutzHU from './locales/hu/legal-datenschutz.js';
import legalImpressumHU from './locales/hu/legal-impressum.js';
import legalWiderrufHU from './locales/hu/legal-widerruf.js';
import legalBatterieHU from './locales/hu/legal-batterie.js';
// 1. Check session storage first
try {
if (typeof sessionStorage !== 'undefined') {
const sessionLang = sessionStorage.getItem('i18nextLng');
if (sessionLang && sessionLang !== 'de') {
return sessionLang;
}
}
} catch {
// Session storage not available
}
// Italian
import legalAgbIT from './locales/it/legal-agb.js';
import legalDatenschutzIT from './locales/it/legal-datenschutz.js';
import legalImpressumIT from './locales/it/legal-impressum.js';
import legalWiderrufIT from './locales/it/legal-widerruf.js';
import legalBatterieIT from './locales/it/legal-batterie.js';
// 2. Check localStorage
try {
if (typeof localStorage !== 'undefined') {
const localLang = localStorage.getItem('i18nextLng');
if (localLang && localLang !== 'de') {
return localLang;
}
}
} catch {
// LocalStorage not available
}
// Polish
import legalAgbPL from './locales/pl/legal-agb.js';
import legalDatenschutzPL from './locales/pl/legal-datenschutz.js';
import legalImpressumPL from './locales/pl/legal-impressum.js';
import legalWiderrufPL from './locales/pl/legal-widerruf.js';
import legalBatteriePL from './locales/pl/legal-batterie.js';
// 3. Always default to German (don't detect browser language)
return 'de';
},
cacheUserLanguage(lng) {
// Only cache in browser environment
if (typeof window === 'undefined') {
return;
}
// Romanian
import legalAgbRO from './locales/ro/legal-agb.js';
import legalDatenschutzRO from './locales/ro/legal-datenschutz.js';
import legalImpressumRO from './locales/ro/legal-impressum.js';
import legalWiderrufRO from './locales/ro/legal-widerruf.js';
import legalBatterieRO from './locales/ro/legal-batterie.js';
// Russian
import legalAgbRU from './locales/ru/legal-agb.js';
import legalDatenschutzRU from './locales/ru/legal-datenschutz.js';
import legalImpressumRU from './locales/ru/legal-impressum.js';
import legalWiderrufRU from './locales/ru/legal-widerruf.js';
import legalBatterieRU from './locales/ru/legal-batterie.js';
// Slovak
import legalAgbSK from './locales/sk/legal-agb.js';
import legalDatenschutzSK from './locales/sk/legal-datenschutz.js';
import legalImpressumSK from './locales/sk/legal-impressum.js';
import legalWiderrufSK from './locales/sk/legal-widerruf.js';
import legalBatterieSK from './locales/sk/legal-batterie.js';
// Slovenian
import legalAgbSL from './locales/sl/legal-agb.js';
import legalDatenschutzSL from './locales/sl/legal-datenschutz.js';
import legalImpressumSL from './locales/sl/legal-impressum.js';
import legalWiderrufSL from './locales/sl/legal-widerruf.js';
import legalBatterieSL from './locales/sl/legal-batterie.js';
// Serbian
import legalAgbSR from './locales/sr/legal-agb.js';
import legalDatenschutzSR from './locales/sr/legal-datenschutz.js';
import legalImpressumSR from './locales/sr/legal-impressum.js';
import legalWiderrufSR from './locales/sr/legal-widerruf.js';
import legalBatterieSR from './locales/sr/legal-batterie.js';
// Swedish
import legalAgbSV from './locales/sv/legal-agb.js';
import legalDatenschutzSV from './locales/sv/legal-datenschutz.js';
import legalImpressumSV from './locales/sv/legal-impressum.js';
import legalWiderrufSV from './locales/sv/legal-widerruf.js';
import legalBatterieSV from './locales/sv/legal-batterie.js';
// Turkish
import legalAgbTR from './locales/tr/legal-agb.js';
import legalDatenschutzTR from './locales/tr/legal-datenschutz.js';
import legalImpressumTR from './locales/tr/legal-impressum.js';
import legalWiderrufTR from './locales/tr/legal-widerruf.js';
import legalBatterieTR from './locales/tr/legal-batterie.js';
// Ukrainian
import legalAgbUK from './locales/uk/legal-agb.js';
import legalDatenschutzUK from './locales/uk/legal-datenschutz.js';
import legalImpressumUK from './locales/uk/legal-impressum.js';
import legalWiderrufUK from './locales/uk/legal-widerruf.js';
import legalBatterieUK from './locales/uk/legal-batterie.js';
// Chinese
import legalAgbZH from './locales/zh/legal-agb.js';
import legalDatenschutzZH from './locales/zh/legal-datenschutz.js';
import legalImpressumZH from './locales/zh/legal-impressum.js';
import legalWiderrufZH from './locales/zh/legal-widerruf.js';
import legalBatterieZH from './locales/zh/legal-batterie.js';
try {
if (typeof sessionStorage !== 'undefined') {
sessionStorage.setItem('i18nextLng', lng);
}
if (typeof localStorage !== 'undefined') {
localStorage.setItem('i18nextLng', lng);
}
} catch {
// Storage not available
}
}
};
// Initialize i18n with only German resources
const resources = {
de: {
translation: translationDE,
@@ -181,185 +131,28 @@ const resources = {
'legal-impressum': legalImpressumDE,
'legal-widerruf': legalWiderrufDE,
'legal-batterie': legalBatterieDE
},
en: {
translation: translationEN,
'legal-agb': legalAgbEN,
'legal-datenschutz': legalDatenschutzEN,
'legal-impressum': legalImpressumEN,
'legal-widerruf': legalWiderrufEN,
'legal-batterie': legalBatterieEN
},
ar: {
translation: translationAR,
'legal-agb': legalAgbAR,
'legal-datenschutz': legalDatenschutzAR,
'legal-impressum': legalImpressumAR,
'legal-widerruf': legalWiderrufAR,
'legal-batterie': legalBatterieAR
},
bg: {
translation: translationBG,
'legal-agb': legalAgbBG,
'legal-datenschutz': legalDatenschutzBG,
'legal-impressum': legalImpressumBG,
'legal-widerruf': legalWiderrufBG,
'legal-batterie': legalBatterieBG
},
cs: {
translation: translationCS,
'legal-agb': legalAgbCS,
'legal-datenschutz': legalDatenschutzCS,
'legal-impressum': legalImpressumCS,
'legal-widerruf': legalWiderrufCS,
'legal-batterie': legalBatterieCS
},
el: {
translation: translationEL,
'legal-agb': legalAgbEL,
'legal-datenschutz': legalDatenschutzEL,
'legal-impressum': legalImpressumEL,
'legal-widerruf': legalWiderrufEL,
'legal-batterie': legalBatterieEL
},
es: {
translation: translationES,
'legal-agb': legalAgbES,
'legal-datenschutz': legalDatenschutzES,
'legal-impressum': legalImpressumES,
'legal-widerruf': legalWiderrufES,
'legal-batterie': legalBatterieES
},
fr: {
translation: translationFR,
'legal-agb': legalAgbFR,
'legal-datenschutz': legalDatenschutzFR,
'legal-impressum': legalImpressumFR,
'legal-widerruf': legalWiderrufFR,
'legal-batterie': legalBatterieFR
},
hr: {
translation: translationHR,
'legal-agb': legalAgbHR,
'legal-datenschutz': legalDatenschutzHR,
'legal-impressum': legalImpressumHR,
'legal-widerruf': legalWiderrufHR,
'legal-batterie': legalBatterieHR
},
hu: {
translation: translationHU,
'legal-agb': legalAgbHU,
'legal-datenschutz': legalDatenschutzHU,
'legal-impressum': legalImpressumHU,
'legal-widerruf': legalWiderrufHU,
'legal-batterie': legalBatterieHU
},
it: {
translation: translationIT,
'legal-agb': legalAgbIT,
'legal-datenschutz': legalDatenschutzIT,
'legal-impressum': legalImpressumIT,
'legal-widerruf': legalWiderrufIT,
'legal-batterie': legalBatterieIT
},
pl: {
translation: translationPL,
'legal-agb': legalAgbPL,
'legal-datenschutz': legalDatenschutzPL,
'legal-impressum': legalImpressumPL,
'legal-widerruf': legalWiderrufPL,
'legal-batterie': legalBatteriePL
},
ro: {
translation: translationRO,
'legal-agb': legalAgbRO,
'legal-datenschutz': legalDatenschutzRO,
'legal-impressum': legalImpressumRO,
'legal-widerruf': legalWiderrufRO,
'legal-batterie': legalBatterieRO
},
ru: {
translation: translationRU,
'legal-agb': legalAgbRU,
'legal-datenschutz': legalDatenschutzRU,
'legal-impressum': legalImpressumRU,
'legal-widerruf': legalWiderrufRU,
'legal-batterie': legalBatterieRU
},
sk: {
translation: translationSK,
'legal-agb': legalAgbSK,
'legal-datenschutz': legalDatenschutzSK,
'legal-impressum': legalImpressumSK,
'legal-widerruf': legalWiderrufSK,
'legal-batterie': legalBatterieSK
},
sl: {
translation: translationSL,
'legal-agb': legalAgbSL,
'legal-datenschutz': legalDatenschutzSL,
'legal-impressum': legalImpressumSL,
'legal-widerruf': legalWiderrufSL,
'legal-batterie': legalBatterieSL
},
sr: {
translation: translationSR,
'legal-agb': legalAgbSR,
'legal-datenschutz': legalDatenschutzSR,
'legal-impressum': legalImpressumSR,
'legal-widerruf': legalWiderrufSR,
'legal-batterie': legalBatterieSR
},
sv: {
translation: translationSV,
'legal-agb': legalAgbSV,
'legal-datenschutz': legalDatenschutzSV,
'legal-impressum': legalImpressumSV,
'legal-widerruf': legalWiderrufSV,
'legal-batterie': legalBatterieSV
},
tr: {
translation: translationTR,
'legal-agb': legalAgbTR,
'legal-datenschutz': legalDatenschutzTR,
'legal-impressum': legalImpressumTR,
'legal-widerruf': legalWiderrufTR,
'legal-batterie': legalBatterieTR
},
uk: {
translation: translationUK,
'legal-agb': legalAgbUK,
'legal-datenschutz': legalDatenschutzUK,
'legal-impressum': legalImpressumUK,
'legal-widerruf': legalWiderrufUK,
'legal-batterie': legalBatterieUK
},
zh: {
translation: translationZH,
'legal-agb': legalAgbZH,
'legal-datenschutz': legalDatenschutzZH,
'legal-impressum': legalImpressumZH,
'legal-widerruf': legalWiderrufZH,
'legal-batterie': legalBatterieZH
}
};
i18n
.use(LanguageDetector)
.use({
type: 'languageDetector',
async: false,
detect: customDetector.lookup,
init() {},
cacheUserLanguage: customDetector.cacheUserLanguage
})
.use(initReactI18next)
.init({
resources,
fallbackLng: 'de', // German as fallback since it's your primary language
lng: 'de', // Force German as default
fallbackLng: 'de',
debug: process.env.NODE_ENV === 'development',
// Language detection options
// Disable automatic language detection from browser
detection: {
// Order of language detection methods
order: ['localStorage', 'navigator', 'htmlTag'],
// Cache the language selection
caches: ['localStorage'],
// Check for language in localStorage
lookupLocalStorage: 'i18nextLng'
order: ['customDetector'],
caches: ['localStorage', 'sessionStorage']
},
interpolation: {
@@ -372,10 +165,57 @@ i18n
// React-specific options
react: {
useSuspense: false // Disable suspense for class components compatibility
}
},
// Load missing keys as fallback
saveMissing: process.env.NODE_ENV === 'development'
});
// Export withI18n and other utilities for easy access
export { withI18n, withTranslation, withLanguage, LanguageContext, LanguageProvider } from './withTranslation.js';
// Override changeLanguage to load languages on demand
const originalChangeLanguage = i18n.changeLanguage.bind(i18n);
i18n.changeLanguage = async (language) => {
if (language !== 'de' && !languageCache.has(language)) {
try {
await loadLanguage(language);
} catch {
console.error(`Failed to load language ${language}, falling back to German`);
language = 'de';
}
}
return originalChangeLanguage(language);
};
export default i18n;
// Check session storage on initialization and load language if needed
const initializeLanguage = async () => {
// Only run in browser environment
if (typeof window === 'undefined' || typeof sessionStorage === 'undefined') {
return;
}
try {
const sessionLang = sessionStorage.getItem('i18nextLng');
if (sessionLang && sessionLang !== 'de' && !languageCache.has(sessionLang)) {
console.log(`🔄 Restoring session language: ${sessionLang}`);
await loadLanguage(sessionLang);
await i18n.changeLanguage(sessionLang);
}
} catch {
console.warn('Failed to restore session language');
}
};
// Initialize language on DOM ready (browser only)
if (typeof window !== 'undefined' && typeof document !== 'undefined') {
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initializeLanguage);
} else {
initializeLanguage();
}
}
export default i18n;
export { loadLanguage };
// Re-export withI18n and other utilities for compatibility
export { withI18n, withTranslation, withLanguage, LanguageContext, LanguageProvider } from './withTranslation.js';

View File

@@ -1,5 +1,6 @@
import React, { Component } from 'react';
import { withTranslation as reactI18nextWithTranslation } from 'react-i18next';
import { loadLanguage } from './index.js';
// HOC to provide translation functions to class components
export const withTranslation = (namespaces = 'translation') => (WrappedComponent) => {
@@ -10,7 +11,7 @@ export const withTranslation = (namespaces = 'translation') => (WrappedComponent
export const LanguageContext = React.createContext({
currentLanguage: 'de',
changeLanguage: () => {},
availableLanguages: ['ar', 'bg', 'cs', 'de', 'el', 'en', 'es', 'fr', 'hr', 'hu', 'it', 'pl', 'ro', 'ru', 'sk', 'sl', 'sr', 'sv', 'tr', 'uk', 'zh']
availableLanguages: ['de'] // Start with only German
});
// Provider component for language management
@@ -18,14 +19,16 @@ export class LanguageProvider extends Component {
constructor(props) {
super(props);
// Get initial language from i18n instance
const currentLanguage = props.i18n?.language || 'de';
// Always start with German
const currentLanguage = 'de';
this.state = {
currentLanguage,
availableLanguages: ['ar', 'bg', 'cs', 'de', 'el', 'en', 'es', 'fr', 'hr', 'hu', 'it', 'pl', 'ro', 'ru', 'sk', 'sl', 'sr', 'sv', 'tr', 'uk', 'zh'],
demoMode: false, // Enable demo mode
currentLanguageIndex: 0
availableLanguages: ['de'], // Start with only German visible
allLanguages: ['ar', 'bg', 'cs', 'de', 'el', 'en', 'es', 'fr', 'hr', 'hu', 'it', 'pl', 'ro', 'ru', 'sk', 'sl', 'sr', 'sv', 'tr', 'uk', 'zh'],
demoMode: false,
currentLanguageIndex: 0,
loadingLanguages: new Set()
};
this.demoInterval = null;
@@ -124,14 +127,47 @@ export class LanguageProvider extends Component {
}
};
changeLanguage = (language) => {
if (this.props.i18n && this.state.availableLanguages.includes(language)) {
changeLanguage = async (language) => {
if (this.props.i18n && this.state.allLanguages.includes(language)) {
// Stop demo mode if user manually changes language
if (this.state.demoMode) {
this.stopDemo();
}
this.props.i18n.changeLanguage(language);
// If language is not German and not already available, load it
if (language !== 'de' && !this.state.availableLanguages.includes(language)) {
// Check if already loading
if (this.state.loadingLanguages.has(language)) {
return; // Already loading
}
// Mark as loading
this.setState(prevState => ({
loadingLanguages: new Set([...prevState.loadingLanguages, language])
}));
try {
console.log(`🌍 Loading language: ${language}`);
await loadLanguage(language);
// Add to available languages after successful load
this.setState(prevState => ({
availableLanguages: [...prevState.availableLanguages, language],
loadingLanguages: new Set([...prevState.loadingLanguages].filter(l => l !== language))
}));
console.log(`✅ Language ${language} now available`);
} catch (error) {
console.error(`❌ Failed to load language ${language}:`, error);
this.setState(prevState => ({
loadingLanguages: new Set([...prevState.loadingLanguages].filter(l => l !== language))
}));
return; // Don't change language if loading failed
}
}
// Change the language
await this.props.i18n.changeLanguage(language);
}
};
@@ -140,6 +176,8 @@ export class LanguageProvider extends Component {
currentLanguage: this.state.currentLanguage,
changeLanguage: this.changeLanguage,
availableLanguages: this.state.availableLanguages,
allLanguages: this.state.allLanguages,
loadingLanguages: this.state.loadingLanguages,
demoMode: this.state.demoMode,
stopDemo: this.stopDemo
};