{
  "id": "iNkDWkBewhVKUVWf",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Auto-Translate YouTube Videos to Japanese with DeepL and Post to Slack",
  "tags": [],
  "nodes": [
    {
      "id": "8908066f-105b-4a8c-a642-921c10e16d25",
      "name": "RSS Trigger",
      "type": "n8n-nodes-base.rssFeedRead",
      "position": [
        -128,
        -160
      ],
      "parameters": {
        "url": "https://www.youtube.com/feeds/videos.xml?channel_id=UCYO_jab_esuFRV4b17AJtAw",
        "options": {}
      },
      "typeVersion": 1
    },
    {
      "id": "23e0fa4a-a16e-4e11-b9b5-879c1001f324",
      "name": "Send to Slack",
      "type": "n8n-nodes-base.slack",
      "position": [
        1616,
        -256
      ],
      "parameters": {
        "text": "=\ud83c\udfa5 *New YouTube Video!*  \n\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501  \n\ud83d\udccc *\u30bf\u30a4\u30c8\u30eb*  \n{{ $json.title }}\n\n\ud83d\udcdd *\u8aac\u660e*  \n{{ $json.description }}\n\n\ud83c\udf0d *Original Title*  \n{{ $('Merge YouTube + DeepL Results1').item.json.title }}\n\n\ud83d\uddd2\ufe0f *Original Description*  \n{{ $('Merge YouTube + DeepL Results1').item.json.description }}\n\n\ud83d\udd17 *Link*  \n<{{ $('Format YouTube Data for DeepL').item.json.link }}|Watch on YouTube>",
        "channel": "=#general",
        "attachments": [],
        "otherOptions": {},
        "authentication": "oAuth2"
      },
      "credentials": {
        "slackOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "ebff7150-ee6d-4dad-ad03-a29944fcf28a",
      "name": "Get a video",
      "type": "n8n-nodes-base.youTube",
      "position": [
        496,
        -256
      ],
      "parameters": {
        "part": [
          "snippet"
        ],
        "options": {},
        "videoId": "={{ String($json.YT_CHANNEL_ID || $json.id || $json.link || '')\n    .replace(/^yt:video:/,'')\n    .replace(/^.*(?:v=|shorts\\/|youtu\\.be\\/)/,'')\n    .split(/[?&]/)[0] }}",
        "resource": "video",
        "operation": "get"
      },
      "credentials": {
        "youTubeOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "a461efd7-0ffc-4a56-b270-a064b0a91d3e",
      "name": "Send a message",
      "type": "n8n-nodes-base.slack",
      "position": [
        496,
        -64
      ],
      "parameters": {
        "text": "= *\u30b9\u30ad\u30c3\u30d7\u3055\u308c\u305f\u52d5\u753b\u3092\u691c\u51fa\u3057\u307e\u3057\u305f* \ud83d\udcfa \u30bf\u30a4\u30c8\u30eb: {{$json.title || $json.snippet.title}} \ud83d\udd17 URL: {{$json.link || `https://www.youtube.com/watch?v=${$json.id}`}} \u26a0\ufe0f \u7406\u7531: \u82f1\u8a9e\u30bf\u30a4\u30c8\u30eb\u3067\u306f\u306a\u3044\u305f\u3081\u3001\u7ffb\u8a33\u5bfe\u8c61\u5916\u3068\u3057\u307e\u3057\u305f",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "list",
          "value": "CKUCBTG0H",
          "cachedResultName": "general"
        },
        "otherOptions": {},
        "authentication": "oAuth2"
      },
      "credentials": {
        "slackOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "bfd54f44-77e7-4ba0-a523-e6cd2b8d432f",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -960,
        -592
      ],
      "parameters": {
        "width": 432,
        "height": 1232,
        "content": "## Overview\nAuto-translate YouTube uploads to Japanese and post to Slack (DeepL + Slack)\n\n## Who\u2019s it for\nMarketing or community teams that follow English-speaking creators but share updates with Japanese audiences; language learners who want JP summaries of newly released videos; internal comms teams curating industry channels for a JP workspace.\n\n## What it does\nThis workflow detects new YouTube uploads, retrieves full metadata, translates the title and description into Japanese using DeepL, and posts a formatted message to a Slack channel. It also skips non-English titles to avoid unnecessary translation.\n## How it works\n\u30fbRSS watches a channel for new items.\n\u30fbYouTube API fetches the full snippet (title/description).\n\u30fbText is combined into a single payload and sent to DeepL.\n\u30fbThe translated result + original metadata is merged and posted to Slack.\n\n## Requirements\n\u30fbYouTube OAuth (for reliable snippet retrieval)\n\u30fbDeepL API key (Free or Pro)\n\u30fbSlack OAuth\n\n## How to set up\n\u30fbDuplicate this template.\n\u30fbOpen the Config (Set) node and fill in YT_CHANNEL_ID, TARGET_LANG, SLACK_CHANNEL.\n\u30fbConnect credentials for YouTube, DeepL, and Slack (don\u2019t hardcode API keys in HTTP nodes).\n\u30fbClick Execute workflow and verify one sample post.\n\n## How to customize\n\u30fbChange TARGET_LANG to any language supported by DeepL.\n\u30fbAdd filters (exclude Shorts, skip videos under N characters).\n\u30fbSwitch to Slack Blocks for richer formatting or thread replies.\n\u30fbAdd a fallback translator or retry logic on HTTP errors.\n## Notes & limits\nDeepL Free/Pro have different endpoints/quotas and monthly character limits. YouTube and Slack also enforce rate limits. Keep credentials in n8n\u2019s credential store; do not commit keys into templates. Rotate keys if you accidentally exposed them."
      },
      "typeVersion": 1
    },
    {
      "id": "dd5f0eef-4159-40db-a27d-02f318ea467a",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        512,
        112
      ],
      "parameters": {
        "color": 7,
        "height": 304,
        "content": "## Send a Message (Skipped)\n\ud83d\udcd8 Setup Steps\n1\ufe0f\u20e3 Connect YouTube, DeepL, and Slack credentials\n2\ufe0f\u20e3 Update the RSS channel ID\n3\ufe0f\u20e3 Adjust Slack channel name if needed\n4\ufe0f\u20e3 Test with one video manually\n\n(Optional) Embed a quick Loom video for reviewers."
      },
      "typeVersion": 1
    },
    {
      "id": "988d0c76-7f0c-4992-bae8-f3bb51369203",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        768,
        -32
      ],
      "parameters": {
        "color": 7,
        "width": 256,
        "height": 288,
        "content": "## Format YouTube Data for DeepL\n\n\ud83e\udde0 Prepare Translation Input\nCombines YouTube data into a single formatted text:\n\u3010Title\u3011\n{title}\n\n\u3010Description\u3011\n{description}\n"
      },
      "typeVersion": 1
    },
    {
      "id": "f790cfce-8057-4636-baee-7dc5939c045b",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        256,
        -368
      ],
      "parameters": {
        "color": 7,
        "height": 192,
        "content": "## Language Filter\n\ud83d\udd0e Filter English Titles Only\nSkips non-English titles based on regex (^[A-Za-z0-9\\\\s\\\\W]+$).\nIf true \u2192 proceed to translation.\nIf false \u2192 send \u201cskipped\u201d message to Slack."
      },
      "typeVersion": 1
    },
    {
      "id": "81f79bc3-f47d-4e9d-81d1-cd3bc0255351",
      "name": "Sticky Note6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -176,
        -32
      ],
      "parameters": {
        "color": 7,
        "width": 224,
        "height": 224,
        "content": "## RSS Trigger\n\n\u23f0 Trigger for new uploads\nChecks the target YouTube channel RSS feed for new video posts.\nAutomatically passes new entries to the next node."
      },
      "typeVersion": 1
    },
    {
      "id": "85c85131-fbd2-4345-a74c-1caf63f8d270",
      "name": "Sticky Note7",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1168,
        -64
      ],
      "parameters": {
        "color": 7,
        "height": 208,
        "content": "## Merge\n\n\ud83d\udd17 Combine Original + Translated Data\nJoins DeepL translation results with YouTube metadata.\nOutput: unified object ready for Slack posting."
      },
      "typeVersion": 1
    },
    {
      "id": "f20df73e-1c72-4847-9a03-8488349b73a5",
      "name": "Sticky Note8",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        976,
        -512
      ],
      "parameters": {
        "color": 7,
        "height": 272,
        "content": "## Translate with DeepL\n\n\ud83c\udf0d Translate text into Japanese\nUses the DeepL API (POST request).\nTranslates the combined YouTube text.\nReturns JSON with translated output.\n\u26a0\ufe0f Never hardcode API keys \u2014 store in credentials."
      },
      "typeVersion": 1
    },
    {
      "id": "8194a237-a490-4354-bb0e-9fe885ac6824",
      "name": "Sticky Note9",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        528,
        -480
      ],
      "parameters": {
        "color": 7,
        "height": 192,
        "content": "## Get a Video\n\ud83c\udfac Retrieve Full Video Details\nUses the YouTube API to fetch snippet information such as title and description.\nInput: video ID from RSS.\nOutput: structured JSON for downstream processing."
      },
      "typeVersion": 1
    },
    {
      "id": "6b5e5ef1-b928-43fb-aab7-64b2414c3ea0",
      "name": "Language Filter",
      "type": "n8n-nodes-base.if",
      "position": [
        272,
        -160
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "00af14f6-2d40-4fb3-bdb1-a16438529179",
              "operator": {
                "type": "string",
                "operation": "regex"
              },
              "leftValue": "={{ String($('RSS Trigger').item.json?.title ?? '') }}",
              "rightValue": "^[A-Za-z0-9\\s\\W]+$"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "239c591d-0625-4257-8f27-1c087058736f",
      "name": "Format YouTube Data for DeepL",
      "type": "n8n-nodes-base.code",
      "position": [
        720,
        -256
      ],
      "parameters": {
        "jsCode": "// --- YouTube\u30c7\u30fc\u30bf\u306e\u53d6\u5f97 ---\nconst item = $json;\n\n// RSS \u304b\u3089\u306f title / link \u3060\u3051\u3001YouTube\u30ce\u30fc\u30c9\u304b\u3089\u306f snippet \u69cb\u9020\u3092\u60f3\u5b9a\nconst title = item.snippet?.title || item.title || \"No title\";\nconst description = item.snippet?.description || item.description || \"No description\";\nconst videoId = item.id?.videoId || item.id?.replace(\"yt:video:\", \"\") || \"unknown\";\nconst link = item.link || `https://www.youtube.com/watch?v=${videoId}`;\n\n// --- \u6574\u5f62\uff08DeepL\u306b\u6e21\u3059\u5f62\uff09 ---\nconst textToTranslate = `\n\u3010Title\u3011\n${title}\n\n\u3010Description\u3011\n${description}\n`;\n\n// --- DeepL\u306b\u6e21\u3059JSON\u5f62\u5f0f\u3067\u51fa\u529b ---\nreturn [\n  {\n    json: {\n      title,\n      description,\n      videoId,\n      link,\n      textToTranslate,\n    }\n  }\n];"
      },
      "typeVersion": 2
    },
    {
      "id": "468941d1-78ab-4a00-a539-d86b81c2c7c7",
      "name": "Sticky Note10",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -432,
        -480
      ],
      "parameters": {
        "color": 7,
        "width": 224,
        "height": 352,
        "content": "## Setup checklist\n1) Connect: YouTube OAuth / DeepL / Slack\n2) Open \u201cSet (Config)\u201d and fill: YT_CHANNEL_ID, TARGET_LANG, SLACK_CHANNEL\n3) Replace DeepL HTTP node with DeepL credential (no hardcoded key)\n4) Run once with a recent video to validate\n5) Optional: turn on Error Trigger \u2192 Slack #alerts\n"
      },
      "typeVersion": 1
    },
    {
      "id": "c2cc4803-9017-4fef-82ad-b1023eace6d6",
      "name": "Translate a language",
      "type": "n8n-nodes-base.deepL",
      "position": [
        944,
        -192
      ],
      "parameters": {
        "text": "={{ $json.textToTranslate }}",
        "translateTo": "JA",
        "additionalFields": {}
      },
      "credentials": {
        "deepLApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "494aff32-6c93-4623-bc66-0138e5cf0694",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1648,
        -48
      ],
      "parameters": {
        "color": 7,
        "height": 192,
        "content": "## Send to Slack\n\ud83d\udcac Post Translated Message to Slack\nSends formatted message to a selected channel.\nIncludes both translated and original texts with a video link.\nExample output:"
      },
      "typeVersion": 1
    },
    {
      "id": "a0179356-9df6-4628-aee6-418cda343144",
      "name": "Merge YouTube + DeepL Results1",
      "type": "n8n-nodes-base.merge",
      "position": [
        1168,
        -256
      ],
      "parameters": {
        "mode": "combine",
        "options": {},
        "combineBy": "combineAll"
      },
      "typeVersion": 3.2
    },
    {
      "id": "63f64775-51e9-4e31-aa66-8662a9f2f6dc",
      "name": "Format Translated Output",
      "type": "n8n-nodes-base.code",
      "position": [
        1392,
        -256
      ],
      "parameters": {
        "jsCode": "// \u5165\u529b\u30c7\u30fc\u30bf\u53d6\u5f97\nconst text = $json.text || '';\n\n// \u30bf\u30a4\u30c8\u30eb\u884c\u3092\u62bd\u51fa\nconst titleMatch = text.match(/\u30bf\u30a4\u30c8\u30eb[^\\n]*\\n(.*?)\\n/);\nconst title = titleMatch ? titleMatch[1].trim() : '';\n\n// \u8aac\u660e\u884c\u3092\u62bd\u51fa\nconst descMatch = text.match(/\u8aac\u660e[^\\n]*\\n([\\s\\S]*)/);\nconst description = descMatch ? descMatch[1].trim() : '';\n\n// Slack\u3067\u898b\u3084\u3059\u3044\u3088\u3046\u306b\u6210\u5f62\nreturn {\n  json: {\n    title,\n    description\n  }\n};\n"
      },
      "typeVersion": 2
    },
    {
      "id": "24384f42-e32b-4ec0-b5de-66c754faa639",
      "name": "Sticky Note12",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1344,
        -704
      ],
      "parameters": {
        "color": 7,
        "width": 320,
        "height": 416,
        "content": "##  Format Translated Output\n\nThis Code node formats the DeepL translation result into a clean JSON object \nthat can be sent to Slack or other integrations.\nInput:\n- Translated text (from DeepL)\n- Original title and description\nOutput:\n{\n  \"translated_title\": \"...\",\n  \"translated_description\": \"...\",\n  \"original_title\": \"...\",\n  \"url\": \"...\"\n}\n\nTip:\nIf you modify the Slack message format, adjust field names here."
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "dc7904c4-4ad8-43fe-a3a4-87b66973cf02",
  "connections": {
    "Get a video": {
      "main": [
        [
          {
            "node": "Format YouTube Data for DeepL",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "RSS Trigger": {
      "main": [
        [
          {
            "node": "Language Filter",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send a message": {
      "main": [
        []
      ]
    },
    "Language Filter": {
      "main": [
        [
          {
            "node": "Get a video",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Send a message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Translate a language": {
      "main": [
        [
          {
            "node": "Merge YouTube + DeepL Results1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Format Translated Output": {
      "main": [
        [
          {
            "node": "Send to Slack",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Format YouTube Data for DeepL": {
      "main": [
        [
          {
            "node": "Translate a language",
            "type": "main",
            "index": 0
          },
          {
            "node": "Merge YouTube + DeepL Results1",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Merge YouTube + DeepL Results1": {
      "main": [
        [
          {
            "node": "Format Translated Output",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}