Guide · Amazon Translate · Document Translation
MCP Server AWS Translate Documents — TranslateDocumentCommand, HTML, Word .docx
TranslateDocumentCommand translates structured documents in-memory without intermediate S3 storage, preserving HTML markup and Word .docx formatting across the translation. Unlike TranslateTextCommand (plain text, 10,000-byte limit), TranslateDocumentCommand accepts text/html, text/plain, and application/vnd.openxmlformats-officedocument.wordprocessingml.document (Word .docx) with a 100KB content limit per call. The key difference from async batch jobs is immediacy: TranslateDocumentCommand is synchronous and returns the translated document bytes in the response body — no S3, no job polling. For MCP tools that receive document uploads, translate them, and return the result immediately, this is the right API. For multi-MB batch jobs, use StartTextTranslationJobCommand instead.
TL;DR
Import TranslateClient and TranslateDocumentCommand from @aws-sdk/client-translate. Send { Document: { Content: uint8Array, ContentType: 'text/html' }, SourceLanguageCode: 'auto', TargetLanguageCode: 'es' }. Read response.TranslatedDocument.Content (a Uint8Array) and decode it back to a string. Size limit is 100KB of document content. HTML tags, attributes, and structure are preserved — only text nodes inside tags are translated. Word .docx formatting (bold, tables, headings) is preserved.
Supported content types
TranslateDocumentCommand accepts three content types. The right choice depends on the input format and whether you need formatting preservation in the output.
| ContentType | Input | Preservation | Size limit |
|---|---|---|---|
text/plain | UTF-8 plain text string | No formatting — whitespace structure only | 100KB |
text/html | Valid HTML markup | HTML tags, attributes, class/id, links preserved; only text nodes translated | 100KB |
application/vnd.openxmlformats-officedocument.wordprocessingml.document | Word .docx binary | Bold/italic/tables/headings/images layout preserved; only run text translated | 100KB |
For text/plain, use TranslateDocumentCommand rather than TranslateTextCommand when your input is between 10KB and 100KB. For inputs under 10KB, TranslateTextCommand is simpler (no Uint8Array encoding needed). For inputs over 100KB, use async batch translation.
Translating an HTML document
Translate preserves all HTML tags and attributes unchanged — only the text content inside elements is translated. This means your CSS classes, hrefs, src attributes, and structural markup survive the translation intact.
import {
TranslateClient,
TranslateDocumentCommand,
type TranslateDocumentCommandInput,
} from '@aws-sdk/client-translate';
const translate = new TranslateClient({ region: process.env.AWS_REGION ?? 'us-east-1' });
export async function translateHtml(
htmlContent: string,
targetLang: string,
sourceLang = 'auto',
): Promise<string> {
const encoder = new TextEncoder();
const decoder = new TextDecoder('utf-8');
const input: TranslateDocumentCommandInput = {
Document: {
Content: encoder.encode(htmlContent), // Uint8Array — raw bytes of the HTML
ContentType: 'text/html',
},
SourceLanguageCode: sourceLang,
TargetLanguageCode: targetLang,
// TerminologyNames: ['my-domain-terms'], // optional custom terminology
// Settings: { Formality: 'FORMAL' }, // optional formality control
};
const response = await translate.send(new TranslateDocumentCommand(input));
if (!response.TranslatedDocument?.Content) {
throw new Error('TranslateDocumentCommand returned empty content');
}
// Content is returned as Uint8Array — decode back to string
return decoder.decode(response.TranslatedDocument.Content);
}
// Example: translate a product description HTML
// const translated = await translateHtml(
// '<h1>MCP Server Monitoring</h1><p>Real-time uptime checks.</p>',
// 'fr',
// );
// → '<h1>Surveillance du serveur MCP</h1><p>Vérifications de disponibilité en temps réel.</p>'
Translate skips the content of some HTML elements by default: <script>, <style>, <code>, and <pre> blocks are not translated — they are passed through unchanged. This is intentional for code blocks (you do not want code examples translated) but may surprise you if you have localization strings inside <script> tags. Move translatable strings out of script blocks before calling the API.
Translating a Word .docx document
Word documents must be provided as raw .docx binary bytes (the OOXML format). Text runs, paragraph formatting, table cell content, and heading levels are preserved; images are retained as binary blobs without translation.
import {
TranslateClient,
TranslateDocumentCommand,
} from '@aws-sdk/client-translate';
import { readFileSync, writeFileSync } from 'fs';
const translate = new TranslateClient({ region: process.env.AWS_REGION ?? 'us-east-1' });
export async function translateDocx(
inputPath: string,
outputPath: string,
targetLang: string,
): Promise<void> {
const docxBytes = readFileSync(inputPath); // Buffer is compatible with Uint8Array
const response = await translate.send(new TranslateDocumentCommand({
Document: {
Content: docxBytes,
ContentType: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
},
SourceLanguageCode: 'auto',
TargetLanguageCode: targetLang,
}));
if (!response.TranslatedDocument?.Content) {
throw new Error('TranslateDocumentCommand returned empty .docx content');
}
// Write translated .docx to disk — it is valid OOXML, openable in Word/LibreOffice
writeFileSync(outputPath, Buffer.from(response.TranslatedDocument.Content));
console.log('Translated .docx written:', {
outputPath,
detectedSourceLang: response.SourceLanguageCode,
translatedTo: response.TargetLanguageCode,
appliedSettings: response.AppliedSettings,
});
}
The translated .docx output is a valid OOXML file that opens normally in Microsoft Word, LibreOffice, and Google Docs. Tables, footnotes, text boxes, and headers/footers are all translated. Comments and tracked changes are not translated — they appear as-is in the output file. Remove tracked changes before submitting to Translate if you want clean output.
Handling the 100KB size limit
The Document.Content limit is 100KB (102,400 bytes). This covers typical short documents — a 15-page report in English is roughly 50-80KB as plain text, less as HTML depending on tag density. For larger files, split on natural boundaries or fall back to async batch.
const TRANSLATE_DOCUMENT_BYTE_LIMIT = 100 * 1024; // 100KB
export async function translateHtmlSafe(
htmlContent: string,
targetLang: string,
): Promise<string> {
const encoder = new TextEncoder();
const bytes = encoder.encode(htmlContent);
if (bytes.length > TRANSLATE_DOCUMENT_BYTE_LIMIT) {
throw new Error(
`Document too large for TranslateDocumentCommand: ${bytes.length} bytes ` +
`(limit: ${TRANSLATE_DOCUMENT_BYTE_LIMIT}). ` +
`Use StartTextTranslationJobCommand for files larger than 100KB.`
);
}
const response = await translate.send(new TranslateDocumentCommand({
Document: { Content: bytes, ContentType: 'text/html' },
SourceLanguageCode: 'auto',
TargetLanguageCode: targetLang,
}));
return new TextDecoder('utf-8').decode(response.TranslatedDocument!.Content!);
}
When splitting HTML documents, do not split in the middle of a tag. A safe approach is to split on paragraph (</p>) or heading (</h1>, </h2>) boundaries, wrap each part in a minimal <html><body>...</body></html> skeleton, translate each part, then strip the wrapper and concatenate. The translated HTML structure is semantically equivalent to translating the whole document.
AppliedSettings and translation metadata in the response
The TranslateDocumentCommand response includes metadata about how the translation was actually applied — useful for logging and debugging.
const response = await translate.send(new TranslateDocumentCommand({
Document: { Content: encoder.encode(html), ContentType: 'text/html' },
SourceLanguageCode: 'auto',
TargetLanguageCode: 'ja',
Settings: { Formality: 'FORMAL' },
TerminologyNames: ['mcp-domain-terms'],
}));
// What actually happened:
console.log({
detectedSource: response.SourceLanguageCode, // 'en' (detected from 'auto')
targetLang: response.TargetLanguageCode, // 'ja'
appliedFormality: response.AppliedSettings?.Formality, // 'FORMAL' if honored, else absent
appliedTerminologies: response.AppliedTerminologies, // [{Name, Terms: [{SourceText, TargetText}]}]
});
AppliedTerminologies shows which terms from your custom terminology were matched in this document. For long documents with custom terminology, this list helps you verify that your controlled vocabulary is being applied. If terms you expected to match are missing from AppliedTerminologies, check for case mismatches or compound-word tokenization boundaries in the source HTML.
When to use TranslateDocumentCommand vs other APIs
| Scenario | Recommended API | Reason |
|---|---|---|
| Short plain text (<10KB) | TranslateTextCommand | Simpler — no Uint8Array encoding needed |
| HTML or .docx up to 100KB | TranslateDocumentCommand | Format preservation, synchronous, no S3 required |
| Plain text 10KB–100KB | TranslateDocumentCommand with text/plain | Synchronous, no chunking logic needed |
| Files >100KB or batches | StartTextTranslationJobCommand | Async, S3-based, handles multi-MB files and multiple targets |
| Real-time streaming (chat, live captions) | TranslateTextCommand per utterance | No streaming API — call TranslateText on each message as it arrives |
Error handling reference
| Error | Cause | Fix |
|---|---|---|
| DocumentSizeLimitExceededException | Document.Content exceeds 100KB | Split the document; use StartTextTranslationJobCommand for files >100KB |
| UnsupportedDocumentEncodingException | HTML or text is not valid UTF-8 | Re-encode the source document to UTF-8 before passing to the API |
| InvalidDocumentException | Malformed HTML or corrupted .docx binary | Validate HTML well-formedness; ensure .docx is a valid OOXML file (not a renamed .doc) |
| UnsupportedLanguagePairException | Source/target combination not supported for document translation | Not all language pairs support document translation — verify the pair in the Amazon Translate docs |
| AccessDeniedException | Missing translate:TranslateDocument IAM action | Add translate:TranslateDocument to the execution role policy |
| TooManyRequestsException | Exceeded throughput quota | Implement exponential backoff; batch large workloads to async jobs |