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 React, { Suspense, useEffect, memo, useMemo, useCallback } from 'react';
|
||||||
import { RouterProvider } from 'react-router-dom';
|
import { RouterProvider } from 'react-router-dom';
|
||||||
import i18next from 'i18next';
|
import i18next from 'i18next';
|
||||||
import { ensureTranslationsLoaded } from './i18n';
|
|
||||||
|
|
||||||
// Components
|
// Components
|
||||||
import ThemeWrapper from './features/theme/ThemeWrapper';
|
import ThemeWrapper from './features/theme/ThemeWrapper';
|
||||||
@@ -66,8 +65,8 @@ const App: React.FC = memo(() => {
|
|||||||
// Initialize CSRF token
|
// Initialize CSRF token
|
||||||
await initializeCsrfToken();
|
await initializeCsrfToken();
|
||||||
|
|
||||||
// Preload essential translations
|
// Note: Translation preloading is handled in i18n.ts initialization
|
||||||
await ensureTranslationsLoaded();
|
// No need to call ensureTranslationsLoaded here to avoid duplicate requests
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (isMounted) {
|
if (isMounted) {
|
||||||
logger.error('Failed to initialize app:', error);
|
logger.error('Failed to initialize app:', error);
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ export const useTranslationPreloader = (
|
|||||||
try {
|
try {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
|
|
||||||
// Ensure translations are loaded
|
// Only load translations for current language to avoid multiple requests
|
||||||
await ensureTranslationsLoaded(namespaces);
|
await ensureTranslationsLoaded(namespaces);
|
||||||
|
|
||||||
// Wait for i18next to be ready
|
// 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 () => {
|
return () => {
|
||||||
isMounted = false;
|
isMounted = false;
|
||||||
};
|
};
|
||||||
}, [namespaces, ready]);
|
}, [namespaces, ready, isLoaded]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
t,
|
t,
|
||||||
|
|||||||
@@ -12,6 +12,10 @@ const ESSENTIAL_NAMESPACES = [
|
|||||||
'settings',
|
'settings',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// Cache to track loaded translations and prevent duplicate requests
|
||||||
|
const loadedTranslations = new Set<string>();
|
||||||
|
const loadingPromises = new Map<string, Promise<any>>();
|
||||||
|
|
||||||
i18n
|
i18n
|
||||||
.use(HttpApi)
|
.use(HttpApi)
|
||||||
.use(initReactI18next)
|
.use(initReactI18next)
|
||||||
@@ -37,33 +41,67 @@ i18n
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Utility function to ensure translations are loaded
|
// Utility function to ensure translations are loaded
|
||||||
export const ensureTranslationsLoaded = async (namespaces: string[] = ESSENTIAL_NAMESPACES) => {
|
export const ensureTranslationsLoaded = async (
|
||||||
const currentLang = i18n.language || 'en';
|
namespaces: string[] = ESSENTIAL_NAMESPACES,
|
||||||
|
languages: string[] = [i18n.language || 'en']
|
||||||
|
) => {
|
||||||
try {
|
try {
|
||||||
// Load all essential namespaces for the current language
|
const loadPromises: Promise<any>[] = [];
|
||||||
await Promise.all(
|
|
||||||
namespaces.map(ns =>
|
|
||||||
i18n.loadNamespaces(ns).catch(() => {
|
|
||||||
logger.error(`Failed to load namespace: ${ns}`);
|
|
||||||
})
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
// Also preload for other languages to prevent delays on language switch
|
for (const lang of languages) {
|
||||||
const otherLangs = ['en', 'es', 'pt', 'alb', 'de'].filter(lang => lang !== currentLang);
|
for (const ns of namespaces) {
|
||||||
await Promise.all(
|
const key = `${lang}:${ns}`;
|
||||||
otherLangs.map(lang =>
|
|
||||||
Promise.all(
|
// Skip if already loaded
|
||||||
namespaces.map(ns =>
|
if (loadedTranslations.has(key)) {
|
||||||
i18n.loadNamespaces(ns).catch(() => {
|
continue;
|
||||||
logger.error(`Failed to load namespace: ${ns}`);
|
}
|
||||||
})
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
|
// 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;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error('Failed to load translations:', error);
|
logger.error('Failed to load translations:', error);
|
||||||
@@ -71,7 +109,13 @@ export const ensureTranslationsLoaded = async (namespaces: string[] = ESSENTIAL_
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Initialize translations on app startup
|
// Preload essential translations for current language only on startup
|
||||||
ensureTranslationsLoaded();
|
const initializeTranslations = async () => {
|
||||||
|
const currentLang = i18n.language || 'en';
|
||||||
|
await ensureTranslationsLoaded(ESSENTIAL_NAMESPACES, [currentLang]);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Initialize translations on app startup (only once)
|
||||||
|
initializeTranslations();
|
||||||
|
|
||||||
export default i18n;
|
export default i18n;
|
||||||
|
|||||||
Reference in New Issue
Block a user