refactor(i18n): optimize translation loading and initialization
- Updated ensureTranslationsLoaded function to prevent duplicate requests by caching loaded translations and managing loading promises. - Simplified translation preloading on app startup to only load essential namespaces for the current language. - Adjusted useTranslationPreloader hook to avoid multiple requests for translations and ensure efficient loading state management.
This commit is contained in:
@@ -2,7 +2,6 @@
|
||||
import React, { Suspense, useEffect, memo, useMemo, useCallback } from 'react';
|
||||
import { RouterProvider } from 'react-router-dom';
|
||||
import i18next from 'i18next';
|
||||
import { ensureTranslationsLoaded } from './i18n';
|
||||
|
||||
// Components
|
||||
import ThemeWrapper from './features/theme/ThemeWrapper';
|
||||
@@ -66,8 +65,8 @@ const App: React.FC = memo(() => {
|
||||
// Initialize CSRF token
|
||||
await initializeCsrfToken();
|
||||
|
||||
// Preload essential translations
|
||||
await ensureTranslationsLoaded();
|
||||
// Note: Translation preloading is handled in i18n.ts initialization
|
||||
// No need to call ensureTranslationsLoaded here to avoid duplicate requests
|
||||
} catch (error) {
|
||||
if (isMounted) {
|
||||
logger.error('Failed to initialize app:', error);
|
||||
|
||||
@@ -26,7 +26,7 @@ export const useTranslationPreloader = (
|
||||
try {
|
||||
setIsLoading(true);
|
||||
|
||||
// Ensure translations are loaded
|
||||
// Only load translations for current language to avoid multiple requests
|
||||
await ensureTranslationsLoaded(namespaces);
|
||||
|
||||
// Wait for i18next to be ready
|
||||
@@ -47,12 +47,18 @@ export const useTranslationPreloader = (
|
||||
}
|
||||
};
|
||||
|
||||
loadTranslations();
|
||||
// Only load if not already loaded
|
||||
if (!isLoaded && !ready) {
|
||||
loadTranslations();
|
||||
} else if (ready && !isLoaded) {
|
||||
setIsLoaded(true);
|
||||
setIsLoading(false);
|
||||
}
|
||||
|
||||
return () => {
|
||||
isMounted = false;
|
||||
};
|
||||
}, [namespaces, ready]);
|
||||
}, [namespaces, ready, isLoaded]);
|
||||
|
||||
return {
|
||||
t,
|
||||
|
||||
@@ -12,6 +12,10 @@ const ESSENTIAL_NAMESPACES = [
|
||||
'settings',
|
||||
];
|
||||
|
||||
// Cache to track loaded translations and prevent duplicate requests
|
||||
const loadedTranslations = new Set<string>();
|
||||
const loadingPromises = new Map<string, Promise<any>>();
|
||||
|
||||
i18n
|
||||
.use(HttpApi)
|
||||
.use(initReactI18next)
|
||||
@@ -37,33 +41,67 @@ i18n
|
||||
});
|
||||
|
||||
// Utility function to ensure translations are loaded
|
||||
export const ensureTranslationsLoaded = async (namespaces: string[] = ESSENTIAL_NAMESPACES) => {
|
||||
const currentLang = i18n.language || 'en';
|
||||
|
||||
export const ensureTranslationsLoaded = async (
|
||||
namespaces: string[] = ESSENTIAL_NAMESPACES,
|
||||
languages: string[] = [i18n.language || 'en']
|
||||
) => {
|
||||
try {
|
||||
// Load all essential namespaces for the current language
|
||||
await Promise.all(
|
||||
namespaces.map(ns =>
|
||||
i18n.loadNamespaces(ns).catch(() => {
|
||||
logger.error(`Failed to load namespace: ${ns}`);
|
||||
})
|
||||
)
|
||||
);
|
||||
const loadPromises: Promise<any>[] = [];
|
||||
|
||||
// Also preload for other languages to prevent delays on language switch
|
||||
const otherLangs = ['en', 'es', 'pt', 'alb', 'de'].filter(lang => lang !== currentLang);
|
||||
await Promise.all(
|
||||
otherLangs.map(lang =>
|
||||
Promise.all(
|
||||
namespaces.map(ns =>
|
||||
i18n.loadNamespaces(ns).catch(() => {
|
||||
logger.error(`Failed to load namespace: ${ns}`);
|
||||
})
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
for (const lang of languages) {
|
||||
for (const ns of namespaces) {
|
||||
const key = `${lang}:${ns}`;
|
||||
|
||||
// Skip if already loaded
|
||||
if (loadedTranslations.has(key)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if already loading
|
||||
if (loadingPromises.has(key)) {
|
||||
loadPromises.push(loadingPromises.get(key)!);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Create loading promise
|
||||
const loadingPromise = new Promise<void>((resolve, reject) => {
|
||||
// Switch to the target language temporarily if needed
|
||||
const currentLang = i18n.language;
|
||||
const shouldSwitchLang = currentLang !== lang;
|
||||
|
||||
const loadForLanguage = async () => {
|
||||
try {
|
||||
if (shouldSwitchLang) {
|
||||
await i18n.changeLanguage(lang);
|
||||
}
|
||||
|
||||
await i18n.loadNamespaces(ns);
|
||||
|
||||
// Switch back to original language if we changed it
|
||||
if (shouldSwitchLang && currentLang) {
|
||||
await i18n.changeLanguage(currentLang);
|
||||
}
|
||||
|
||||
loadedTranslations.add(key);
|
||||
resolve();
|
||||
} catch (error) {
|
||||
logger.error(`Failed to load namespace: ${ns} for language: ${lang}`, error);
|
||||
reject(error);
|
||||
} finally {
|
||||
loadingPromises.delete(key);
|
||||
}
|
||||
};
|
||||
|
||||
loadForLanguage();
|
||||
});
|
||||
|
||||
loadingPromises.set(key, loadingPromise);
|
||||
loadPromises.push(loadingPromise);
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for all loading promises to complete
|
||||
await Promise.all(loadPromises);
|
||||
return true;
|
||||
} catch (error) {
|
||||
logger.error('Failed to load translations:', error);
|
||||
@@ -71,7 +109,13 @@ export const ensureTranslationsLoaded = async (namespaces: string[] = ESSENTIAL_
|
||||
}
|
||||
};
|
||||
|
||||
// Initialize translations on app startup
|
||||
ensureTranslationsLoaded();
|
||||
// Preload essential translations for current language only on startup
|
||||
const initializeTranslations = async () => {
|
||||
const currentLang = i18n.language || 'en';
|
||||
await ensureTranslationsLoaded(ESSENTIAL_NAMESPACES, [currentLang]);
|
||||
};
|
||||
|
||||
// Initialize translations on app startup (only once)
|
||||
initializeTranslations();
|
||||
|
||||
export default i18n;
|
||||
|
||||
Reference in New Issue
Block a user