Creating Advanced Multilingual Welcome Message
This guide demonstrates how to create multilingual welcome message using an advanced JavaScript approach with a single configuration. The key innovation is separating button display text from the actual messages sent to the LLM for better user experience and AI responses.
Key Innovation: Separating Display Text from LLM Messages
The advanced approach uses data-key
attributes to separate:
- Button Display Text: What users see (short, localized labels)
- LLM Messages: Detailed questions sent to the AI (comprehensive, also localized)
This allows for concise button labels while sending detailed questions that generate better AI responses.
Step-by-Step Implementation
Step 1: Configure AI Settings and Branding
- Go to AI Settings → Branding
- Upload screenshots of your website
- The system will automatically extract branding information including colors, fonts, and styling
Step 2: Create Web Control Configuration
- Navigate to Web Controls → ai12z-bot
- Create a new configuration
- Under Display, select Theme → Custom
- Run the branding wizard - this will automatically update CSS based on your branding information
Step 3: Configure Welcome Message
- Go to the Info tab
- Under Welcome Message, you'll see three tabs:
- HTML tab
- Styles tab
- Script tab
- Use the wizard to generate suggested questions based on your organization
- The wizard will research your business and suggest relevant buttons
Step 4: Implement Advanced JavaScript Translation
Add JavaScript code that handles both display text and LLM message translations separately.
Complete Example: Kaluani Beach Resort
Here's a complete implementation using the advanced approach with data-key
attributes:
HTML Tab
<div class="my-welcome-panel">
<div class="my-welcome-header">🌴 Welcome to Kaluani Beach Resort!</div>
<div class="my-welcome-message">
How can we make your stay unforgettable?
<div class="my-welcome-sub">Select a question below or ask your own!</div>
</div>
<div class="my-btn-row">
<button class="ai12zbutton" data-key="Check Room Availability">
Check Room Availability
</button>
<button class="ai12zbutton" data-key="Explore Resort Amenities">
Explore Resort Amenities
</button>
<button class="ai12zbutton" data-key="Discover Local Attractions">
Discover Local Attractions
</button>
<button class="ai12zbutton" data-key="View Special Offers">
View Special Offers
</button>
<button class="ai12zbutton" data-key="Book Dining or Spa">
Book Dining or Spa
</button>
<button class="ai12zbutton" data-key="Ask About Cancellation Policy">
Ask About Cancellation Policy
</button>
<button class="ai12zbutton" data-key="Arrange Transportation">
Arrange Transportation
</button>
<button class="ai12zbutton" data-key="What is on the Menu">
What is on the Menu
</button>
<button class="ai12zbutton" data-key="Local Restaurants">
Local Restaurants
</button>
<button class="ai12zbutton" data-key="Movies Playing">
Movies Playing
</button>
</div>
</div>
Styles Tab
.my-welcome-panel {
background: #f0f8ff; /* Light ocean blue background */
border-radius: 18px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05);
padding: 32px 28px 18px 28px;
margin: 16px;
max-width: 520px;
text-align: center;
font-family: inherit;
border: 1px solid #e0f2fe;
}
.my-welcome-header {
font-size: 1.4rem;
font-weight: 600;
margin-bottom: 8px;
color: #0277bd; /* Ocean blue */
letter-spacing: 0.02em;
}
.my-welcome-message {
font-size: 1.08rem;
margin-bottom: 18px;
color: #2a3342;
}
.my-welcome-sub {
font-size: 0.92rem;
color: #7685a6;
margin-top: 2px;
}
.my-btn-row {
display: flex;
flex-direction: column;
gap: 10px;
align-items: stretch;
}
.ai12zbutton {
color: white;
background: #0277bd; /* Resort blue theme */
font-weight: 500;
border: 3px solid #0277bd;
border-radius: 32px;
padding: 12px 18px;
cursor: pointer;
margin: 0;
transition: background 0.18s;
font-size: 1rem;
}
.ai12zbutton:hover {
background: #01579b; /* Darker blue on hover */
border-color: #4fc3f7; /* Light blue border */
}
Script Tab
// Welcome Message (scoped to avoid globals)
;(() => {
function getLayoutNode() {
return ai12zBot.shadowRoot?.querySelector(".layout")
}
// Display text translations (what users see on buttons)
var displayTranslations = {
"Ask me anything": {
es: "Pregúntame lo que quieras",
fr: "Posez-moi une question",
it: "Chiedimi qualsiasi cosa",
},
"🌴 Welcome to Kaluani Beach Resort!": {
fr: "🌴 Bienvenue au Kaluani Beach Resort !",
es: "🌴 ¡Bienvenido al Kaluani Beach Resort!",
it: "🌴 Benvenuto al Kaluani Beach Resort!",
},
"How can we make your stay unforgettable?": {
fr: "Comment pouvons-nous rendre votre séjour inoubliable ?",
es: "¿Cómo podemos hacer que su estadía sea inolvidable?",
it: "Come possiamo rendere il vostro soggiorno indimenticabile?",
},
"Select a question below or ask your own!": {
fr: "Sélectionnez une question ci-dessous ou posez la vôtre !",
es: "Seleccione una pregunta a continuación o haga la suya propia.",
it: "Seleziona una domanda qui sotto o fai la tua!",
},
"Check Room Availability": {
fr: "Vérifier disponibilité",
es: "Verificar disponibilidad",
it: "Verifica disponibilità",
},
"Explore Resort Amenities": {
fr: "Explorer les équipements",
es: "Explorar comodidades",
it: "Esplora servizi",
},
"Discover Local Attractions": {
fr: "Attractions locales",
es: "Atracciones locales",
it: "Attrazioni locali",
},
"View Special Offers": {
fr: "Offres spéciales",
es: "Ofertas especiales",
it: "Offerte speciali",
},
"Book Dining or Spa": {
fr: "Réserver restaurant/spa",
es: "Reservar comedor/spa",
it: "Prenota ristorante/spa",
},
"Ask About Cancellation Policy": {
fr: "Politique d'annulation",
es: "Política de cancelación",
it: "Politica di cancellazione",
},
"Arrange Transportation": {
fr: "Organiser transport",
es: "Organizar transporte",
it: "Organizzare trasporto",
},
"What is on the Menu": {
fr: "Menu disponible",
es: "Qué hay en el menú",
it: "Cosa c'è nel menu",
},
"Local Restaurants": {
fr: "Restaurants locaux",
es: "Restaurantes locales",
it: "Ristoranti locali",
},
"Movies Playing": {
fr: "Films à l'affiche",
es: "Películas en cartelera",
it: "Film in programmazione",
},
}
// LLM message translations (detailed questions sent to AI)
var llmMessages = {
"Check Room Availability": {
en: "What rooms and suites are available right now? Can you show me current availability and rates?",
fr: "Quelles chambres et suites sont disponibles en ce moment ? Pouvez-vous me montrer la disponibilité actuelle et les tarifs ?",
es: "¿Qué habitaciones y suites están disponibles ahora mismo? ¿Puede mostrarme la disponibilidad actual y las tarifas?",
it: "Quali camere e suite sono disponibili in questo momento? Puoi mostrarmi la disponibilità attuale e le tariffe?",
},
"Explore Resort Amenities": {
en: "What amenities and activities does the resort offer? Tell me about pools, fitness, spa, and recreational facilities.",
fr: "Quels équipements et activités le resort propose-t-il ? Parlez-moi des piscines, fitness, spa et installations récréatives.",
es: "¿Qué comodidades y actividades ofrece el resort? Hábleme de las piscinas, gimnasio, spa e instalaciones recreativas.",
it: "Quali servizi e attività offre il resort? Parlami di piscine, fitness, spa e strutture ricreative.",
},
"Discover Local Attractions": {
en: "What local attractions and tours do you recommend? What are the must-see places and activities near the resort?",
fr: "Quelles attractions locales et excursions recommandez-vous ? Quels sont les lieux et activités incontournables près du resort ?",
es: "¿Qué atracciones locales y tours recomienda? ¿Cuáles son los lugares y actividades imperdibles cerca del resort?",
it: "Quali attrazioni locali e tour consigli? Quali sono i luoghi e le attività da non perdere vicino al resort?",
},
"View Special Offers": {
en: "Are there any current promotions or special packages available? What deals can you offer for my stay?",
fr: "Y a-t-il des promotions ou forfaits spéciaux actuellement disponibles ? Quelles offres pouvez-vous proposer pour mon séjour ?",
es: "¿Hay alguna promoción o paquete especial disponible actualmente? ¿Qué ofertas puede ofrecerme para mi estadía?",
it: "Ci sono promozioni o pacchetti speciali attualmente disponibili? Quali offerte puoi offrire per il mio soggiorno?",
},
"Book Dining or Spa": {
en: "Can I book dining or spa reservations? What restaurants and spa services are available, and how do I make reservations?",
fr: "Puis-je réserver un restaurant ou un spa ? Quels restaurants et services de spa sont disponibles, et comment faire des réservations ?",
es: "¿Puedo hacer reservas para cenar o en el spa? ¿Qué restaurantes y servicios de spa están disponibles, y cómo hago reservas?",
it: "Posso prenotare ristorante o spa? Quali ristoranti e servizi spa sono disponibili, e come posso fare prenotazioni?",
},
"Ask About Cancellation Policy": {
en: "What is your cancellation and refund policy? What are the terms and conditions for modifying or canceling my reservation?",
fr: "Quelle est votre politique d'annulation et de remboursement ? Quelles sont les conditions pour modifier ou annuler ma réservation ?",
es: "¿Cuál es su política de cancelación y reembolso? ¿Cuáles son los términos y condiciones para modificar o cancelar mi reserva?",
it: "Qual è la vostra politica di cancellazione e rimborso? Quali sono i termini e le condizioni per modificare o cancellare la mia prenotazione?",
},
"Arrange Transportation": {
en: "How can I arrange airport transfers or transportation? What transportation options are available to and from the resort?",
fr: "Comment puis-je organiser des transferts aéroport ou du transport ? Quelles options de transport sont disponibles vers et depuis le resort ?",
es: "¿Cómo puedo organizar traslados al aeropuerto o transporte? ¿Qué opciones de transporte están disponibles hacia y desde el resort?",
it: "Come posso organizzare trasferimenti aeroportuali o trasporti? Quali opzioni di trasporto sono disponibili da e per il resort?",
},
"What is on the Menu": {
en: "What dining options and menus are available at the resort? Can you show me the restaurant menus and meal options?",
fr: "Quelles options de restauration et menus sont disponibles au resort ? Pouvez-vous me montrer les menus des restaurants et les options de repas ?",
es: "¿Qué opciones gastronómicas y menús están disponibles en el resort? ¿Puede mostrarme los menús de los restaurantes y las opciones de comida?",
it: "Quali opzioni gastronomiche e menu sono disponibili al resort? Puoi mostrarmi i menu dei ristoranti e le opzioni pasto?",
},
"Local Restaurants": {
en: "What local restaurants do you recommend? What are the best dining options outside the resort?",
fr: "Quels restaurants locaux recommandez-vous ? Quelles sont les meilleures options de restauration en dehors du resort ?",
es: "¿Qué restaurantes locales recomienda? ¿Cuáles son las mejores opciones gastronómicas fuera del resort?",
it: "Quali ristoranti locali consigli? Quali sono le migliori opzioni gastronomiche fuori dal resort?",
},
"Movies Playing": {
en: "What entertainment and movies are playing? What evening entertainment options are available at the resort?",
fr: "Quels divertissements et films sont à l'affiche ? Quelles options de divertissement en soirée sont disponibles au resort ?",
es: "¿Qué entretenimiento y películas están en cartelera? ¿Qué opciones de entretenimiento nocturno están disponibles en el resort?",
it: "Che intrattenimento e film ci sono in programmazione? Quali opzioni di intrattenimento serale sono disponibili al resort?",
},
}
var selectedLang = "en" // Default language
// Listen for dropdown change (if using external language selector)
var dropdown = document.getElementById("languageDropdown")
if (dropdown) {
dropdown.addEventListener("change", function () {
selectedLang = this.value
ai12zBot.dataAttributes = { lang: selectedLang }
const node = getLayoutNode()
if (node) translateWelcomePanel(node)
})
}
function translateWelcomePanel(target) {
if (!target) return
const innerContent = target.querySelector(".my-welcome-panel")
if (!innerContent) return
const form = target.querySelector("form")
const field = form?.querySelector("fieldset")
const parentdiv = field?.querySelector(".textarea")
setTimeout(() => {
const textarea = parentdiv?.querySelector("textarea")
const originalText = "Ask me anything"
if (textarea) {
textarea.placeholder =
displayTranslations[originalText.trim()]?.[selectedLang] ||
originalText
}
}, 100)
// Translate header
const header = innerContent.querySelector(".my-welcome-header")
if (header) {
const originalText = header.textContent.trim()
header.textContent =
displayTranslations[originalText]?.[selectedLang] || originalText
}
// Translate main message
const message = innerContent.querySelector(".my-welcome-message")
if (message?.firstChild?.nodeType === Node.TEXT_NODE) {
const originalText = message.firstChild.textContent.trim()
message.firstChild.textContent =
(displayTranslations[originalText]?.[selectedLang] || originalText) +
" "
}
// Translate sub message
const sub = innerContent.querySelector(".my-welcome-sub")
if (sub) {
const originalText = sub.textContent.trim()
sub.textContent =
displayTranslations[originalText]?.[selectedLang] || originalText
}
// Translate buttons and set up click handlers
innerContent.querySelectorAll("button.ai12zbutton").forEach((button) => {
const key = button.getAttribute("data-key")
// Set display text
button.textContent = displayTranslations[key]?.[selectedLang] || key
// Set click handler with translated LLM message
const clickMessage =
llmMessages[key]?.[selectedLang] || llmMessages[key]?.en || key
button.onclick = () => {
ai12zBot.sendMessage(clickMessage)
}
})
}
// Observe layout open state
var observer = new MutationObserver((mutationsList) => {
for (const mutation of mutationsList) {
if (
mutation.type === "attributes" &&
mutation.attributeName === "class"
) {
const target = mutation.target
if (target.classList.contains("is-open")) {
translateWelcomePanel(target)
}
}
}
})
function waitAndObserveLayout() {
var layoutNodeLocal = getLayoutNode()
if (!layoutNodeLocal) {
setTimeout(waitAndObserveLayout, 100)
return
}
observer.observe(layoutNodeLocal, {
attributes: true,
subtree: true,
attributeFilter: ["class"],
})
if (layoutNodeLocal.classList.contains("is-open")) {
translateWelcomePanel(layoutNodeLocal)
}
}
waitAndObserveLayout()
})()
External Web Page with Language Selector
<!doctype html>
<html>
<head>
<meta charset="UTF-8" />
<script
type="module"
src="https://cdn.ai12z.net/pkg/ai12z@latest/dist/esm/library.js"
></script>
<link
rel="stylesheet"
href="https://cdn.ai12z.net/pkg/ai12z@latest/dist/library/library.css"
/>
<style>
label {
font-size: 16px;
margin-right: 10px;
}
select {
padding: 8px 12px;
border: 1px solid #ccc;
border-radius: 5px;
font-size: 14px;
background-color: #fff;
cursor: pointer;
min-width: 200px;
}
select:focus {
outline: none;
border-color: #0277bd;
box-shadow: 0 0 4px rgba(2, 119, 189, 0.5);
}
</style>
</head>
<body>
<label for="languageDropdown">Choose Language:</label>
<select id="languageDropdown">
<option value="en">English</option>
<option value="fr">Français</option>
<option value="es">Español</option>
<option value="it">Italiano</option>
</select>
<script>
const languages = [
{ code: "en", name: "English" },
{ code: "fr", name: "Français" },
{ code: "es", name: "Español" },
{ code: "it", name: "Italiano" },
]
var dropdown = document.getElementById("languageDropdown")
// Populate dropdown
languages.forEach((lang) => {
const option = document.createElement("option")
option.value = lang.code
option.textContent = lang.name
if (!dropdown.querySelector(`option[value="${lang.code}"]`)) {
dropdown.appendChild(option)
}
})
</script>
<ai12z-bot data-key="YOUR_BOT_KEY_HERE"></ai12z-bot>
</body>
</html>
Key Advantages of the Advanced Approach
1. Separation of Concerns
- Display Text: Short, user-friendly button labels
- LLM Messages: Comprehensive, context-rich questions for better AI responses
2. Better AI Responses
- Detailed questions provide more context to the AI
- Results in more accurate and helpful responses
- Maintains user experience with concise button labels
3. Enhanced Scalability
- Single configuration handles multiple languages
- Easy to add new languages by extending translation objects
- Maintainable codebase without configuration proliferation
4. Professional Implementation
- Uses
data-key
attributes for clean HTML structure - Separates translation logic from display logic
- Enables professional translation workflows
Implementation Benefits
For Developers
- Clean Code: Separation of display and functionality
- Maintainable: Single source of truth for translations
- Flexible: Easy to modify messages without changing UI
For Content Managers
- Professional Translation: Can send detailed messages to translation services
- Context-Aware: Translators understand both button labels and full questions
- Quality Control: Better translations lead to better AI responses
For Users
- Concise Interface: Short, clear button labels
- Better Results: Detailed questions yield more helpful AI responses
- Consistent Experience: Seamless language switching
This advanced approach provides the best of both worlds: a clean, professional user interface with powerful backend functionality that maximizes AI effectiveness across multiple languages.
Best Practices
Translation Management
- Consistent Keys: Use the exact text as translation keys
- Fallback Strategy: Always provide English fallback
- Professional Translation: Use professional translation services for customer-facing content
- Context Awareness: Consider cultural context, not just literal translation
Implementation Tips
- Test Thoroughly: Test with different browser language settings
- Handle Edge Cases: Account for regional language variants (en-US vs en-GB)
- Performance: Keep translation objects reasonable in size
- Accessibility: Ensure translations maintain accessibility features
Maintenance
- Version Control: Track translation changes
- Review Process: Implement review process for translation updates
- Analytics: Monitor which languages are most used
- Feedback: Provide way for users to report translation issues
This approach provides a scalable, maintainable solution for multilingual support that grows with your business without exponentially increasing configuration complexity.