- TRA-76 - Send Button color changes implemented
- TRA-72 - Translation of privacy statement and T&C - TRA-73 - Strange characters in Tenant Make Name - Addition of meta information in Specialist Form Fields
This commit is contained in:
@@ -1,4 +1,6 @@
|
||||
import json
|
||||
import copy
|
||||
import re
|
||||
from typing import Dict, Any, Optional
|
||||
|
||||
from flask import session
|
||||
@@ -50,8 +52,8 @@ class TranslationServices:
|
||||
if isinstance(config_data, str):
|
||||
config_data = json.loads(config_data)
|
||||
|
||||
# Maak een kopie van de originele data om te wijzigen
|
||||
translated_config = config_data.copy()
|
||||
# Maak een deep copy van de originele data om te wijzigen en input-mutatie te vermijden
|
||||
translated_config = copy.deepcopy(config_data)
|
||||
|
||||
# Haal type en versie op voor de Business Event span
|
||||
config_type = config_data.get('type', 'Unknown')
|
||||
@@ -65,71 +67,124 @@ class TranslationServices:
|
||||
if not context and 'metadata' in config_data and 'description' in config_data['metadata']:
|
||||
description_context = config_data['metadata']['description']
|
||||
|
||||
# Hulpfuncties
|
||||
def is_nonempty_str(val: Any) -> bool:
|
||||
return isinstance(val, str) and val.strip() != ''
|
||||
|
||||
def safe_translate(text: str, ctx: Optional[str]):
|
||||
try:
|
||||
res = cache_manager.translation_cache.get_translation(
|
||||
text=text,
|
||||
target_lang=target_language,
|
||||
source_lang=source_language,
|
||||
context=ctx
|
||||
)
|
||||
return res.translated_text if res else None
|
||||
except Exception as e:
|
||||
if current_event:
|
||||
current_event.log_error('translation_error', {
|
||||
'tenant_id': tenant_id,
|
||||
'config_type': config_type,
|
||||
'config_version': config_version,
|
||||
'field_config': field_config,
|
||||
'error': str(e)
|
||||
})
|
||||
return None
|
||||
|
||||
tag_pair_pattern = re.compile(r'<([a-zA-Z][\w-]*)>[\s\S]*?<\/\1>')
|
||||
|
||||
def extract_tag_counts(text: str) -> Dict[str, int]:
|
||||
counts: Dict[str, int] = {}
|
||||
for m in tag_pair_pattern.finditer(text or ''):
|
||||
tag = m.group(1)
|
||||
counts[tag] = counts.get(tag, 0) + 1
|
||||
return counts
|
||||
|
||||
def tags_valid(source: str, translated: str) -> bool:
|
||||
return extract_tag_counts(source) == extract_tag_counts(translated)
|
||||
|
||||
# Counters
|
||||
meta_consentRich_translated_count = 0
|
||||
meta_aria_translated_count = 0
|
||||
meta_inline_tags_invalid_after_translation_count = 0
|
||||
|
||||
# Loop door elk veld in de configuratie
|
||||
for field_name, field_data in fields.items():
|
||||
# Vertaal name als het bestaat en niet leeg is
|
||||
if 'name' in field_data and field_data['name']:
|
||||
# Gebruik context indien opgegeven, anders description_context
|
||||
# Vertaal name als het bestaat en niet leeg is (alleen strings)
|
||||
if 'name' in field_data and is_nonempty_str(field_data['name']):
|
||||
field_context = context if context else description_context
|
||||
translated_name = cache_manager.translation_cache.get_translation(
|
||||
text=field_data['name'],
|
||||
target_lang=target_language,
|
||||
source_lang=source_language,
|
||||
context=field_context
|
||||
)
|
||||
if translated_name:
|
||||
translated_config[field_config][field_name]['name'] = translated_name.translated_text
|
||||
t = safe_translate(field_data['name'], field_context)
|
||||
if t:
|
||||
translated_config[field_config][field_name]['name'] = t
|
||||
|
||||
if 'title' in field_data and field_data['title']:
|
||||
# Gebruik context indien opgegeven, anders description_context
|
||||
if 'title' in field_data and is_nonempty_str(field_data.get('title')):
|
||||
field_context = context if context else description_context
|
||||
translated_title = cache_manager.translation_cache.get_translation(
|
||||
text=field_data['title'],
|
||||
target_lang=target_language,
|
||||
source_lang=source_language,
|
||||
context=field_context
|
||||
)
|
||||
if translated_title:
|
||||
translated_config[field_config][field_name]['title'] = translated_title.translated_text
|
||||
t = safe_translate(field_data['title'], field_context)
|
||||
if t:
|
||||
translated_config[field_config][field_name]['title'] = t
|
||||
|
||||
# Vertaal description als het bestaat en niet leeg is
|
||||
if 'description' in field_data and field_data['description']:
|
||||
# Gebruik context indien opgegeven, anders description_context
|
||||
if 'description' in field_data and is_nonempty_str(field_data.get('description')):
|
||||
field_context = context if context else description_context
|
||||
translated_desc = cache_manager.translation_cache.get_translation(
|
||||
text=field_data['description'],
|
||||
target_lang=target_language,
|
||||
source_lang=source_language,
|
||||
context=field_context
|
||||
)
|
||||
if translated_desc:
|
||||
translated_config[field_config][field_name]['description'] = translated_desc.translated_text
|
||||
t = safe_translate(field_data['description'], field_context)
|
||||
if t:
|
||||
translated_config[field_config][field_name]['description'] = t
|
||||
|
||||
# Vertaal context als het bestaat en niet leeg is
|
||||
if 'context' in field_data and field_data['context']:
|
||||
translated_ctx = cache_manager.translation_cache.get_translation(
|
||||
text=field_data['context'],
|
||||
target_lang=target_language,
|
||||
source_lang=source_language,
|
||||
context=context
|
||||
)
|
||||
if translated_ctx:
|
||||
translated_config[field_config][field_name]['context'] = translated_ctx.translated_text
|
||||
if 'context' in field_data and is_nonempty_str(field_data.get('context')):
|
||||
t = safe_translate(field_data['context'], context)
|
||||
if t:
|
||||
translated_config[field_config][field_name]['context'] = t
|
||||
|
||||
# vertaal allowed values als het veld bestaat en de waarden niet leeg zijn.
|
||||
if 'allowed_values' in field_data and field_data['allowed_values']:
|
||||
# vertaal allowed_values als het veld bestaat en waarden niet leeg zijn (alleen string-items)
|
||||
if 'allowed_values' in field_data and isinstance(field_data['allowed_values'], list) 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 is_nonempty_str(allowed_value):
|
||||
t = safe_translate(allowed_value, context)
|
||||
translated_allowed_values.append(t if t else allowed_value)
|
||||
else:
|
||||
translated_allowed_values.append(allowed_value)
|
||||
if translated_allowed_values:
|
||||
translated_config[field_config][field_name]['allowed_values'] = translated_allowed_values
|
||||
|
||||
# Vertaal meta.consentRich en meta.aria*
|
||||
meta = field_data.get('meta')
|
||||
if isinstance(meta, dict):
|
||||
# consentRich
|
||||
if is_nonempty_str(meta.get('consentRich')):
|
||||
consent_ctx = (context if context else description_context) or ''
|
||||
consent_ctx = f"Consent rich text with inline tags. Keep tag names intact and translate only inner text. {consent_ctx}".strip()
|
||||
t = safe_translate(meta['consentRich'], consent_ctx)
|
||||
if t and tags_valid(meta['consentRich'], t):
|
||||
translated_config[field_config][field_name].setdefault('meta', {})['consentRich'] = t
|
||||
meta_consentRich_translated_count += 1
|
||||
else:
|
||||
if t and not tags_valid(meta['consentRich'], t) and current_event:
|
||||
src_counts = extract_tag_counts(meta['consentRich'])
|
||||
dst_counts = extract_tag_counts(t)
|
||||
current_event.log_error('inline_tags_validation_failed', {
|
||||
'tenant_id': tenant_id,
|
||||
'config_type': config_type,
|
||||
'config_version': config_version,
|
||||
'field_config': field_config,
|
||||
'field_name': field_name,
|
||||
'target_language': target_language,
|
||||
'source_tag_counts': src_counts,
|
||||
'translated_tag_counts': dst_counts
|
||||
})
|
||||
meta_inline_tags_invalid_after_translation_count += 1
|
||||
# fallback: keep original (already in deep copy)
|
||||
# aria*
|
||||
for k, v in list(meta.items()):
|
||||
if isinstance(k, str) and k.startswith('aria') and is_nonempty_str(v):
|
||||
aria_ctx = (context if context else description_context) or ''
|
||||
aria_ctx = f"ARIA label for accessibility. Short, imperative, descriptive. Form '{config_type} {config_version}', field '{field_name}'. {aria_ctx}".strip()
|
||||
t2 = safe_translate(v, aria_ctx)
|
||||
if t2:
|
||||
translated_config[field_config][field_name].setdefault('meta', {})[k] = t2
|
||||
meta_aria_translated_count += 1
|
||||
|
||||
return translated_config
|
||||
|
||||
@staticmethod
|
||||
|
||||
@@ -9,8 +9,11 @@ content: >
|
||||
|
||||
'{context}'
|
||||
|
||||
Do not translate text in between double square brackets, as these are names or terms that need to remain intact.
|
||||
Remove the triple quotes in your translation!
|
||||
These are best practices you should follow:
|
||||
|
||||
- Do not translate text in between double square brackets, as these are names or terms that need to remain intact. Remove the square brackets in the translation!
|
||||
- We use inline tags (Custom HTML/XML-like tags). Ensure the tags themself are not translated and remain intact in the translation. The text inbetween the tags should be translated. e.g. "<terms_and_conditions>Terms & Conditions</terms_and_conditions>" translates in Dutch to <terms_and_conditions>Gebruiksvoorwaarden</terms_and_conditions>
|
||||
- Remove the triple quotes in your translation!
|
||||
|
||||
I only want you to return the translation. No explanation, no options. I need to be able to directly use your answer
|
||||
without further interpretation. If more than one option is available, present me with the most probable one.
|
||||
|
||||
@@ -6,8 +6,11 @@ content: >
|
||||
|
||||
into '{target_language}'.
|
||||
|
||||
Do not translate text in between double square brackets, as these are names or terms that need to remain intact.
|
||||
Remove the triple quotes in your translation!
|
||||
These are best practices you should follow:
|
||||
|
||||
- Do not translate text in between double square brackets, as these are names or terms that need to remain intact. Remove the square brackets in the translation!
|
||||
- We use inline tags (Custom HTML/XML-like tags). Ensure the tags themself are not translated and remain intact in the translation. The text inbetween the tags should be translated. e.g. "<terms_and_conditions>Terms & Conditions</terms_and_conditions>" translates in Dutch to <terms_and_conditions>Gebruiksvoorwaarden</terms_and_conditions>
|
||||
- Remove the triple quotes in your translation!
|
||||
|
||||
I only want you to return the translation. No explanation, no options. I need to be able to directly use your answer
|
||||
without further interpretation. If more than one option is available, present me with the most probable one.
|
||||
|
||||
@@ -24,6 +24,11 @@ fields:
|
||||
type: "boolean"
|
||||
description: "Consent"
|
||||
required: true
|
||||
meta:
|
||||
kind: "consent"
|
||||
consentRich: "Ik Agree with the <terms>Terms and Conditions</terms> and the <privacy>Privacy Statement</privacy> of Ask Eve AI"
|
||||
ariaPrivacy: "Open privacyverklaring in a modal dialog"
|
||||
ariaTerms: "Open algemene voorwaarden in a modal dialog"
|
||||
metadata:
|
||||
author: "Josako"
|
||||
date_added: "2025-07-29"
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
<template>
|
||||
<span class="consent-rich-text">
|
||||
<template v-for="(node, idx) in nodes" :key="idx">
|
||||
<component
|
||||
v-if="node.type !== 'text'"
|
||||
:is="linkTag"
|
||||
:href="linkTag === 'a' ? '#' : undefined"
|
||||
class="consent-link"
|
||||
:aria-label="node.aria"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
@click.prevent="emitClick(node.type)"
|
||||
@keydown.enter.prevent="emitClick(node.type)"
|
||||
@keydown.space.prevent="emitClick(node.type)"
|
||||
>{{ node.label }}</component>
|
||||
<span v-else>{{ node.text }}</span>
|
||||
</template>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'ConsentRichText',
|
||||
props: {
|
||||
template: { type: String, required: true },
|
||||
asButton: { type: Boolean, default: false },
|
||||
ariaPrivacy: { type: String, default: 'Open privacy statement in a dialog' },
|
||||
ariaTerms: { type: String, default: 'Open terms and conditions in a dialog' }
|
||||
},
|
||||
emits: ['open-privacy', 'open-terms'],
|
||||
computed: {
|
||||
linkTag() {
|
||||
return this.asButton ? 'button' : 'a';
|
||||
},
|
||||
nodes() {
|
||||
// Parse only allowed tags <privacy>...</privacy> and <terms>...</terms>
|
||||
const source = (this.template || '');
|
||||
|
||||
// 2) parse only allowed tags <privacy>...</privacy> and <terms>...</terms>
|
||||
const pattern = /<(privacy|terms)>([\s\S]*?)<\/\1>/gi;
|
||||
const out = [];
|
||||
let lastIndex = 0;
|
||||
let match;
|
||||
while ((match = pattern.exec(source)) !== null) {
|
||||
const [full, tag, label] = match;
|
||||
const start = match.index;
|
||||
if (start > lastIndex) {
|
||||
out.push({ type: 'text', text: source.slice(lastIndex, start) });
|
||||
}
|
||||
out.push({
|
||||
type: tag, // 'privacy' | 'terms'
|
||||
label: (label || '').trim(),
|
||||
aria: tag === 'privacy' ? this.ariaPrivacy : this.ariaTerms
|
||||
});
|
||||
lastIndex = start + full.length;
|
||||
}
|
||||
if (lastIndex < source.length) {
|
||||
out.push({ type: 'text', text: source.slice(lastIndex) });
|
||||
}
|
||||
return out;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
emitClick(kind) {
|
||||
if (kind === 'privacy') this.$emit('open-privacy');
|
||||
if (kind === 'terms') this.$emit('open-terms');
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.consent-link {
|
||||
color: #007bff;
|
||||
text-decoration: underline;
|
||||
cursor: pointer;
|
||||
transition: color 0.2s ease;
|
||||
}
|
||||
.consent-link:hover {
|
||||
color: #0056b3;
|
||||
}
|
||||
.consent-link:focus {
|
||||
outline: 2px solid #007bff;
|
||||
outline-offset: 2px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
button.consent-link {
|
||||
background: none;
|
||||
border: none;
|
||||
padding: 0;
|
||||
color: #007bff;
|
||||
text-decoration: underline;
|
||||
}
|
||||
</style>
|
||||
@@ -104,14 +104,16 @@
|
||||
>
|
||||
<!-- Regular checkbox label -->
|
||||
<span v-if="!isConsentField" class="checkbox-text">{{ field.name }}</span>
|
||||
<!-- Consent field with privacy and terms links -->
|
||||
<span v-else class="checkbox-text consent-text">
|
||||
{{ texts.consentPrefix }}
|
||||
<a href="#" @click="openPrivacyModal" class="consent-link">{{ texts.privacyLink }}</a>
|
||||
{{ texts.consentMiddle }}
|
||||
<a href="#" @click="openTermsModal" class="consent-link">{{ texts.termsLink }}</a>
|
||||
{{ texts.consentSuffix }}
|
||||
</span>
|
||||
<!-- Consent field with privacy and terms links (rich, multilingual) -->
|
||||
<ConsentRichText
|
||||
v-else
|
||||
class="checkbox-text consent-text"
|
||||
:template="texts.consentRich"
|
||||
:aria-privacy="texts.ariaPrivacy || 'Open privacy statement in a dialog'"
|
||||
:aria-terms="texts.ariaTerms || 'Open terms and conditions in a dialog'"
|
||||
@open-privacy="openPrivacyModal"
|
||||
@open-terms="openTermsModal"
|
||||
/>
|
||||
<span v-if="field.required" class="required" style="color: #d93025; margin-left: 2px;">*</span>
|
||||
</label>
|
||||
</div>
|
||||
@@ -180,9 +182,11 @@
|
||||
|
||||
<script>
|
||||
import { useComponentTranslations } from '../js/services/LanguageProvider.js';
|
||||
import ConsentRichText from './ConsentRichText.vue';
|
||||
|
||||
export default {
|
||||
name: 'FormField',
|
||||
components: { ConsentRichText },
|
||||
props: {
|
||||
field: {
|
||||
type: Object,
|
||||
@@ -201,13 +205,11 @@ export default {
|
||||
},
|
||||
emits: ['update:modelValue', 'open-privacy-modal', 'open-terms-modal', 'keydown-enter'],
|
||||
setup() {
|
||||
// Consent text constants (English base)
|
||||
// Consent text constants (English base) - rich template
|
||||
const consentTexts = {
|
||||
consentPrefix: "I agree with the",
|
||||
consentMiddle: "and",
|
||||
consentSuffix: "of AskEveAI",
|
||||
privacyLink: "privacy statement",
|
||||
termsLink: "terms and conditions"
|
||||
consentRich: "I agree with the <privacy>privacy statement</privacy> and the <terms>terms and conditions</terms>",
|
||||
ariaPrivacy: 'Open privacy statement in a dialog',
|
||||
ariaTerms: 'Open terms and conditions in a dialog'
|
||||
};
|
||||
|
||||
try {
|
||||
@@ -230,24 +232,36 @@ export default {
|
||||
},
|
||||
computed: {
|
||||
texts() {
|
||||
// Robust consent texts that always return valid values
|
||||
// Use translated texts if available and valid, otherwise use fallback
|
||||
if (this.translatedTexts && typeof this.translatedTexts === 'object') {
|
||||
const translated = this.translatedTexts;
|
||||
// Check if translated texts have all required properties
|
||||
if (translated.consentPrefix && translated.consentMiddle && translated.consentSuffix &&
|
||||
translated.privacyLink && translated.termsLink) {
|
||||
return translated;
|
||||
}
|
||||
// Validate that consentRich exists and includes both required tags; otherwise fallback to English base
|
||||
const hasValidRich = (t) => t && typeof t.consentRich === 'string'
|
||||
&& /<privacy>[\s\S]*?<\/privacy>/.test(t.consentRich)
|
||||
&& /<terms>[\s\S]*?<\/terms>/.test(t.consentRich);
|
||||
|
||||
// 1) Prefer backend-provided rich string on the field's meta (already localized)
|
||||
const meta = this.field && this.field.meta ? this.field.meta : (this.field.i18n || null);
|
||||
if (meta && typeof meta.consentRich === 'string' && hasValidRich(meta)) {
|
||||
return {
|
||||
consentRich: meta.consentRich,
|
||||
ariaPrivacy: meta.ariaPrivacy || this.fallbackTexts.ariaPrivacy,
|
||||
ariaTerms: meta.ariaTerms || this.fallbackTexts.ariaTerms
|
||||
};
|
||||
}
|
||||
|
||||
// Fallback to English texts
|
||||
return this.fallbackTexts || {
|
||||
consentPrefix: "I agree with the",
|
||||
consentMiddle: "and",
|
||||
consentSuffix: "of AskEveAI",
|
||||
privacyLink: "privacy statement",
|
||||
termsLink: "terms and conditions"
|
||||
// 2) Otherwise, use client-side translated texts if available and valid
|
||||
if (this.translatedTexts && typeof this.translatedTexts === 'object' && hasValidRich(this.translatedTexts)) {
|
||||
return this.translatedTexts;
|
||||
}
|
||||
|
||||
// 3) Fallback to English texts (rich template)
|
||||
if (this.fallbackTexts && hasValidRich(this.fallbackTexts)) {
|
||||
return this.fallbackTexts;
|
||||
}
|
||||
|
||||
// 4) Ultimate fallback (should not happen): provide a safe default
|
||||
return {
|
||||
consentRich: "I agree with the <privacy>privacy statement</privacy> and the <terms>terms and conditions</terms>",
|
||||
ariaPrivacy: 'Open privacy statement in a dialog',
|
||||
ariaTerms: 'Open terms and conditions in a dialog'
|
||||
};
|
||||
},
|
||||
value: {
|
||||
@@ -317,12 +331,10 @@ export default {
|
||||
this.value = file;
|
||||
}
|
||||
},
|
||||
openPrivacyModal(event) {
|
||||
event.preventDefault();
|
||||
openPrivacyModal() {
|
||||
this.$emit('open-privacy-modal');
|
||||
},
|
||||
openTermsModal(event) {
|
||||
event.preventDefault();
|
||||
openTermsModal() {
|
||||
this.$emit('open-terms-modal');
|
||||
},
|
||||
|
||||
|
||||
@@ -9,10 +9,10 @@
|
||||
<script>
|
||||
// Definieer chatConfig voordat componenten worden geladen
|
||||
window.chatConfig = {
|
||||
explanation: `{{ customisation.sidebar_markdown|default('') }}`,
|
||||
progress_tracker_insights: `{{ customisation.progress_tracker_insights|default('No Information') }}`,
|
||||
form_title_display: `{{ customisation.form_title_display|default('Full Title') }}`,
|
||||
conversationId: '{{ conversation_id|default("default") }}',
|
||||
explanation: {{ customisation.sidebar_markdown|default('')|tojson }},
|
||||
progress_tracker_insights: {{ customisation.progress_tracker_insights|default('No Information')|tojson }},
|
||||
form_title_display: {{ customisation.form_title_display|default('Full Title')|tojson }},
|
||||
conversationId: {{ conversation_id|default('default')|tojson }},
|
||||
messages: {{ messages|tojson|safe }},
|
||||
settings: {
|
||||
maxMessageLength: {{ settings.max_message_length|default(2000) }},
|
||||
@@ -22,15 +22,15 @@
|
||||
allowReactions: {{ settings.allow_reactions|default('true')|lower }}
|
||||
},
|
||||
apiPrefix: '/chat-client/chat',
|
||||
language: '{{ session.magic_link.specialist_args.language|default("en") }}',
|
||||
language: {{ session.magic_link.specialist_args.language|default('en')|tojson }},
|
||||
supportedLanguageDetails: {{ config.SUPPORTED_LANGUAGE_DETAILS|tojson|safe }},
|
||||
allowedLanguages: {{ tenant_make.allowed_languages|tojson|safe }},
|
||||
tenantMake: {
|
||||
name: "{{ tenant_make.name|default('EveAI') }}",
|
||||
logo_url: "{{ tenant_make.logo_url|default('') }}"
|
||||
},
|
||||
tenantMake: {{ {
|
||||
'name': tenant_make.name or 'EveAI',
|
||||
'logo_url': tenant_make.logo_url or ''
|
||||
}|tojson|safe }},
|
||||
// Environment-aware static base provided by Flask's overridden url_for
|
||||
staticBase: '{{ static_url }}'
|
||||
staticBase: {{ static_url|tojson }}
|
||||
};
|
||||
|
||||
// Debug info om te controleren of chatConfig correct is ingesteld
|
||||
|
||||
@@ -10,8 +10,8 @@
|
||||
try { globalThis.staticUrl = window.staticUrl; } catch (e) {}
|
||||
} else {
|
||||
// Prefer runtime chatConfig.staticBase; else fallback to server-provided base or default
|
||||
var serverStaticBase = '{{ static_url|default("") }}' || '';
|
||||
if (!serverStaticBase) { serverStaticBase = '{{ url_for("static", filename="") }}'; }
|
||||
var serverStaticBase = {{ static_url|default('')|tojson }} || '';
|
||||
if (!serverStaticBase) { serverStaticBase = {{ url_for('static', filename='')|tojson }}; }
|
||||
var base = (window.chatConfig && window.chatConfig.staticBase) ? window.chatConfig.staticBase : (serverStaticBase || '/static/');
|
||||
var normalizedBase = String(base).replace(/\/+$/, '/');
|
||||
window.staticUrl = function(path) {
|
||||
@@ -34,19 +34,19 @@
|
||||
window.chatConfig.supportedLanguages = [
|
||||
{% for lang_code in config.SUPPORTED_LANGUAGES %}
|
||||
{
|
||||
code: "{{ lang_code }}",
|
||||
name: "{{ config.SUPPORTED_LANGUAGE_DETAILS[config.SUPPORTED_LANGUAGES_FULL[loop.index0]]['iso 639-1'] }}",
|
||||
flag: "{{ config.SUPPORTED_LANGUAGE_DETAILS[config.SUPPORTED_LANGUAGES_FULL[loop.index0]]['flag'] }}"
|
||||
code: {{ lang_code|tojson }},
|
||||
name: {{ config.SUPPORTED_LANGUAGE_DETAILS[config.SUPPORTED_LANGUAGES_FULL[loop.index0]]['iso 639-1']|tojson }},
|
||||
flag: {{ config.SUPPORTED_LANGUAGE_DETAILS[config.SUPPORTED_LANGUAGES_FULL[loop.index0]]['flag']|tojson }}
|
||||
}{% if not loop.last %},{% endif %}
|
||||
{% endfor %}
|
||||
];
|
||||
|
||||
// Voeg tenantMake toe aan chatConfig als die nog niet bestaat
|
||||
if (!window.chatConfig.tenantMake) {
|
||||
window.chatConfig.tenantMake = {
|
||||
name: "{{ tenant_make.name|default('EveAI') }}",
|
||||
logo_url: "{{ tenant_make.logo_url|default('') }}"
|
||||
};
|
||||
window.chatConfig.tenantMake = {{ {
|
||||
'name': tenant_make.name or 'EveAI',
|
||||
'logo_url': tenant_make.logo_url or ''
|
||||
}|tojson|safe }};
|
||||
}
|
||||
|
||||
console.log('Taalinstellingen toegevoegd aan chatConfig');
|
||||
|
||||
Reference in New Issue
Block a user