Skip to content

Instantly share code, notes, and snippets.

@vielhuber
Last active February 15, 2024 13:21
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save vielhuber/b7739bf50b2edcf636c43a8f8910def9 to your computer and use it in GitHub Desktop.
Save vielhuber/b7739bf50b2edcf636c43a8f8910def9 to your computer and use it in GitHub Desktop.
Google Translate API Hacking #knowhow #tools
function Bp(a, b) {
var c = b.split(".");
b = Number(c[0]) || 0;
for (var d = [], e = 0, f = 0; f < a.length; f++) {
var h = a.charCodeAt(f);
128 > h ? d[e++] = h : (2048 > h ? d[e++] = h >> 6 | 192 : (55296 == (h & 64512) && f + 1 < a.length && 56320 == (a.charCodeAt(f + 1) & 64512) ? (h = 65536 + ((h & 1023) << 10) + (a.charCodeAt(++f) & 1023), d[e++] = h >> 18 | 240, d[e++] = h >> 12 & 63 | 128) : d[e++] = h >> 12 | 224, d[e++] = h >> 6 & 63 | 128), d[e++] = h & 63 | 128)
}
a = b;
for (e = 0; e < d.length; e++) a += d[e], a = Ap(a, "+-a^+6");
a = Ap(a, "+-3^+b+-f");
a ^= Number(c[1]) || 0;
0 > a && (a = (a & 2147483647) + 2147483648);
c = a % 1E6;
return c.toString() +
"." + (c ^ b)
}
Tr.prototype.translate = function (a, b, c, d, e, f, h, k) {
var l = this,
m = this.a.wc(a),
n = {
q: b,
sl: c,
tl: d
};
this.h.sp && 0 == this.h.sp.indexOf("nmt") || (n.sp = "nmt");
n.tc = e;
f && (n.ctt = 1);
h && (n.dom = 1);
k && (n.sr = 1);
n[Dp()] = Bp(b.join(""), Hq);
return this.s ? this.s.b().then(function (r) {
null != r && (n.jwtt = r, n.rurl = location.hostname);
return l.a.na.send(n, C(Wr(m), l))
}, function (r) {
r && l.Vb && l.Vb(r)
}) : this.a.na.send(n, m)
};
Hq = function () {
function a(d) {
return function () {
return d
}
}
var b = String.fromCharCode(107),
c = a(String.fromCharCode(116));
b = a(b);
c = [c(), c()];
c[1] = b();
return yq["_c" + c.join(b())] || ""
}(),
Hq = function () {
var b = 'k',
c = 't';
c = [c, c];
c[1] = b();
return yq['_c' + c.join(b())] || ''
}(),
Hq = function () {
return yq['_ctkk'] || ''
}(),
var yq = window.google && google.translate && google.translate._const;
console.log(window.google.translate._const._ctkk);
GURL TranslateScript::GetTranslateScriptURL() {
GURL translate_script_url;
// Check if command-line contains an alternative URL for translate service.
const base::CommandLine& command_line =
*base::CommandLine::ForCurrentProcess();
if (command_line.HasSwitch(translate::switches::kTranslateScriptURL)) {
translate_script_url = GURL(command_line.GetSwitchValueASCII(
translate::switches::kTranslateScriptURL));
if (!translate_script_url.is_valid() ||
!translate_script_url.query().empty()) {
LOG(WARNING) << "The following translate URL specified at the "
<< "command-line is invalid: "
<< translate_script_url.spec();
translate_script_url = GURL();
}
}
// Use default URL when command-line argument is not specified, or specified
// URL is invalid.
if (translate_script_url.is_empty())
translate_script_url = GURL(kScriptURL);
translate_script_url = net::AppendQueryParameter(
translate_script_url,
kCallbackQueryName,
kCallbackQueryValue);
translate_script_url = net::AppendQueryParameter(
translate_script_url,
kAlwaysUseSslQueryName,
kAlwaysUseSslQueryValue);
translate_script_url = net::AppendQueryParameter(
translate_script_url,
kCssLoaderCallbackQueryName,
kCssLoaderCallbackQueryValue);
translate_script_url = net::AppendQueryParameter(
translate_script_url,
kJavascriptLoaderCallbackQueryName,
kJavascriptLoaderCallbackQueryValue);
translate_script_url = AddHostLocaleToUrl(translate_script_url);
translate_script_url = AddApiKeyToUrl(translate_script_url);
return translate_script_url;
}
const char TranslateScript::kScriptURL[] =
"https://translate.googleapis.com/translate_a/element.js";
function _setupNS(b) {
b = b.split(".");
for (var a = window, c = 0; c < b.length; ++c) a.hasOwnProperty ? a.hasOwnProperty(b[c]) ? a = a[b[c]] : a = a[b[c]] = {} : a = a[b[c]] || (a[b[c]] = {});
return a
}
window.addEventListener && "undefined" == typeof document.readyState && window.addEventListener("DOMContentLoaded", function () {
document.readyState = "complete"
}, !1);
if (_isNS('google.translate.Element')) {
return
}(function () {
var c = _setupNS('google.translate._const');
c._cest = gtConstEvalStartTime;
gtConstEvalStartTime = undefined;
c._cl = 'de';
c._cac = '';
c._cam = '';
c._ctkk = '440159.776620256';
var h = 'translate.googleapis.com';
var s = (true ? 'https' : window.location.protocol == 'https:' ? 'https' : 'http') + '://';
var b = s + h;
c._pah = h;
c._pas = s;
c._pbi = b + '/translate_static/img/te_bk.gif';
c._pci = b + '/translate_static/img/te_ctrl3.gif';
c._pli = b + '/translate_static/img/loading.gif';
c._plla = h + '/translate_a/l';
c._pmi = b + '/translate_static/img/mini_google.png';
c._ps = b + '/translate_static/css/translateelement.css';
c._puh = 'translate.google.com';
_loadCss(c._ps);
_loadJs(b + '/translate_static/js/element/main_de.js');
})();
GURL AddApiKeyToUrl(const GURL& url) {
return net::AppendQueryParameter(url, kApiKeyName, google_apis::GetAPIKey());
}
#if !defined(GOOGLE_API_KEY)
#define GOOGLE_API_KEY DUMMY_API_TOKEN
#endif
x.Pf = function (a, b) {
b.g && this.l.remove(b.f);
if (!this.b) return !1;
if (this.l.has(b.ea(), !1)) {
var c = this.l;
if (c.has(b.f, !1)) {
var d = b.f,
e = c.a[d];
e || (e = c.b[d], c.a[d] = e);
b.b = e;
b.K = !0
} else c.remove(b.f), b.g = !0;
zt(b)
} else if (c = this.l, b.g) c.remove(b.f);
else if (b.o) {
d = b.o.replace(/<a /g, "<span ").replace(/\/a>/g, "/span>");
b.K = !0;
delete b.o;
b.o = null;
b.b = [];
e = jg(document, Za);
Q(e, !1);
e.innerHTML = 0 <= d.indexOf("<i>") ? d : "<b>" + d + "</b>";
document.body.appendChild(e);
d = [];
var f;
for (f = e.firstChild; f; f = f.nextSibling)
if ("I" ==
f.tagName) var h = yt(b, Kg(f), f.innerHTML);
else if ("B" == f.tagName) {
h || (h = yt(b, "", ""));
if (1 == b.a.length) xt(h.$, d, 0, f);
else {
var k = d;
var l = f;
var m = b.a;
h = h.$;
for (var n = [], r, w = l.firstChild; w; w = r) r = w.nextSibling, Ct(w);
for (r = l.firstChild; r; r = r.nextSibling) r.attributes && r.attributes.i ? (l = parseInt(r.attributes.i.nodeValue, 10), !isNaN(l) && 0 <= l && l < m.length && (m[l].ee && n[l] ? n[l].T += r.firstChild && 3 == r.firstChild.nodeType ? r.firstChild.nodeValue : Kg(r) : n[l] = xt(h, k, l, r))) : 3 == r.nodeType && h.push({
R: -1,
T: De(r.nodeValue)
});
null != h && 0 < h.length && -1 == h[0].R && (1 == h.length ? h[0].R = 0 : (h[1].T = h[0].T + h[1].T, h.shift()))
}
h = void 0
}
f = b.b;
for (k = 0; k < f.length - 1; ++k) m = f[k], h = ze(m.$[m.$.length - 1].T), h = h.charCodeAt(h.length - 1), 12288 <= h && 12351 >= h || 65280 <= h && 65519 >= h || (m.$[m.$.length - 1].T += " ");
sg(e);
for (e = 0; e < b.a.length; ++e) e < d.length && e < b.l.length && null != d[e] && (f = b.l[e], k = d[e].start, null != k && (m = f.substring(0, f.length - ye(f).length), " " == m && (m = ""), m && (k.T = m + ye(k.T))), k = d[e].end, null != k && (f = f.substring(ze(f).length), " " == f && (f = ""), f && (k.T =
ze(k.T) + f)));
1 != b.b.length || b.b[0].lf || (b.b[0].lf = b.f);
c.write(b.f, b.b);
zt(b)
}
b.H || (this.W = !1);
c = b.g ? !0 : void 0;
a.K += b.G;
null != c && (a.qa = !0);
b = Math.min(Math.floor(100 * a.K / a.f), 100);
if (a.o != b || c) a.o = b, a.L ? (a.l(a.o, !0, c), a.W(a.K)) : a.l(a.o, !1, c);
return !1
};
<?php
require_once __DIR__ . '/vendor/autoload.php';
use vielhuber\stringhelper\__;
use Faker\Factory;
class GoogleTranslate
{
function translate($string)
{
$string = $this->parseResultPre($string);
$args = [
'anno' => 3,
'client' => 'te_lib',
'format' => 'html',
'v' => '1.0',
'key' => 'AIzaSyBOti4mM-6x9WDnZIjIeyEU21OpBXqWBgw',
'logld' => 'vTE_20200210_00',
'sl' => 'de',
'tl' => 'en',
'sp' => 'nmt',
'tc' => 1,
'sr' => 1,
'tk' => $this->generateTk($string, $this->generateTkk()),
'mode' => 1
];
$response = __::curl(
'https://translate.googleapis.com/translate_a/t?' . http_build_query($args),
['q' => $string],
'POST',
[
'User-Agent' =>
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36',
'Content-Length' => strlen('q=' . urlencode($string))
],
false,
false,
3
);
return ['result' => $this->parseResultPost($response->result), 'status' => $response->status];
}
private function parseResultPre($input)
{
// google sometimes surrounds the translation with <i> and <b> tags
// do distinguish real i-/b-tags, replace them (we undo that later on)
$dom = __::str_to_dom($input);
$xpath = new \DOMXPath($dom);
foreach (['i', 'b'] as $tags__value) {
foreach ($dom->getElementsByTagName($tags__value) as $divs__value) {
$divs__value->setAttribute('data-native', 'true');
}
}
$nodes = $xpath->query('/html/body//*');
if (count($nodes) > 0) {
$id = 1;
foreach ($nodes as $nodes__value) {
$nodes__value->setAttribute('gtid', $id);
$id++;
}
}
$output = __::dom_to_str($dom);
return $output;
}
private function parseResultPost($input)
{
// sometimes google returns an array
if (is_array($input) && !empty($input)) {
$input = $input[0];
}
// discard the (outer) <i>-tags and take the content of the <b>-tags
$output = '';
$pointer = 0;
$lvl_i = 0;
$lvl_i_inner = 0;
$lvl_b = 0;
$lvl_b_inner = 0;
// multibyte split to array of chars
foreach (preg_split('//u', $input, -1, PREG_SPLIT_NO_EMPTY) as $chars__value) {
if ($pointer >= 3 && mb_substr($input, $pointer - 3, 3) === '<i>') {
$lvl_i_inner++;
}
if ($pointer >= 3 && mb_substr($input, $pointer - 3, 3) === '<b>') {
$lvl_b_inner++;
}
if (mb_substr($input, $pointer, 4) === '</i>' && $lvl_i_inner > 0) {
$lvl_i_inner--;
}
if (mb_substr($input, $pointer, 4) === '</b>' && $lvl_b_inner > 0) {
$lvl_b_inner--;
}
if (mb_substr($input, $pointer, 3) === '<i>') {
$lvl_i++;
}
if (mb_substr($input, $pointer, 3) === '<b>') {
$lvl_b++;
}
if ($pointer >= 4 && mb_substr($input, $pointer - 4, 4) === '</i>' && $lvl_i > 0) {
$lvl_i--;
}
if ($pointer >= 4 && mb_substr($input, $pointer - 4, 4) === '</b>' && $lvl_b > 0) {
$lvl_b--;
}
$pointer++;
// discard multiple spaces
if ($chars__value === ' ' && mb_strlen($output) > 0 && mb_substr($output, -1) === ' ') {
continue;
}
// save
if (($lvl_b_inner >= 1 && $lvl_i_inner === 0) || ($lvl_b === 0 && $lvl_i === 0)) {
$output .= $chars__value;
}
}
$output = trim($output);
$dom = __::str_to_dom($output);
$xpath = new \DOMXPath($dom);
foreach (['i', 'b'] as $tags__value) {
foreach ($dom->getElementsByTagName($tags__value) as $divs__value) {
$divs__value->removeAttribute('data-native');
}
}
// merge neighbour elements with the same id together
$nodes = $xpath->query('/html/body//*[@gtid]');
if (count($nodes) > 0) {
foreach ($nodes as $nodes__value) {
if ($nodes__value->hasAttribute('please-remove')) {
continue;
}
$id = $nodes__value->getAttribute('gtid');
$html = $nodes__value->nodeValue;
$nextSibling = $nodes__value->nextSibling;
if ($nextSibling === null) {
continue;
}
if ($nextSibling->nodeName === '#text' && trim($nextSibling->textContent) == '') {
$nextSibling = $nextSibling->nextSibling;
}
if ($nextSibling === null || $nextSibling->nodeName === '#text') {
continue;
}
$id2 = $nextSibling->getAttribute('gtid');
if ($id !== $id2) {
continue;
}
$nextSibling->setAttribute('please-remove', '1');
$html .= ' ' . $nextSibling->nodeValue;
$nodes__value->nodeValue = $html;
}
foreach ($nodes as $nodes__value) {
$nodes__value->removeAttribute('gtid');
if ($nodes__value->hasAttribute('please-remove')) {
$nodes__value->parentNode->removeChild($nodes__value);
}
}
}
$output = __::dom_to_str($dom);
return $output;
}
private function generateTkk()
{
$cache = sys_get_temp_dir() . '/tkk.cache';
if (file_exists($cache) && filemtime($cache) > strtotime('now - 1 hour')) {
return file_get_contents($cache);
}
$data = __::curl('https://translate.googleapis.com/translate_a/element.js', null, 'GET');
$response = $data->result;
$pos1 = mb_strpos($response, 'c._ctkk=\'') + mb_strlen('c._ctkk=\'');
$pos2 = mb_strpos($response, '\'', $pos1);
$tkk = mb_substr($response, $pos1, $pos2 - $pos1);
file_put_contents($cache, $tkk);
return $tkk;
}
private function generateTk($f0, $w1)
{
// ported from js to php from https://translate.googleapis.com/element/TE_20200210_00/e/js/element/element_main.js
$w1 = explode('.', $w1);
$n2 = $w1[0];
for ($j3 = [], $t4 = 0, $h5 = 0; $h5 < strlen(mb_convert_encoding($f0, 'UTF-16LE', 'UTF-8')) / 2; $h5++) {
$z6 =
ord(mb_convert_encoding($f0, 'UTF-16LE', 'UTF-8')[$h5 * 2]) +
(ord(mb_convert_encoding($f0, 'UTF-16LE', 'UTF-8')[$h5 * 2 + 1]) << 8);
if (128 > $z6) {
$j3[$t4++] = $z6;
} else {
if (2048 > $z6) {
$j3[$t4++] = ($z6 >> 6) | 192;
} else {
if (
55296 == ($z6 & 64512) &&
$h5 + 1 < strlen(mb_convert_encoding($f0, 'UTF-16LE', 'UTF-8')) / 2 &&
56320 ==
((ord(mb_convert_encoding($f0, 'UTF-16LE', 'UTF-8')[($h5 + 1) * 2]) +
(ord(mb_convert_encoding($f0, 'UTF-16LE', 'UTF-8')[($h5 + 1) * 2 + 1]) << 8)) &
64512)
) {
$h5++;
$z6 =
65536 +
(($z6 & 1023) << 10) +
((ord(mb_convert_encoding($f0, 'UTF-16LE', 'UTF-8')[$h5 * 2]) +
(ord(mb_convert_encoding($f0, 'UTF-16LE', 'UTF-8')[$h5 * 2 + 1]) << 8)) &
1023);
$j3[$t4++] = ($z6 >> 18) | 240;
$j3[$t4++] = (($z6 >> 12) & 63) | 128;
} else {
$j3[$t4++] = ($z6 >> 12) | 224;
}
$j3[$t4++] = (($z6 >> 6) & 63) | 128;
}
$j3[$t4++] = ($z6 & 63) | 128;
}
}
$f0 = $n2;
for ($t4 = 0; $t4 < count($j3); $t4++) {
$f0 += $j3[$t4];
$c7 = $f0;
$x8 = '+-a^+6';
for ($r9 = 0; $r9 < strlen($x8) - 2; $r9 += 3) {
$u10 = $x8[$r9 + 2];
$u10 = 'a' <= $u10 ? ord($u10[0]) - 87 : intval($u10);
$a11 = $c7;
$c12 = $u10;
if ($c12 >= 32 || $c12 < -32) {
$c13 = (int) ($c12 / 32);
$c12 = $c12 - $c13 * 32;
}
if ($c12 < 0) {
$c12 = 32 + $c12;
}
if ($c12 == 0) {
return (($a11 >> 1) & 0x7fffffff) * 2 + (($a11 >> $c12) & 1);
}
if ($a11 < 0) {
$a11 = $a11 >> 1;
$a11 &= 2147483647;
$a11 |= 0x40000000;
$a11 = $a11 >> $c12 - 1;
} else {
$a11 = $a11 >> $c12;
}
$b14 = $a11;
$u10 = '+' == $x8[$r9 + 1] ? $b14 : $c7 << $u10;
$c7 = '+' == $x8[$r9] ? ($c7 + $u10) & 4294967295 : $c7 ^ $u10;
}
$f0 = $c7;
}
$c7 = $f0;
$x8 = '+-3^+b+-f';
for ($r9 = 0; $r9 < strlen($x8) - 2; $r9 += 3) {
$u10 = $x8[$r9 + 2];
$u10 = 'a' <= $u10 ? ord($u10[0]) - 87 : intval($u10);
$a11 = $c7;
$c12 = $u10;
if ($c12 >= 32 || $c12 < -32) {
$c13 = (int) ($c12 / 32);
$c12 = $c12 - $c13 * 32;
}
if ($c12 < 0) {
$c12 = 32 + $c12;
}
if ($c12 == 0) {
return (($a11 >> 1) & 0x7fffffff) * 2 + (($a11 >> $c12) & 1);
}
if ($a11 < 0) {
$a11 = $a11 >> 1;
$a11 &= 2147483647;
$a11 |= 0x40000000;
$a11 = $a11 >> $c12 - 1;
} else {
$a11 = $a11 >> $c12;
}
$b14 = $a11;
$u10 = '+' == $x8[$r9 + 1] ? $b14 : $c7 << $u10;
$c7 = '+' == $x8[$r9] ? ($c7 + $u10) & 4294967295 : $c7 ^ $u10;
}
$f0 = $c7;
$f0 ^= $w1[1] ? $w1[1] + 0 : 0;
if (0 > $f0) {
$f0 = ($f0 & 2147483647) + 2147483648;
}
$f0 = fmod($f0, pow(10, 6));
return $f0 . '.' . ($f0 ^ $n2);
}
}
$gt = new GoogleTranslate();
$faker = Factory::create('de_DE');
$chars = 0;
for ($i = 0; $i < 1000; $i++) {
$orig = $faker->realText(250);
$chars += mb_strlen($orig);
$response = $gt->translate($orig);
logStatus([$response['status'], $chars, $orig, $response['result']]);
echo $response['status'] . ': ' . $chars . PHP_EOL;
}
function logStatus($msg)
{
file_put_contents('log.txt', date('Y-m-d H:i:s') . "\t" . implode("\t", $msg) . PHP_EOL, FILE_APPEND);
}
@QGB
Copy link

QGB commented Jul 19, 2021

How to use that @vielhuber?

@vielhuber
Copy link
Author

@rubobaquero
Copy link

Is this still working for you? It was working for me for some time, but now Google answers randomly with "[]" and a 200 status code.

@vielhuber
Copy link
Author

Yes, just tested.
I just had to add a small line since google returns sometimes arrays instead of strings:
https://gist.github.com/vielhuber/b7739bf50b2edcf636c43a8f8910def9/revisions#diff-13c8070b20b0c3271f899b6381691ae2604aecb086fb2a01fd7f99f08ff70fbe

@desis123
Copy link

It's working

@sbh96
Copy link

sbh96 commented Jan 6, 2024

its not working. are you have any new solution?

@qijianchuan
Copy link

its not working,google translate changed

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment