This workflow corresponds to n8n.io template #11822 — we link there as the canonical source.
This workflow follows the Agent → Gmail recipe pattern — see all workflows that pair these two integrations.
The workflow JSON
Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →
{
"id": "AvORV0uVnYSN0wtlioH9i",
"name": "Translation & Localization with DeepL and GPT-4o-mini",
"tags": [],
"nodes": [
{
"id": "90f17861-8d01-4ad3-8039-b3663b3be4d9",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
0,
0
],
"parameters": {
"width": 400,
"height": 760,
"content": "## Translation & Localization with DeepL and GPT-4o-mini\n\n### Who is this for?\nContent teams, marketing agencies, and businesses that need to translate and culturally adapt content for international audiences.\n\n### What this workflow does\n- Receives content via webhook and validates target languages\n- Splits content for parallel processing using Split Out node\n- Translates using DeepL neural machine translation\n- Reviews quality with AI Agent and scores each output\n- Applies cultural localization adjustments\n- Aggregates all translations and returns unified response\n\n### Setup\n1. Connect DeepL API credentials\n2. Configure OpenAI credentials for AI Agent\n3. Set up Google Sheets for logging (optional)\n4. Configure Gmail for notifications\n\n### Requirements\n- DeepL API account\n- OpenAI API key\n- Google Sheets (optional)\n- Gmail account"
},
"typeVersion": 1
},
{
"id": "a79dd7d0-3557-42cd-886d-97ff9026d660",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"position": [
544,
112
],
"parameters": {
"color": 7,
"width": 280,
"height": 128,
"content": "### **Step 1: Content Intake**\nReceive translation request via webhook and validate target language codes."
},
"typeVersion": 1
},
{
"id": "6cbf8440-42ef-4e9a-b60b-8dc08757a273",
"name": "Sticky Note2",
"type": "n8n-nodes-base.stickyNote",
"position": [
1152,
112
],
"parameters": {
"color": 7,
"width": 320,
"height": 128,
"content": "### **Step 2: Parallel Translation**\nSplit Out creates parallel streams for each language. DeepL translates with glossary support."
},
"typeVersion": 1
},
{
"id": "2be5ce49-2ba8-43c9-aa35-be93c8d01d26",
"name": "Sticky Note3",
"type": "n8n-nodes-base.stickyNote",
"position": [
2064,
96
],
"parameters": {
"color": 7,
"width": 280,
"height": 128,
"content": "### **Step 3: Quality Review**\nAI Agent evaluates accuracy, fluency, and style. Flags items needing human review."
},
"typeVersion": 1
},
{
"id": "ff572b40-5d5a-47a6-8471-0870e4321924",
"name": "Sticky Note4",
"type": "n8n-nodes-base.stickyNote",
"position": [
2624,
96
],
"parameters": {
"color": 7,
"width": 348,
"height": 128,
"content": "### **Step 4: Localization & Output**\nApply cultural adaptations, aggregate results, log to Sheets, and send notification."
},
"typeVersion": 1
},
{
"id": "7a7e9a20-f4e7-498e-8f22-f4f9a894fcfa",
"name": "Translation Request",
"type": "n8n-nodes-base.webhook",
"position": [
528,
288
],
"parameters": {
"path": "translate",
"options": {},
"httpMethod": "POST",
"responseMode": "responseNode"
},
"typeVersion": 2
},
{
"id": "832fc1f8-561c-4be5-9884-ab7b4febc4e8",
"name": "Initialize Translation Job",
"type": "n8n-nodes-base.set",
"position": [
752,
288
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "1",
"name": "jobId",
"type": "string",
"value": "={{ 'TRN-' + $now.format('yyyyMMddHHmmss') }}"
},
{
"id": "2",
"name": "sourceText",
"type": "string",
"value": "={{ $json.body.sourceText }}"
},
{
"id": "3",
"name": "targetLanguages",
"type": "object",
"value": "={{ $json.body.targetLanguages }}"
},
{
"id": "4",
"name": "contentType",
"type": "string",
"value": "={{ $json.body.contentType || 'general' }}"
},
{
"id": "5",
"name": "glossaryId",
"type": "string",
"value": "={{ $json.body.glossaryId || '' }}"
},
{
"id": "6",
"name": "createdAt",
"type": "string",
"value": "={{ $now.toISO() }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "d30e3d66-8e87-455b-aaac-263f7079e759",
"name": "Detect Source Language",
"type": "n8n-nodes-base.code",
"position": [
976,
288
],
"parameters": {
"jsCode": "const input = $input.first().json;\nconst languages = input.targetLanguages || ['en'];\n\nconst validLanguages = ['en', 'ja', 'de', 'fr', 'es', 'zh', 'ko', 'pt', 'it', 'nl', 'pl', 'ru'];\nconst normalizedLangs = languages.filter(l => validLanguages.includes(l.toLowerCase()));\n\nif (normalizedLangs.length === 0) {\n throw new Error('No valid target languages specified');\n}\n\nconst sourceText = input.sourceText || '';\nlet detectedSource = 'en';\nif (/[\\u3040-\\u30ff\\u4e00-\\u9fff]/.test(sourceText)) detectedSource = 'ja';\nelse if (/[\\u0400-\\u04ff]/.test(sourceText)) detectedSource = 'ru';\n\nreturn [{\n json: {\n ...input,\n detectedSourceLang: detectedSource,\n validatedTargetLangs: normalizedLangs,\n languageCount: normalizedLangs.length\n }\n}];"
},
"typeVersion": 2
},
{
"id": "4c4ff15f-e87f-4f0e-a6b1-d7b9f91ad635",
"name": "Split by Target Language",
"type": "n8n-nodes-base.splitOut",
"position": [
1200,
288
],
"parameters": {
"include": "allOtherFields",
"options": {},
"fieldToSplitOut": "validatedTargetLangs"
},
"typeVersion": 1
},
{
"id": "e73c5ac3-6d99-46db-97e5-3b1d04447f7b",
"name": "Prepare Translation Request",
"type": "n8n-nodes-base.set",
"position": [
1424,
288
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "1",
"name": "currentTargetLang",
"type": "string",
"value": "={{ $json.validatedTargetLangs }}"
}
]
},
"includeOtherFields": true
},
"typeVersion": 3.4
},
{
"id": "2cb0ee68-21ca-48cf-8b07-f7845794da92",
"name": "DeepL Translate",
"type": "n8n-nodes-base.deepL",
"position": [
1648,
288
],
"parameters": {
"text": "={{ $json.sourceText }}",
"translateTo": "={{ $json.currentTargetLang }}",
"additionalFields": {}
},
"typeVersion": 1
},
{
"id": "8b1f1c6f-e5e8-4950-9184-90c17717195f",
"name": "Process Translation Result",
"type": "n8n-nodes-base.code",
"position": [
1872,
288
],
"parameters": {
"jsCode": "const input = $input.first().json;\nconst prevData = $('Prepare Translation Request').first().json;\n\nreturn [{\n json: {\n jobId: prevData.jobId,\n sourceText: prevData.sourceText,\n targetLang: prevData.currentTargetLang,\n translatedText: input.translations?.[0]?.text || input.text || '',\n detectedSourceLang: input.translations?.[0]?.detected_source_language || prevData.detectedSourceLang,\n contentType: prevData.contentType,\n glossaryId: prevData.glossaryId\n }\n}];"
},
"typeVersion": 2
},
{
"id": "c7053efc-8d4c-466b-987e-301fe2cf3bb0",
"name": "AI Quality Review",
"type": "@n8n/n8n-nodes-langchain.agent",
"position": [
2096,
288
],
"parameters": {
"text": "=Review this translation for quality:\n\nSource ({{ $json.detectedSourceLang }}): {{ $json.sourceText }}\nTranslation ({{ $json.targetLang }}): {{ $json.translatedText }}\nContent Type: {{ $json.contentType }}\n\nEvaluate:\n1. Accuracy (1-10)\n2. Fluency (1-10)\n3. Style (1-10)\n\nReturn JSON: { score: number, issues: string[], suggestions: string[] }",
"options": {
"systemMessage": "You are a professional translator. Evaluate translations objectively. Output valid JSON only."
},
"promptType": "define"
},
"typeVersion": 1.7
},
{
"id": "dbc30730-1d81-417c-9616-032c0ffd9c80",
"name": "OpenAI Chat Model",
"type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
"position": [
2176,
512
],
"parameters": {
"model": "gpt-4o-mini",
"options": {
"temperature": 0.3
}
},
"typeVersion": 1.2
},
{
"id": "da662a3d-1541-407d-a4c1-22deb3c7a160",
"name": "Parse Quality Score",
"type": "n8n-nodes-base.code",
"position": [
2448,
288
],
"parameters": {
"jsCode": "const input = $input.first().json;\nconst aiOutput = input.output || '{}';\nconst translationData = $('Process Translation Result').first().json;\n\nlet qualityData = { score: 7, issues: [], suggestions: [] };\ntry {\n const jsonMatch = aiOutput.match(/\\{[\\s\\S]*\\}/);\n if (jsonMatch) qualityData = JSON.parse(jsonMatch[0]);\n} catch (e) {}\n\nreturn [{\n json: {\n ...translationData,\n qualityScore: qualityData.score || 7,\n issues: qualityData.issues || [],\n suggestions: qualityData.suggestions || [],\n needsReview: (qualityData.score || 7) < 6\n }\n}];"
},
"typeVersion": 2
},
{
"id": "8419c74f-934c-47eb-ad2a-4203229bc536",
"name": "Apply Localization",
"type": "n8n-nodes-base.code",
"position": [
2672,
288
],
"parameters": {
"jsCode": "const input = $input.first().json;\n\nconst localizationRules = {\n 'ja': { dateFormat: 'YYYY\u5e74MM\u6708DD\u65e5', currency: '\u00a5' },\n 'de': { dateFormat: 'DD.MM.YYYY', currency: '\u20ac' },\n 'fr': { dateFormat: 'DD/MM/YYYY', currency: '\u20ac' },\n 'es': { dateFormat: 'DD/MM/YYYY', currency: '\u20ac' },\n 'zh': { dateFormat: 'YYYY\u5e74MM\u6708DD\u65e5', currency: '\u00a5' }\n};\n\nconst rules = localizationRules[input.targetLang] || {};\n\nreturn [{\n json: {\n ...input,\n localizationApplied: Object.keys(rules).length > 0,\n localizedText: input.translatedText,\n localeSettings: rules,\n processedAt: new Date().toISOString()\n }\n}];"
},
"typeVersion": 2
},
{
"id": "ada3fc35-ef3a-498e-87b5-59844ad353e2",
"name": "Aggregate All Translations",
"type": "n8n-nodes-base.aggregate",
"position": [
2896,
288
],
"parameters": {
"options": {},
"aggregate": "aggregateAllItemData",
"destinationFieldName": "translations"
},
"typeVersion": 1
},
{
"id": "ed8f18b0-a537-40af-b6e8-1380bc590e51",
"name": "Build Translation Summary",
"type": "n8n-nodes-base.code",
"position": [
3120,
288
],
"parameters": {
"jsCode": "const aggregated = $input.first().json;\nconst translations = aggregated.translations || [];\n\nconst jobId = translations[0]?.jobId || 'unknown';\nconst sourceText = translations[0]?.sourceText || '';\n\nconst summary = {\n jobId: jobId,\n sourceText: sourceText,\n totalLanguages: translations.length,\n averageQualityScore: translations.reduce((sum, t) => sum + (t.qualityScore || 0), 0) / translations.length,\n needsReviewCount: translations.filter(t => t.needsReview).length,\n results: translations.map(t => ({\n language: t.targetLang,\n text: t.localizedText,\n score: t.qualityScore,\n needsReview: t.needsReview\n })),\n completedAt: new Date().toISOString()\n};\n\nreturn [{ json: summary }];"
},
"typeVersion": 2
},
{
"id": "f6d2b1cb-66a2-4031-a66d-97b60bc76dc8",
"name": "Log to Google Sheets",
"type": "n8n-nodes-base.googleSheets",
"position": [
3344,
288
],
"parameters": {
"columns": {
"value": {
"Job ID": "={{ $json.jobId }}",
"Completed": "={{ $json.completedAt }}",
"Languages": "={{ $json.totalLanguages }}",
"Avg Quality": "={{ $json.averageQualityScore.toFixed(1) }}",
"Source Text": "={{ $json.sourceText.substring(0, 200) }}"
},
"mappingMode": "defineBelow"
},
"options": {},
"operation": "append",
"sheetName": {
"__rl": true,
"mode": "list",
"value": "Translations"
},
"documentId": {
"__rl": true,
"mode": "list",
"value": ""
}
},
"typeVersion": 4.5
},
{
"id": "c58c2d5a-c38d-4915-80eb-b16aeb404f93",
"name": "Send Completion Email",
"type": "n8n-nodes-base.gmail",
"position": [
3344,
528
],
"parameters": {
"sendTo": "=team@company.com",
"message": "=Translation Job Completed\n\nJob ID: {{ $json.jobId }}\nLanguages: {{ $json.totalLanguages }}\nAvg Quality: {{ $json.averageQualityScore.toFixed(1) }}/10\n\nResults:\n{{ $json.results.map(r => `- ${r.language}: ${r.score}/10`).join('\\n') }}",
"options": {},
"subject": "=Translation Complete: {{ $json.jobId }}"
},
"typeVersion": 2.1
},
{
"id": "720c0c5e-8779-4b6c-b7bf-b38a74080094",
"name": "Return Results",
"type": "n8n-nodes-base.respondToWebhook",
"position": [
3568,
288
],
"parameters": {
"options": {},
"respondWith": "json",
"responseBody": "={{ JSON.stringify({ success: true, jobId: $json.jobId, totalLanguages: $json.totalLanguages, averageQuality: $json.averageQualityScore, results: $json.results }) }}"
},
"typeVersion": 1.1
}
],
"active": false,
"settings": {
"availableInMCP": false,
"executionOrder": "v1"
},
"versionId": "a4175205-73ca-4783-84c1-701b9ca3a64a",
"connections": {
"DeepL Translate": {
"main": [
[
{
"node": "Process Translation Result",
"type": "main",
"index": 0
}
]
]
},
"AI Quality Review": {
"main": [
[
{
"node": "Parse Quality Score",
"type": "main",
"index": 0
}
]
]
},
"OpenAI Chat Model": {
"ai_languageModel": [
[
{
"node": "AI Quality Review",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"Apply Localization": {
"main": [
[
{
"node": "Aggregate All Translations",
"type": "main",
"index": 0
}
]
]
},
"Parse Quality Score": {
"main": [
[
{
"node": "Apply Localization",
"type": "main",
"index": 0
}
]
]
},
"Translation Request": {
"main": [
[
{
"node": "Initialize Translation Job",
"type": "main",
"index": 0
}
]
]
},
"Log to Google Sheets": {
"main": [
[
{
"node": "Return Results",
"type": "main",
"index": 0
}
]
]
},
"Detect Source Language": {
"main": [
[
{
"node": "Split by Target Language",
"type": "main",
"index": 0
}
]
]
},
"Split by Target Language": {
"main": [
[
{
"node": "Prepare Translation Request",
"type": "main",
"index": 0
}
]
]
},
"Build Translation Summary": {
"main": [
[
{
"node": "Log to Google Sheets",
"type": "main",
"index": 0
},
{
"node": "Send Completion Email",
"type": "main",
"index": 0
}
]
]
},
"Aggregate All Translations": {
"main": [
[
{
"node": "Build Translation Summary",
"type": "main",
"index": 0
}
]
]
},
"Initialize Translation Job": {
"main": [
[
{
"node": "Detect Source Language",
"type": "main",
"index": 0
}
]
]
},
"Process Translation Result": {
"main": [
[
{
"node": "AI Quality Review",
"type": "main",
"index": 0
}
]
]
},
"Prepare Translation Request": {
"main": [
[
{
"node": "DeepL Translate",
"type": "main",
"index": 0
}
]
]
}
}
}
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
Managing high-quality translations across multiple languages often requires more than just machine translation; it requires cultural context and quality assurance. This workflow automates the entire pipeline, from initial translation to AI-driven quality scoring and cultural…
Source: https://n8n.io/workflows/11822/ — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
Enhance your support, onboarding, and internal knowledge workflows with an intelligent RAG-powered chatbot that responds using live data stored in Google Sheets. 🤖📚 Built for teams that rely on struct
leads. Uses supabase, gmail, formTrigger, httpRequest. Webhook trigger; 62 nodes.
Nps-Com-Atendimento-Ao-Cliente. Uses toolSerpApi, lmChatOpenAi, microsoftSql, gmail. Webhook trigger; 34 nodes.
Universal Expense tracker. Uses telegram, httpRequest, openAi, googleSheets. Webhook trigger; 33 nodes.
🧾 Short Description