Languages
Two questions matter for every outbound guest message:
- What language does the guest speak? — the detector
- What language do we reply in? — the outbound decision
Both are codified in one place in the codebase: server/services/operations/_helpers.ts (functions detectGuestLanguage, pickOutboundLanguage, resolveOutboundLanguageForReservation). The full reference doc is at docs/language-resolution.md. This page is the operator-facing summary.
Detecting the guest's language
In priority order (top wins):
| # | Signal | Source |
|---|---|---|
| 0 | Operator override | Auto (XX) dropdown on the reservation → reservations.languageOverride |
| 1 | Most recent inbound message's detected language | messages.detectedLanguage (LLM-tagged at arrival) |
| 2 | Guest country | Country → language map (SK→sk, BG→bg, UA→uk, RU→ru, …) |
| 3 | Phone-number prefix | +421→SK, +49→DE, +33→FR, … then country → language |
| 4 | NULL | Caller decides what to fall back to |
What's NOT a signal (intentionally)
- The host's recent reply language — REMOVED. The host writes in whatever language the operator chose; that shouldn't speak to what the guest speaks.
rawJson.guest.languagefrom Booking.com directly — REMOVED. Booking.com reports the guest's UI language, not their actual spoken language. Country is the stronger signal.- Voting across many guest messages — REMOVED. Most-recent inbound wins.
These exclusions were learned from real incidents on 2026-05-08 (the "Marina Russian-bug" and the "Martina 1-EN-message bug").
Picking the outbound language
Inputs:
guestLang— output of the detectorteammateCommLang— the responsible teammate'slanguagecolumn (e.g. Peter hasen)noTranslateLanguages— the teammate'snoTranslateLanguagesJSON array (e.g.["sk","cs"])
Rule (one sentence):
If the guest's language matches the teammate's comm language OR is in the teammate's
noTranslateLanguageslist → write in the guest's language. Otherwise → write in the teammate's comm language.
Worked examples (Peter as operator: teammateCommLang=en, noTranslateLanguages=['sk','cs'])
| Guest language | Reply in |
|---|---|
en |
en (matches Peter's comm language) |
sk |
sk (in noTranslate list) |
cs |
cs (in noTranslate list) |
de |
en (Peter's comm language) |
ru |
en (Peter's comm language) |
null |
en (falls back to teammate's comm language) |
Settings sources
- Per teammate —
teammates.language(string) +teammates.noTranslateLanguages(JSON array). Edit in Team & Account → Teammates. - Tenant default (when no teammate is assigned to the reservation) —
tenant_default_target_language+tenant_no_translate_languagesrows inconfigurations. Edit in Settings → Tenant Configuration.
Operator override
If you want to force a specific language for a guest — e.g. they've explicitly asked for English even though they're from Bulgaria — set the Auto (XX) → en override on the reservation. This becomes priority #0 in the detector and beats every other signal.
Related issues
- #156 — synthetic system messages no longer pollute language detection.
- #182 — cleaner WA walkthrough mixed-language fix (was sending Slovak scaffolding + cleaner-language labels in one message).