From 4ad621428e94794e82bea365ed9d92b0750d7a36 Mon Sep 17 00:00:00 2001 From: Josako Date: Mon, 21 Jul 2025 21:45:46 +0200 Subject: [PATCH] - verbeteringen client - Enkel nog probleem met vertaling van de ProgressTracker constanten --- common/services/utils/translation_services.py | 14 + .../assets/js/composables/useTranslation.js | 6 +- .../js/composables/useTranslation.js.backup | 238 ----------------- .../assets/js/services/LanguageProvider.js | 181 +++++++++++++ .../static/assets/vue-components/ChatApp.vue | 24 +- .../assets/vue-components/ChatMessage.vue | 42 +-- .../vue-components/LanguageSelector.vue | 26 +- .../assets/vue-components/ProgressTracker.vue | 129 ++++----- .../static/assets/vue-components/SideBar.vue | 2 +- .../vue-components/SideBarExplanation.vue | 59 ++--- eveai_chat_client/templates/chat.html | 2 +- .../TRAICIE_SELECTION_SPECIALIST/1_4.py | 2 +- nginx/frontend_src/js/chat-client.js | 23 +- test_english_base_language_implementation.py | 244 ++++++++++++++++++ test_language_provider_implementation.py | 219 ++++++++++++++++ test_sidebar_provider_fix.py | 149 +++++++++++ 16 files changed, 982 insertions(+), 378 deletions(-) delete mode 100644 eveai_chat_client/static/assets/js/composables/useTranslation.js.backup create mode 100644 eveai_chat_client/static/assets/js/services/LanguageProvider.js create mode 100644 test_english_base_language_implementation.py create mode 100644 test_language_provider_implementation.py create mode 100644 test_sidebar_provider_fix.py diff --git a/common/services/utils/translation_services.py b/common/services/utils/translation_services.py index cb551a9..4f0e273 100644 --- a/common/services/utils/translation_services.py +++ b/common/services/utils/translation_services.py @@ -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 diff --git a/eveai_chat_client/static/assets/js/composables/useTranslation.js b/eveai_chat_client/static/assets/js/composables/useTranslation.js index 058c9bc..1097ec4 100644 --- a/eveai_chat_client/static/assets/js/composables/useTranslation.js +++ b/eveai_chat_client/static/assets/js/composables/useTranslation.js @@ -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); diff --git a/eveai_chat_client/static/assets/js/composables/useTranslation.js.backup b/eveai_chat_client/static/assets/js/composables/useTranslation.js.backup deleted file mode 100644 index 3ab1839..0000000 --- a/eveai_chat_client/static/assets/js/composables/useTranslation.js.backup +++ /dev/null @@ -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} 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} 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} texts - Array of texts to translate - * @param {string} targetLang - Target language code - * @param {Object} options - Translation options - * @returns {Promise>} 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 - }; -} \ No newline at end of file diff --git a/eveai_chat_client/static/assets/js/services/LanguageProvider.js b/eveai_chat_client/static/assets/js/services/LanguageProvider.js new file mode 100644 index 0000000..ba11e79 --- /dev/null +++ b/eveai_chat_client/static/assets/js/services/LanguageProvider.js @@ -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 + }; +} \ No newline at end of file diff --git a/eveai_chat_client/static/assets/vue-components/ChatApp.vue b/eveai_chat_client/static/assets/vue-components/ChatApp.vue index d5efa59..ef1815d 100644 --- a/eveai_chat_client/static/assets/vue-components/ChatApp.vue +++ b/eveai_chat_client/static/assets/vue-components/ChatApp.vue @@ -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(), diff --git a/eveai_chat_client/static/assets/vue-components/ChatMessage.vue b/eveai_chat_client/static/assets/vue-components/ChatMessage.vue index 35938c9..e08d838 100644 --- a/eveai_chat_client/static/assets/vue-components/ChatMessage.vue +++ b/eveai_chat_client/static/assets/vue-components/ChatMessage.vue @@ -97,7 +97,7 @@ {{ message.content }} @@ -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 diff --git a/eveai_chat_client/static/assets/vue-components/LanguageSelector.vue b/eveai_chat_client/static/assets/vue-components/LanguageSelector.vue index f0126ef..477b01b 100644 --- a/eveai_chat_client/static/assets/vue-components/LanguageSelector.vue +++ b/eveai_chat_client/static/assets/vue-components/LanguageSelector.vue @@ -21,12 +21,26 @@ diff --git a/eveai_chat_client/templates/chat.html b/eveai_chat_client/templates/chat.html index 096d8ab..ca66fc0 100644 --- a/eveai_chat_client/templates/chat.html +++ b/eveai_chat_client/templates/chat.html @@ -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: { diff --git a/eveai_chat_workers/specialists/traicie/TRAICIE_SELECTION_SPECIALIST/1_4.py b/eveai_chat_workers/specialists/traicie/TRAICIE_SELECTION_SPECIALIST/1_4.py index cf33c90..728f40c 100644 --- a/eveai_chat_workers/specialists/traicie/TRAICIE_SELECTION_SPECIALIST/1_4.py +++ b/eveai_chat_workers/specialists/traicie/TRAICIE_SELECTION_SPECIALIST/1_4.py @@ -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": diff --git a/nginx/frontend_src/js/chat-client.js b/nginx/frontend_src/js/chat-client.js index 7f455aa..357f809 100644 --- a/nginx/frontend_src/js/chat-client.js +++ b/nginx/frontend_src/js/chat-client.js @@ -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); diff --git a/test_english_base_language_implementation.py b/test_english_base_language_implementation.py new file mode 100644 index 0000000..b25e361 --- /dev/null +++ b/test_english_base_language_implementation.py @@ -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) \ No newline at end of file diff --git a/test_language_provider_implementation.py b/test_language_provider_implementation.py new file mode 100644 index 0000000..ee2f514 --- /dev/null +++ b/test_language_provider_implementation.py @@ -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) \ No newline at end of file diff --git a/test_sidebar_provider_fix.py b/test_sidebar_provider_fix.py new file mode 100644 index 0000000..2b6bfb4 --- /dev/null +++ b/test_sidebar_provider_fix.py @@ -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) \ No newline at end of file