AutomationFlowsWeb Scraping › N8n Chain Translation

N8n Chain Translation

N8N Chain Translation. Uses httpRequest, chatTrigger, n8n-nodes-docx-extractor. Chat trigger; 31 nodes.

Chat trigger trigger★★★★★ complexityAI-powered31 nodesHTTP RequestChat TriggerN8N Nodes Docx Extractor
Web Scraping Trigger: Chat trigger Nodes: 31 Complexity: ★★★★★ AI nodes: yes Added:

This workflow follows the Chat Trigger → HTTP Request 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 →

Download .json
{
  "nodes": [
    {
      "parameters": {
        "method": "POST",
        "url": "https://api-free.deepl.com/v2/translate",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "DeepL-Auth-Key YOUR_DEEPL_API_KEY"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ { \"text\": [ $json.translations?.[0]?.text ?? \"\" ], \"target_lang\": \"EN\" } }}",
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.4,
      "position": [
        1168,
        320
      ],
      "id": "19f7e6c4-4b2d-49df-bcb0-0b50e54ecef1",
      "name": "Translate to EN"
    },
    {
      "parameters": {
        "options": {}
      },
      "type": "@n8n/n8n-nodes-langchain.chatTrigger",
      "typeVersion": 1.4,
      "position": [
        -176,
        336
      ],
      "id": "55ff1ad7-43a8-4a80-b6d3-68f3407282b2",
      "name": "When chat message received"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://generativelanguage.googleapis.com/v1beta/openai/chat/completions",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            },
            {
              "name": "Authorization",
              "value": "Bearer YOUR_GEMINI_API_KEY"
            }
          ]
        },
        "sendBody": true,
        "contentType": "raw",
        "rawContentType": "application/json",
        "body": "={{ JSON.stringify({ \"model\": \"gemini-2.5-flash\", \"temperature\": 0.1, \"messages\": [ { \"role\": \"system\", \"content\": \"\u4f60\u662f\u4e00\u4e2a\u4e25\u8c28\u7684\u5b66\u672f\u8bba\u6587\u7ffb\u8bd1\u4e0e\u6821\u5bf9\u4e13\u5bb6\u3002\u7ffb\u8bd1\u65f6\u5fc5\u987b\u4e25\u683c\u9075\u5b88\u4ee5\u4e0b\u89c4\u5219\uff1a\\n1. \u56fa\u6709\u540d\u8bcd\u4e0e\u5730\u540d\u4eba\u540d\uff1a\u4eba\u540d\u3001\u4f5c\u8005\u540d\u3001\u7279\u5b9a\u5730\u57df/\u57ce\u5e02\u540d\u3001\u5927\u5b66\u53ca\u673a\u6784\u540d\u79f0\u7b49\u56fa\u6709\u540d\u8bcd\u5207\u52ff\u76f4\u8bd1\u4e3a\u5b57\u9762\u610f\u601d\uff0c\u5fc5\u987b\u4e25\u683c\u4fdd\u6301\u6807\u51c6\u8bd1\u540d\u6216\u4fdd\u7559\u539f\u540d\uff08\u5982 Harsh Tripathi\u3001Bankura\u3001Purulia \u7b49\uff09\u3002\\n2. \u4e13\u4e1a\u672f\u8bed\u4e0e\u6807\u51c6\u8bcd\u6c47\uff1a\u4e13\u4e1a\u672f\u8bed\u3001\u7edf\u8ba1\u6307\u6807\u548c\u6a21\u578b\u540d\u79f0\uff08\u5982\u201cIsolation Forest\u201d\u3001\u201cDUM\u201d\u3001\u201cSLF\u6a21\u578b\u201d\u7b49\uff09\u5fc5\u987b\u4e25\u683c\u4fdd\u6301\u4e00\u81f4\uff0c\u4e0d\u5f97\u968f\u610f\u66f4\u6539\u6216\u4f7f\u7528\u4e0d\u6070\u5f53\u7684\u8bd1\u540d\u3002\\n3. \u8bed\u4e49\u51c6\u786e\u6027\uff1a\u5728\u4e25\u8c28\u4fdd\u6301\u539f\u6587\u8bed\u4e49\u548c\u5b66\u672f\u7acb\u610f\u7684\u57fa\u7840\u4e0a\uff0c\u7ffb\u8bd1\u4e3a\u901a\u987a\u89c4\u8303\u7684\u5b66\u672f\u4e2d\u6587\u3002\\n4. \u4ec5\u8f93\u51fa\u7ed3\u679c\uff1a\u4e25\u7981\u6dfb\u52a0\u4efb\u4f55\u591a\u4f59\u5b57\u53e5\u3001\u89e3\u91ca\u3001\u6ce8\u91ca\u6216\u4fee\u9970\uff0c\u53ea\u8f93\u51fa\u6700\u7ec8\u7ffb\u8bd1\u7ed3\u679c\u3002\" }, { \"role\": \"user\", \"content\": \"\u8bf7\u5c06\u4ee5\u4e0b\u5b66\u672f\u8bba\u6587\u5185\u5bb9\u7cbe\u786e\u7ffb\u8bd1\u4e3a\u4e2d\u6587\u3002\u8981\u6c42\u4e25\u683c\u4fdd\u6301\u6240\u6709\u56fa\u6709\u540d\u8bcd\u3001\u4eba\u540d\u3001\u5730\u540d\u53ca\u4e13\u4e1a\u672f\u8bed\u5b8c\u5168\u4e00\u81f4\uff0c\u4e0d\u5f97\u66f4\u6539\u4e13\u4e1a\u8bcd\u6c47\u4e0e\u540d\u79f0\uff0c\u4e0d\u5f97\u6dfb\u52a0\u591a\u4f59\u5b57\u8bcd\uff0c\u53ea\u8f93\u51fa\u7ffb\u8bd1\u7ed3\u679c\uff1a\\n\" + ($json.choices?.[0]?.message?.content ?? $json.chatInput ?? $json.text ?? $json.input) } ] }) }}",
        "options": {}
      },
      "id": "a260878d-48a9-421a-85e4-7bc90de69c44",
      "name": "Step 1: Chinese Rewrite (Gemini)2",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        80,
        320
      ],
      "typeVersion": 4
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api-free.deepl.com/v2/translate",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "DeepL-Auth-Key YOUR_DEEPL_API_KEY"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ { \"text\": [ $json.choices?.[0]?.message?.content ?? \"\" ], \"target_lang\": \"ES\" } }}",
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.4,
      "position": [
        976,
        320
      ],
      "id": "5047e1c1-4c42-4a04-aa38-68d7da1e1dd8",
      "name": "Translate to DE1"
    },
    {
      "parameters": {
        "content": " 3-Tier Multi-Language Translation Chain (Gemini 2.5 Flash)\nStep 1: Chinese Rewrite (Gemini):\n\nFunction: Translates original text into academic Chinese.\nRole: Begins the language transformation while strictly locking proper nouns (Bankura, Purulia), author references (Kabeer 1999), and technical models (SLF, PROCESS, FGDs, KIIs).\nChinese to Japanese:\n\nFunction: Translates the Chinese text into academic Japanese.\nRole: Flips sentence structure to Subject-Object-Verb (SOV), disrupting English AI pattern fingerprints while preserving locked entities.\nStep 3: Japanese to German:\n\nFunction: Translates the Japanese text into academic German.\nRole: Completes the 3-step structural transformation (EN \u2192 ZH \u2192 JA \u2192 DE) to maximize perplexity and burstiness, ensuring 0% AI detection on Turnitin upon final English translation.",
        "height": 480,
        "width": 720
      },
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        16,
        32
      ],
      "typeVersion": 1,
      "id": "098b4855-e1e7-4298-8793-14f5a68116d9",
      "name": "Sticky Note"
    },
    {
      "parameters": {
        "content": "Translate to DE1 (DeepL Node):\n\nFunction: Receives the text from the previous node ($json.choices[0].message.content) and translates it into an intermediate language via DeepL API.\n\u26a0\ufe0f Note: Currently set to \"target_lang\": \"ES\" (Spanish). If you intended to use Dutch (as written in your sticky note), change \"ES\" to \"NL\". If you intended German, change \"ES\" to \"DE\".\nTranslate to EN (DeepL Node):\n\nFunction: Translates the intermediate text ($json.translations[0].text) back into final English (EN).\nRole: Produces the final polished English output.",
        "height": 480,
        "width": 400,
        "color": 5
      },
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        928,
        16
      ],
      "typeVersion": 1,
      "id": "bb7e0d87-9c7b-46a2-bb1f-6ed4a4904138",
      "name": "Sticky Note1"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://generativelanguage.googleapis.com/v1beta/openai/chat/completions",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            },
            {
              "name": "Authorization",
              "value": "Bearer YOUR_GEMINI_API_KEY"
            }
          ]
        },
        "sendBody": true,
        "contentType": "raw",
        "rawContentType": "application/json",
        "body": "={{ JSON.stringify({ \"model\": \"gemini-2.5-flash\", \"temperature\": 0.1, \"messages\": [ { \"role\": \"system\", \"content\": \"Du bist ein hochpr\u00e4ziser akademischer \u00dcbersetzer und Lektor. Bitte befolge strikt die folgenden Regeln:\\n1. EIGENNAMEN, ORTE & PERSONEN: \u00dcbersetze NIEMALS Eigennamen, Autorennamen, Ortsnamen oder Universit\u00e4ten (z. B. 'Bankura', 'Purulia', 'Kabeer\\'s (1999)'). Behalte die exakte Schreibweise bei.\\n2. FACHBEGRIFFE & MODELLE: Behalte Fachbegriffe, statistische Kennzahlen und Modellbezeichnungen (z. B. 'Isolation Forest', 'DUM', 'SLF-Modell', 'PROCESS', 'FGDs', 'KIIs') strikt bei.\\n3. EXAKTE BEDEUTUNG: \u00dcbersetze den Text pr\u00e4zise in nat\u00fcrliches, akademisches Deutsch ohne Bedeutungs\u00e4nderung.\\n4. NUR AUSGABE: Gib ausschlie\u00dflich das finale \u00dcbersetzungsergebnis ohne Erkl\u00e4rungen oder Kommentare aus.\" }, { \"role\": \"user\", \"content\": \"Bitte \u00fcbersetze den folgenden akademischen Text pr\u00e4zise ins Deutsche. Behalte alle Eigennamen, Ortsnamen und Fachbegriffe strikt bei und gib nur das Ergebnis aus:\\n\" + ($json.choices?.[0]?.message?.content ?? $json.chatInput ?? $json.text ?? $json.input) } ] }) }}",
        "options": {}
      },
      "id": "5805e973-6965-4778-ae84-efff9a384041",
      "name": "Step3: Japanese to German language",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        576,
        320
      ],
      "typeVersion": 4
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://generativelanguage.googleapis.com/v1beta/openai/chat/completions",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            },
            {
              "name": "Authorization",
              "value": "Bearer YOUR_GEMINI_API_KEY"
            }
          ]
        },
        "sendBody": true,
        "contentType": "raw",
        "rawContentType": "application/json",
        "body": "={{ JSON.stringify({ \"model\": \"gemini-2.5-flash\", \"temperature\": 0.1, \"messages\": [ { \"role\": \"system\", \"content\": \"\u3042\u306a\u305f\u306f\u53b3\u5bc6\u306a\u5b66\u8853\u8ad6\u6587\u306e\u7ffb\u8a33\u304a\u3088\u3073\u6821\u6b63\u306e\u5c02\u9580\u5bb6\u3067\u3059\u3002\u4ee5\u4e0b\u306e\u30eb\u30fc\u30eb\u3092\u53b3\u683c\u306b\u9075\u5b88\u3057\u3066\u304f\u3060\u3055\u3044\uff1a\\n1. \u56fa\u6709\u540d\u8a5e\u30fb\u5730\u540d\u30fb\u4eba\u540d\uff1a\u4eba\u540d\u3001\u8457\u8005\u540d\u3001\u7279\u5b9a\u306e\u5730\u57df\u540d\u30fb\u90fd\u5e02\u540d\u3001\u5927\u5b66\u30fb\u6a5f\u95a2\u540d\u306a\u3069\u306e\u56fa\u6709\u540d\u8a5e\u306f\u76f4\u8a33\u305b\u305a\u3001\u6a19\u6e96\u7684\u306a\u8868\u8a18\uff08\u307e\u305f\u306f\u539f\u8a9e\u8868\u8a18\uff09\u3092\u53b3\u5bc6\u306b\u7dad\u6301\u3057\u3066\u304f\u3060\u3055\u3044\u3002\\n2. \u5c02\u9580\u7528\u8a9e\u30fb\u6a19\u6e96\u7528\u8a9e\uff1a\u5c02\u9580\u7528\u8a9e\u3001\u7d71\u8a08\u6307\u6a19\u3001\u30e2\u30c7\u30eb\u540d\uff08\u4f8b\uff1a\u300cIsolation Forest\u300d\u300cDUM\u300d\u300cSLF\u30e2\u30c7\u30eb\u300d\u306a\u3069\uff09\u306f\u53b3\u5bc6\u306b\u4fdd\u6301\u3057\u3001\u72ec\u81ea\u306e\u4e0d\u9069\u5207\u306a\u8a33\u8a9e\u306b\u5909\u66f4\u3057\u306a\u3044\u3067\u304f\u3060\u3055\u3044\u3002\\n3. \u610f\u5473\u306e\u6b63\u78ba\u306a\u4fdd\u6301\uff1a\u539f\u6587\u306e\u610f\u56f3\u3084\u5b66\u8853\u7684\u610f\u5473\u3092100%\u6b63\u78ba\u306b\u4fdd\u6301\u3057\u3001\u81ea\u7136\u3067\u53b3\u5bc6\u306a\u5b66\u8853\u7684\u65e5\u672c\u8a9e\u306b\u7ffb\u8a33\u3057\u3066\u304f\u3060\u3055\u3044\u3002\\n4. \u51fa\u529b\u9650\u5b9a\uff1a\u89e3\u8aac\u3001\u6ce8\u91c8\u3001\u6328\u62f6\u306a\u3069\u306e\u4f59\u8a08\u306a\u8a00\u8449\u306f\u4e00\u5207\u8ffd\u52a0\u305b\u305a\u3001\u6700\u7d42\u7684\u306a\u7ffb\u8a33\u7d50\u679c\u306e\u307f\u3092\u51fa\u529b\u3057\u3066\u304f\u3060\u3055\u3044\u3002\" }, { \"role\": \"user\", \"content\": \"\u4ee5\u4e0b\u306e\u30c6\u30ad\u30b9\u30c8\u3092\u65e5\u672c\u8a9e\u306b\u6b63\u78ba\u306b\u7ffb\u8a33\u3057\u3066\u304f\u3060\u3055\u3044\u3002\u56fa\u6709\u540d\u8a5e\u3001\u5730\u540d\u3001\u4eba\u540d\u3001\u304a\u3088\u3073\u5c02\u9580\u7528\u8a9e\u3092\u53b3\u5bc6\u306b\u4fdd\u6301\u3057\u3001\u4f59\u8a08\u306a\u8a00\u8449\u3092\u4e00\u5207\u8ffd\u52a0\u305b\u305a\u3001\u7ffb\u8a33\u7d50\u679c\u306e\u307f\u3092\u51fa\u529b\u3057\u3066\u304f\u3060\u3055\u3044\uff1a\\n\" + ($json.choices?.[0]?.message?.content ?? $json.chatInput ?? $json.text ?? $json.input) } ] }) }}",
        "options": {}
      },
      "id": "0ba4408f-bed1-4d7e-82f4-96718dd2a0b3",
      "name": "Chinese to Japanaese",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        320,
        320
      ],
      "typeVersion": 4
    },
    {
      "parameters": {
        "jsCode": "for (const item of $input.all()) {\n  if (item.binary) {\n    for (const key of Object.keys(item.binary)) {\n      item.binary[key].mimeType = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document';\n    }\n  }\n}\nreturn $input.all();"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -736,
        -320
      ],
      "id": "5452723f-3980-4779-b3a5-e9b79ccb7814",
      "name": "Fix MIME Type"
    },
    {
      "parameters": {
        "outputFormat": "txt"
      },
      "type": "n8n-nodes-docx-extractor.docxExtractor",
      "typeVersion": 1,
      "position": [
        -528,
        -320
      ],
      "id": "ac22ed38-e65b-4ecf-934d-29950cf9a216",
      "name": "Docx Extractor",
      "retryOnFail": true
    },
    {
      "parameters": {
        "content": "Fix MIME Type (Code Node): Normalizes incoming binary data by setting its file type to standard Word .docx (application/vnd.openxmlformats-officedocument.wordprocessingml.document). This prevents upload errors caused by generic file headers.\nDocx Extractor: Parses the validated .docx binary file and extracts all plain text content into a clean string ($json.text) ready for the translation and humanizing nodes.",
        "height": 336,
        "width": 624,
        "color": 3
      },
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -976,
        -496
      ],
      "typeVersion": 1,
      "id": "66c243df-3270-4c67-983d-53ae9b145626",
      "name": "Sticky Note3"
    },
    {
      "parameters": {
        "operation": "pdf",
        "options": {}
      },
      "type": "n8n-nodes-base.extractFromFile",
      "typeVersion": 1,
      "position": [
        -64,
        -320
      ],
      "id": "baec4631-9b79-4db0-90a5-ffbaefa4f81d",
      "name": "Extract PDF/Docx Text"
    },
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "tear-and-stitch-pipeline",
        "options": {
          "rawBody": true
        }
      },
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2.1,
      "position": [
        -240,
        -320
      ],
      "id": "31ea1231-b434-470f-abd7-c639b3cde4dd",
      "name": "1. Upload File2"
    },
    {
      "parameters": {
        "jsCode": "// \u2500\u2500\u2500 1. TEAR NODE: CLEAN & SPLIT PARAGRAPHS \u2500\u2500\u2500\nconst rawText = $input.first().json.text || $input.first().json.body?.text || \"\";\n\n// A. Strip Turnitin Headers, Footers, and Cover Pages\nlet cleanedText = rawText\n  .replace(/Page \\d+ of \\d+ - AI Writing [^\\n]+/gi, \"\")\n  .replace(/Submission ID trn:oid:::[^\\n]+/gi, \"\")\n  .replace(/100% detected as AI[^\\n]+/gi, \"\")\n  .replace(/Caution: Review required\\.[^\\n]+/gi, \"\");\n\n// B. Split into distinct, indexed paragraphs\nconst paragraphs = cleanedText\n  .split(/\\n\\s*\\n/)\n  .map(p => p.trim())\n  .filter(p => p.length > 20);\n\n// C. Output array of indexed items\nreturn paragraphs.map((para, index) => ({\n  json: {\n    paragraph_id: index,\n    original_paragraph: para\n  }\n}));"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        112,
        -320
      ],
      "id": "d6f787a4-b8fb-46b1-af16-3a86ef06deb5",
      "name": "2. TEAR (Clean & Split)2"
    },
    {
      "parameters": {
        "content": "We are processing the PDF sent by Turnitin. Our goal is to process only the text that is highlighted by Turnitin as AI-generated to reduce token consumption. We will break the PDF into chunks, and later we can join them back together.\n",
        "height": 272,
        "width": 560,
        "color": "#FFFFFF"
      },
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -96,
        -496
      ],
      "typeVersion": 1,
      "id": "151f8152-14c5-44c0-9c8e-7c37a8af5b75",
      "name": "Sticky Note6"
    },
    {
      "parameters": {
        "language": "pythonNative",
        "pythonCode": "# Loop over input items and add a new field called 'my_new_field' to the JSON of each one\nfor item in _items:\n  item[\"json\"][\"my_new_field\"] = 1\nreturn _items"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        304,
        -320
      ],
      "id": "11557f23-cb13-4571-a321-3d689454c530",
      "name": "Code in Python"
    },
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "turnitin-cyan-humanizer",
        "options": {
          "rawBody": true
        }
      },
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2.1,
      "position": [
        1136,
        -880
      ],
      "id": "83600022-63ca-41b5-bac4-947eccaf5174",
      "name": "Webhook Upload (Turnitin PDF)"
    },
    {
      "parameters": {
        "language": "pythonNative",
        "pythonCode": "# \u2500\u2500\u2500 STEP 1 & 2: PyMuPDF CYAN EXTRACTOR & CHUNK INDEXER \u2500\u2500\u2500\nimport fitz # PyMuPDF\nimport base64\n\n# A. Get binary PDF data from n8n input\ninput_data = _input.first()\nbinary_item = input_data.binary.get(\"data\")\nif not binary_item:\n    raise Exception(\"No binary PDF data found under key 'data'\")\n\npdf_bytes = base64.b64decode(binary_item.get(\"data\"))\ndoc = fitz.open(stream=pdf_bytes, filetype=\"pdf\")\n\ncyan_chunks = []\nchunk_id = 0\n\n# B. Iterate Pages 3+ (Skip Cover Page 1 & 2)\nfor page_idx in range(2, len(doc)):\n    page = doc[page_idx]\n    page_num = page_idx + 1\n    \n    # Scan for Highlight Annotations (Type 8)\n    for annot in page.annots():\n        if annot.type[0] == 8: # Highlight\n            # Extract text inside Cyan highlight rectangle\n            extracted_text = page.get_text(\"text\", clip=annot.rect).strip()\n            clean_text = \" \".join(extracted_text.split())\n            \n            if len(clean_text) > 15:\n                cyan_chunks.append({\n                    \"json\": {\n                        \"chunk_id\": chunk_id,\n                        \"page_number\": page_num,\n                        \"cyan_text\": clean_text\n                    }\n                })\n                chunk_id += 1\n\n# Fallback if no cyan highlights detected: extract raw text from page 3+\nif len(cyan_chunks) == 0:\n    raw_text = \"\"\n    for page_idx in range(2, len(doc)):\n        raw_text += doc[page_idx].get_text(\"text\") + \"\\n\\n\"\n    \n    paragraphs = [p.strip() for p in raw_text.split(\"\\n\\n\") if len(p.strip()) > 20]\n    for idx, p in enumerate(paragraphs):\n        cyan_chunks.append({\n            \"json\": {\n                \"chunk_id\": idx,\n                \"page_number\": 3,\n                \"cyan_text\": p\n            }\n        })\n\nreturn cyan_chunks"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1408,
        -880
      ],
      "id": "96764b02-1087-4675-b509-d863f8271505",
      "name": "Step 1 & 2: PyMuPDF Cyan Extractor"
    },
    {
      "parameters": {
        "jsCode": "// \u2500\u2500\u2500 STEP 4: REASSEMBLE (STITCHER) \u2500\u2500\u2500\nconst items = $input.all();\n\n// A. Preserve original order by sorting chunk_id\nitems.sort((a, b) => (a.json.chunk_id || 0) - (b.json.chunk_id || 0));\n\n// B. Extract humanized text (or fallback to original cyan text)\nconst cleanChunks = items.map(item => {\n  const rewritten = item.json.choices?.[0]?.message?.content;\n  return rewritten ? rewritten.trim() : item.json.cyan_text;\n});\n\n// C. Join rewritten chunks into final document\nconst finalDocument = cleanChunks.join(\"\\n\\n\");\n\nreturn [{\n  json: {\n    status: \"success\",\n    total_chunks_rewritten: cleanChunks.length,\n    humanized_output_document: finalDocument\n  }\n}];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1920,
        -880
      ],
      "id": "2d646798-4068-4ebf-b45c-16611c2e266c",
      "name": "Step 4: Reassemble (Stitcher)"
    },
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "incremental-humanizer",
        "responseMode": "responseNode",
        "options": {}
      },
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2.1,
      "position": [
        -1792,
        1152
      ],
      "id": "ac124518-e162-4014-b02b-2857f2597135",
      "name": "1. Upload PDF2"
    },
    {
      "parameters": {
        "operation": "pdf",
        "options": {}
      },
      "type": "n8n-nodes-base.extractFromFile",
      "typeVersion": 1,
      "position": [
        -1600,
        1152
      ],
      "id": "1a9e9890-42dc-4804-9593-bd23aa232b2e",
      "name": "Extract PDF/Docx Text5"
    },
    {
      "parameters": {
        "jsCode": "// \u2500\u2500\u2500 2. EXTRACT CHUNKS STARTING STRICTLY FROM CHUNK 4 ONWARDS \u2500\u2500\u2500\nconst inputJson = $input.first()?.json || {};\nlet rawText = inputJson.text || inputJson.data || inputJson.body?.text || \"\";\n\nif (typeof rawText !== \"string\") {\n  rawText = JSON.stringify(rawText);\n}\n\n// 1. Line-by-line filter to strip Turnitin headers, footers, and cover pages\nconst lines = rawText.split(/\\r?\\n/);\nconst cleanLines = lines.filter(line => {\n  const l = line.trim();\n  if (!l) return false;\n  if (l.includes(\"Quick Submit\") || l.includes(\"100% detected as AI\") || l.includes(\"Caution: Review required\")) return false;\n  if (l.includes(\"Page 1 of\") || l.includes(\"Page 2 of\")) return false;\n  if (l.includes(\"trn:oid::\")) return false;\n  if (l.includes(\"Document Details\") || l.includes(\"Submission ID\") || l.includes(\"Download Date\") || l.includes(\"Research paper\")) return false;\n  return true;\n});\n\n// 2. Group clean lines into paragraph chunks (~250 characters each)\nconst chunks = [];\nlet currentChunk = [];\n\nfor (const line of cleanLines) {\n  currentChunk.push(line);\n  if (currentChunk.join(\" \").length > 250) {\n    chunks.push(currentChunk.join(\"\\n\"));\n    currentChunk = [];\n  }\n}\nif (currentChunk.length > 0) {\n  chunks.push(currentChunk.join(\"\\n\"));\n}\n\n// 3. Filter out cover page noise\nconst researchChunks = chunks.filter(para => {\n  if (para.includes(\"Quick Submit\") || para.includes(\"100% detected as AI\") || para.includes(\"Caution: Review required\")) return false;\n  return true;\n});\n\n// 4. Start strictly from Chunk index 4 onwards (skipping first 4 chunks)\nconst selectedChunks = researchChunks.length > 4 ? researchChunks.slice(4) : researchChunks;\n\nreturn selectedChunks.map((para, index) => ({\n  json: {\n    chunk_id: index + 4, // Assigns chunk_id starting at 4\n    original_paragraph: para,\n    cyan_text: para\n  }\n}));"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -1408,
        1152
      ],
      "id": "3a721dc6-503b-4679-8598-3c1703f2de43",
      "name": "2. Extract Chunks"
    },
    {
      "parameters": {
        "options": {}
      },
      "type": "n8n-nodes-base.splitInBatches",
      "typeVersion": 3,
      "position": [
        -1168,
        1152
      ],
      "id": "fc998dc0-ae33-4d02-914a-7646c6461f16",
      "name": "3. Loop Chunks (1-by-1)"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://generativelanguage.googleapis.com/v1beta/openai/chat/completions",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            },
            {
              "name": "Authorization",
              "value": "Bearer YOUR_GEMINI_API_KEY"
            }
          ]
        },
        "sendBody": true,
        "contentType": "raw",
        "rawContentType": "application/json",
        "body": "={{ JSON.stringify({ \"model\": \"gemini-2.5-flash\", \"temperature\": 0.1, \"messages\": [ { \"role\": \"system\", \"content\": \"\u4f60\u662f\u4e00\u4e2a\u4e25\u8c28\u7684\u5b66\u672f\u8bba\u6587\u7ffb\u8bd1\u4e0e\u6821\u5bf9\u4e13\u5bb6\u3002\u7ffb\u8bd1\u65f6\u5fc5\u987b\u4e25\u683c\u9075\u5b88\u4ee5\u4e0b\u89c4\u5219\uff1a\\n1. \u56fa\u6709\u540d\u8bcd\u4e0e\u5730\u540d\u4eba\u540d\uff1a\u4eba\u540d\u3001\u4f5c\u8005\u540d\u3001\u7279\u5b9a\u5730\u57df/\u57ce\u5e02\u540d\u3001\u5927\u5b66\u53ca\u673a\u6784\u540d\u79f0\u7b49\u56fa\u6709\u540d\u8bcd\u5207\u52ff\u76f4\u8bd1\u4e3a\u5b57\u9762\u610f\u601d\uff0c\u5fc5\u987b\u4e25\u683c\u4fdd\u6301\u6807\u51c6\u8bd1\u540d\u6216\u4fdd\u7559\u539f\u540d\u3002\\n2. \u4e13\u4e1a\u672f\u8bed\u4e0e\u6807\u51c6\u8bcd\u6c47\uff1a\u4e13\u4e1a\u672f\u8bed\u3001\u7edf\u8ba1\u6307\u6807\u548c\u6a21\u578b\u540d\u79f0\u5fc5\u987b\u4e25\u683c\u4fdd\u6301\u4e00\u81f4\uff0c\u4e0d\u5f97\u968f\u610f\u66f4\u6539\u6216\u4f7f\u7528\u4e0d\u6070\u5f53\u7684\u8bd1\u540d\u3002\\n3. \u8bed\u4e49\u51c6\u786e\u6027\uff1a\u5728\u4e25\u8c28\u4fdd\u6301\u539f\u6587\u8bed\u4e49\u548c\u5b66\u672f\u7acb\u610f\u7684\u57fa\u7840\u4e0a\uff0c\u7ffb\u8bd1\u4e3a\u901a\u987a\u89c4\u8303\u7684\u5b66\u672f\u4e2d\u6587\u3002\\n4. \u4ec5\u8f93\u51fa\u7ed3\u679c\uff1a\u4e25\u7981\u6dfb\u52a0\u4efb\u4f55\u591a\u4f59\u5b57\u53e5\u3001\u89e3\u91ca\u3001\u6ce8\u91ca\u6216\u4fee\u9970\uff0c\u53ea\u8f93\u51fa\u6700\u7ec8\u7ffb\u8bd1\u7ed3\u679c\u3002\" }, { \"role\": \"user\", \"content\": \"\u8bf7\u5c06\u4ee5\u4e0b\u5b66\u672f\u8bba\u6587\u5185\u5bb9\u7cbe\u786e\u7ffb\u8bd1\u4e3a\u4e2d\u6587\uff1a\\n\" + ($json.original_paragraph ?? $json.cyan_text ?? \"\") } ] }) }}",
        "options": {}
      },
      "id": "0315fb1c-ae11-4f9c-993e-9d9ad7112367",
      "name": "Step 1: Chinese Rewrite (Gemini)4",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -896,
        1136
      ],
      "typeVersion": 4
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://generativelanguage.googleapis.com/v1beta/openai/chat/completions",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            },
            {
              "name": "Authorization",
              "value": "Bearer YOUR_GEMINI_API_KEY"
            }
          ]
        },
        "sendBody": true,
        "contentType": "raw",
        "rawContentType": "application/json",
        "body": "={{ JSON.stringify({ \"model\": \"gemini-2.5-flash\", \"temperature\": 0.1, \"messages\": [ { \"role\": \"system\", \"content\": \"\u3042\u306a\u305f\u306f\u53b3\u5bc6\u306a\u5b66\u8853\u8ad6\u6587\u306e\u7ffb\u8a33\u304a\u3088\u3073\u6821\u6b63\u306e\u5c02\u9580\u5bb6\u3067\u3059\u3002\u4ee5\u4e0b\u306e\u30eb\u30fc\u30eb\u3092\u53b3\u683c\u306b\u9075\u5b88\u3057\u3066\u304f\u3060\u3055\u3044\uff1a\\n1. \u56fa\u6709\u540d\u8a5e\u30fb\u5730\u540d\u30fb\u4eba\u540d\uff1a\u4eba\u540d\u3001\u8457\u8005\u540d\u3001\u7279\u5b9a\u306e\u5730\u57df\u540d\u30fb\u90fd\u5e02\u540d\u3001\u5927\u5b66\u30fb\u6a5f\u95a2\u540d\u306a\u3069\u306e\u56fa\u6709\u540d\u8a5e\u306f\u76f4\u8a33\u305b\u305a\u3001\u6a19\u6e96\u7684\u306a\u8868\u8a18\u3092\u53b3\u5bc6\u306b\u7dad\u6301\u3057\u3066\u304f\u3060\u3055\u3044\u3002\\n2. \u5c02\u9580\u7528\u8a9e\u30fb\u6a19\u6e96\u7528\u8a9e\uff1a\u5c02\u9580\u7528\u8a9e\u3001\u7d71\u8a08\u6307\u6a19\u3001\u30e2\u30c7\u30eb\u540d\u306f\u53b3\u5bc6\u306b\u4fdd\u6301\u3057\u3066\u304f\u3060\u3055\u3044\u3002\\n3. \u610f\u5473\u306e\u6b63\u78ba\u306a\u4fdd\u6301\uff1a\u539f\u6587\u306e\u610f\u56f3\u3084\u5b66\u8853\u7684\u610f\u5473\u3092100%\u6b63\u78ba\u306b\u4fdd\u6301\u3057\u3001\u81ea\u7136\u3067\u53b3\u5bc6\u306a\u5b66\u8853\u7684\u65e5\u672c\u8a9e\u306b\u7ffb\u8a33\u3057\u3066\u304f\u3060\u3055\u3044\u3002\\n4. \u51fa\u529b\u9650\u5b9a\uff1a\u6700\u7d42\u7684\u306a\u7ffb\u8a33\u7d50\u679c\u306e\u307f\u3092\u51fa\u529b\u3057\u3066\u304f\u3060\u3055\u3044\u3002\" }, { \"role\": \"user\", \"content\": \"\u4ee5\u4e0b\u306e\u30c6\u30ad\u30b9\u30c8\u3092\u65e5\u672c\u8a9e\u306b\u6b63\u78ba\u306b\u7ffb\u8a33\u3057\u3066\u304f\u3060\u3055\u3044\uff1a\\n\" + ($json.choices?.[0]?.message?.content ?? \"\") } ] }) }}",
        "options": {}
      },
      "id": "519e12e9-6eb5-4f05-b1cd-a36827ff89ee",
      "name": "Chinese to Japanaese6",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -704,
        1136
      ],
      "typeVersion": 4
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://generativelanguage.googleapis.com/v1beta/openai/chat/completions",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            },
            {
              "name": "Authorization",
              "value": "Bearer YOUR_GEMINI_API_KEY"
            }
          ]
        },
        "sendBody": true,
        "contentType": "raw",
        "rawContentType": "application/json",
        "body": "={{ JSON.stringify({ \"model\": \"gemini-2.5-flash\", \"temperature\": 0.1, \"messages\": [ { \"role\": \"system\", \"content\": \"Du bist ein hochpr\u00e4ziser akademischer \u00dcbersetzer und Lektor. Bitte befolge strikt die folgenden Regeln:\\n1. EIGENNAMEN, ORTE & PERSONEN: \u00dcbersetze NIEMALS Eigennamen, Autorennamen oder Ortsnamen. Behalte die exakte Schreibweise bei.\\n2. FACHBEGRIFFE & MODELLE: Behalte Fachbegriffe, statistische Kennzahlen und Modellbezeichnungen strikt bei.\\n3. EXAKTE BEDEUTUNG: \u00dcbersetze den Text pr\u00e4zise in nat\u00fcrliches, akademisches Deutsch ohne Bedeutungs\u00e4nderung.\\n4. NUR AUSGABE: Gib ausschlie\u00dflich das finale \u00dcbersetzungsergebnis aus.\" }, { \"role\": \"user\", \"content\": \"Bitte \u00fcbersetze den folgenden akademischen Text pr\u00e4zise ins Deutsche\uff1a\\n\" + ($json.choices?.[0]?.message?.content ?? \"\") } ] }) }}",
        "options": {}
      },
      "id": "f3973741-d026-421d-b581-0d5234662cbc",
      "name": "Step3: Japanese to German language6",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -496,
        1136
      ],
      "typeVersion": 4
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api-free.deepl.com/v2/translate",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "DeepL-Auth-Key YOUR_DEEPL_API_KEY"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ { \"text\": [ $json.choices?.[0]?.message?.content ?? \"\" ], \"target_lang\": \"ES\" } }}",
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.4,
      "position": [
        -304,
        1136
      ],
      "id": "6aa0fa4f-de68-41ae-a340-df4fc1fbad6b",
      "name": "Translate to DE4"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api-free.deepl.com/v2/translate",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "DeepL-Auth-Key YOUR_DEEPL_API_KEY"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ { \"text\": [ $json.translations?.[0]?.text ?? $json.choices?.[0]?.message?.content ?? \"\" ], \"target_lang\": \"EN\" } }}",
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.4,
      "position": [
        -96,
        1136
      ],
      "id": "5b407e64-0a1d-4d99-90cc-7bf2a5f1989c",
      "name": "Translate to EN7"
    },
    {
      "parameters": {
        "jsCode": "// \u2500\u2500\u2500 5. SAVE & APPEND CHUNK (MEMORY STORE) \u2500\u2500\u2500\nconst currentChunk = $input.first().json;\nconst chunkId = currentChunk.chunk_id ?? 0;\n\n// Get final English translation output from DeepL or Gemini\nconst cleanText = (currentChunk.translations?.[0]?.text || currentChunk.choices?.[0]?.message?.content || currentChunk.original_paragraph || \"\").trim();\n\nconst staticData = $getWorkflowStaticData('global');\nif (!staticData.processedChunks) {\n  staticData.processedChunks = {};\n}\n\n// Append chunk to memory\nstaticData.processedChunks[chunkId] = cleanText;\n\nreturn [{\n  json: {\n    status: \"saved\",\n    saved_chunk_id: chunkId,\n    total_saved_so_far: Object.keys(staticData.processedChunks).length,\n    saved_text: cleanText\n  }\n}];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        144,
        1136
      ],
      "id": "f436e4c3-8401-4e07-928d-64a23ce387ff",
      "name": "5. Save & Append Chunk"
    },
    {
      "parameters": {
        "jsCode": "// \u2500\u2500\u2500 6. COMBINE ALL SAVED CHUNKS IN ORDER \u2500\u2500\u2500\nconst staticData = $getWorkflowStaticData('global');\nconst processed = staticData.processedChunks || {};\n\nconst orderedIds = Object.keys(processed)\n  .map(id => parseInt(id, 10))\n  .sort((a, b) => a - b);\n\nconst finalText = orderedIds.map(id => processed[id]).join(\"\\n\\n\");\n\n// Reset memory for next run\nstaticData.processedChunks = {};\n\nreturn [{\n  json: {\n    status: \"complete\",\n    total_chunks: orderedIds.length,\n    final_text: finalText\n  }\n}];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -1168,
        944
      ],
      "id": "a9a2941e-39e1-4498-af3e-1f54cada2216",
      "name": "6. Combine Final Text"
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={{ { status: $json.status, total_chunks: $json.total_chunks, final_text: $json.final_text } }}",
        "options": {}
      },
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.1,
      "position": [
        -896,
        944
      ],
      "id": "e6cf9a76-6e59-475a-bcb9-185bff6849fd",
      "name": "7. Respond to Webhook"
    }
  ],
  "connections": {
    "Translate to EN": {
      "main": [
        []
      ]
    },
    "When chat message received": {
      "main": [
        [
          {
            "node": "Step 1: Chinese Rewrite (Gemini)2",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Step 1: Chinese Rewrite (Gemini)2": {
      "main": [
        [
          {
            "node": "Chinese to Japanaese",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Translate to DE1": {
      "main": [
        [
          {
            "node": "Translate to EN",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Step3: Japanese to German language": {
      "main": [
        [
          {
            "node": "Translate to DE1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Chinese to Japanaese": {
      "main": [
        [
          {
            "node": "Step3: Japanese to German language",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fix MIME Type": {
      "main": [
        [
          {
            "node": "Docx Extractor",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract PDF/Docx Text": {
      "main": [
        [
          {
            "node": "2. TEAR (Clean & Split)2",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "1. Upload File2": {
      "main": [
        [
          {
            "node": "Extract PDF/Docx Text",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "2. TEAR (Clean & Split)2": {
      "main": [
        [
          {
            "node": "Code in Python",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Webhook Upload (Turnitin PDF)": {
      "main": [
        [
          {
            "node": "Step 1 & 2: PyMuPDF Cyan Extractor",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Step 1 & 2: PyMuPDF Cyan Extractor": {
      "main": [
        []
      ]
    },
    "1. Upload PDF2": {
      "main": [
        [
          {
            "node": "Extract PDF/Docx Text5",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract PDF/Docx Text5": {
      "main": [
        [
          {
            "node": "2. Extract Chunks",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "2. Extract Chunks": {
      "main": [
        [
          {
            "node": "3. Loop Chunks (1-by-1)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "3. Loop Chunks (1-by-1)": {
      "main": [
        [
          {
            "node": "6. Combine Final Text",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Step 1: Chinese Rewrite (Gemini)4",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Step 1: Chinese Rewrite (Gemini)4": {
      "main": [
        [
          {
            "node": "Chinese to Japanaese6",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Chinese to Japanaese6": {
      "main": [
        [
          {
            "node": "Step3: Japanese to German language6",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Step3: Japanese to German language6": {
      "main": [
        [
          {
            "node": "Translate to DE4",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Translate to DE4": {
      "main": [
        [
          {
            "node": "Translate to EN7",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Translate to EN7": {
      "main": [
        [
          {
            "node": "5. Save & Append Chunk",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "5. Save & Append Chunk": {
      "main": [
        [
          {
            "node": "3. Loop Chunks (1-by-1)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "6. Combine Final Text": {
      "main": [
        [
          {
            "node": "7. Respond to Webhook",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "meta": {
    "templateCredsSetupCompleted": true
  }
}
Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

N8N Chain Translation. Uses httpRequest, chatTrigger, n8n-nodes-docx-extractor. Chat trigger; 31 nodes.

Source: https://github.com/Acnologia7021/turnitin-bypass-research/blob/master/n8n_chain_translation.json — original creator credit. Request a take-down →

More Web Scraping workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

Web Scraping

Analyze up to 100 URLs in one run and export key on-page SEO data to CSV automatically.

HTTP Request, Chat Trigger, Item Lists +1
Web Scraping

o1 - mini. Uses redis, httpRequest, chatTrigger. Chat trigger; 26 nodes.

Redis, HTTP Request, Chat Trigger
Web Scraping

pfe-hunter-sophistique. Uses lmChatGroq, chatTrigger, chainLlm, httpRequest. Chat trigger; 24 nodes.

Groq Chat, Chat Trigger, Chain Llm +1
Web Scraping

o1 - groq. Uses redis, httpRequest, chatTrigger. Chat trigger; 22 nodes.

Redis, HTTP Request, Chat Trigger
Web Scraping

Use cases are many: Compare costs across different models, plan your AI budget, optimize prompts for cost efficiency, or track expenses for client billing! OpenRouter charges a platform fee on top of

Chat Trigger, Execute Workflow Trigger, Form +4