219 lines
7.7 KiB
JavaScript
219 lines
7.7 KiB
JavaScript
|
|
// LanguageProvider.js - Central language management system
|
|
import { ref, reactive, computed, provide, inject } from 'vue';
|
|
import { useConstantsTranslation } from '../composables/useTranslation.js';
|
|
|
|
// Injection key for type safety
|
|
export const LANGUAGE_PROVIDER_KEY = Symbol('LanguageProvider');
|
|
|
|
/**
|
|
* Language Provider Service
|
|
* Central management of language state and translations
|
|
*/
|
|
export function createLanguageProvider(initialLanguage = 'en', apiPrefix = '') {
|
|
// Reactive state
|
|
const currentLanguage = ref(initialLanguage);
|
|
const isTranslating = ref(false);
|
|
const translationError = ref(null);
|
|
|
|
// Translation composable
|
|
const { translateConstants, getCachedTranslations, clearCache } = useConstantsTranslation();
|
|
|
|
// Component-specific translations cache
|
|
const componentTranslations = reactive({});
|
|
|
|
/**
|
|
* Register a component for translations with component-specific caching
|
|
*/
|
|
const registerComponent = (componentName, originalTexts) => {
|
|
console.log(`LanguageProvider: Registering component ${componentName} with language ${currentLanguage.value}`);
|
|
|
|
if (!componentTranslations[componentName]) {
|
|
componentTranslations[componentName] = reactive({
|
|
original: originalTexts,
|
|
translated: { ...originalTexts }, // Start with original English texts
|
|
isLoading: false,
|
|
error: null
|
|
});
|
|
|
|
// Force initial translation if current language is not English
|
|
if (currentLanguage.value !== 'en') {
|
|
console.log(`LanguageProvider: Component ${componentName} needs initial translation to ${currentLanguage.value}`);
|
|
translateComponentTexts(componentName, currentLanguage.value);
|
|
}
|
|
}
|
|
|
|
return componentTranslations[componentName];
|
|
};
|
|
|
|
/**
|
|
* Translate texts for a specific component
|
|
*/
|
|
const translateComponentTexts = async (componentName, targetLanguage) => {
|
|
const component = componentTranslations[componentName];
|
|
if (!component) {
|
|
console.warn(`LanguageProvider: Component ${componentName} not found for translation`);
|
|
return;
|
|
}
|
|
|
|
component.isLoading = true;
|
|
component.error = null;
|
|
|
|
try {
|
|
if (targetLanguage === 'en') {
|
|
// For English, use original texts (no translation needed)
|
|
component.translated = { ...component.original };
|
|
console.log(`LanguageProvider: Using original English texts for ${componentName}`);
|
|
} else {
|
|
// For other languages, translate from English
|
|
console.log(`LanguageProvider: Translating ${componentName} from English to ${targetLanguage}`);
|
|
const translatedTexts = await translateConstants(
|
|
component.original,
|
|
targetLanguage,
|
|
{
|
|
context: componentName,
|
|
apiPrefix
|
|
}
|
|
);
|
|
component.translated = translatedTexts;
|
|
console.log(`LanguageProvider: Successfully translated ${componentName} to ${targetLanguage}`);
|
|
}
|
|
} catch (error) {
|
|
console.error(`LanguageProvider: Translation error for ${componentName}:`, error);
|
|
component.error = error;
|
|
// Fallback to original English texts
|
|
component.translated = { ...component.original };
|
|
} finally {
|
|
component.isLoading = false;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Update language for all registered components
|
|
*/
|
|
const setLanguage = async (newLanguage) => {
|
|
if (currentLanguage.value === newLanguage) {
|
|
return;
|
|
}
|
|
|
|
console.log('LanguageProvider: Setting language to', newLanguage);
|
|
currentLanguage.value = newLanguage;
|
|
isTranslating.value = true;
|
|
translationError.value = null;
|
|
|
|
try {
|
|
// Update all registered components
|
|
const translationPromises = Object.keys(componentTranslations).map(componentName =>
|
|
translateComponentTexts(componentName, newLanguage)
|
|
);
|
|
|
|
await Promise.all(translationPromises);
|
|
console.log(`LanguageProvider: Successfully updated all components to ${newLanguage}`);
|
|
} catch (error) {
|
|
console.error('LanguageProvider: Error setting language:', error);
|
|
translationError.value = error;
|
|
} finally {
|
|
isTranslating.value = false;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Get translations voor een specifieke component
|
|
*/
|
|
const getComponentTranslations = (componentName) => {
|
|
return componentTranslations[componentName] || null;
|
|
};
|
|
|
|
/**
|
|
* Clear alle caches
|
|
*/
|
|
const clearAllCaches = () => {
|
|
clearCache();
|
|
Object.keys(componentTranslations).forEach(key => {
|
|
delete componentTranslations[key];
|
|
});
|
|
};
|
|
|
|
// === NIEUWE TOEVOEGING: DOM Event Integration ===
|
|
|
|
/**
|
|
* Setup DOM event listeners for backwards compatibility
|
|
*/
|
|
const setupDOMEventListeners = () => {
|
|
if (typeof document !== 'undefined') {
|
|
const handleLanguageChangeEvent = (event) => {
|
|
const newLanguage = event.detail?.language || event.detail;
|
|
console.log(`LanguageProvider: Received DOM language change event: ${newLanguage}`);
|
|
|
|
if (newLanguage && currentLanguage.value !== newLanguage) {
|
|
console.log(`LanguageProvider: Triggering setLanguage(${newLanguage}) from DOM event`);
|
|
setLanguage(newLanguage);
|
|
}
|
|
};
|
|
|
|
// Luister naar DOM language change events
|
|
document.addEventListener('language-changed', handleLanguageChangeEvent);
|
|
console.log('LanguageProvider: DOM event listener registered for language-changed events');
|
|
|
|
// Return cleanup function
|
|
return () => {
|
|
document.removeEventListener('language-changed', handleLanguageChangeEvent);
|
|
console.log('LanguageProvider: DOM event listener cleanup completed');
|
|
};
|
|
}
|
|
|
|
return () => {}; // No-op cleanup for non-browser environments
|
|
};
|
|
|
|
// Setup DOM event listeners immediately
|
|
const cleanupDOMListeners = setupDOMEventListeners();
|
|
|
|
return {
|
|
// State
|
|
currentLanguage: computed(() => currentLanguage.value),
|
|
isTranslating: computed(() => isTranslating.value),
|
|
translationError: computed(() => translationError.value),
|
|
|
|
// Methods
|
|
registerComponent,
|
|
setLanguage,
|
|
getComponentTranslations,
|
|
clearAllCaches,
|
|
|
|
// Computed
|
|
componentTranslations: computed(() => componentTranslations),
|
|
|
|
// Cleanup method
|
|
cleanup: cleanupDOMListeners
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Composable voor het gebruiken van de Language Provider
|
|
*/
|
|
export function useLanguageProvider() {
|
|
const provider = inject(LANGUAGE_PROVIDER_KEY);
|
|
|
|
if (!provider) {
|
|
throw new Error('useLanguageProvider must be used within a LanguageProvider');
|
|
}
|
|
|
|
return provider;
|
|
}
|
|
|
|
/**
|
|
* Composable voor component-specifieke vertalingen
|
|
*/
|
|
export function useComponentTranslations(componentName, originalTexts) {
|
|
const provider = useLanguageProvider();
|
|
|
|
// Registreer component bij eerste gebruik
|
|
const translations = provider.registerComponent(componentName, originalTexts);
|
|
|
|
return {
|
|
translations: computed(() => translations.translated),
|
|
isLoading: computed(() => translations.isLoading),
|
|
error: computed(() => translations.error),
|
|
currentLanguage: provider.currentLanguage
|
|
};
|
|
} |