Auto-translate incoming foreign-language emails
Append an English translation as a draft reply so the team can respond fluently in the original language.
Published Jan 27, 2026
Northwind’s support@ inbox gets the occasional email in a language nobody on
the desk reads. The current routine is to paste it into a translation site,
read the gist, then write a reply — fiddly, and it interrupts whoever picked up
the thread. Worse, the translation is gone the moment the tab is closed, so the
next person to touch the thread starts from scratch.
This script does the lookup once, up front. It scans support@ for unread
non-English mail, detects the language, translates the body into English, and
attaches that translation as a draft reply on the thread. The draft is an
internal note for context — the team still writes the real reply themselves —
and the thread is labelled so it is never translated twice.
What you’ll need
- A Gmail account (or delegated mailbox) where
support@mail carries thesupportlabel. - Permission to add a time-driven trigger and create drafts on those threads.
- Nothing else — the script creates the
support/translatedlabel itself.
The script
// The label applied to incoming support mail.
const SUPPORT_LABEL = 'support';
// The label this script adds once a thread has been translated.
const TRANSLATED_LABEL = 'support/translated';
/**
* Finds unread, non-English support threads, translates the first
* message into English, and attaches it as an internal-only draft reply.
*/
function translateIncoming() {
// 1. Find unread support threads that have not been translated yet.
const query =
'label:' + SUPPORT_LABEL +
' is:unread -label:' + TRANSLATED_LABEL;
const threads = GmailApp.search(query);
if (!threads.length) {
Logger.log('No untranslated support threads — nothing to do.');
return;
}
// 2. Get the "translated" label, creating it on first run.
const translated =
GmailApp.getUserLabelByName(TRANSLATED_LABEL) ||
GmailApp.createLabel(TRANSLATED_LABEL);
let count = 0;
// 3. Handle each thread in turn.
for (const t of threads) {
const msg = t.getMessages()[0];
const body = msg.getPlainBody();
// 4. Detect the language; skip anything already in English.
const lang = LanguageApp.detect(body);
if (!lang || lang === 'en') {
t.addLabel(translated); // mark it so we don't re-check next run
continue;
}
// 5. Translate to English and attach it as an internal draft note.
const english = LanguageApp.translate(body, lang, 'en');
t.createDraftReply(
'[Internal note — auto-translated from ' + lang + ']\n\n' + english
);
// 6. Label the thread so it is not translated again.
t.addLabel(translated);
count++;
}
Logger.log('Translated ' + count + ' thread(s).');
}
How it works
translateIncomingsearches Gmail for threads that carry thesupportlabel, are unread, and do not carrysupport/translated— so each thread is only ever handled once.- It looks up the
support/translatedlabel, creating it the first time the script runs. - For each thread it reads the plain-text body of the first message — plain text keeps the translation clean of HTML markup.
- It runs
LanguageApp.detecton the body. If the language comes back as English (or cannot be detected), it labels the thread and skips it, so an English email is not pointlessly re-checked every run. - For genuinely foreign mail it calls
LanguageApp.translateto English and creates a draft reply on the thread, prefixed with a clear[Internal note]marker so nobody mistakes it for an outgoing message. - It adds the
support/translatedlabel, taking the thread out of scope for future runs.
Example run
An email lands in support@, labelled support, unread:
Subject: Problema con la factura Hola, no he recibido la factura del mes pasado. ¿Pueden reenviarla?
After the next run, the thread has a draft reply waiting:
[Internal note — auto-translated from es]
Hello, I have not received last month’s invoice. Can you resend it?
Whoever picks up the thread reads the draft for context, writes the real reply
in Spanish, and deletes or overwrites the note. The thread now carries
support/translated and is skipped from then on.
Trigger it
Run this on a time-driven trigger so translations are ready before anyone opens the inbox:
- In the Apps Script editor, open Triggers (the clock icon).
- Click Add Trigger.
- Choose
translateIncoming, event source Time-driven, type Minutes timer, and Every 15 minutes. - Save and approve the authorisation prompt.
Watch out for
LanguageApp.translateis fine for understanding an incoming message but rough for outgoing copy. Pair it with Auto-label and route emails by language so the actual reply comes from a native speaker.- The draft note is internal context only. Make sure the team knows to delete or
replace it before sending — a stray
[Internal note]block in a customer reply is embarrassing. - Only the first message of each thread is translated. A long back-and-forth
where later replies switch language will not get a fresh translation, because
the thread is already labelled
support/translated. LanguageApp.detectworks on the whole body, so a message mixing languages — or one with a long English signature under a short foreign request — can be misdetected. Plain-text bodies and a little quoted-text trimming help.- Both
LanguageAppand Gmail draft creation are subject to daily quotas. A normal support volume is well within them; a sudden flood of mail is not.
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