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:
chamikaJ
2025-07-07 12:31:11 +05:30
parent a6f9046b42
commit 26b47aac53
3 changed files with 81 additions and 32 deletions

View File

@@ -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);

View File

@@ -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,

View File

@@ -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;