Is Udemy Dual Subtitles - Subtitle Translator safe?
Udemy Dual Subtitles sends tab URL, tab title, and a persistent user UUID to its own backend and to Amplitude on every subtitle translation request.
Each time the extension translates subtitles, it POSTs the current tab URL, tab title, source and target languages, and a persistent device UUID to api.udemydualsubtitles.com. Separately, the background service worker forwards tab URL and tab title as properties of every runtime message event to Amplitude's analytics API, including changes to extension storage. The UUID is generated once and stored in chrome.storage.local, allowing the developer to correlate requests across sessions.
AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.
Publishers can request a review.
Findings
Subtitle translation sends Udemy tab details to backend
The source code shows a translation request sends the Udemy tab URL, tab title, page origin, subtitle text, and language choices to api.udemydualsubtitles.com, plus an install UUID from local storage (ga.cid), generated on first use.
What actually happens
You request translated subtitles while viewing a Udemy course page.
The content script packages the selected subtitle text with source and target languages.
The extension posts that subtitle request with the Udemy tab URL, tab title, page origin, and stored installation UUID.
The request goes to api.udemydualsubtitles.com over HTTPS.
Fields included in the translation POST
- Installation identifier4f8a2b7c-91d3-4c6e-a51d-6b0f9a8e1234
Lets separate translation requests be tied back to the same extension installation.
- Udemy page URLhttps://www.udemy.com/course/python-for-data-science/learn/lecture/12345678
Shows the exact course or lecture page where you requested translation.
- Page originhttps://www.udemy.com
Shows the site context for the translation request.
- Page titlePython for Data Science - Lecture 12: Pandas Basics
Reveals the course or lesson title visible in the browser tab.
- Subtitle textIn this lecture we will load the CSV file and inspect the first rows.
Contains the caption text selected for translation.
- Language choicesfrom=en, to=es
Shows the source and target languages selected for the translation.
Captured request
- Content-Type
- application/json
The code that does this
The translation path carries tab details into the backend request
Content script sends the translate command
content.jsreturn ( C.debug("sendMessage, cmd: translate, from:", t, "to:", n), (e.next = 3), chrome.runtime .sendMessage({ cmd: "translate", payload: { from: t, to: n, q: B(r.slice(o, o + u)) }, }) .then(function (e) { C.debug("sendMessage[translate] rsp:", JSON.stringify(e)); if (!e.errcode) { _(!1); _(function (t) { C.debug("getCues2Processing state:", t); v(W(r, g, e.rslt)); return t; }); } }));Service worker reads or creates ga.cid
background.jsU = (function () { var e = u( a().mark(function e() { var t, n, r; return a().wrap(function (e) { for (;;) switch ((e.prev = e.next)) { case 0: return ( (t = f.storageKeys.gaClientId), (n = null), (e.next = 1), chrome.storage.local.get([t]) ); case 1: return ( (r = e.sent), m.debug("storage get:", r), r[t] ? (m.debug("got id from storage: ", r[t]), (n = r[t])) : ((n = k()), m.debug("generate id from uuidv4: ", n), chrome.storage.local.set(p({}, t, n))), e.abrupt("return", n) ); case 2: case "end": return e.stop(); } }, e); }), ); return function () { return e.apply(this, arguments); };})();Service worker builds and posts the translation JSON
background.js(P = e.sent),(N = n.origin),(C = null == n || null === (u = n.tab) || void 0 === u ? void 0 : u.url),(j = null == n || null === (s = n.tab) || void 0 === s ? void 0 : s.title),(L = t.payload.from),(U = t.payload.to),(D = t.payload.q),(M = O.stringify.call(O, { ext: "UdemyDualSubtitles", cid: P, origin: N, tab_url: C, tab_title: j, from: L, to: U, q: D,})),(e.next = 4),x(k, { method: "POST", mode: "cors", headers: T, body: M }) .then(function (e) { return e.json(); }) .then(function (e) { return e.trans_result; }) .catch(function (e) { return m.error(e), null; });Destination receiving the translation request
- api.udemydualsubtitles.com
Extension backend that receives subtitle translation POSTs with cid, origin, tab_url, tab_title, language choices, and q.
Runtime messages attach Udemy tab details to Amplitude
The source code logs each background runtime message as an Amplitude event, Bg Runtime Message, with properties including the request object plus sender.url/tab.title/tab.url, sent to api2.amplitude.com/2/httpapi.
What actually happens
You use an extension feature that sends a message to the background service worker.
Translation, license, and usage commands are routed through the runtime-message handler.
The service worker records the message as an Amplitude analytics event with the current tab URL and title.
The same service worker also records extension storage changes with old and new values.
Analytics properties populated by the service worker
- Current tab URLhttps://www.udemy.com/course/python-for-data-science/learn/lecture/12345678
Shows which Udemy page was open when the extension processed the message.
- Current tab titlePython for Data Science - Lecture 12: Pandas Basics
Reveals the course or lesson title visible in the browser tab.
- Sender URLhttps://www.udemy.com/course/python-for-data-science/learn/
Adds the page context that sent the message to the background service worker.
- Extension requestcmd=translate, from=en, to=es
Includes the command and payload associated with the feature you used.
- Changed storage valueskey=lastSubtitleLang, oldValue=en, newValue=es
Records extension settings or state before and after a storage update.
Captured request
The code that does this
Runtime messages and storage changes flow into Amplitude events
Amplitude HTTP API endpoint
background.jsvar ce = "".concat("AMP", "_unsent"), le = "https://api2.amplitude.com/2/httpapi", fe = "dclid", de = "fbclid", pe = "gbraid", he = "gclid", ve = "ko_click_id", ge = "li_fat_id";Analytics wrapper calls Amplitude track
background.jskr = (function () { var e = u( a().mark(function e(t, n) { var r; return a().wrap( function (e) { for (;;) switch ((e.prev = e.next)) { case 0: return ((e.prev = 0), (e.next = 1), Sr(t, n)); case 1: e.next = 3; break; case 2: ((e.prev = 2), (r = e.catch(0)), m.error("[amp] catch err:", r.message)); case 3: case "end": return e.stop(); } }, e, null, [[0, 2]], ); }), ); return function (t, n) { return e.apply(this, arguments); };})();Runtime message event includes request and tab fields
background.jskr("Bg Runtime Message", { extname: f.ext.name, status: "begin", cmd: e.cmd, req: e, sender: { url: null == t ? void 0 : t.url, tab: { title: null == t || null === (r = t.tab) || void 0 === r ? void 0 : r.title, url: null == t || null === (i = t.tab) || void 0 === i ? void 0 : i.url, }, },});var o = null;return "translate" === e.cmd ? (Pr(e, t, n), !0) : "license" === e.cmd ? (Rr(e, t, n), !0) : "usage" === e.cmd ? (Cr(e, t, n), !0) : (m.error("wrong req.cmd"), n((o = { errcode: -1, errmsg: "wrong req.cmd" })), void m.debug("rsp:", o));Storage changes include old and new values
background.jskr("Bg Storage Change", { extname: f.ext.name, namespace: t, key: u, oldValue: a, newValue: c,});sendBeacon transport serializes the event payload
background.jso.navigator.sendBeacon(e, JSON.stringify(t)) ? n.buildResponse({ code: 200, events_ingested: t.events.length, payload_size_bytes: u.length, server_upload_time: Date.now(), }) : n.buildResponse({ code: 500 });Analytics destination
- api2.amplitude.com
Amplitude HTTP API endpoint that receives extension analytics events and their event properties.
Where it sends data
Destinations our analysis observed Udemy Dual Subtitles - Subtitle Translator contacting. Sending data somewhere is not a finding on its own - an extension that syncs your settings has to talk to its own server - but it is where your data can go, and who else it goes to.
- api2.amplitude.comwidely used
Udemy Dual Subtitles - Subtitle Translator sends data to api2.amplitude.com. A widely used service: 42 other extensions we have analyzed send data here.
- api.udemydualsubtitles.com
Udemy Dual Subtitles - Subtitle Translator sends data to api.udemydualsubtitles.com. No other extension we have analyzed sends data here.