AutomationFlowsGeneral › Translate Srt Files with Google Translate

Translate Srt Files with Google Translate

ByDanielV @danielv on n8n.io

This workflow is designed to translate SRT subtitle files from one language to another using Google Translate. Accept an SRT file upload and target language selection Extract and parse the SRT file content Split the content into translatable segments Translate each segment using…

Event trigger★★★★☆ complexity15 nodesGoogle TranslateFormForm Trigger
General Trigger: Event Nodes: 15 Complexity: ★★★★☆ Added:

This workflow corresponds to n8n.io template #3620 — we link there as the canonical source.

This workflow follows the Form → Form Trigger 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
{
  "id": "vssVsRO0FW6InbaY",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Translate",
  "tags": [],
  "nodes": [
    {
      "id": "7e55613e-c304-47cb-a017-2d912014ea8e",
      "name": "Split Out",
      "type": "n8n-nodes-base.splitOut",
      "position": [
        1180,
        140
      ],
      "parameters": {
        "options": {},
        "fieldToSplitOut": "txt"
      },
      "typeVersion": 1
    },
    {
      "id": "1ab3e545-e7a1-4b3d-a190-d38cb55ebf96",
      "name": "Google Translate",
      "type": "n8n-nodes-base.googleTranslate",
      "position": [
        1620,
        140
      ],
      "parameters": {
        "text": "={{ JSON.stringify($json.parts.secondPart) }}",
        "translateTo": "={{ $json.language }}"
      },
      "credentials": {
        "googleTranslateOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2
    },
    {
      "id": "07de7be3-5477-4e6c-b709-f632a3d5f162",
      "name": "Aggregate",
      "type": "n8n-nodes-base.aggregate",
      "position": [
        520,
        340
      ],
      "parameters": {
        "options": {},
        "aggregate": "aggregateAllItemData"
      },
      "typeVersion": 1
    },
    {
      "id": "cbe5892e-3661-42fb-a850-1e0448a53e0a",
      "name": "Edit Fields",
      "type": "n8n-nodes-base.set",
      "position": [
        960,
        340
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "498c663a-f372-40fb-9ac9-79f7a60875cc",
              "name": "complete_text",
              "type": "string",
              "value": "={{ $json.complete_text }}"
            },
            {
              "id": "34f3bc06-151d-4819-b6b8-515cf9c05c60",
              "name": "file",
              "type": "object",
              "value": "={{$('Receive SRT File to Translate').first().json}}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "a4e1cc2e-bd2f-4cf7-af03-73e43cda83d3",
      "name": "Convert to File",
      "type": "n8n-nodes-base.convertToFile",
      "position": [
        1400,
        340
      ],
      "parameters": {
        "options": {
          "fileName": "={{ $json['Upload SRT file'].filename.replaceAll('.srt',` ${$('Prep Parts for Translate').first().json.language}.srt`)}}",
          "mimeType": "={{ $json['Upload SRT file'].mimetype }}"
        },
        "operation": "toBinary",
        "sourceProperty": "=data",
        "binaryPropertyName": "file"
      },
      "typeVersion": 1.1
    },
    {
      "id": "380bc679-4e08-4d5d-a263-d3d873f4f38f",
      "name": "Split SRT Lines",
      "type": "n8n-nodes-base.code",
      "position": [
        960,
        140
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "let text = $json.data\n\ndelete $json.base64\ndelete $json.binary\n\n\n// Split by single newlines\nconst lines = text.split('\\n')\n\n// Create an array to hold grouped subtitle entries\nlet subtitleGroups = []\nlet currentGroup = []\n\n// Process each line\nfor (let i = 0; i < lines.length; i++) {\n  const line = lines[i].trim()\n  \n  // If line is empty and we have content in currentGroup, \n  // it's the end of a subtitle entry\n  if (line === '' && currentGroup.length > 0) {\n    subtitleGroups.push(currentGroup.join('\\n'))\n    currentGroup = []\n  } \n  // If line is not empty, add to current group\n  else if (line !== '') {\n    currentGroup.push(line)\n  }\n}\n\n// Add the last group if it has content\nif (currentGroup.length > 0) {\n  subtitleGroups.push(currentGroup.join('\\n'))\n}\n\n// Remove any quotes at the beginning and end of the first and last entries\nif (subtitleGroups.length > 0) {\n  subtitleGroups[0] = subtitleGroups[0].replace(/^\"/, '')\n  subtitleGroups[subtitleGroups.length - 1] = subtitleGroups[subtitleGroups.length - 1].replace(/\"$/, '')\n}\n\n// Store the result\n$input.item.json.txt = subtitleGroups\n\nreturn $input.item;"
      },
      "typeVersion": 2
    },
    {
      "id": "08215886-05f6-4ecc-9c1f-55c0e4cb6194",
      "name": "Generate Binary",
      "type": "n8n-nodes-base.code",
      "position": [
        1180,
        340
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "function encodeBase64(text) {\n  try {\n    // For browser environments\n    if (typeof window !== 'undefined') {\n      // First, create a UTF-8 encoded string\n      const utf8String = encodeURIComponent(text)\n        .replace(/%([0-9A-F]{2})/g, (_, hex) => {\n          return String.fromCharCode(parseInt(hex, 16));\n        });\n      \n      // Then encode to Base64\n      return btoa(utf8String);\n    } \n    // For Node.js environments\n    else if (typeof Buffer !== 'undefined') {\n      return Buffer.from(text).toString('base64');\n    }\n    \n    throw new Error('Environment not supported for Base64 encoding');\n  } catch (error) {\n    console.error('Error encoding to Base64:', error);\n    return null;\n  }\n}\n\nlet data = encodeBase64($json.complete_text);\n\nconsole.log(data)\n\nlet file = $json.file\n\nfile.data = data;\n\nlet paddingCount = 0;\nif (data.endsWith('==')) paddingCount = 2;\nelse if (data.endsWith('=')) paddingCount = 1;\n\n// Calculate the decoded size (in bytes)\nfile.size = Math.floor(data.length * 3 / 4) - paddingCount;\n\n\nreturn file"
      },
      "typeVersion": 2
    },
    {
      "id": "299122c1-61d1-4ce4-81b9-ce15d22cd49c",
      "name": "Prep Parts for Translate",
      "type": "n8n-nodes-base.code",
      "position": [
        1400,
        140
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "function splitBySecondNewline(text) {\n  // Find the position of the first newline\n  const firstNewlinePos = text.indexOf('\\n');\n  \n  if (firstNewlinePos === -1) {\n    return { firstPart: text, secondPart: '' }; // No newlines found\n  }\n  \n  // Find the position of the second newline\n  const secondNewlinePos = text.indexOf('\\n', firstNewlinePos + 1);\n  \n  if (secondNewlinePos === -1) {\n    return { firstPart: text, secondPart: '' }; // Only one newline found\n  }\n  \n  // Split the string at the second newline\n  const firstPart = text.substring(0, secondNewlinePos);\n  const secondPart = text.substring(secondNewlinePos + 1);\n  \n  return { firstPart, secondPart };\n}\n\nlet lang = $('Receive SRT File to Translate').first().json['Translate to Language']\n\nreturn {\n  parts: splitBySecondNewline($json.txt),\n  language: lang\n}"
      },
      "typeVersion": 2
    },
    {
      "id": "8a810ef3-febe-42f7-91c9-6c82dddcc93a",
      "name": "Clean Translations & Group Titles",
      "type": "n8n-nodes-base.code",
      "position": [
        300,
        340
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "let translated = $json.translatedText.replaceAll(\"\\\\n\",\"\\n\").replaceAll('&quot;',\"\").replaceAll('&#39;',\"'\");\n\nfunction splitIntoTwoLines(text, maxLength = 40) {\n  // If text already contains a newline or is short enough, return as is\n  if (text.includes('\\n') || text.length <= maxLength) {\n    return text;\n  }\n  \n  // Find the last space before or at the maxLength\n  let splitIndex = text.lastIndexOf(' ', maxLength);\n  \n  // If no space was found (rare case with very long words)\n  if (splitIndex === -1) {\n    splitIndex = maxLength; // Force split at maxLength\n  }\n  \n  // Split the text and join with a newline\n  const firstLine = text.substring(0, splitIndex);\n  const secondLine = text.substring(splitIndex + 1); // +1 to skip the space\n  \n  return firstLine + '\\n' + secondLine;\n}\n\n// Add a new field called 'myNewField' to the JSON of the item\n$input.item.json.complete = `${$('Prep Parts for Translate').item.json.parts.firstPart}\\n` + splitIntoTwoLines(translated)\n\nreturn $input.item;"
      },
      "typeVersion": 2
    },
    {
      "id": "15b2781c-4b6f-43e7-9ca9-6d6114e5fdab",
      "name": "Join completed text with double new line",
      "type": "n8n-nodes-base.code",
      "position": [
        740,
        340
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "let texts = $json.data.map(item=>{\n  return item.complete\n})\n\n\n$input.item.json.complete_text = texts.join('\\n\\n')\n\nreturn $input.item;"
      },
      "typeVersion": 2
    },
    {
      "id": "c43efbb6-3fe8-4aa3-8d65-ed3064bcc948",
      "name": "Respond with file",
      "type": "n8n-nodes-base.form",
      "position": [
        1620,
        340
      ],
      "parameters": {
        "options": {},
        "operation": "completion",
        "respondWith": "returnBinary",
        "completionTitle": "Done",
        "inputDataFieldName": "file"
      },
      "typeVersion": 1
    },
    {
      "id": "13103a23-3b1a-46d1-9731-c281ff1cac06",
      "name": "Receive SRT File to Translate",
      "type": "n8n-nodes-base.formTrigger",
      "position": [
        300,
        140
      ],
      "parameters": {
        "options": {
          "appendAttribution": false
        },
        "formTitle": "upload srt",
        "formFields": {
          "values": [
            {
              "fieldType": "dropdown",
              "fieldLabel": "Translate to Language",
              "fieldOptions": {
                "values": [
                  {
                    "option": "EN"
                  },
                  {
                    "option": "JP"
                  }
                ]
              },
              "requiredField": true
            },
            {
              "fieldType": "file",
              "fieldLabel": "Upload SRT file",
              "multipleFiles": false,
              "requiredField": true,
              "acceptFileTypes": ".srt"
            }
          ]
        },
        "responseMode": "lastNode"
      },
      "typeVersion": 2.2
    },
    {
      "id": "7e0f06f4-1e9d-436f-9310-325214e74bb9",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        280,
        -280
      ],
      "parameters": {
        "width": 760,
        "height": 300,
        "content": "## Required Credentials\nhttps://docs.n8n.io/integrations/builtin/credentials/google/\n\n## Selecting Language\nYou can update the form to include your preferred language code (that you are translating to), by updating the dropdown field with a new option. \nOr update the Google Translate node language option back to 'fixed' and select your desired language. This will ignore the form option, but is safe to do."
      },
      "typeVersion": 1
    },
    {
      "id": "29f9621e-3756-48ee-b6f0-e26a9f7aa247",
      "name": "Extract text from Binary File",
      "type": "n8n-nodes-base.extractFromFile",
      "position": [
        740,
        140
      ],
      "parameters": {
        "options": {},
        "operation": "text",
        "binaryPropertyName": "Upload_SRT_file"
      },
      "typeVersion": 1
    },
    {
      "id": "0924754e-6d1f-4d82-bb58-f64ebeac7b05",
      "name": "Expose Binary",
      "type": "n8n-nodes-base.code",
      "position": [
        520,
        140
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "// Add a new field called 'myNewField' to the JSON of the item\n$input.item.json.binary = $binary;\n\nreturn $input.item;"
      },
      "typeVersion": 2
    }
  ],
  "active": true,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "824adb39-806e-4d28-8e41-efd9f2e179a8",
  "connections": {
    "Aggregate": {
      "main": [
        [
          {
            "node": "Join completed text with double new line",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Split Out": {
      "main": [
        [
          {
            "node": "Prep Parts for Translate",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Edit Fields": {
      "main": [
        [
          {
            "node": "Generate Binary",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Expose Binary": {
      "main": [
        [
          {
            "node": "Extract text from Binary File",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Convert to File": {
      "main": [
        [
          {
            "node": "Respond with file",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate Binary": {
      "main": [
        [
          {
            "node": "Convert to File",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Split SRT Lines": {
      "main": [
        [
          {
            "node": "Split Out",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Google Translate": {
      "main": [
        [
          {
            "node": "Clean Translations & Group Titles",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prep Parts for Translate": {
      "main": [
        [
          {
            "node": "Google Translate",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract text from Binary File": {
      "main": [
        [
          {
            "node": "Split SRT Lines",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Receive SRT File to Translate": {
      "main": [
        [
          {
            "node": "Expose Binary",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Clean Translations & Group Titles": {
      "main": [
        [
          {
            "node": "Aggregate",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Join completed text with double new line": {
      "main": [
        [
          {
            "node": "Edit Fields",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}

Credentials you'll need

Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.

Pro

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

About this workflow

This workflow is designed to translate SRT subtitle files from one language to another using Google Translate. Accept an SRT file upload and target language selection Extract and parse the SRT file content Split the content into translatable segments Translate each segment using…

Source: https://n8n.io/workflows/3620/ — original creator credit. Request a take-down →

More General workflows → · Browse all categories →

Related workflows

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

General

Translate. Uses googleTranslate, form, formTrigger, stopAndError. Event-driven trigger; 16 nodes.

Google Translate, Form, Form Trigger +1
General

This interactive tutorial teaches you how to build in n8n from scratch, using a live walkthrough with real-time examples. Rather than static documentation, this guided workflow explains key n8n concep

Form Trigger, Form
General

This workflow contains community nodes that are only compatible with the self-hosted version of n8n.

Form Trigger, n8n, Form +2
General

Credentials Transfer. Uses form, httpRequest, executeCommand, readWriteFile. Event-driven trigger; 22 nodes.

Form, HTTP Request, Execute Command +2
General

Waitlist Form Stored In Googlesheet With Email Verification Step. Uses googleSheets, emailSend, form, stickyNote. Event-driven trigger; 19 nodes.

Google Sheets, Email Send, Form +2