Reply to customers in their own language
Build a multilingual Northwind response generator — same support voice, native phrasing.
Published Oct 10, 2025
Northwind sells worldwide, but its support team writes in English. A question in French or Japanese either waits for someone who can answer it, or gets a machine-translated reply that reads like a machine wrote it. Neither is the warm, on-brand response a customer expects.
This script closes the gap. It scans unread support email, detects the language each customer wrote in, and asks Claude to draft a reply in that same language — keeping Northwind’s friendly, concise support voice. The draft lands in the thread for an agent to skim and send. Nobody on the team needs to speak the language; they just need to trust the draft enough to hit send.
What you’ll need
- A Gmail account where support mail carries a
supportlabel — a filter that applies it on arrival is the easiest way. - An Anthropic API key saved as
ANTHROPIC_API_KEYin Script Properties — see Store API keys and secrets securely. - Nothing else — the script creates its own
support/multi-langlabel to mark threads it has already handled.
The script
// Only threads with this label are considered for a draft.
const SUPPORT_LABEL = 'support';
// Applied once a draft exists, so the same thread is never drafted twice.
const DONE_LABEL = 'support/multi-lang';
// Keep replies short — agents skim, customers appreciate brevity.
const MAX_WORDS = 120;
/**
* Finds unread support threads, detects the customer's language, and
* drafts a reply in that language for an agent to review and send.
*/
function multilingualDraft() {
// 1. Pull unread support threads that have not been drafted yet.
const query = `label:${SUPPORT_LABEL} is:unread -label:${DONE_LABEL}`;
const threads = GmailApp.search(query);
if (!threads.length) {
Logger.log('No unread support threads — nothing to draft.');
return;
}
// 2. Get (or create) the label that marks a thread as handled.
const tag = GmailApp.getUserLabelByName(DONE_LABEL)
|| GmailApp.createLabel(DONE_LABEL);
for (const t of threads) {
// 3. Read the first message — the customer's original question.
const msg = t.getMessages()[0];
const body = msg.getPlainBody();
// 4. Detect the language; fall back to English if detection fails.
const lang = LanguageApp.detect(body) || 'en';
// 5. Ask Claude for a reply in that language, in Northwind's voice.
const prompt =
`Reply in ${lang} to this Northwind support question. ` +
`Be friendly and concise (≤${MAX_WORDS} words). ` +
`Write only the reply — no greeting line about the language.\n\n` +
body;
const reply = callClaude(prompt, 'claude-sonnet-4-6');
// 6. Attach the draft to the thread and mark it handled.
t.createDraftReply(reply);
t.addLabel(tag);
}
Logger.log('Drafted replies for ' + threads.length + ' thread(s).');
}
/**
* Minimal Anthropic API call. The key lives in Script Properties — it
* is never pasted into the code.
*/
function callClaude(prompt, model = 'claude-haiku-4-5-20251001', maxTokens = 400) {
const key = PropertiesService.getScriptProperties()
.getProperty('ANTHROPIC_API_KEY');
const res = UrlFetchApp.fetch('https://api.anthropic.com/v1/messages', {
method: 'post',
contentType: 'application/json',
headers: { 'x-api-key': key, 'anthropic-version': '2023-06-01' },
payload: JSON.stringify({
model,
max_tokens: maxTokens,
messages: [{ role: 'user', content: prompt }],
}),
muteHttpExceptions: true,
});
return JSON.parse(res.getContentText()).content[0].text.trim();
}
How it works
multilingualDraftsearches Gmail for unread threads labelledsupportthat do not yet carry thesupport/multi-langlabel — the threads still waiting for a reply.- If the search comes back empty, it logs a message and stops — no wasted work.
- It fetches the
support/multi-langlabel, creating it on the first run. - For each thread it reads the first message, the customer’s original question.
LanguageApp.detectreturns the language code —fr,ja,de— and falls back toenif it cannot tell.- It asks Claude Sonnet to draft a reply in that language, keeping it friendly
and under
MAX_WORDS. Sonnet is worth the cost here: phrasing a reply that sounds native, not translated, needs more than a cheap model. createDraftReplyattaches the draft to the thread, and the label marks it handled so the next run skips it.
Example run
Say an unread support thread contains this customer message:
Bonjour, je n’arrive pas à réinitialiser mon mot de passe. Le lien dans l’email ne fonctionne pas. Pouvez-vous m’aider ?
LanguageApp.detect returns fr, and Claude drafts a reply in French:
Bonjour, merci de nous avoir contactés. Le lien de réinitialisation expire après 30 minutes — demandez-en un nouveau depuis la page de connexion et utilisez-le aussitôt. Si le problème persiste, répondez à cet email et nous réinitialiserons votre mot de passe manuellement. Bonne journée !
The draft sits in the thread, and the agent sends it after a quick read — no French required on the team’s side.
Trigger it
Run this on a schedule so drafts are waiting before anyone opens the inbox:
- In the Apps Script editor, open Triggers (the clock icon).
- Add a trigger for
multilingualDraft, Time-driven, every 15 minutes. - Approve the authorisation prompt the first time it runs.
A short interval keeps drafts fresh without hammering the API — the
-label:support/multi-lang filter means each run only touches new threads.
Watch out for
- These are drafts, not sent replies. Always keep an agent in the loop — they catch the rare case where Claude misreads the question or the detected language is wrong.
LanguageApp.detectstruggles with very short messages. A one-line “merci?” may detect as English; the fallback toenkeeps the script running, but the reply language can miss.- The script only reads the first message in the thread. For a long back-and-forth it will not see later context — fine for a fresh question, less so for a reply deep in an existing thread.
- API calls run inside the loop, so a large backlog can hit Apps Script’s six-minute limit. If the inbox is busy, lower the schedule interval so each run has fewer threads to clear.
- Detection and drafting both cost a model call’s worth of latency per thread. For very high volume, batch the language detection or cache replies to common questions instead of drafting every one fresh.
Related
Generate and test email subject lines
A/B test AI-written Northwind subject lines for open rate — outputs ranked by past performance.
Updated Mar 3, 2026
Build retrieval-augmented Q&A over your data
Answer Northwind questions grounded in your own Sheet data — pass relevant rows as context.
Updated Feb 27, 2026
Build an AI weekly-report narrator
Turn Northwind metrics into a written executive summary — numbers in, prose out.
Updated Feb 23, 2026
Build a multi-step AI agent workflow
Chain Claude prompts to complete a Northwind task end to end — research → draft → critique → finalise.
Updated Feb 11, 2026
Adapt marketing copy per region
Localise Northwind tone and references by market with AI — same message, regional flavour.
Updated Jan 30, 2026