- verbeteringen client

- Enkel nog probleem met vertaling van de ProgressTracker constanten
This commit is contained in:
Josako
2025-07-21 21:45:46 +02:00
parent 0f33beddf4
commit 4ad621428e
16 changed files with 982 additions and 378 deletions

View File

@@ -116,6 +116,20 @@ class TranslationServices:
if translated_ctx:
translated_config[field_config][field_name]['context'] = translated_ctx.translated_text
# vertaal allowed values als het veld bestaat en de waarden niet leeg zijn.
if 'allowed_values' in field_data and field_data['allowed_values']:
translated_allowed_values = []
for allowed_value in field_data['allowed_values']:
translated_allowed_value = cache_manager.translation_cache.get_translation(
text=allowed_value,
target_lang=target_language,
source_lang=source_language,
context=context
)
translated_allowed_values.append(translated_allowed_value.translated_text)
if translated_allowed_values:
translated_config[field_config][field_name]['allowed_values'] = translated_allowed_values
return translated_config
@staticmethod

View File

@@ -8,7 +8,7 @@ import { ref, computed, onMounted } from 'vue';
*/
export function useTranslation() {
const isTranslationReady = ref(false);
const currentLanguage = ref('nl');
const currentLanguage = ref('en');
const isTranslating = ref(false);
const lastError = ref(null);
@@ -139,7 +139,7 @@ export function useTranslation() {
* Get current language from chatConfig or fallback
*/
const getCurrentLanguage = () => {
return window.chatConfig?.language || currentLanguage.value || 'nl';
return window.chatConfig?.language || currentLanguage.value || 'en';
};
/**
@@ -215,6 +215,8 @@ export function useConstantsTranslation() {
CONSTANTS_CACHE.translations = translated;
console.log('useConstantsTranslation: Successfully translated and cached constants');
console.log('useConstantsTranslation: Current language:', CONSTANTS_CACHE.currentLanguage);
console.log('useConstantsTranslation: Cached translations:', CONSTANTS_CACHE.translations);
return translated;
} catch (error) {
console.error('useConstantsTranslation: Error translating constants:', error);

View File

@@ -1,238 +0,0 @@
// eveai_chat_client/static/assets/js/composables/useTranslation.js
import { ref, computed, onMounted } from 'vue';
/**
* Vue 3 Composable for translation management
* Provides modern alternative to window.TranslationClient
*/
export function useTranslation() {
const isTranslationReady = ref(false);
const currentLanguage = ref('nl');
const isTranslating = ref(false);
const lastError = ref(null);
// Check if translation system is available with retry mechanism
const checkTranslationReady = () => {
if (window.TranslationClient && typeof window.TranslationClient.translate === 'function') {
isTranslationReady.value = true;
return true;
}
return false;
};
onMounted(() => {
// Initial check
if (checkTranslationReady()) {
return;
}
// Retry mechanism - wait for TranslationClient to become available
let retryCount = 0;
const maxRetries = 10;
const retryInterval = 100; // 100ms
const retryCheck = () => {
if (checkTranslationReady()) {
return; // Success!
}
retryCount++;
if (retryCount < maxRetries) {
setTimeout(retryCheck, retryInterval);
} else {
console.warn('TranslationClient is not available after retries');
isTranslationReady.value = false;
}
};
// Start retry process
setTimeout(retryCheck, retryInterval);
});
/**
* Translate text to target language
* @param {string} text - Text to translate
* @param {string} targetLang - Target language code
* @param {string|null} sourceLang - Source language code (optional)
* @param {string|null} context - Translation context (optional)
* @param {string} apiPrefix - API prefix for tenant routing
* @returns {Promise<object>} Translation result
*/
const translate = async (text, targetLang, sourceLang = null, context = null, apiPrefix = '') => {
if (!isTranslationReady.value || !window.TranslationClient) {
const error = new Error('Translation system not ready');
lastError.value = error;
throw error;
}
if (!text || !text.trim()) {
const error = new Error('No text provided for translation');
lastError.value = error;
throw error;
}
isTranslating.value = true;
lastError.value = null;
try {
const result = await window.TranslationClient.translate(
text,
targetLang,
sourceLang,
context,
apiPrefix
);
// Update current language if translation was successful
if (result.success) {
currentLanguage.value = targetLang;
}
return result;
} catch (error) {
console.error('Translation error in composable:', error);
lastError.value = error;
throw error;
} finally {
isTranslating.value = false;
}
};
/**
* Translate text with automatic error handling and loading state
* @param {string} text - Text to translate
* @param {string} targetLang - Target language code
* @param {Object} options - Translation options
* @returns {Promise<string|null>} Translated text or null on error
*/
const translateSafe = async (text, targetLang, options = {}) => {
const {
sourceLang = null,
context = null,
apiPrefix = '',
fallbackText = text
} = options;
try {
const result = await translate(text, targetLang, sourceLang, context, apiPrefix);
return result.success ? result.translated_text : fallbackText;
} catch (error) {
console.warn('Safe translation failed, using fallback:', error.message);
return fallbackText;
}
};
/**
* Batch translate multiple texts
* @param {Array<string>} texts - Array of texts to translate
* @param {string} targetLang - Target language code
* @param {Object} options - Translation options
* @returns {Promise<Array<string>>} Array of translated texts
*/
const translateBatch = async (texts, targetLang, options = {}) => {
const results = await Promise.allSettled(
texts.map(text => translateSafe(text, targetLang, options))
);
return results.map((result, index) =>
result.status === 'fulfilled' ? result.value : texts[index]
);
};
/**
* Get current language from chatConfig or fallback
*/
const getCurrentLanguage = () => {
return window.chatConfig?.language || currentLanguage.value;
};
/**
* Get API prefix from chatConfig or fallback
*/
const getApiPrefix = () => {
return window.chatConfig?.apiPrefix || '';
};
return {
// State
isTranslationReady,
currentLanguage: computed(() => getCurrentLanguage()),
isTranslating,
lastError,
// Methods
translate,
translateSafe,
translateBatch,
// Utilities
getCurrentLanguage,
getApiPrefix
};
}
/**
* Simplified composable for basic translation needs
* Use this when you only need simple text translation
*/
export function useTranslationClient() {
const { translate, translateSafe, isTranslationReady, isTranslating, lastError } = useTranslation();
return {
translate,
translateSafe,
isTranslationReady,
isTranslating,
lastError
};
}
/**
* Composable for reactive text translation
* Automatically translates text when language changes
*/
export function useReactiveTranslation(text, options = {}) {
const { translateSafe, currentLanguage } = useTranslation();
const translatedText = ref(text);
const isLoading = ref(false);
const {
context = null,
sourceLang = null,
autoTranslate = true
} = options;
// Watch for language changes and auto-translate
if (autoTranslate) {
// We'll implement this when we have proper reactivity setup
// For now, provide manual translation method
}
const updateTranslation = async (newLanguage = null) => {
const targetLang = newLanguage || currentLanguage.value;
if (!text || targetLang === sourceLang) {
translatedText.value = text;
return;
}
isLoading.value = true;
try {
const result = await translateSafe(text, targetLang, {
sourceLang,
context,
apiPrefix: window.chatConfig?.apiPrefix || ''
});
translatedText.value = result;
} finally {
isLoading.value = false;
}
};
return {
translatedText,
isLoading,
updateTranslation
};
}

View File

@@ -0,0 +1,181 @@
// 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];
});
};
return {
// State
currentLanguage: computed(() => currentLanguage.value),
isTranslating: computed(() => isTranslating.value),
translationError: computed(() => translationError.value),
// Methods
registerComponent,
setLanguage,
getComponentTranslations,
clearAllCaches,
// Computed
componentTranslations: computed(() => componentTranslations)
};
}
/**
* 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
};
}

View File

@@ -43,6 +43,10 @@ import ProgressTracker from './ProgressTracker.vue';
import LanguageSelector from './LanguageSelector.vue';
import ChatInput from './ChatInput.vue';
// Import language provider
import { createLanguageProvider, LANGUAGE_PROVIDER_KEY } from '../js/services/LanguageProvider.js';
import { provide } from 'vue';
export default {
name: 'ChatApp',
components: {
@@ -54,12 +58,28 @@ export default {
ProgressTracker,
ChatInput
},
setup() {
// Haal initiële taal uit chatConfig
const initialLanguage = window.chatConfig?.language || 'nl';
const apiPrefix = window.chatConfig?.apiPrefix || '';
// Creëer language provider
const languageProvider = createLanguageProvider(initialLanguage, apiPrefix);
// Provide aan alle child components
provide(LANGUAGE_PROVIDER_KEY, languageProvider);
return {
languageProvider
};
},
data() {
// Maak een lokale kopie van de chatConfig om undefined errors te voorkomen
const chatConfig = window.chatConfig || {};
const settings = chatConfig.settings || {};
const initialLanguage = chatConfig.language || 'nl';
const initialLanguage = chatConfig.language || 'en';
const originalExplanation = chatConfig.explanation || '';
const tenantMake = chatConfig.tenantMake || {};
@@ -331,7 +351,7 @@ export default {
// Add a placeholder AI message that will be updated by the progress tracker
const placeholderMessage = {
id: this.messageIdCounter++,
content: 'Bezig met verwerken...',
// content: 'Bezig met verwerken...',
sender: 'ai',
type: 'text',
timestamp: new Date().toISOString(),

View File

@@ -97,7 +97,7 @@
{{ message.content }}
</div>
<button v-if="message.retryable" @click="$emit('retry-message', message.id)" class="retry-btn">
Opnieuw proberen
{{ messageTexts.retry }}
</button>
</div>
</template>
@@ -116,6 +116,7 @@
import DynamicForm from './DynamicForm.vue';
import ProgressTracker from './ProgressTracker.vue';
import { useIconManager } from '../js/composables/useIconManager.js';
import { useComponentTranslations } from '../js/services/LanguageProvider.js';
export default {
name: 'ChatMessage',
@@ -129,7 +130,25 @@ export default {
// Watch message.formData.icon for automatic icon loading
watchIcon(() => props.message.formData?.icon);
return {};
// Use component translations from provider (English as base language)
const originalTexts = {
retry: 'Try again',
copy: 'Copy',
timestamp: 'Timestamp',
errorMessage: 'An error occurred while processing your request.'
};
const { translations, isLoading, error, currentLanguage } = useComponentTranslations(
'chat_message',
originalTexts
);
return {
messageTexts: translations,
translationLoading: isLoading,
translationError: error,
currentLanguage
};
},
props: {
message: {
@@ -163,12 +182,10 @@ export default {
}
},
mounted() {
// Luister naar taalwijzigingen
document.addEventListener('language-changed', this.handleLanguageChange);
// Component initialization if needed
},
beforeUnmount() {
// Verwijder event listener bij verwijderen component
document.removeEventListener('language-changed', this.handleLanguageChange);
// Component cleanup if needed
},
computed: {
hasFormData() {
@@ -214,22 +231,17 @@ export default {
return hasActualValues;
},
async handleLanguageChange(event) {
// Controleer of dit het eerste bericht is in een gesprek met maar één bericht
// Dit wordt al afgehandeld door MessageHistory component, dus we hoeven hier niets te doen
// De implementatie hiervan blijft in MessageHistory om dubbele vertaling te voorkomen
},
handleSpecialistError(eventData) {
console.log('ChatMessage received specialist-error event:', eventData);
// Creëer een error message met correcte styling
// Create an error message with correct styling
this.message.type = 'error';
this.message.content = eventData.message || 'Er is een fout opgetreden bij het verwerken van uw verzoek.';
this.message.content = eventData.message || this.messageTexts.errorMessage;
this.message.retryable = true;
this.message.error = true; // Voeg error flag toe voor styling
this.message.error = true; // Add error flag for styling
// Bubble up naar parent component voor verdere afhandeling
// Bubble up to parent component for further handling
this.$emit('specialist-error', {
messageId: this.message.id,
...eventData

View File

@@ -21,12 +21,26 @@
</template>
<script>
import { useLanguageProvider } from '../js/services/LanguageProvider.js';
export default {
name: 'LanguageSelector',
setup() {
// Optionally use provider for reactive current language
try {
const provider = useLanguageProvider();
return {
providerLanguage: provider.currentLanguage
};
} catch {
// Provider not available, use props
return {};
}
},
props: {
initialLanguage: {
type: String,
default: 'nl'
default: 'en'
},
currentLanguage: {
type: String,
@@ -53,11 +67,17 @@ export default {
this.$emit('language-changed', this.selectedLanguage);
// DOM event
const event = new CustomEvent('vue:language-changed', {
const event = new CustomEvent('language-changed', {
detail: { language: this.selectedLanguage }
});
document.dispatchEvent(event);
},
computed: {
// Use provider language if available, otherwise use props
effectiveCurrentLanguage() {
return this.providerLanguage || this.currentLanguage || this.initialLanguage;
}
},
methods: {
getAvailableLanguages() {
const languages = [];
@@ -109,7 +129,7 @@ export default {
this.$emit('language-changed', languageCode);
// DOM event
const event = new CustomEvent('vue:language-changed', {
const event = new CustomEvent('language-changed', {
detail: { language: languageCode }
});
document.dispatchEvent(event);

View File

@@ -53,10 +53,34 @@
</template>
<script>
import { useTranslationClient, useConstantsTranslation } from '../js/composables/useTranslation.js';
import { useComponentTranslations } from '../js/services/LanguageProvider.js';
export default {
name: 'ProgressTracker',
setup() {
// Define original English texts (base language for developers)
const originalTexts = {
error: 'Error while processing',
completed: 'Processing completed',
processing: 'Processing...'
};
// Gebruik component translations
const { translations, isLoading, error, currentLanguage } = useComponentTranslations(
'progress_tracker',
originalTexts
);
console.log('Translations:', translations);
console.log('Current language:', currentLanguage);
return {
statusTexts: translations,
translationLoading: isLoading,
translationError: error,
currentLanguage
};
},
props: {
taskId: {
type: String,
@@ -76,99 +100,56 @@ export default {
connecting: true,
error: null,
progressLines: [],
eventSource: null,
// Vertaalde status teksten
translatedStatusTexts: {
error: 'Error while processing',
completed: 'Processing completed',
processing: 'Processing...'
},
currentLanguage: 'nl'
eventSource: null
};
},
computed: {
isProcessing() {
return !this.isCompleted && !this.hasError && !this.connecting;
},
// Computed properties voor vertaalde status teksten
// Computed properties voor vertaalde status teksten uit provider
errorText() {
return this.translatedStatusTexts.error;
return this.statusTexts.error;
},
completedText() {
return this.translatedStatusTexts.completed;
return this.statusTexts.completed;
},
processingText() {
return this.translatedStatusTexts.processing;
return this.statusTexts.processing;
}
},
created() {
// Create named handler for language changes
this.languageChangeHandler = (event) => {
if (event.detail && event.detail.language) {
console.log('ProgressTracker: Received language change event:', event.detail.language);
// The LanguageProvider will automatically handle the translation update
// We just need to ensure the component is aware of the change
this.handleLanguageChange(event);
}
};
// Listen for language changes
document.addEventListener('language-changed', this.languageChangeHandler);
},
mounted() {
this.connectToProgressStream();
// Setup translation composables
const { translateSafe } = useTranslationClient();
const { translateConstants, getCachedTranslations, getCachedLanguage } = useConstantsTranslation();
this.translateSafe = translateSafe;
this.translateConstants = translateConstants;
this.getCachedTranslations = getCachedTranslations;
this.getCachedLanguage = getCachedLanguage;
// Check if we already have cached translations and apply them
const cachedTranslations = this.getCachedTranslations();
if (cachedTranslations) {
console.log('ProgressTracker: Applying cached translations on mount');
this.translatedStatusTexts = { ...cachedTranslations };
this.currentLanguage = this.getCachedLanguage();
}
// Luister naar taalwijzigingen
this.languageChangeHandler = (event) => {
if (event.detail && event.detail.language) {
this.handleLanguageChange(event.detail.language);
}
};
document.addEventListener('language-changed', this.languageChangeHandler);
},
beforeUnmount() {
this.disconnectEventSource();
// Cleanup language change listener
// Remove language change event listener
if (this.languageChangeHandler) {
document.removeEventListener('language-changed', this.languageChangeHandler);
}
},
methods: {
async handleLanguageChange(newLanguage) {
console.log('ProgressTracker: Language change to', newLanguage);
// Skip if same language
if (this.currentLanguage === newLanguage) {
return;
}
this.currentLanguage = newLanguage;
// Define the original Dutch constants
const originalTexts = {
error: 'Fout bij verwerking',
completed: 'Verwerking voltooid',
processing: 'Bezig met redeneren...'
};
try {
// Use global constants translation with caching
const translatedTexts = await this.translateConstants(originalTexts, newLanguage, {
context: 'progress_tracker',
apiPrefix: this.apiPrefix
});
// Update component state with translated texts
this.translatedStatusTexts = translatedTexts;
console.log('ProgressTracker: Successfully updated status texts for', newLanguage);
} catch (error) {
console.error('ProgressTracker: Error translating status texts:', error);
// Fallback to original Dutch texts
this.translatedStatusTexts = originalTexts;
handleLanguageChange(event) {
if (event.detail && event.detail.language) {
console.log(`ProgressTracker: Language changed to ${event.detail.language}`);
// The LanguageProvider automatically updates translations through reactive system
// Force component update to ensure UI reflects the new translations
this.$forceUpdate();
}
},
@@ -205,7 +186,7 @@ export default {
} catch (error) {
console.error('Failed to create EventSource:', error);
this.error = 'Kan geen verbinding maken met de voortgangsstream.';
this.error = 'Unable to connect to the server SSE stream.';
this.connecting = false;
}
},
@@ -343,7 +324,7 @@ export default {
});
} catch (error) {
console.error('Error parsing specialist error data:', error);
this.error = 'Er is een onbekende fout opgetreden.';
this.error = 'An unknown error occurred while processing your request.';
this.isCompleted = true;
this.hasError = true;
this.connecting = false;
@@ -351,7 +332,7 @@ export default {
// Emit generic error
this.$emit('specialist-error', {
message: 'Er is een onbekende fout opgetreden.',
message: 'An unknown error occurred while processing your request.',
originalError: 'Failed to parse error data',
taskId: this.taskId
});
@@ -360,7 +341,7 @@ export default {
handleError(event) {
console.error('SSE Error event:', event);
this.error = 'Er is een fout opgetreden bij het verwerken van updates.';
this.error = 'An unknown error occurred while connecting to the server. Please try again later.';
this.connecting = false;
// Try to parse error data

View File

@@ -49,7 +49,7 @@ const props = defineProps({
},
initialLanguage: {
type: String,
default: 'nl'
default: 'en'
},
supportedLanguageDetails: {
type: Object,

View File

@@ -17,7 +17,7 @@
<script setup>
import { ref, computed, watch, onMounted } from 'vue';
import { useTranslationClient } from '../js/composables/useTranslation.js';
import { useComponentTranslations } from '../js/services/LanguageProvider.js';
const props = defineProps({
originalText: {
@@ -26,7 +26,7 @@ const props = defineProps({
},
currentLanguage: {
type: String,
default: 'nl'
default: 'en'
},
apiPrefix: {
type: String,
@@ -34,9 +34,17 @@ const props = defineProps({
}
});
const { translateSafe } = useTranslationClient();
const translatedText = ref(props.originalText);
const isLoading = ref(false);
// Use component translations from provider
const originalTexts = computed(() => ({
explanation: props.originalText || ''
}));
const { translations, isLoading, error, currentLanguage } = useComponentTranslations(
'sidebar_explanation',
originalTexts.value
);
const translatedText = computed(() => translations.value.explanation || props.originalText);
// Render markdown content
const renderedExplanation = computed(() => {
@@ -52,41 +60,12 @@ const renderedExplanation = computed(() => {
}
});
// Watch for language changes
watch(() => props.currentLanguage, async (newLanguage) => {
await updateTranslation(newLanguage);
});
// Watch for text changes
watch(() => props.originalText, async () => {
await updateTranslation(props.currentLanguage);
});
const updateTranslation = async (targetLanguage) => {
if (!props.originalText || targetLanguage === 'nl') {
translatedText.value = props.originalText;
return;
}
isLoading.value = true;
try {
const result = await translateSafe(props.originalText, targetLanguage, {
context: 'sidebar_explanation',
apiPrefix: props.apiPrefix,
fallbackText: props.originalText
});
translatedText.value = result;
} catch (error) {
console.warn('Sidebar explanation translation failed:', error);
translatedText.value = props.originalText;
} finally {
isLoading.value = false;
}
};
onMounted(() => {
updateTranslation(props.currentLanguage);
// Watch for text changes to update the provider
watch(() => props.originalText, () => {
// Update original texts when prop changes
originalTexts.value = {
explanation: props.originalText || ''
};
});
</script>

View File

@@ -20,7 +20,7 @@
allowReactions: {{ settings.allow_reactions|default('true')|lower }}
},
apiPrefix: '{{ request.headers.get("X-Forwarded-Prefix", "") }}/chat',
language: '{{ session.magic_link.specialist_args.language|default("nl") }}',
language: '{{ session.magic_link.specialist_args.language|default("en") }}',
supportedLanguageDetails: {{ config.SUPPORTED_LANGUAGE_DETAILS|tojson|safe }},
allowedLanguages: {{ tenant_make.allowed_languages|tojson|safe }},
tenantMake: {

View File

@@ -222,7 +222,7 @@ class SpecialistExecutor(CrewAIBaseSpecialistExecutor):
for criterium, answer in arguments.form_values.items():
for qa in previous_ko_questions:
if qa.title == criterium:
if qa.answer_positive != answer:
if TranslationServices.translate(self.tenant_id, qa.answer_positive, arguments.language) != answer:
evaluation = "negative"
break
if evaluation == "negative":

View File

@@ -13,6 +13,9 @@ import { createApp, version } from 'vue';
import { marked } from 'marked';
import { FormField } from '../../../../../../../../../Users/josako/Library/Application Support/JetBrains/PyCharm2025.1/scratches/old js files/FormField.js';
// Import LanguageProvider for sidebar translation support
import { createLanguageProvider, LANGUAGE_PROVIDER_KEY } from '../../../eveai_chat_client/static/assets/js/services/LanguageProvider.js';
// Vue en andere bibliotheken beschikbaar maken
window.Vue = { createApp, version };
window.marked = marked;
@@ -78,6 +81,12 @@ function initializeSidebar() {
// Mount de component
const app = createApp(SideBar, props);
// Create and provide LanguageProvider for sidebar components
const initialLanguage = window.chatConfig?.language || 'nl';
const apiPrefix = window.chatConfig?.apiPrefix || '';
const languageProvider = createLanguageProvider(initialLanguage, apiPrefix);
app.provide(LANGUAGE_PROVIDER_KEY, languageProvider);
// Error handler
app.config.errorHandler = (err, vm, info) => {
console.error('🚨 [Vue Error in Sidebar]', err);
@@ -87,7 +96,19 @@ function initializeSidebar() {
const mountedApp = app.mount(container);
console.log('✅ Sidebar component successfully mounted');
// Listen to language change events and update the sidebar's language provider
const languageChangeHandler = (event) => {
if (event.detail && event.detail.language) {
console.log('Sidebar: Received language change event:', event.detail.language);
languageProvider.setLanguage(event.detail.language);
}
};
document.addEventListener('language-changed', languageChangeHandler);
// Store the handler for cleanup if needed
mountedApp._languageChangeHandler = languageChangeHandler;
console.log('✅ Sidebar component successfully mounted with LanguageProvider');
return mountedApp;
} catch (error) {
console.error('🚨 [CRITICAL ERROR] Bij initialiseren sidebar:', error);

View File

@@ -0,0 +1,244 @@
#!/usr/bin/env python3
"""
Test script to validate the English base language implementation
Tests component-specific translations and initial language checks
"""
import os
import sys
import subprocess
import time
from pathlib import Path
# Add project root to path
project_root = Path(__file__).parent
sys.path.append(str(project_root))
def test_file_contains(file_path, search_terms, description):
"""Test if a file contains specific terms"""
full_path = project_root / file_path
if not full_path.exists():
print(f"❌ FAIL - {description}: File not found - {file_path}")
return False
try:
content = full_path.read_text(encoding='utf-8')
all_found = all(term in content for term in search_terms)
status = "✅ PASS" if all_found else "❌ FAIL"
print(f"{status} - {description}")
if not all_found:
missing = [term for term in search_terms if term not in content]
print(f" Missing terms: {missing}")
return all_found
except Exception as e:
print(f"❌ FAIL - {description}: Error reading file - {e}")
return False
def test_file_not_contains(file_path, search_terms, description):
"""Test if a file does NOT contain specific terms"""
full_path = project_root / file_path
if not full_path.exists():
print(f"❌ FAIL - {description}: File not found - {file_path}")
return False
try:
content = full_path.read_text(encoding='utf-8')
none_found = not any(term in content for term in search_terms)
status = "✅ PASS" if none_found else "❌ FAIL"
print(f"{status} - {description}")
if not none_found:
found = [term for term in search_terms if term in content]
print(f" Found unwanted terms: {found}")
return none_found
except Exception as e:
print(f"❌ FAIL - {description}: Error reading file - {e}")
return False
def run_tests():
"""Run all tests for the English base language implementation"""
print("🧪 Testing English Base Language Implementation")
print("=" * 60)
tests_passed = 0
total_tests = 0
# Test 1: LanguageProvider uses English as default
total_tests += 1
if test_file_contains("eveai_chat_client/static/assets/js/services/LanguageProvider.js",
["initialLanguage = 'en'", "Central language management system"],
"LanguageProvider uses English as default language"):
tests_passed += 1
# Test 2: LanguageProvider has component-specific translation function
total_tests += 1
if test_file_contains("eveai_chat_client/static/assets/js/services/LanguageProvider.js",
["translateComponentTexts", "component-specific", "targetLanguage === 'en'"],
"LanguageProvider has component-specific translation logic"):
tests_passed += 1
# Test 3: LanguageProvider forces initial translation for non-English languages
total_tests += 1
if test_file_contains("eveai_chat_client/static/assets/js/services/LanguageProvider.js",
["currentLanguage.value !== 'en'", "needs initial translation", "translateComponentTexts(componentName, currentLanguage.value)"],
"LanguageProvider forces initial translation for non-English languages"):
tests_passed += 1
# Test 4: ProgressTracker uses English original texts
total_tests += 1
if test_file_contains("eveai_chat_client/static/assets/vue-components/ProgressTracker.vue",
["Error while processing", "Processing completed", "Processing..."],
"ProgressTracker uses English original texts"):
tests_passed += 1
# Test 5: ProgressTracker does NOT use Dutch texts
total_tests += 1
if test_file_not_contains("eveai_chat_client/static/assets/vue-components/ProgressTracker.vue",
["Fout bij verwerking", "Verwerking voltooid", "Bezig met redeneren"],
"ProgressTracker does not use Dutch texts"):
tests_passed += 1
# Test 6: ChatMessage uses English original texts
total_tests += 1
if test_file_contains("eveai_chat_client/static/assets/vue-components/ChatMessage.vue",
["Try again", "Copy", "Timestamp", "An error occurred while processing"],
"ChatMessage uses English original texts"):
tests_passed += 1
# Test 7: ChatMessage does NOT use Dutch texts
total_tests += 1
if test_file_not_contains("eveai_chat_client/static/assets/vue-components/ChatMessage.vue",
["Opnieuw proberen", "Kopiëren", "Tijdstempel", "Er is een fout opgetreden"],
"ChatMessage does not use Dutch texts"):
tests_passed += 1
# Test 8: SideBarExplanation uses English as default language
total_tests += 1
if test_file_contains("eveai_chat_client/static/assets/vue-components/SideBarExplanation.vue",
["default: 'en'", "Translating..."],
"SideBarExplanation uses English as default language"):
tests_passed += 1
# Test 9: LanguageProvider handles English base language correctly
total_tests += 1
if test_file_contains("eveai_chat_client/static/assets/js/services/LanguageProvider.js",
["For English, use original texts", "no translation needed", "Start with original English texts"],
"LanguageProvider handles English base language correctly"):
tests_passed += 1
# Test 10: LanguageProvider has proper logging for debugging
total_tests += 1
if test_file_contains("eveai_chat_client/static/assets/js/services/LanguageProvider.js",
["console.log", "Registering component", "needs initial translation", "Successfully translated"],
"LanguageProvider has proper logging for debugging"):
tests_passed += 1
print("\n" + "=" * 60)
print(f"🧪 Test Results: {tests_passed}/{total_tests} tests passed")
if tests_passed == total_tests:
print("🎉 All tests passed! English base language implementation looks good.")
print("\n✅ Key improvements implemented:")
print(" - English is now the base language for all components")
print(" - Component-specific caching prevents cross-contamination")
print(" - Initial language translation works at component registration")
print(" - No fallback to Dutch - English is the source language")
print(" - Proper logging for debugging translation issues")
return True
else:
print(f"⚠️ {total_tests - tests_passed} tests failed. Please review the implementation.")
return False
def check_cache_logic():
"""Check the cache logic implementation"""
print("\n🔍 Checking Cache Logic Implementation")
print("=" * 50)
issues_found = 0
# Check if global cache is still being used incorrectly
language_provider_path = project_root / "eveai_chat_client/static/assets/js/services/LanguageProvider.js"
if language_provider_path.exists():
content = language_provider_path.read_text(encoding='utf-8')
# Check for component-specific cache
if 'componentTranslations[componentName]' in content:
print("✅ Component-specific cache implementation found")
else:
print("⚠️ Component-specific cache not properly implemented")
issues_found += 1
# Check for proper English base language handling
if "targetLanguage === 'en'" in content:
print("✅ English base language handling found")
else:
print("⚠️ English base language handling not found")
issues_found += 1
# Check for initial translation logic
if "needs initial translation" in content:
print("✅ Initial translation logic found")
else:
print("⚠️ Initial translation logic not found")
issues_found += 1
if issues_found == 0:
print("✅ Cache logic implementation looks good")
else:
print(f"⚠️ {issues_found} issue(s) found in cache logic")
return issues_found == 0
def check_startup_behavior():
"""Check startup behavior for different language scenarios"""
print("\n🔍 Checking Startup Behavior")
print("=" * 50)
print("📋 Expected startup behavior:")
print(" 1. App starts with chatConfig.language (e.g., 'nl', 'fr', 'de')")
print(" 2. Components register with English original texts")
print(" 3. If initial language ≠ 'en', automatic translation is triggered")
print(" 4. Cache is populated with correct translations for initial language")
print(" 5. No fallback to Dutch - English is always the source")
# Check chat.html for language configuration
chat_html_path = project_root / "eveai_chat_client/templates/chat.html"
if chat_html_path.exists():
content = chat_html_path.read_text(encoding='utf-8')
if "language: '{{ session.magic_link.specialist_args.language|default(" in content:
print("✅ Dynamic language configuration found in chat.html")
if '|default("nl")' in content:
print("⚠️ Default language in chat.html is still 'nl' - consider changing to 'en'")
elif '|default("en")' in content:
print("✅ Default language in chat.html is 'en'")
else:
print("⚠️ Language configuration not found in chat.html")
return True
if __name__ == "__main__":
print("🚀 English Base Language Implementation Test Suite")
print("Testing component-specific translations with English as base language")
print("=" * 70)
# Run main tests
success = run_tests()
# Check cache logic
cache_ok = check_cache_logic()
# Check startup behavior
startup_ok = check_startup_behavior()
if success and cache_ok and startup_ok:
print("\n✅ Implementation validation successful!")
print("🎯 The system now uses English as the base language with proper component-specific caching.")
print("🔧 Initial language translation should work correctly at startup.")
sys.exit(0)
else:
print("\n❌ Implementation validation failed!")
print("Please review and fix the issues before testing in browser.")
sys.exit(1)

View File

@@ -0,0 +1,219 @@
#!/usr/bin/env python3
"""
Test script to validate the Language Provider implementation
Tests the provider/inject pattern for translation management
"""
import os
import sys
import subprocess
import time
from pathlib import Path
# Add project root to path
project_root = Path(__file__).parent
sys.path.append(str(project_root))
def test_file_exists(file_path, description):
"""Test if a file exists"""
full_path = project_root / file_path
exists = full_path.exists()
status = "✅ PASS" if exists else "❌ FAIL"
print(f"{status} - {description}: {file_path}")
return exists
def test_file_contains(file_path, search_terms, description):
"""Test if a file contains specific terms"""
full_path = project_root / file_path
if not full_path.exists():
print(f"❌ FAIL - {description}: File not found - {file_path}")
return False
try:
content = full_path.read_text(encoding='utf-8')
all_found = all(term in content for term in search_terms)
status = "✅ PASS" if all_found else "❌ FAIL"
print(f"{status} - {description}")
if not all_found:
missing = [term for term in search_terms if term not in content]
print(f" Missing terms: {missing}")
return all_found
except Exception as e:
print(f"❌ FAIL - {description}: Error reading file - {e}")
return False
def test_javascript_syntax(file_path, description):
"""Test JavaScript syntax using Node.js if available"""
full_path = project_root / file_path
if not full_path.exists():
print(f"❌ FAIL - {description}: File not found")
return False
try:
# Try to check syntax with node if available
result = subprocess.run(['node', '-c', str(full_path)],
capture_output=True, text=True, timeout=10)
if result.returncode == 0:
print(f"✅ PASS - {description}: JavaScript syntax valid")
return True
else:
print(f"❌ FAIL - {description}: JavaScript syntax error")
print(f" Error: {result.stderr}")
return False
except (subprocess.TimeoutExpired, FileNotFoundError):
print(f"⚠️ SKIP - {description}: Node.js not available for syntax check")
return True # Don't fail if Node.js is not available
def run_tests():
"""Run all tests for the Language Provider implementation"""
print("🧪 Testing Language Provider Implementation")
print("=" * 50)
tests_passed = 0
total_tests = 0
# Test 1: LanguageProvider service exists
total_tests += 1
if test_file_exists("eveai_chat_client/static/assets/js/services/LanguageProvider.js",
"LanguageProvider service file exists"):
tests_passed += 1
# Test 2: LanguageProvider contains required exports
total_tests += 1
if test_file_contains("eveai_chat_client/static/assets/js/services/LanguageProvider.js",
["createLanguageProvider", "useLanguageProvider", "useComponentTranslations", "LANGUAGE_PROVIDER_KEY"],
"LanguageProvider exports required functions"):
tests_passed += 1
# Test 3: ChatApp.vue provides the language provider
total_tests += 1
if test_file_contains("eveai_chat_client/static/assets/vue-components/ChatApp.vue",
["createLanguageProvider", "provide", "LANGUAGE_PROVIDER_KEY"],
"ChatApp.vue provides language provider"):
tests_passed += 1
# Test 4: ProgressTracker uses component translations
total_tests += 1
if test_file_contains("eveai_chat_client/static/assets/vue-components/ProgressTracker.vue",
["useComponentTranslations", "progress_tracker"],
"ProgressTracker uses component translations"):
tests_passed += 1
# Test 5: SideBarExplanation uses component translations
total_tests += 1
if test_file_contains("eveai_chat_client/static/assets/vue-components/SideBarExplanation.vue",
["useComponentTranslations", "sidebar_explanation"],
"SideBarExplanation uses component translations"):
tests_passed += 1
# Test 6: ChatMessage uses component translations
total_tests += 1
if test_file_contains("eveai_chat_client/static/assets/vue-components/ChatMessage.vue",
["useComponentTranslations", "chat_message"],
"ChatMessage uses component translations"):
tests_passed += 1
# Test 7: LanguageSelector optionally uses provider
total_tests += 1
if test_file_contains("eveai_chat_client/static/assets/vue-components/LanguageSelector.vue",
["useLanguageProvider", "providerLanguage", "effectiveCurrentLanguage"],
"LanguageSelector optionally uses provider"):
tests_passed += 1
# Test 8: Check JavaScript syntax of LanguageProvider
total_tests += 1
if test_javascript_syntax("eveai_chat_client/static/assets/js/services/LanguageProvider.js",
"LanguageProvider JavaScript syntax"):
tests_passed += 1
# Test 9: Verify old event-based code is removed from ProgressTracker
total_tests += 1
progress_tracker_path = "eveai_chat_client/static/assets/vue-components/ProgressTracker.vue"
full_path = project_root / progress_tracker_path
if full_path.exists():
content = full_path.read_text(encoding='utf-8')
old_patterns = ["handleLanguageChange", "language-changed", "translateConstants"]
has_old_code = any(pattern in content for pattern in old_patterns)
if not has_old_code:
print("✅ PASS - ProgressTracker: Old event-based translation code removed")
tests_passed += 1
else:
print("❌ FAIL - ProgressTracker: Still contains old event-based translation code")
else:
print("❌ FAIL - ProgressTracker: File not found")
# Test 10: Verify old event-based code is removed from ChatMessage
total_tests += 1
chat_message_path = "eveai_chat_client/static/assets/vue-components/ChatMessage.vue"
full_path = project_root / chat_message_path
if full_path.exists():
content = full_path.read_text(encoding='utf-8')
old_patterns = ["handleLanguageChange", "language-changed"]
has_old_code = any(pattern in content for pattern in old_patterns)
if not has_old_code:
print("✅ PASS - ChatMessage: Old event-based translation code removed")
tests_passed += 1
else:
print("❌ FAIL - ChatMessage: Still contains old event-based translation code")
else:
print("❌ FAIL - ChatMessage: File not found")
print("\n" + "=" * 50)
print(f"🧪 Test Results: {tests_passed}/{total_tests} tests passed")
if tests_passed == total_tests:
print("🎉 All tests passed! Language Provider implementation looks good.")
return True
else:
print(f"⚠️ {total_tests - tests_passed} tests failed. Please review the implementation.")
return False
def check_implementation_completeness():
"""Check if the implementation is complete"""
print("\n🔍 Checking Implementation Completeness")
print("=" * 50)
# Check if all required files exist
required_files = [
"eveai_chat_client/static/assets/js/services/LanguageProvider.js",
"eveai_chat_client/static/assets/vue-components/ChatApp.vue",
"eveai_chat_client/static/assets/vue-components/ProgressTracker.vue",
"eveai_chat_client/static/assets/vue-components/SideBarExplanation.vue",
"eveai_chat_client/static/assets/vue-components/ChatMessage.vue",
"eveai_chat_client/static/assets/vue-components/LanguageSelector.vue"
]
all_exist = True
for file_path in required_files:
full_path = project_root / file_path
if full_path.exists():
print(f"{file_path}")
else:
print(f"{file_path} - MISSING")
all_exist = False
return all_exist
if __name__ == "__main__":
print("🚀 Language Provider Implementation Test Suite")
print("Testing provider/inject pattern for translation management")
print("=" * 60)
# Check completeness first
if not check_implementation_completeness():
print("\n❌ Implementation incomplete. Please ensure all files exist.")
sys.exit(1)
# Run tests
success = run_tests()
if success:
print("\n✅ Implementation validation successful!")
print("The provider/inject pattern should resolve timing issues.")
sys.exit(0)
else:
print("\n❌ Implementation validation failed!")
print("Please review and fix the issues before proceeding.")
sys.exit(1)

View File

@@ -0,0 +1,149 @@
#!/usr/bin/env python3
"""
Test script to validate the Sidebar LanguageProvider fix
Tests that both SideBar and ChatApp have access to LanguageProvider
"""
import os
import sys
import subprocess
import time
from pathlib import Path
# Add project root to path
project_root = Path(__file__).parent
sys.path.append(str(project_root))
def test_file_contains(file_path, search_terms, description):
"""Test if a file contains specific terms"""
full_path = project_root / file_path
if not full_path.exists():
print(f"❌ FAIL - {description}: File not found - {file_path}")
return False
try:
content = full_path.read_text(encoding='utf-8')
all_found = all(term in content for term in search_terms)
status = "✅ PASS" if all_found else "❌ FAIL"
print(f"{status} - {description}")
if not all_found:
missing = [term for term in search_terms if term not in content]
print(f" Missing terms: {missing}")
return all_found
except Exception as e:
print(f"❌ FAIL - {description}: Error reading file - {e}")
return False
def run_tests():
"""Run all tests for the Sidebar LanguageProvider fix"""
print("🧪 Testing Sidebar LanguageProvider Fix")
print("=" * 50)
tests_passed = 0
total_tests = 0
# Test 1: chat-client.js imports LanguageProvider
total_tests += 1
if test_file_contains("nginx/frontend_src/js/chat-client.js",
["import { createLanguageProvider, LANGUAGE_PROVIDER_KEY }"],
"chat-client.js imports LanguageProvider"):
tests_passed += 1
# Test 2: initializeSidebar creates and provides LanguageProvider
total_tests += 1
if test_file_contains("nginx/frontend_src/js/chat-client.js",
["createLanguageProvider(initialLanguage, apiPrefix)", "app.provide(LANGUAGE_PROVIDER_KEY, languageProvider)"],
"initializeSidebar creates and provides LanguageProvider"):
tests_passed += 1
# Test 3: SideBar app listens to language-changed events
total_tests += 1
if test_file_contains("nginx/frontend_src/js/chat-client.js",
["languageChangeHandler", "document.addEventListener('language-changed'", "languageProvider.setLanguage"],
"SideBar app listens to language-changed events"):
tests_passed += 1
# Test 4: SideBarExplanation uses useComponentTranslations
total_tests += 1
if test_file_contains("eveai_chat_client/static/assets/vue-components/SideBarExplanation.vue",
["useComponentTranslations", "sidebar_explanation"],
"SideBarExplanation uses useComponentTranslations"):
tests_passed += 1
# Test 5: ChatApp still provides LanguageProvider
total_tests += 1
if test_file_contains("eveai_chat_client/static/assets/vue-components/ChatApp.vue",
["createLanguageProvider", "provide(LANGUAGE_PROVIDER_KEY"],
"ChatApp still provides LanguageProvider"):
tests_passed += 1
print("\n" + "=" * 50)
print(f"🧪 Test Results: {tests_passed}/{total_tests} tests passed")
if tests_passed == total_tests:
print("🎉 All tests passed! Sidebar LanguageProvider fix looks good.")
print("\n✅ Expected fixes:")
print(" - SideBarExplanation should no longer throw 'useLanguageProvider must be used within a LanguageProvider' error")
print(" - Sidebar explanation text should be visible and translatable")
print(" - Both sidebar and chat translations should work correctly")
return True
else:
print(f"⚠️ {total_tests - tests_passed} tests failed. Please review the implementation.")
return False
def check_potential_issues():
"""Check for potential issues with the implementation"""
print("\n🔍 Checking for Potential Issues")
print("=" * 50)
issues_found = 0
# Check if both apps create separate LanguageProvider instances
chat_client_path = project_root / "nginx/frontend_src/js/chat-client.js"
if chat_client_path.exists():
content = chat_client_path.read_text(encoding='utf-8')
provider_creations = content.count('createLanguageProvider(')
if provider_creations >= 1:
print(f"✅ Found {provider_creations} LanguageProvider creation(s) in chat-client.js")
else:
print("⚠️ No LanguageProvider creation found in chat-client.js")
issues_found += 1
# Check if ChatApp also creates LanguageProvider
chatapp_path = project_root / "eveai_chat_client/static/assets/vue-components/ChatApp.vue"
if chatapp_path.exists():
content = chatapp_path.read_text(encoding='utf-8')
if 'createLanguageProvider(' in content:
print("✅ ChatApp creates LanguageProvider")
else:
print("⚠️ ChatApp doesn't create LanguageProvider")
issues_found += 1
if issues_found == 0:
print("✅ No potential issues detected")
else:
print(f"⚠️ {issues_found} potential issue(s) detected")
return issues_found == 0
if __name__ == "__main__":
print("🚀 Sidebar LanguageProvider Fix Test Suite")
print("Testing fix for 'useLanguageProvider must be used within a LanguageProvider' error")
print("=" * 70)
# Run tests
success = run_tests()
# Check for potential issues
no_issues = check_potential_issues()
if success and no_issues:
print("\n✅ Implementation validation successful!")
print("The sidebar should now have access to LanguageProvider and translations should work.")
sys.exit(0)
else:
print("\n❌ Implementation validation failed!")
print("Please review and fix the issues before testing in browser.")
sys.exit(1)