Greasy Fork is available in English.

ACT.YouTube.DM.Auto-translate

Automatically translate any non-specified language Subtitles/CC.

// ==UserScript==
// @name               ACT.YouTube.DM.Auto-translate
// @name:zh-CN         ACT.YouTube.DM.自动翻译
// @description        Automatically translate any non-specified language Subtitles/CC.
// @description:zh-CN  自动翻译任何非指定语言字幕。
// @author             ACTCD
// @version            20231108.1
// @license            GPL-3.0-or-later
// @namespace          ACTCD/Userscripts
// @supportURL         https://github.com/ACTCD/Userscripts#contact
// @homepageURL        https://github.com/ACTCD/Userscripts
// @match              *://*.youtube.com/*
// @match              *://www.youtube-nocookie.com/embed/*
// @grant              none
// @inject-into        content
// @run-at             document-start
// ==/UserScript==

(function () {
	"use strict";

	const inline_script = () => {
		const tlang = navigator.language || "zh-CN"; // Specified language // 指定语言
		const cache = { req_url: null, obj_url: null };
		const XMLHttpRequest_open = XMLHttpRequest.prototype.open;
		XMLHttpRequest.prototype.open = function () {
			const url = new URL(arguments[1], location.href);
			if (url.pathname == "/api/timedtext") {
				const lang = url.searchParams.get("lang");
				if (lang && lang != tlang) {
					const req_url = url.href;
					if (req_url == cache.req_url) {
						arguments[1] = cache.obj_url;
					} else {
						URL.revokeObjectURL(cache.obj_url);
						this.addEventListener("load", (event) => {
							cache.req_url = req_url;
							cache.obj_url = URL.createObjectURL(
								new Blob([this.responseText], { type: "application/json" }),
							);
						});
						url.searchParams.set("tlang", tlang);
						arguments[1] = url.href;
					}
				}
			}
			XMLHttpRequest_open.apply(this, arguments);
		};
	};

	const div = document.createElement("div");
	div.style.display = "none";
	const shadowRoot = div.attachShadow({ mode: "closed" });
	const script = document.createElement("script");
	script.textContent = "(" + inline_script + ")();";
	shadowRoot.append(script);

	if (document.body) {
		document.body.append(div);
	} else {
		new MutationObserver((mutationList, observer) => {
			document.body && (observer.disconnect(), document.body.append(div));
		}).observe(document, { subtree: true, childList: true });
	}
})();