- verbeteringen client

- Invoer van een 'constanten' cache op niveau van useTranslation.js, om in de ProgressTracker de boodschappen in de juiste taal te zetten.
This commit is contained in:
Josako
2025-07-21 17:39:52 +02:00
parent f8f941d1e1
commit 0f33beddf4
6 changed files with 379 additions and 5 deletions

View File

@@ -167,6 +167,94 @@ export function useTranslation() {
}; };
} }
// Global cache for constants - shared across all component instances
const CONSTANTS_CACHE = {
currentLanguage: null,
translations: {}
};
/**
* Composable for translating application constants with global caching
* This ensures all component instances share the same cached translations
*/
export function useConstantsTranslation() {
const { translateSafe } = useTranslation();
/**
* Translate constants with global caching
* @param {Object} constants - Object with key-value pairs of constants to translate
* @param {string} targetLang - Target language code
* @param {Object} options - Translation options
* @returns {Promise<Object>} Object with translated constants
*/
const translateConstants = async (constants, targetLang, options = {}) => {
console.log('useConstantsTranslation: translateConstants called', { targetLang, currentCached: CONSTANTS_CACHE.currentLanguage });
// Check if we already have translations for this language
if (CONSTANTS_CACHE.currentLanguage === targetLang && CONSTANTS_CACHE.translations) {
console.log('useConstantsTranslation: Using cached translations for', targetLang);
return { ...CONSTANTS_CACHE.translations };
}
console.log('useConstantsTranslation: Translating constants to', targetLang);
try {
const translated = {};
// Translate each constant
for (const [key, originalText] of Object.entries(constants)) {
translated[key] = await translateSafe(originalText, targetLang, {
context: 'constants',
fallbackText: originalText,
...options
});
}
// Update global cache
CONSTANTS_CACHE.currentLanguage = targetLang;
CONSTANTS_CACHE.translations = translated;
console.log('useConstantsTranslation: Successfully translated and cached constants');
return translated;
} catch (error) {
console.error('useConstantsTranslation: Error translating constants:', error);
// Return original constants as fallback
return { ...constants };
}
};
/**
* Get cached translations for current language
* @returns {Object|null} Cached translations or null if not available
*/
const getCachedTranslations = () => {
return CONSTANTS_CACHE.currentLanguage ? { ...CONSTANTS_CACHE.translations } : null;
};
/**
* Clear the constants cache (useful for testing or language reset)
*/
const clearCache = () => {
CONSTANTS_CACHE.currentLanguage = null;
CONSTANTS_CACHE.translations = {};
console.log('useConstantsTranslation: Cache cleared');
};
/**
* Get current cached language
*/
const getCachedLanguage = () => {
return CONSTANTS_CACHE.currentLanguage;
};
return {
translateConstants,
getCachedTranslations,
clearCache,
getCachedLanguage
};
}
/** /**
* Simplified composable for basic translation needs * Simplified composable for basic translation needs
* Use this when you only need simple text translation * Use this when you only need simple text translation

View File

@@ -243,6 +243,7 @@ export default {
if (eventData.answer) { if (eventData.answer) {
console.log('Updating message content with answer:', eventData.answer); console.log('Updating message content with answer:', eventData.answer);
this.message.content = eventData.answer; this.message.content = eventData.answer;
this.message.originalContent = eventData.answer;
} else { } else {
console.error('No answer in specialist-complete event data'); console.error('No answer in specialist-complete event data');
} }

View File

@@ -145,7 +145,15 @@ export default {
const firstMessage = this.messages[0]; const firstMessage = this.messages[0];
// Controleer of we een originele inhoud hebben om te vertalen // Controleer of we een originele inhoud hebben om te vertalen
if (firstMessage.originalContent) { if (firstMessage.content) {
if (!firstMessage.originalContent) {
firstMessage.originalContent = firstMessage.content;
}
console.log('Originele inhoud van eerste AI bericht:', firstMessage.originalContent);
console.log('Content eerste AI bericht:', firstMessage.content);
console.log('Vertaling van eerste AI bericht naar:', event.detail.language); console.log('Vertaling van eerste AI bericht naar:', event.detail.language);
try { try {

View File

@@ -22,9 +22,9 @@
<span v-else-if="isCompleted" class="status-icon completed progress-icon"></span> <span v-else-if="isCompleted" class="status-icon completed progress-icon"></span>
<!-- Status tekst --> <!-- Status tekst -->
<span v-if="error">Fout bij verwerking</span> <span v-if="error">{{ errorText }}</span>
<span v-else-if="isCompleted">Verwerking voltooid</span> <span v-else-if="isCompleted">{{ completedText }}</span>
<span v-else>Bezig met redeneren...</span> <span v-else>{{ processingText }}</span>
</div> </div>
<div class="progress-toggle"> <div class="progress-toggle">
{{ isExpanded ? '▼' : '◄' }} {{ isExpanded ? '▼' : '◄' }}
@@ -53,6 +53,8 @@
</template> </template>
<script> <script>
import { useTranslationClient, useConstantsTranslation } from '../js/composables/useTranslation.js';
export default { export default {
name: 'ProgressTracker', name: 'ProgressTracker',
props: { props: {
@@ -74,21 +76,102 @@ export default {
connecting: true, connecting: true,
error: null, error: null,
progressLines: [], progressLines: [],
eventSource: null eventSource: null,
// Vertaalde status teksten
translatedStatusTexts: {
error: 'Error while processing',
completed: 'Processing completed',
processing: 'Processing...'
},
currentLanguage: 'nl'
}; };
}, },
computed: { computed: {
isProcessing() { isProcessing() {
return !this.isCompleted && !this.hasError && !this.connecting; return !this.isCompleted && !this.hasError && !this.connecting;
},
// Computed properties voor vertaalde status teksten
errorText() {
return this.translatedStatusTexts.error;
},
completedText() {
return this.translatedStatusTexts.completed;
},
processingText() {
return this.translatedStatusTexts.processing;
} }
}, },
mounted() { mounted() {
this.connectToProgressStream(); 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() { beforeUnmount() {
this.disconnectEventSource(); this.disconnectEventSource();
// Cleanup language change listener
if (this.languageChangeHandler) {
document.removeEventListener('language-changed', this.languageChangeHandler);
}
}, },
methods: { 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;
}
},
connectToProgressStream() { connectToProgressStream() {
if (!this.taskId) { if (!this.taskId) {
console.error('Geen task ID beschikbaar voor progress tracking'); console.error('Geen task ID beschikbaar voor progress tracking');

View File

@@ -188,6 +188,7 @@ class SpecialistExecutor(CrewAIBaseSpecialistExecutor):
"icon": "verified", "icon": "verified",
"fields": fields, "fields": fields,
} }
ko_form = TranslationServices.translate_config(self.tenant_id, ko_form, "fields", arguments.language)
rag_answer = self._check_and_execute_rag(arguments, formatted_context, citations) rag_answer = self._check_and_execute_rag(arguments, formatted_context, citations)
if rag_answer: if rag_answer:

View File

@@ -0,0 +1,193 @@
#!/usr/bin/env python3
"""
Test script to verify the translation implementation in eveai_chat_client.
This script checks:
1. ProgressTracker.vue has proper translation setup
2. MessageHistory.vue has correct historical message exclusion logic
3. Translation composable is properly imported
4. Caching mechanism is implemented
"""
import os
import re
import sys
def check_file_exists(filepath):
"""Check if file exists and return True/False"""
return os.path.exists(filepath)
def read_file_content(filepath):
"""Read file content safely"""
try:
with open(filepath, 'r', encoding='utf-8') as f:
return f.read()
except Exception as e:
print(f"Error reading {filepath}: {e}")
return None
def test_progress_tracker_implementation():
"""Test ProgressTracker.vue implementation"""
print("🔍 Testing ProgressTracker.vue implementation...")
filepath = "/Volumes/OWC4M2_1/Development/Josako/EveAI/TBD/eveai_chat_client/static/assets/vue-components/ProgressTracker.vue"
if not check_file_exists(filepath):
print("❌ ProgressTracker.vue not found")
return False
content = read_file_content(filepath)
if not content:
return False
checks = [
("Translation composable import", r"import.*useTranslationClient.*from.*useTranslation"),
("Cache object", r"const statusTextCache = \{\}"),
("Translated status texts data", r"translatedStatusTexts:\s*\{"),
("Error text computed property", r"errorText\(\)\s*\{"),
("Completed text computed property", r"completedText\(\)\s*\{"),
("Processing text computed property", r"processingText\(\)\s*\{"),
("Template uses computed properties", r"\{\{\s*errorText\s*\}\}"),
("Language change handler", r"handleLanguageChange\(newLanguage\)"),
("Cache check logic", r"if \(statusTextCache\[newLanguage\]\)"),
("Backend translation calls", r"await this\.translateSafe"),
("Event listener setup", r"document\.addEventListener\('language-changed'"),
("Event listener cleanup", r"document\.removeEventListener\('language-changed'")
]
passed = 0
for check_name, pattern in checks:
if re.search(pattern, content, re.MULTILINE | re.DOTALL):
print(f"{check_name}")
passed += 1
else:
print(f"{check_name}")
print(f"ProgressTracker.vue: {passed}/{len(checks)} checks passed")
return passed == len(checks)
def test_message_history_implementation():
"""Test MessageHistory.vue implementation"""
print("\n🔍 Testing MessageHistory.vue implementation...")
filepath = "/Volumes/OWC4M2_1/Development/Josako/EveAI/TBD/eveai_chat_client/static/assets/vue-components/MessageHistory.vue"
if not check_file_exists(filepath):
print("❌ MessageHistory.vue not found")
return False
content = read_file_content(filepath)
if not content:
return False
checks = [
("Translation composable import", r"import.*useTranslationClient.*from.*useTranslation"),
("Setup function with translateSafe", r"setup\(\)\s*\{.*translateSafe.*\}"),
("Historical message exclusion", r"if \(this\.messages\.length === 1.*sender === 'ai'\)"),
("Original content check", r"if \(firstMessage\.originalContent\)"),
("Translation with fallback", r"fallbackText:\s*firstMessage\.originalContent"),
("Event listener setup", r"document\.addEventListener\('language-changed'"),
("Event listener cleanup", r"document\.removeEventListener\('language-changed'")
]
passed = 0
for check_name, pattern in checks:
if re.search(pattern, content, re.MULTILINE | re.DOTALL):
print(f"{check_name}")
passed += 1
else:
print(f"{check_name}")
print(f"MessageHistory.vue: {passed}/{len(checks)} checks passed")
return passed == len(checks)
def test_chat_message_original_content():
"""Test ChatMessage.vue original content storage"""
print("\n🔍 Testing ChatMessage.vue original content storage...")
filepath = "/Volumes/OWC4M2_1/Development/Josako/EveAI/TBD/eveai_chat_client/static/assets/vue-components/ChatMessage.vue"
if not check_file_exists(filepath):
print("❌ ChatMessage.vue not found")
return False
content = read_file_content(filepath)
if not content:
return False
checks = [
("Original content storage", r"this\.message\.originalContent = this\.message\.content"),
("AI message check", r"this\.message\.sender === 'ai'"),
("Original content condition", r"!this\.message\.originalContent")
]
passed = 0
for check_name, pattern in checks:
if re.search(pattern, content, re.MULTILINE):
print(f"{check_name}")
passed += 1
else:
print(f"{check_name}")
print(f"ChatMessage.vue: {passed}/{len(checks)} checks passed")
return passed == len(checks)
def test_translation_composable():
"""Test translation composable exists"""
print("\n🔍 Testing translation composable...")
filepath = "/Volumes/OWC4M2_1/Development/Josako/EveAI/TBD/eveai_chat_client/static/assets/js/composables/useTranslation.js"
if not check_file_exists(filepath):
print("❌ useTranslation.js not found")
return False
content = read_file_content(filepath)
if not content:
return False
checks = [
("useTranslationClient export", r"export function useTranslationClient"),
("translateSafe function", r"const translateSafe = async"),
("Backend API call", r"await translate\("),
("Fallback handling", r"fallbackText")
]
passed = 0
for check_name, pattern in checks:
if re.search(pattern, content, re.MULTILINE):
print(f"{check_name}")
passed += 1
else:
print(f"{check_name}")
print(f"useTranslation.js: {passed}/{len(checks)} checks passed")
return passed == len(checks)
def main():
"""Run all tests"""
print("🚀 Starting translation implementation tests...\n")
tests = [
test_progress_tracker_implementation,
test_message_history_implementation,
test_chat_message_original_content,
test_translation_composable
]
results = []
for test in tests:
results.append(test())
print(f"\n📊 Test Results:")
print(f"Passed: {sum(results)}/{len(results)} tests")
if all(results):
print("🎉 All tests passed! Translation implementation is complete.")
return 0
else:
print("❌ Some tests failed. Please review the implementation.")
return 1
if __name__ == "__main__":
sys.exit(main())