Translate and resolve Doc comments
Localise reviewer feedback on a shared Doc so multilingual teams can collaborate.
Published Jan 25, 2026
Northwind’s London team reviews documents in English. Their Berlin partners read the same Docs but think in German, and the comment thread quickly turns into a mix nobody can follow. Someone ends up copying each comment into a translator and pasting it back as a reply — slow, and always a few comments behind.
This script does that translation pass automatically. It walks every open
comment on a Doc, detects the language, and for anything that is not already
German it posts the German translation as a reply prefixed with [de]. The
original comment stays put, so the English reviewer’s intent is preserved and
the Berlin team gets a version they can act on.
What you’ll need
- A Google Doc with comments to translate, and edit access to it.
- The Drive API advanced service enabled — in the Apps Script editor open
Services, add Drive API, and keep the identifier
Drive. The built-inDriveAppcannot read or write comments; the advanced service can. - Nothing else.
LanguageAppis built in and needs no setup or key.
The script
// The language code reviewers' comments should be translated into.
const TARGET_LANG = 'de';
// The prefix added to every translated reply, so it is easy to spot.
const REPLY_PREFIX = '[de] ';
// The fields to request from the Drive API — keeps the response small.
const COMMENT_FIELDS = 'comments(id,content,resolved,replies)';
/**
* Posts a German translation as a reply to every open comment on a Doc.
*
* @param {string} docId The ID of the Doc whose comments to translate.
*/
function translateDocComments(docId) {
// 1. Bail out early if no Doc was passed in.
if (!docId) {
Logger.log('No docId supplied — nothing to translate.');
return;
}
// 2. Fetch every comment on the Doc, asking only for the fields we use.
const res = Drive.Comments.list(docId, { fields: COMMENT_FIELDS });
const comments = res.comments || [];
if (!comments.length) {
Logger.log('No comments on this Doc — nothing to do.');
return;
}
let translated = 0;
// 3. Walk every comment on the Doc.
for (const c of comments) {
// 4. Skip comments that are already resolved — no need to translate.
if (c.resolved) continue;
// 5. Detect the comment's language; skip anything already German.
const lang = LanguageApp.detect(c.content);
if (lang === TARGET_LANG) continue;
// 6. Skip if a translated reply already exists, so re-runs are safe.
if (hasTranslatedReply(c)) continue;
// 7. Translate the comment and post it back as a reply.
const text = LanguageApp.translate(c.content, lang || 'en', TARGET_LANG);
Drive.Replies.create(
{ content: REPLY_PREFIX + text },
docId,
c.id,
{ fields: 'id' }
);
translated++;
}
Logger.log('Posted ' + translated + ' translated replies.');
}
/**
* Checks whether a comment already has a reply we posted, so the script
* never translates the same comment twice.
*
* @param {Object} comment A comment object from Drive.Comments.list.
* @return {boolean} True if a [de] reply is already present.
*/
function hasTranslatedReply(comment) {
const replies = comment.replies || [];
return replies.some((r) => (r.content || '').startsWith(REPLY_PREFIX));
}
How it works
translateDocCommentschecks it was given adocIdand stops if not, so a missing argument never throws halfway through.- It calls
Drive.Comments.listwith afieldsmask. Asking only forid,content,resolvedandreplieskeeps the response small and fast. - If the Doc has no comments it logs and stops — no point looping over nothing.
- For each comment it skips anything already resolved. Resolved threads are closed conversations and do not need a translation.
LanguageApp.detectreads the comment text and returns a language code. If the comment is already in German the script moves on.hasTranslatedReplyscans the comment’s existing replies for a[de]prefix. This is what makes the script safe to run repeatedly — it never double-translates a comment it has already handled.LanguageApp.translateconverts the text into German, andDrive.Replies.createposts it as a reply on the same thread. The original English comment is never changed.
Example run
Suppose a shared Doc has three open comments from the London team:
| Comment author | Original content |
|---|---|
| Priya | ”This paragraph needs a source.” |
| Tom | ”Can we shorten the intro?” |
| Lena | ”Sieht gut aus.” |
After a run, the Doc’s comment threads look like this:
| Thread | Reply posted |
|---|---|
| ”This paragraph needs a source.” | [de] Dieser Absatz braucht eine Quelle. |
| ”Can we shorten the intro?” | [de] Können wir die Einleitung kürzen? |
| ”Sieht gut aus.” | no reply — already German |
The Berlin team now reads the [de] replies and the conversation stays in one
place.
Run it
Translation is best run on demand, when a review round closes, rather than on a schedule. Add a custom menu so an editor can trigger it from the Doc:
function onOpen() {
DocumentApp.getUi()
.createMenu('Northwind')
.addItem('Translate comments', 'translateActiveDoc')
.addToUi();
}
/**
* Menu wrapper — translates comments on the Doc the user has open.
*/
function translateActiveDoc() {
translateDocComments(DocumentApp.getActiveDocument().getId());
}
The editor opens the Northwind menu, clicks Translate comments, and approves the authorisation prompt the first time.
Watch out for
LanguageApp.detectcan misread very short comments. A two-word comment like “Fix this” may detect as the wrong language; thelang || 'en'fallback only covers an empty result, not a confidently wrong one.- Machine translation is a draft, not a final wording. For contractual or legal
Docs treat the
[de]reply as a starting point a bilingual reviewer checks. - The script never resolves comments itself, despite the title’s promise of “resolve” — resolving a thread is a human decision once both sides agree. Translating it is the part that can safely be automated.
- Drive API comment endpoints are quota-limited. On a Doc with hundreds of
comments, batch the runs or add a short
Utilities.sleepbetweenDrive.Replies.createcalls to stay under the per-minute limit.
Related
Generate personalized study guides from notes
Reformat raw notes into structured study guides — for Northwind's internal training programme.
Updated Feb 8, 2026
Build a contract-clause assembly system
Construct Northwind agreements from a library of approved clauses — drag-drop in code.
Updated Feb 1, 2026
Auto-archive finalized Docs to dated folders
File completed Northwind Docs by month so the active folder stays focused on in-flight work.
Updated Jan 18, 2026
Build a fillable intake form inside a Doc
Create structured intake forms with placeholder fields readers can fill — for client briefs.
Updated Jan 11, 2026
Generate a printable employee handbook
Compile policy sections into one formatted Northwind handbook Doc.
Updated Jan 4, 2026