Is Google Translate Assistant – Side-by-side Translation | PDF Translation safe?
Google Translate Assistant sends page URLs, device identifiers, and tab screenshots to its vendor servers during normal use.
On every user action and daily check-in, the extension transmits a persistent device UUID, user ID, IP country code, and OS fingerprint to translate-useraction-api.xiangtatech.com. When auto-translate is checked for a page, the full URL of that page is sent to translate-api.xiangtatech.com to determine eligibility. If the user invokes the OCR translate feature, a screenshot of the visible tab is captured and uploaded to the same vendor server for processing.
Who publishes it轻主页 - 3 other listings from the same operator, none carrying a finding
轻主页 - 3 other listings from the same operator, none carrying a finding
What this publisher told the store about itself, and the other listings that told it the same thing.
Same store account
3 other listings published from this account, 24k+ users between them, none of them carrying a finding.
Shared hosts - 11 hostnames
Hostnames hardcoded in this extension that few other listings call. That can mean one operator behind both, and it can equally mean a small shared vendor, so it is context rather than a conclusion. Hosts that many listings call are left out: they are services, not connections.
AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.
Publishers can request a review.
Findings
OCR translation uploads selected page images
OCR translation captures the visible tab as a PNG for cropping.
After you confirm the crop, the image and language choices go into multipart form data posted to the picture-translation endpoint.
A runtime test did not trigger this path.
What actually happens
You start screenshot translation and confirm a cropped region of the current page.
The extension captures the visible tab, converts your selected crop into a PNG file, and sends it for picture translation.
Multipart fields prepared for picture translation
- Selected page imageimage.png from the selected crop
Sends the visible text and graphics inside the crop you confirm.
- Source languagefrom auto
Tells the translation service which language the selected image starts from.
- Target languageto en
Tells the translation service which language to translate into.
- Page range markersstart_page -1, end_page -1
Adds fixed page-range fields even though the upload is a screenshot image.
Captured request
Source-code proof: the shipped code posts multipart form data containing file, from, to, start_page, and end_page. The runtime test recorded no request to this endpoint.
The code that does this
The screenshot path captures the tab and uploads the cropped image
assets/background.js beautified screenshot case
assets/background.js case "screenshot": chrome.tabs.captureVisibleTab(t.tab.windowId, { format: "png" }, (e => n(e))); break;assets/main6.js beautified screenshot methods
assets/main6.js enter() { this.captureing || (this.captureing = !0, chrome.runtime.sendMessage({ type: "screenshot" }, (e => { this.bodyOverflow = document.documentElement.style.overflow, document.documentElement.style.overflow = "hidden", document.addEventListener("keydown", this.onKeydown_), this.imgURL = e, this.isShow = !0 }))) }, confirm() { if (!this.cropper) return this.cancel(); const e = { from: this.sourceItem.gid, to: this.targetItem.gid }; this.cropper.getCroppedCanvas().toBlob((t => { this.cancel(), this.$emit("confirm", t, e) })) }assets/main6.js beautified OCR upload method
assets/main6.js ocr(e, t) { var n, o; if (this.loading) return void eo(this.$t("ocr.loading"), { appendToEl: this.$refs.box }); if (!e && !this.ocrBlob) return; this.initOriginImg(e || this.ocrBlob), t && (t.from && (null == (n = this.$refs.switchLangRef) || n.setSourceLang(t.from)), t.to && (null == (o = this.$refs.switchLangRef) || o.setTargetLang(t.to))), this.showSceneCoupon = !1, e ? this.ocrBlob = e : e = this.ocrBlob; const a = new FormData, i = new File([e], "image.png", { type: e.type }); a.append("file", i), a.append("from", this.sourceItem.gid), a.append("to", this.targetItem.gid), a.append("start_page", -1), a.append("end_page", -1), ao({ url: "https://translate-api.xiangtatech.com/translate/translation/webs/picture", type: "post", formData: a, dataType: "json", success: e => { var t, n; e.data ? (this.clearTransData(), this.loading = !0, this.queryTaskTime(null == (t = e.data) ? void 0 : t.file_translation_id), this.isShow = !0, this.currentTaskId = null == (n = e.data) ? void 0 : n.file_translation_id) : (this.ocrError(e.msg || this.$t("ocr.noText")), this.showErrorDialog = !0) }, error: e => { var t, n; 1011 !== (null == (t = e.response) ? void 0 : t.code) ? 406 !== (null == (n = e.response) ? void 0 : n.code) ? (this.ocrError(e.response && (e.response.msg || e.response.message) || this.$t("ocr.fail"), e.response && e.response.code || e.status), this.showErrorDialog = !0) : eo(this.$t("ocr.notSupportedFile")) : eo(this.$t("ocr.loading"), { appendToEl: this.$refs.box }) } }) },assets/main6.js beautified OCR entry point
assets/main6.js startOcrTranslate(e) { return c(this, null, (function*() { if (yield this.closeOpenedFunDialog(), !this.canScreenshot) return; this.inScreenshot = !0, setTimeout((() => { this.$refs.screenshotTool.enter() }), 24); const t = { param15: "default" }; e && Object.assign(t, e), this.$mergeSubmitReport({ event_id: 107, extend_fields: t }) })) }, onScreenshotConfirm(e, t) { return c(this, null, (function*() { var n; this.inScreenshot = !1, null == (n = this.$refs.imageTranslate) || n.close((() => { var n; null == (n = this.$refs.ocrTranslate) || n.ocr(e, t) })) })) },Picture-translation destination
- translate-api.xiangtatech.com
Vendor picture-translation endpoint that receives the cropped screenshot image form.
Persistent browser identifiers sent in action reports
The extension posts a user-action report with a persistent browser-ID prefix, OS values, event IDs, and IP-country context to translate-api.kangaroooffice.com.
It reads stored account/device state, posts reports on install and daily checks.
What actually happens
You browse or use extension features while Google Translate Assistant is active.
The extension builds a user-action report with a persistent browser identifier, device details, event IDs, and account fields when available.
Captured request
Observed during dynamic analysis: pcid beginning fe13fa8a, os_version 41658296553177090, os_type 1, event_id 1, extend_fields param10=美国; repeated reports used event IDs 100, 101, and 243.
- Content-Type
- application/json
Fields assembled for the user-action report
- Persistent browser identifierpcid beginning fe13fa8a
Lets the reporting service connect separate reports from the same browser over time.
- Account identifier when signed inuser_id 92837465 (illustrative)
Links the report to the extension account if account data is stored locally.
- IP-country context美国
Adds the country associated with your network location to the report.
- Operating system detailsos_type 1, os_version 41658296553177090
Adds device context that helps distinguish browsers and environments.
- Extension event IDsevent_id 1, 100, 101, or 243
Shows which extension action or scheduled check produced the report.
The code that does this
The shipped report builders read storage fields and post the report
assets/browser.js beautified function mu
assets/browser.jsfunction mu(e) { chrome.storage.local.get(["uuid", "token", "loginRegion", "loginType", "userInfo", "lastRefreshUserInfoTime", "ipCountry", "extShareUnionId", "reportAgree"], (({ uuid: t, token: n, loginRegion: a, loginType: r, userInfo: i, lastRefreshUserInfoTime: o, ipCountry: s, extShareUnionId: l, reportAgree: c }) => { t = (t = t || "").length > 32 ? t.slice(0, 32) : t.padStart(32, "0"); const _ = f({ token: n, ipCountry: s, loginRegion: a }), d = { bs: "bs", sub_module: "TranslateAssistant", appid: Number("US" === _ ? "132" : "113"), unionid: Number(l) || Number("3000") || 1, version: gu("1.2.0.2"), pcid: t, os_version: gu(Eu().version), arch: 65535, os_type: "mac" === bu() ? 5 : 1, events: [] }, p = { c_timestamp: Math.round(Date.now() / 1e3), extend_fields: { scene: 300, param13: "3000" } }; if (s && (p.extend_fields.param10 = s), n && (p.extend_fields.param11 = (r || "wx") + "Login", null == i ? void 0 : i.user_id)) { if (p.extend_fields.user_id = i.user_id + "", o && i.buy_right_package) { const e = new Date - o; e > 0 && (i.server_time += Math.round(e / 1e3), i.is_member = i.buy_right_package.some((e => e.unlimited || new Date(e.expire_at).getTime() / 1e3 > i.server_time))) } p.extend_fields.param1 = i.is_member ? 1 : 0 } e.forEach((([e]) => { var t, a; "string" != typeof e && "number" != typeof e || (e = { event_id: Number(e) }); const r = Ap({}, p); 129 === e.event_id && n && (null == (a = null == (t = null == i ? void 0 : i.buy_right_package) ? void 0 : t[0]) ? void 0 : a.buy_time) && (r.extend_fields.param12 = i.buy_right_package[0].buy_time), e = Ap(r, e), d.events.push(e) })), jp({ url: "https://translate-useraction-api.xiangtatech.com/api/v2/report", type: "post", noAppId: !0, noToken: !0, headers: { "Content-Type": "application/json", "Sign-Type": 0, Business: "TranslateAssistant" }, data: d, success() { e && e.forEach((([, e]) => { "function" == typeof(null == e ? void 0 : e.success) && e.success() })), e = null }, error() { e && e.forEach((([, e]) => { "function" == typeof(null == e ? void 0 : e.error) && e.error() })), e = null } }) }))}js/content-start.js beautified function Rp
js/content-start.js function Rp(e) { chrome.storage.local.get(["uuid", "token", "loginRegion", "loginType", "userInfo", "lastRefreshUserInfoTime", "ipCountry", "extShareUnionId", "reportAgree"], (({ uuid: t, token: n, loginRegion: a, loginType: r, userInfo: i, lastRefreshUserInfoTime: o, ipCountry: s, extShareUnionId: l, reportAgree: c }) => { t = (t = t || "").length > 32 ? t.slice(0, 32) : t.padStart(32, "0"); const _ = Ep({ token: n, ipCountry: s, loginRegion: a }), d = { bs: "bs", sub_module: "TranslateAssistant", appid: Number("US" === _ ? "132" : "113"), unionid: Number(l) || Number("3000") || 1, version: vp("1.2.0.2"), pcid: t, os_version: vp(Np().version), arch: 65535, os_type: "mac" === yp() ? 5 : 1, events: [] }, p = { c_timestamp: Math.round(Date.now() / 1e3), extend_fields: { scene: 300, param13: "3000" } }; if (s && (p.extend_fields.param10 = s), n && (p.extend_fields.param11 = (r || "wx") + "Login", null == i ? void 0 : i.user_id)) { if (p.extend_fields.user_id = i.user_id + "", o && i.buy_right_package) { const e = new Date - o; e > 0 && (i.server_time += Math.round(e / 1e3), i.is_member = i.buy_right_package.some((e => e.unlimited || new Date(e.expire_at).getTime() / 1e3 > i.server_time))) } p.extend_fields.param1 = i.is_member ? 1 : 0 } e.forEach((([e]) => { var t, a; "string" != typeof e && "number" != typeof e || (e = { event_id: Number(e) }); const r = _p({}, p); 129 === e.event_id && n && (null == (a = null == (t = null == i ? void 0 : i.buy_right_package) ? void 0 : t[0]) ? void 0 : a.buy_time) && (r.extend_fields.param12 = i.buy_right_package[0].buy_time), e = _p(r, e), d.events.push(e) })), Tp({ url: "https://translate-useraction-api.xiangtatech.com/api/v2/report", type: "post", noAppId: !0, noToken: !0, headers: { "Content-Type": "application/json", "Sign-Type": 0, Business: "TranslateAssistant" }, data: d, success() { e && e.forEach((([, e]) => { "function" == typeof(null == e ? void 0 : e.success) && e.success() })), e = null }, error() { e && e.forEach((([, e]) => { "function" == typeof(null == e ? void 0 : e.error) && e.error() })), e = null } }) })) }Reporting hosts named by evidence and source code
- translate-api.kangaroooffice.com
Observed vendor user-action report destination during dynamic analysis.
- translate-useraction-api.xiangtatech.com
User-action report host hardcoded in the shipped reporting functions.
Where it sends data
Destinations our analysis observed Google Translate Assistant – Side-by-side Translation | PDF Translation 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.
- translate-api.xiangtatech.com
Google Translate Assistant – Side-by-side Translation | PDF Translation sends data to translate-api.xiangtatech.com. 2 other extensions we have analyzed send data here.
- translate-useraction-api.xiangtatech.com
Google Translate Assistant – Side-by-side Translation | PDF Translation sends data to translate-useraction-api.xiangtatech.com. No other extension we have analyzed sends data here.