A French-speaking customer lands on your English chat widget, types “Bonjour, j’ai un problème avec ma facture”, and gets routed to an English-speaking agent who replies in stilted Google Translate French. The customer abandons. Language routing in Freshchat looks like a settings problem. It is actually a detection-with-fallback problem, and the failure modes are subtle.
The three signals you have
Three places to learn the visitor’s language, ranked by reliability:
- Explicit selection — visitor clicked a language toggle on the widget or your site. Highest confidence. Use first.
- Conversation content — what they actually typed. High confidence once you have 5+ words. Requires NLP.
- Browser hints —
Accept-Languageheader, geolocation. Low confidence. Visitors travel, share devices, use VPNs.
The routing pipeline uses all three with descending weight.
The pipeline
1. If visitor has explicit lang preference -> route to that pool
2. Else, if first message > 10 chars -> NLP detect, route to detected pool
3. Else, if Accept-Language header -> route to header lang pool
4. Else -> route to English pool (or your largest pool)
5. Always offer an in-chat language switcher
Step 5 is the safety net. Detection is never 100%. The visitor must be one click away from saying “actually, route me to Spanish”.
NLP detection that does not embarrass you
Out-of-the-box language detection libraries struggle with three-to-five word messages, code-switching (“hi can u help me con mi cuenta”), and named entities (a sentence that is mostly an email address and a product name). Freddy on Freshchat handles this reasonably well but is not infallible.
Defensive rules to layer on top:
- Require minimum 10 characters of natural language before trusting detection. Below that, fall back to the next signal.
- Score detection confidence. If under 0.8, do not auto-route — present a language confirmation prompt.
- Code-switch detection: if the message contains tokens from two languages, route to the most-represented one but tag the conversation
code_switch_observedfor analytics.
async function classifyLanguage(message) {
const stripped = message
.replace(/\S+@\S+\.\S+/g, '')
.replace(/https?:\/\/\S+/g, '')
.replace(/[0-9]+/g, '')
.trim();
if (stripped.length < 10) return { lang: null, confidence: 0, reason: 'too_short' };
const result = await freddyDetect(stripped);
if (result.confidence < 0.8) {
return { lang: result.lang, confidence: result.confidence, reason: 'low_confidence' };
}
return { lang: result.lang, confidence: result.confidence, reason: 'ok' };
}
Pre-cleaning matters. A message that is 60% an email address and 40% Spanish should classify on the Spanish part.
Routing pools
In IntelliAssign, build one routing pool per supported language. Agents tagged with their language proficiencies. A pool labelled lang:fr contains every agent with French fluency at a working level. Some agents are in multiple pools (a bilingual agent is in both lang:fr and lang:en).
Workload weighting matters here. If your French pool has 4 agents and English has 40, a small bias in detection toward English starves the French team. Configure IntelliAssign to balance within the language pool, not across. See freshchat-intelliassign-routing for the workload-weighting mechanics.
The off-hours problem
Your French team works 09:00–18:00 Paris time. A French customer chats at 23:00. What happens?
Three options, ranked:
- Bot triage with deferred handoff — bot collects context, promises follow-up by 10:00 next day, sends email summary. Works if the visitor has email.
- Cross-language coverage with disclaimer — route to the English team with a clear “your usual French team is offline, we can help in English now” message. Honest, often acceptable.
- Machine translation bridge — agent types English, system translates to French in transit. Tempting, frequently terrible, and customers usually notice. Avoid unless you have measured quality on real conversations.
Default to option 1 for B2B. Default to option 2 for B2C consumer apps where immediacy matters more than language match.
Override mechanism
The in-chat language switcher is a small dropdown or set of flag icons in the widget header. Click changes the routing pool for the active conversation. Importantly, click does not start the conversation over — it transfers the existing thread, preserving message history.
Wire the switcher as a postback action on a quick reply or as a custom button in the widget header. When triggered:
{
"action": "transfer_conversation",
"target_pool": "lang:de",
"transfer_message": "Transferring to a German-speaking agent. Please wait a moment.",
"preserve_history": true,
"audit_event": "manual_lang_switch"
}
The audit event matters. Frequent manual switches from the same detected language to a different one is a signal that your detector or routing has a bug. Surface those switches in a weekly report.
Greeting in the right language
The first message the bot or welcome workflow sends should be in the detected language — even before routing. A visitor on a French IP getting an English “Hi! How can we help?” is already feeling unwelcome. Hold the greeting until after step 1–3 above complete.
If detection is unsure, send a bilingual greeting:
Bonjour ! Comment pouvons-nous vous aider ?
Hi! How can we help?
[Continue in: Français | English]
Three lines, both languages, explicit choice. Choose-your-own works because it converts the ambiguity into the visitor’s preference, which is the highest-quality signal anyway.
Analytics on language distribution
A monthly report on conversation language distribution catches drift:
- Total conversations by detected language.
- Manual override rate per language (high = detection problem).
- Off-hours conversation share per language (informs staffing).
- Resolution time variance by language (might reveal an undersized pool).
If lang:pt accounts for 6% of conversations but you have one part-time Portuguese-speaking agent, you have a quality risk that wait times are about to expose.
When to add a new language
Three criteria. Add a language when:
- It accounts for over 3% of detected conversations for two consecutive months.
- Manual override into that language is over 1% of all conversations.
- A geographic market expansion has been announced for the next two quarters.
Below 3% you cannot staff it sustainably, the agent’s skills degrade, and quality suffers. Better to gracefully translate or defer than to half-support.
See freshdesk-multilingual-support-setup for the ticket-side equivalent of the same decision framework.
Bottom line
Language routing is detection plus fallback plus override. Trust explicit selection first, NLP next, headers last, and always offer a one-click switcher. Build per-language pools with workload-aware routing, plan for off-hours, and audit manual overrides. The visitor who feels heard in their language stays. The one who gets translated badly leaves and writes a review.