Auto-label and route emails by language
Detect the message language and send each thread to the localised support owner.
Published Jan 20, 2026
Northwind’s support inbox is shared, but the team is not. Clients in Berlin email in German, Madrid writes in Spanish, and Paris in French. When every thread lands in one undifferentiated pile, the wrong person opens it first, skims a language they only half-read, and either replies slowly or hands it on. The delay is small per message and large across a week.
This script reads each new support thread, detects the language of the first
message, and applies a lang/... label that matches. A saved Gmail filter or a
quick glance then routes each thread to the teammate who speaks that language —
Hans takes lang/de, María takes lang/es, and English stays with Awadesh. The
inbox sorts itself before anyone opens it.
What you’ll need
- A Gmail account where support mail already carries a
supportlabel, applied by a filter on arrival. - Language labels created up front:
lang/de,lang/es,lang/fr, andlang/en. The script creates any that are missing, but making them yourself lets you assign colours and filters first. - Optional: a Gmail filter per language label that forwards or stars the thread for the right owner.
The script
// The Gmail label your arrival filter applies to support mail.
const SUPPORT_LABEL = 'support';
// Detected ISO language code -> the label to apply.
// Add a row here for any language you want to route.
const ROUTES = {
de: 'lang/de',
es: 'lang/es',
fr: 'lang/fr',
};
// Fallback label for English and anything we can't detect confidently.
const DEFAULT_LABEL = 'lang/en';
// How many characters of the message to sample for detection.
// The opening lines carry enough signal — full bodies waste quota.
const SAMPLE_CHARS = 500;
/**
* Finds unread support threads with no language label yet, detects the
* language of the first message, and applies the matching lang/... label.
*/
function routeByLanguage() {
// 1. Search for support threads that are still unlabelled and unread.
// Excluding every lang/... label keeps already-routed threads out.
const query =
'label:' + SUPPORT_LABEL +
' -label:' + DEFAULT_LABEL +
Object.values(ROUTES).map((l) => ' -label:' + l).join('') +
' is:unread';
const threads = GmailApp.search(query);
if (!threads.length) {
Logger.log('No unrouted support threads — nothing to do.');
return;
}
// 2. Walk each thread and label it by detected language.
for (const thread of threads) {
// Sample the start of the first message — enough for a reliable guess.
const body = thread.getMessages()[0]
.getPlainBody()
.slice(0, SAMPLE_CHARS);
// 3. LanguageApp.detect() returns an ISO code ('en', 'de', 'es', ...).
// Fall back to English if it returns nothing.
const code = LanguageApp.detect(body) || 'en';
// 4. Map the code to a label, defaulting to English for anything
// we don't have a route for.
const labelName = ROUTES[code] || DEFAULT_LABEL;
const label = getOrCreateLabel(labelName);
thread.addLabel(label);
}
Logger.log('Routed ' + threads.length + ' support threads.');
}
/**
* Returns the named Gmail label, creating it if it does not exist yet.
*/
function getOrCreateLabel(name) {
return GmailApp.getUserLabelByName(name) || GmailApp.createLabel(name);
}
How it works
routeByLanguagebuilds a search query for threads that carry thesupportlabel, are still unread, and have nolang/...label yet — so each thread is routed exactly once.- If the search finds nothing, it logs a message and stops, avoiding wasted work on an empty inbox.
- For each thread it reads the first message and takes the opening 500
characters (
SAMPLE_CHARS). Those lines carry enough signal, and trimming keeps theLanguageAppcalls light. LanguageApp.detectreturns an ISO language code. If it returns nothing — common for very short messages — the code falls back toen.- The code is looked up in
ROUTESto get a label name; anything not in the map (including English) falls through toDEFAULT_LABEL. getOrCreateLabelfetches the label, creating it on the fly if it is missing, and the thread is labelled.
Example run
The support inbox holds three unread, unrouted threads:
| Subject | First line | Detected | Label applied |
|---|---|---|---|
| Rechnung Frage | ”Guten Tag, ich habe eine Frage zur…“ | de | lang/de |
| Pedido retrasado | ”Hola, mi pedido no ha llegado…“ | es | lang/es |
| Login issue | ”Hi team, I can’t sign in to…“ | en | lang/en |
After a run, each thread carries a single language label, and Hans, María, and Awadesh each see only the threads they can act on.
Trigger it
Run this on a short timer so threads are sorted within a few minutes of arriving:
- In the Apps Script editor, open Triggers (the clock icon).
- Click Add Trigger.
- Choose
routeByLanguage, event source Time-driven, and a Minutes timer every 5 or 10 minutes.
A 10-minute interval keeps you well inside Gmail’s daily read quota even for a busy inbox.
Watch out for
- One-word or very short emails detect unreliably. The script already defaults
to
lang/enwhen detection fails, which is safer than guessing — but expect the odd terse message to land in the English pile. - Quoted replies and signatures can skew detection. Sampling only the first 500 characters helps, but a German reply to an English thread may still read as mixed; check the first message, not the whole thread.
LanguageAppshares your account’s daily URL Fetch / translation quota. A 5-minute timer over a normal support volume is fine; re-scanning thousands of threads at once is not.- The script labels but does not move or assign threads. Pair each
lang/...label with a Gmail filter if you want the thread starred, forwarded, or filed for its owner automatically.
Related
Convert long email threads into a summary note
Collapse a thread's history into a Doc for handover — perfect for client transitions or vacation cover.
Updated Jun 6, 2026
Pull event RSVPs from emails into a Sheet
Parse yes/no replies to event invites and tally attendance automatically.
Updated Jun 2, 2026
Turn forwarded emails into project tasks
Forward to [email protected] and a row lands in the Projects sheet under the right client.
Updated May 30, 2026
Turn starred emails into a task list
Sync every starred thread into the Northwind Tasks sheet automatically.
Updated May 26, 2026
Alert when a label hits a backlog threshold
Warn the Northwind team in Slack when a Gmail label has more than N unread threads.
Updated Mar 31, 2026