{"id":2597,"date":"2020-03-19T01:48:56","date_gmt":"2020-03-19T00:48:56","guid":{"rendered":"https:\/\/vielhuber.de\/?p=2597"},"modified":"2022-01-03T00:40:15","modified_gmt":"2022-01-02T23:40:15","slug":"google-translation-api-hacking","status":"publish","type":"post","link":"https:\/\/vielhuber.de\/blog\/google-translation-api-hacking\/","title":{"rendered":"Google Translation API Hacking"},"content":{"rendered":"\n<p>Google bietet im Rahmen seiner Google Cloud die <a aria-label=\"Google Translation API (opens in a new tab)\" rel=\"noreferrer noopener\" href=\"https:\/\/cloud.google.com\/translate\/docs\" target=\"_blank\">Google Translation API<\/a> mit einer nutzungsbasierten <a aria-label=\"Kostenstruktur (opens in a new tab)\" rel=\"noreferrer noopener\" href=\"https:\/\/cloud.google.com\/translate\/pricing\" target=\"_blank\">Kostenstruktur<\/a> an. Daneben gibt es eine <a href=\"https:\/\/weblog.west-wind.com\/posts\/2011\/aug\/06\/translating-with-google-translate-without-api-and-c-code\" target=\"_blank\" aria-label=\"undefined (opens in a new tab)\" rel=\"noreferrer noopener\">undokumentierte, ohne Key nutzbare API<\/a>, die jedoch schon nach wenigen Requests den Dienst verweigert. Bei der Benutzung der <a aria-label=\"Webseiten-\u00dcbersetzungsfunktion (opens in a new tab)\" rel=\"noreferrer noopener\" href=\"https:\/\/support.google.com\/chrome\/answer\/173424\" target=\"_blank\">Webseiten-\u00dcbersetzungsfunktion<\/a> von Google Chrome f\u00e4llt auf, dass hier ohne sp\u00fcrbare Begrenzung Seiten in sehr guter Qualit\u00e4t \u00fcbersetzt werden k\u00f6nnen.<\/p>\n\n\n\n<!--more-->\n\n\n\n<p>Offenbar wird hier auch bereits das fortgeschrittene <a rel=\"noreferrer noopener\" href=\"https:\/\/en.wikipedia.org\/wiki\/Google_Neural_Machine_Translation\" target=\"_blank\">nmt-Model<\/a> genutzt. Doch welche API nutzt Google Chrome hier intern, um den Inhalt zu \u00fcbersetzen und kann man diese API auch direkt \u2013 sogar serverseitig \u2013 ansprechen? Um Netzwerktraffic zu analysieren, empfehlen sich Tools wie <a rel=\"noreferrer noopener\" aria-label=\"Wireshark (opens in a new tab)\" href=\"https:\/\/www.wireshark.org\/\" target=\"_blank\">Wireshark<\/a> oder <a rel=\"noreferrer noopener\" aria-label=\"Telerik Fiddler (opens in a new tab)\" href=\"https:\/\/www.telerik.com\/fiddler\" target=\"_blank\">Telerik Fiddler<\/a>, die auch verschl\u00fcsselten Traffic analysieren k\u00f6nnen. Doch Chrome liefert die Requests, die es bei der Seiten\u00fcbersetzung absetzt, sogar frei Haus: Sie sind leicht einsehbar \u00fcber die <a rel=\"noreferrer noopener\" aria-label=\"Chrome DevTools (opens in a new tab)\" href=\"https:\/\/developers.google.com\/web\/tools\/chrome-devtools\" target=\"_blank\">Chrome DevTools<\/a>:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1109\" height=\"461\" src=\"https:\/\/vielhuber.de\/wp-content\/uploads\/image-5.png\" alt=\"\" class=\"wp-image-2600\"\/><\/figure>\n\n\n\n<p>F\u00fchrt man eine \u00dcbersetzung durch, catcht anschlie\u00dfend den entscheidenden POST-Request an <strong>https:\/\/translate.googleapis.com<\/strong> via \"Copy &gt; Copy as cURL (bash)\" und f\u00fchrt ihn beispielsweise in einem Tool wie <a rel=\"noreferrer noopener\" aria-label=\"Postman (opens in a new tab)\" href=\"https:\/\/www.postman.com\/\" target=\"_blank\">Postman<\/a> aus, kann man den Request ohne Probleme erneut absenden:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"696\" height=\"362\" src=\"https:\/\/vielhuber.de\/wp-content\/uploads\/image-7.png\" alt=\"\" class=\"wp-image-2602\"\/><\/figure>\n\n\n\n<p>Auch die Bedeutung der URL-Parameter sind gr\u00f6\u00dftenteils offensichtlich:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><tbody><tr><td><strong>Key<\/strong><\/td><td class=\"has-text-align-left\" data-align=\"left\"><strong>Beispiel-Value<\/strong><\/td><td class=\"has-text-align-right\" data-align=\"right\"><strong>Bedeutung<\/strong><\/td><\/tr><tr><td>anno<\/td><td class=\"has-text-align-left\" data-align=\"left\">3<\/td><td class=\"has-text-align-right\" data-align=\"right\">Annotation-Mode (hat Auswirkungen auf das R\u00fcckgabeformat)<\/td><\/tr><tr><td>client<\/td><td class=\"has-text-align-left\" data-align=\"left\">te_lib<\/td><td class=\"has-text-align-right\" data-align=\"right\">Client-Information (variiert, \u00fcber das Webinterface von Google-Translate lautet der Wert \"webapp\"; Hat Auswirkungen auf das R\u00fcckgabeformat sowie auf das Rate Limiting)<\/td><\/tr><tr><td>format<\/td><td class=\"has-text-align-left\" data-align=\"left\">html<\/td><td class=\"has-text-align-right\" data-align=\"right\">String-Format (wichtig f\u00fcr die \u00dcbersetzung von HTML-Tags)<\/td><\/tr><tr><td>v<\/td><td class=\"has-text-align-left\" data-align=\"left\">1.0<\/td><td class=\"has-text-align-right\" data-align=\"right\">Versionsnummer von Google Translate<\/td><\/tr><tr><td>key<\/td><td class=\"has-text-align-left\" data-align=\"left\">AIzaSyBOti4mM-6x9WDnZIjIeyEU21OpBXqWBgw<\/td><td class=\"has-text-align-right\" data-align=\"right\">API-Key (siehe unten)<\/td><\/tr><tr><td>logld<\/td><td class=\"has-text-align-left\" data-align=\"left\">vTE_20200210_00<\/td><td class=\"has-text-align-right\" data-align=\"right\">Protokollversion<\/td><\/tr><tr><td>sl<\/td><td class=\"has-text-align-left\" data-align=\"left\">de<\/td><td class=\"has-text-align-right\" data-align=\"right\">Quellsprache<\/td><\/tr><tr><td>tl<\/td><td class=\"has-text-align-left\" data-align=\"left\">en<\/td><td class=\"has-text-align-right\" data-align=\"right\">Zielsprache<\/td><\/tr><tr><td>sp<\/td><td class=\"has-text-align-left\" data-align=\"left\">nmt<\/td><td class=\"has-text-align-right\" data-align=\"right\">ML-Modell<\/td><\/tr><tr><td>tc<\/td><td class=\"has-text-align-left\" data-align=\"left\">1<\/td><td class=\"has-text-align-right\" data-align=\"right\">unbekannt<\/td><\/tr><tr><td>sr<\/td><td class=\"has-text-align-left\" data-align=\"left\">1<\/td><td class=\"has-text-align-right\" data-align=\"right\">unbekannt<\/td><\/tr><tr><td>tk<\/td><td class=\"has-text-align-left\" data-align=\"left\">709408.812158<\/td><td class=\"has-text-align-right\" data-align=\"right\">Token (siehe unten)<\/td><\/tr><tr><td>mode<\/td><td class=\"has-text-align-left\" data-align=\"left\">1<\/td><td class=\"has-text-align-right\" data-align=\"right\">unbekannt<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p>Es werden ebenfalls einige Request Header gesetzt \u2013 diese k\u00f6nnen jedoch gr\u00f6\u00dftenteils ignoriert werden. Nach dem manuellen Abw\u00e4hlen aller Header, auch von <a rel=\"noreferrer noopener\" href=\"https:\/\/de.wikipedia.org\/wiki\/User_Agent\" target=\"_blank\">User-Agent<\/a>, stellt man bei der Eingabe von Sonderzeichen jedoch ein Encoding-Problem fest (hier bei der \u00dcbersetzung von \u201e<strong>\u2014Hallo Welt\u2014<\/strong>\u201c):<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"799\" height=\"681\" src=\"https:\/\/vielhuber.de\/wp-content\/uploads\/image-13.png\" alt=\"\" class=\"wp-image-2651\"\/><\/figure>\n\n\n\n<p>Aktiviert man den User-Agent wieder (das schadet generell nicht), liefert die API UTF-8 encodete Zeichen aus:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1063\" height=\"689\" src=\"https:\/\/vielhuber.de\/wp-content\/uploads\/image-14.png\" alt=\"\" class=\"wp-image-2652\"\/><\/figure>\n\n\n\n<p>Sind wir nun schon am Ziel und haben alle Informationen, um diese API auch au\u00dferhalb von Google Chrome zu nutzen? \u00c4ndert man die zu \u00fcbersetzende Zeichenkette (Datenfeld <strong>q<\/strong> des POST-Requests) von beispielsweise \u201eHallo Welt\u201c auf \u201eHallo Welt<strong>!<\/strong>\u201c, erhalten wir jedoch eine Fehlermeldung:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"656\" height=\"474\" src=\"https:\/\/vielhuber.de\/wp-content\/uploads\/image-8.png\" alt=\"\" class=\"wp-image-2603\"\/><\/figure>\n\n\n\n<p>Wir f\u00fchren nun eine erneute \u00dcbersetzung dieses abge\u00e4nderten innerhalb von Google Chrome mit Hilfe der Webseiten-\u00dcbersetzungsfunktion durch und stellen fest, dass sich neben dem Parameter <strong>q<\/strong> auch der Parameter <strong>tk<\/strong> ge\u00e4ndert hat (alle anderen Parameter sind gleichgeblieben):<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"694\" height=\"370\" src=\"https:\/\/vielhuber.de\/wp-content\/uploads\/image-9.png\" alt=\"\" class=\"wp-image-2604\"\/><\/figure>\n\n\n\n<p>Offenbar handelt es sich um ein vom String abh\u00e4ngiges Token, dessen Aufbau nicht einfach ersichtlich ist. Startet man die Webseiten\u00fcbersetzung, werden die folgenden Dateien geladen:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>1 CSS-Datei: <strong>translateelement.css<\/strong><\/li><li>4 Grafiken: <strong>translate_24dp.png<\/strong> (2x), <strong>gen204<\/strong> (2x)<\/li><li>2 JS-Dateien: <strong>main_de.js<\/strong>, <strong>element_main.js<\/strong><\/li><\/ul>\n\n\n\n<p>Die beiden JavaScript-Dateien sind verschleiert und minifiziert. Tools wie <a rel=\"noreferrer noopener\" aria-label=\"JS Nice (opens in a new tab)\" href=\"http:\/\/www.jsnice.org\/\" target=\"_blank\">JS Nice<\/a> und <a rel=\"noreferrer noopener\" aria-label=\"de4js (opens in a new tab)\" href=\"https:\/\/lelinhtinh.github.io\/de4js\/\" target=\"_blank\">de4js<\/a> helfen uns nun dabei, diese Dateien besser lesbar zu machen. Um sie \u00fcberdies live zu debuggen, empfiehlt sich die Chrome Extension <a rel=\"noreferrer noopener\" aria-label=\"Requestly, (opens in a new tab)\" href=\"https:\/\/chrome.google.com\/webstore\/detail\/requestly-redirect-url-mo\/mdnleldcmiljblolnjhpnblkcekpdkpa?hl=en\" target=\"_blank\">Requestly,<\/a> die on-the-fly Remote-Dateien lokal tunnelt:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1392\" height=\"332\" src=\"https:\/\/vielhuber.de\/wp-content\/uploads\/image-10.png\" alt=\"\" class=\"wp-image-2606\"\/><\/figure>\n\n\n\n<p>Nun k\u00f6nnen wir den Code debuggen (auf dem lokalen Server muss zuvor noch <a rel=\"noreferrer noopener\" href=\"https:\/\/de.wikipedia.org\/wiki\/Cross-Origin_Resource_Sharing\" target=\"_blank\">CORS<\/a> aktiviert werden). Der relevante Code-Abschnitt f\u00fcr die Generierung des Tokens scheint sich in der Datei <strong>element_main.js<\/strong> in diesem Abschnitt zu verbergen:<\/p>\n\n\n\n<p class=\"githubgist\" data-gist-file=\"01.js\">b7739bf50b2edcf636c43a8f8910def9<\/p>\n\n\n\n<p>Hier wird u.a. mit Hilfe von einigen <a rel=\"noreferrer noopener\" aria-label=\"Bitshifts (opens in a new tab)\" href=\"https:\/\/de.wikipedia.org\/wiki\/Bitweiser_Operator\" target=\"_blank\">Bitshifts<\/a> der Text gehasht. Doch leider fehlt uns noch ein Puzzlest\u00fcck: An die Funktion <strong>Bp()<\/strong> wird neben dem Argument <strong>a<\/strong> (das der zu \u00fcbersetzende Text ist) ein weiteres Argument <strong>b<\/strong> \u00fcbergeben \u2013 eine Art Seed, der sich von Zeit zu Zeit zu \u00e4ndern scheint und der ebenfalls mit in das Hashing einflie\u00dft. Doch woher kommt er? Springen wir zum Funktionsaufruf von <strong>Bp()<\/strong>, finden wir folgenden Codeabschnitt:<\/p>\n\n\n\n<p class=\"githubgist\" data-gist-file=\"02.js\">b7739bf50b2edcf636c43a8f8910def9<\/p>\n\n\n\n<p>Die Funktion <strong>Hq<\/strong> wird dabei vorher wie folgt deklariert:<\/p>\n\n\n\n<p class=\"githubgist\" data-gist-file=\"03.js\">b7739bf50b2edcf636c43a8f8910def9<\/p>\n\n\n\n<p>Hier hat der Deobfuscater noch etwas Unrat hinterlassen; Nachdem wir <strong>String.fromCharCode('...')<\/strong> durch die jeweiligen Zeichenketten ersetzt haben, das obsolete <strong>a()<\/strong> entfernen und die Funktionsaufrufe <strong>[c(), c()]<\/strong> zusammenst\u00fcckeln, ergibt sich:<\/p>\n\n\n\n<p class=\"githubgist\" data-gist-file=\"04.js\">b7739bf50b2edcf636c43a8f8910def9<\/p>\n\n\n\n<p>Oder noch einfacher:<\/p>\n\n\n\n<p class=\"githubgist\" data-gist-file=\"05.js\">b7739bf50b2edcf636c43a8f8910def9<\/p>\n\n\n\n<p>Die Funktion <strong>yq<\/strong> ist vorher definiert als:<\/p>\n\n\n\n<p class=\"githubgist\" data-gist-file=\"06.js\">b7739bf50b2edcf636c43a8f8910def9<\/p>\n\n\n\n<p>Der Seed scheint also im globalen Object <strong>google.translate._const._ctkk<\/strong> zu stecken, das zur Laufzeit verf\u00fcgbar ist. Doch wo wird es gesetzt? In der anderen, zuvor geladenen JS-Datei <strong>main_de.js<\/strong> zumindest ist es ebenfalls schon zu Beginn verf\u00fcgbar. Wir f\u00fcgen dazu am Anfang folgendes ein:<\/p>\n\n\n\n<p class=\"githubgist\" data-gist-file=\"07.js\">b7739bf50b2edcf636c43a8f8910def9<\/p>\n\n\n\n<p>In der Konsole erhalten wir nun tats\u00e4chlich den aktuellen Seed:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"468\" height=\"35\" src=\"https:\/\/vielhuber.de\/wp-content\/uploads\/image-11.png\" alt=\"\" class=\"wp-image-2607\"\/><\/figure>\n\n\n\n<p>Damit bleibt als letzte M\u00f6glichkeit noch Google Chrome selbst, der den Seed offenbar zur Verf\u00fcgung stellt. Gl\u00fccklicherweise ist dessen Source Code (Chromium, inkl. der Translate-Komponente) Open-Source und damit \u00f6ffentlich einsehbar. Wir ziehen uns das <a rel=\"noreferrer noopener\" aria-label=\"Repository (opens in a new tab)\" href=\"https:\/\/github.com\/chromium\/chromium\" target=\"_blank\">Repository<\/a> lokal und finden in der Datei <strong>translate_script.cc <\/strong>im Ordner <strong>components\/translate\/core\/browser<\/strong> den Aufruf der Funktion <strong>TranslateScript::GetTranslateScriptURL<\/strong>:<\/p>\n\n\n\n<p class=\"githubgist\" data-gist-file=\"08.cc\">b7739bf50b2edcf636c43a8f8910def9<\/p>\n\n\n\n<p>Die Variable mit der URL ist in derselben Datei hart definiert:<\/p>\n\n\n\n<p class=\"githubgist\" data-gist-file=\"09.cc\">b7739bf50b2edcf636c43a8f8910def9<\/p>\n\n\n\n<p>Untersuchen wir nun die Datei <strong>element.js<\/strong> (nach erneutem deobfuskieren)  genauer, finden wir den hart gesetzten Eintrag <strong>c._ctkk<\/strong> \u2013 auch das <strong>google.translate<\/strong> Objekt wird entsprechend gesetzt sowie das Laden aller relevanten Assets (die wir zuvor bereits entdeckt haben) wird getriggert:<\/p>\n\n\n\n<p class=\"githubgist\" data-gist-file=\"10.js\">b7739bf50b2edcf636c43a8f8910def9<\/p>\n\n\n\n<p>Nun verbleibt zur Betrachtung noch der Parameter <strong>key<\/strong> (mit dem Wert AIzaSyBOti4mM-6x9WDnZIjIeyEU21OpBXqWBgw). Das scheint ein generischer Browser-API-Key zu sein (zu dem sich auch einige <a href=\"https:\/\/www.google.com\/search?q=AIzaSyBOti4mM-6x9WDnZIjIeyEU21OpBXqWBgw\" target=\"_blank\" rel=\"noreferrer noopener\" aria-label=\" (opens in a new tab)\">Google-Ergebnisse finden<\/a>). Er wird in Chromium in der Datei <strong>translate_url_util.cc<\/strong> im Ordner <strong>components\/translate\/core\/browser<\/strong> gesetzt:<\/p>\n\n\n\n<p class=\"githubgist\" data-gist-file=\"11.cc\">b7739bf50b2edcf636c43a8f8910def9<\/p>\n\n\n\n<p>Der Key wird in <strong>google_apis\/google_api_keys.cc<\/strong> aus einem Dummy-Wert generiert:<\/p>\n\n\n\n<p class=\"githubgist\" data-gist-file=\"12.cc\">b7739bf50b2edcf636c43a8f8910def9<\/p>\n\n\n\n<p>Ein Test ergibt jedoch, dass die API-Aufrufe ohne diesen Key-Parameter genauso funktionieren. Experimentiert man nun mit der API, erh\u00e4lt man im Erfolgsfall den Status-Code <strong>200 <\/strong>zur\u00fcck. L\u00e4uft man dann in ein Limit, erh\u00e4lt man den Status-Code <strong>411 <\/strong>mit der Meldung \"<strong>POST requests require a Content-length header<\/strong>\" zur\u00fcck. Deshalb empfiehlt es sich, diesen (in Postman als <strong>Temporary Header<\/strong> automatisch gesetzten) Header ebenfalls mit zu setzen.<\/p>\n\n\n\n<p>Das R\u00fcckgabeformat der \u00fcbersetzten Strings ist bei mehreren S\u00e4tzen in einem Request ungew\u00f6hnlich. Die einzelnen S\u00e4tze sind durch die i-\/b-HTML-Tags umschlossen:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"634\" height=\"366\" src=\"https:\/\/vielhuber.de\/wp-content\/uploads\/image-15.png\" alt=\"\" class=\"wp-image-2657\"\/><\/figure>\n\n\n\n<p>Auch sendet Google Chrome nicht das komplette HTML an die API, sondern spart Attributwerte wie <strong>href <\/strong>bereits im Request aus (und setzt stattdessen Indizes, um die Tags sp\u00e4ter wieder clientseitig zuordnen zu k\u00f6nnen):<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"809\" height=\"349\" src=\"https:\/\/vielhuber.de\/wp-content\/uploads\/image-18.png\" alt=\"\" class=\"wp-image-2663\"\/><\/figure>\n\n\n\n<p>\u00c4ndert man den Wert des POST-Keys <strong>client<\/strong> von <strong>te_lib<\/strong> (Google Chrome)<strong> <\/strong>auf <strong>webapp<\/strong> (<a rel=\"noreferrer noopener\" aria-label=\"Google Translation Webseite (opens in a new tab)\" href=\"https:\/\/translate.google.com\/?hl=de\" target=\"_blank\">Google Translation Webseite<\/a>), erh\u00e4lt man den final \u00fcbersetzten String:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"633\" height=\"718\" src=\"https:\/\/vielhuber.de\/wp-content\/uploads\/image-16.png\" alt=\"\" class=\"wp-image-2658\"\/><\/figure>\n\n\n\n<p>Das Problem ist nun aber, dass man deutlich eher in ein Rate Limiting l\u00e4uft als \u00fcber <strong>te_lib<\/strong> (zum Vergleich: mit <strong>webapp <\/strong>erreicht man dieses bereits nach 40.000 Chars, mit <strong>te_lib <\/strong>gibt es kein Rate Limiting). Deshalb m\u00fcssen wir uns genauer ansehen, wie Chrome das Ergebnis parst. In der <strong>element_main.js<\/strong> werden wir hier f\u00fcndig:<\/p>\n\n\n\n<p class=\"githubgist\" data-gist-file=\"13.js\">b7739bf50b2edcf636c43a8f8910def9<\/p>\n\n\n\n<p>Sendet man den gesamten HTML-Code an die API, bel\u00e4sst diese die Attribute auch in der \u00fcbersetzten Response. Wir m\u00fcssen deshalb nicht das komplette Parse-Verhalten imitieren, sondern lediglich den finalen, \u00fcbersetzten String aus der Response extrahieren. Dazu bauen wir uns einen kleinen HTML-Tag-Parser, der die jeweils \u00e4u\u00dfersten &lt;i>-Tags inkl. deren Inhalte verwirft und die \u00e4u\u00dfersten &lt;b>-Tags entfernt. Mit diesen Erkenntnissen k\u00f6nnen wir (nach der Installation von Dependencies mit <strong>composer require fzaninotto\/faker vielhuber\/stringhelper<\/strong>) nun eine serverseitige Version der \u00dcbersetzungs-API aufbauen: <\/p>\n\n\n\n<p class=\"githubgist\" data-gist-file=\"14.php\">b7739bf50b2edcf636c43a8f8910def9<\/p>\n\n\n\n<p>Es folgen die Ergebnisse eines ersten Tests, die auf f\u00fcnf unterschiedlichen Systemen mit unterschiedlichen Bandbreiten und IP-Adressen durchgef\u00fchrt wurden:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><tbody><tr><td><strong>Zeichen<\/strong><\/td><td><strong>Zeichen pro Request<\/strong><\/td><td class=\"has-text-align-right\" data-align=\"right\"><strong>Dauer<\/strong><\/td><td class=\"has-text-align-right\" data-align=\"right\"><strong>Fehlerrate<\/strong><\/td><td class=\"has-text-align-right\" data-align=\"right\"><strong>Kosten \u00fcber offizielle API<\/strong><\/td><\/tr><tr><td>13.064.662<\/td><td>~250<\/td><td class=\"has-text-align-right\" data-align=\"right\">03:36:17h<\/td><td class=\"has-text-align-right\" data-align=\"right\">0%<\/td><td class=\"has-text-align-right\" data-align=\"right\">237,78\u20ac<\/td><\/tr><tr><td>24.530.510<\/td><td>~250<\/td><td class=\"has-text-align-right\" data-align=\"right\">11:09:13h<\/td><td class=\"has-text-align-right\" data-align=\"right\">0%<\/td><td class=\"has-text-align-right\" data-align=\"right\">446,46\u20ac<\/td><\/tr><tr><td>49.060.211<\/td><td>~250<\/td><td class=\"has-text-align-right\" data-align=\"right\">20:39:10h<\/td><td class=\"has-text-align-right\" data-align=\"right\">0%<\/td><td class=\"has-text-align-right\" data-align=\"right\">892,90\u20ac<\/td><\/tr><tr><td>99.074.487<\/td><td>~1000<\/td><td class=\"has-text-align-right\" data-align=\"right\">61:24:37h<\/td><td class=\"has-text-align-right\" data-align=\"right\">0%<\/td><td class=\"has-text-align-right\" data-align=\"right\">1803,16\u20ac<\/td><\/tr><tr><td>99.072.896<\/td><td>~1000<\/td><td class=\"has-text-align-right\" data-align=\"right\">62:22:20h<\/td><td class=\"has-text-align-right\" data-align=\"right\">0%<\/td><td class=\"has-text-align-right\" data-align=\"right\">1803,13\u20ac<\/td><\/tr><tr><td><strong>\u03a3284.802.766<\/strong><\/td><td><strong>~\u00d8550<\/strong><\/td><td class=\"has-text-align-right\" data-align=\"right\"><strong>\u03a3159:11:37h<\/strong><\/td><td class=\"has-text-align-right\" data-align=\"right\">0%<\/td><td class=\"has-text-align-right\" data-align=\"right\"><strong>\u03a35183,41\u20ac<\/strong><\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p><strong>Hinweis:<\/strong> Dieser Blogpost inkl. aller Scripte wurde nur zu Testzwecken verfasst. Verwenden Sie die Scripte <em>nicht<\/em> f\u00fcr den produktiven Einsatz, sondern arbeiten Sie stattdessen mit der offiziellen <a rel=\"noreferrer noopener\" aria-label=\"Google Translation API (opens in a new tab)\" href=\"https:\/\/cloud.google.com\/translate\/docs\" target=\"_blank\">Google Translation API<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Google bietet im Rahmen seiner Google Cloud die Google Translation API mit einer nutzungsbasierten Kostenstruktur an. Daneben gibt es eine undokumentierte, ohne Key nutzbare API, die jedoch schon nach wenigen Requests den Dienst verweigert. Bei der Benutzung der Webseiten-\u00dcbersetzungsfunktion von Google Chrome f\u00e4llt auf, dass hier ohne sp\u00fcrbare Begrenzung Seiten in sehr guter Qualit\u00e4t \u00fcbersetzt [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"gtbabel_prevent_lngs":"","gtbabel_alt_lng":"","footnotes":""},"categories":[1],"tags":[],"class_list":{"0":"post-2597","1":"post","2":"type-post","3":"status-publish","4":"format-standard","6":"category-blog"},"acf":[],"yoast_head":"<title>Google Translation API Hacking &#060; Vielhuber David<\/title>\n<meta name=\"description\" content=\"Google bietet im Rahmen seiner Google Cloud die Google Translation API mit einer nutzungsbasierten Kostenstruktur an. Daneben gibt es eine\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/vielhuber.de\/blog\/google-translation-api-hacking\/\" \/>\n<meta property=\"og:locale\" content=\"de_DE\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Google Translation API Hacking &#060; Vielhuber David\" \/>\n<meta property=\"og:description\" content=\"Google bietet im Rahmen seiner Google Cloud die Google Translation API mit einer nutzungsbasierten Kostenstruktur an. Daneben gibt es eine\" \/>\n<meta property=\"og:url\" content=\"https:\/\/vielhuber.de\/blog\/google-translation-api-hacking\/\" \/>\n<meta property=\"og:site_name\" content=\"Vielhuber David\" \/>\n<meta property=\"article:published_time\" content=\"2020-03-19T00:48:56+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2022-01-02T23:40:15+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/vielhuber.de\/wp-content\/uploads\/image-5.png\" \/>\n<meta name=\"author\" content=\"David\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@vielhuber\" \/>\n<meta name=\"twitter:site\" content=\"@vielhuber\" \/>\n<meta name=\"twitter:label1\" content=\"Verfasst von\" \/>\n\t<meta name=\"twitter:data1\" content=\"David\" \/>\n\t<meta name=\"twitter:label2\" content=\"Gesch\u00e4tzte Lesezeit\" \/>\n\t<meta name=\"twitter:data2\" content=\"9\u00a0Minuten\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/vielhuber.de\\\/blog\\\/google-translation-api-hacking\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/vielhuber.de\\\/blog\\\/google-translation-api-hacking\\\/\"},\"author\":{\"name\":\"David\",\"@id\":\"https:\\\/\\\/vielhuber.de\\\/#\\\/schema\\\/person\\\/64d4ff14713d413ea4d9b210d0c2c6ef\"},\"headline\":\"Google Translation API Hacking\",\"datePublished\":\"2020-03-19T00:48:56+00:00\",\"dateModified\":\"2022-01-02T23:40:15+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/vielhuber.de\\\/blog\\\/google-translation-api-hacking\\\/\"},\"wordCount\":1381,\"publisher\":{\"@id\":\"https:\\\/\\\/vielhuber.de\\\/#\\\/schema\\\/person\\\/64d4ff14713d413ea4d9b210d0c2c6ef\"},\"image\":{\"@id\":\"https:\\\/\\\/vielhuber.de\\\/blog\\\/google-translation-api-hacking\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/vielhuber.de\\\/wp-content\\\/uploads\\\/image-5.png\",\"articleSection\":[\"Blog\"],\"inLanguage\":\"de\"},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/vielhuber.de\\\/blog\\\/google-translation-api-hacking\\\/\",\"url\":\"https:\\\/\\\/vielhuber.de\\\/blog\\\/google-translation-api-hacking\\\/\",\"name\":\"Google Translation API Hacking &#060; Vielhuber David\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/vielhuber.de\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/vielhuber.de\\\/blog\\\/google-translation-api-hacking\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/vielhuber.de\\\/blog\\\/google-translation-api-hacking\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/vielhuber.de\\\/wp-content\\\/uploads\\\/image-5.png\",\"datePublished\":\"2020-03-19T00:48:56+00:00\",\"dateModified\":\"2022-01-02T23:40:15+00:00\",\"description\":\"Google bietet im Rahmen seiner Google Cloud die Google Translation API mit einer nutzungsbasierten Kostenstruktur an. Daneben gibt es eine\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/vielhuber.de\\\/blog\\\/google-translation-api-hacking\\\/#breadcrumb\"},\"inLanguage\":\"de\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/vielhuber.de\\\/blog\\\/google-translation-api-hacking\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"de\",\"@id\":\"https:\\\/\\\/vielhuber.de\\\/blog\\\/google-translation-api-hacking\\\/#primaryimage\",\"url\":\"https:\\\/\\\/vielhuber.de\\\/wp-content\\\/uploads\\\/image-5.png\",\"contentUrl\":\"https:\\\/\\\/vielhuber.de\\\/wp-content\\\/uploads\\\/image-5.png\",\"width\":1109,\"height\":461},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/vielhuber.de\\\/blog\\\/google-translation-api-hacking\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/vielhuber.de\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Google Translation API Hacking\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/vielhuber.de\\\/#website\",\"url\":\"https:\\\/\\\/vielhuber.de\\\/\",\"name\":\"Vielhuber David\",\"description\":\"Full-Stack Developer\",\"publisher\":{\"@id\":\"https:\\\/\\\/vielhuber.de\\\/#\\\/schema\\\/person\\\/64d4ff14713d413ea4d9b210d0c2c6ef\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/vielhuber.de\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"de\"},{\"@type\":[\"Person\",\"Organization\"],\"@id\":\"https:\\\/\\\/vielhuber.de\\\/#\\\/schema\\\/person\\\/64d4ff14713d413ea4d9b210d0c2c6ef\",\"name\":\"David\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"de\",\"@id\":\"https:\\\/\\\/vielhuber.de\\\/wp-content\\\/uploads\\\/about.jpg\",\"url\":\"https:\\\/\\\/vielhuber.de\\\/wp-content\\\/uploads\\\/about.jpg\",\"contentUrl\":\"https:\\\/\\\/vielhuber.de\\\/wp-content\\\/uploads\\\/about.jpg\",\"width\":700,\"height\":552,\"caption\":\"David\"},\"logo\":{\"@id\":\"https:\\\/\\\/vielhuber.de\\\/wp-content\\\/uploads\\\/about.jpg\"},\"sameAs\":[\"https:\\\/\\\/x.com\\\/vielhuber\"]}]}<\/script>","yoast_head_json":{"title":"Google Translation API Hacking &#060; Vielhuber David","description":"Google bietet im Rahmen seiner Google Cloud die Google Translation API mit einer nutzungsbasierten Kostenstruktur an. Daneben gibt es eine","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/vielhuber.de\/blog\/google-translation-api-hacking\/","og_locale":"de_DE","og_type":"article","og_title":"Google Translation API Hacking &#060; Vielhuber David","og_description":"Google bietet im Rahmen seiner Google Cloud die Google Translation API mit einer nutzungsbasierten Kostenstruktur an. Daneben gibt es eine","og_url":"https:\/\/vielhuber.de\/blog\/google-translation-api-hacking\/","og_site_name":"Vielhuber David","article_published_time":"2020-03-19T00:48:56+00:00","article_modified_time":"2022-01-02T23:40:15+00:00","og_image":[{"url":"https:\/\/vielhuber.de\/wp-content\/uploads\/image-5.png","type":"","width":"","height":""}],"author":"David","twitter_card":"summary_large_image","twitter_creator":"@vielhuber","twitter_site":"@vielhuber","twitter_misc":{"Verfasst von":"David","Gesch\u00e4tzte Lesezeit":"9\u00a0Minuten"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/vielhuber.de\/blog\/google-translation-api-hacking\/#article","isPartOf":{"@id":"https:\/\/vielhuber.de\/blog\/google-translation-api-hacking\/"},"author":{"name":"David","@id":"https:\/\/vielhuber.de\/#\/schema\/person\/64d4ff14713d413ea4d9b210d0c2c6ef"},"headline":"Google Translation API Hacking","datePublished":"2020-03-19T00:48:56+00:00","dateModified":"2022-01-02T23:40:15+00:00","mainEntityOfPage":{"@id":"https:\/\/vielhuber.de\/blog\/google-translation-api-hacking\/"},"wordCount":1381,"publisher":{"@id":"https:\/\/vielhuber.de\/#\/schema\/person\/64d4ff14713d413ea4d9b210d0c2c6ef"},"image":{"@id":"https:\/\/vielhuber.de\/blog\/google-translation-api-hacking\/#primaryimage"},"thumbnailUrl":"https:\/\/vielhuber.de\/wp-content\/uploads\/image-5.png","articleSection":["Blog"],"inLanguage":"de"},{"@type":"WebPage","@id":"https:\/\/vielhuber.de\/blog\/google-translation-api-hacking\/","url":"https:\/\/vielhuber.de\/blog\/google-translation-api-hacking\/","name":"Google Translation API Hacking &#060; Vielhuber David","isPartOf":{"@id":"https:\/\/vielhuber.de\/#website"},"primaryImageOfPage":{"@id":"https:\/\/vielhuber.de\/blog\/google-translation-api-hacking\/#primaryimage"},"image":{"@id":"https:\/\/vielhuber.de\/blog\/google-translation-api-hacking\/#primaryimage"},"thumbnailUrl":"https:\/\/vielhuber.de\/wp-content\/uploads\/image-5.png","datePublished":"2020-03-19T00:48:56+00:00","dateModified":"2022-01-02T23:40:15+00:00","description":"Google bietet im Rahmen seiner Google Cloud die Google Translation API mit einer nutzungsbasierten Kostenstruktur an. Daneben gibt es eine","breadcrumb":{"@id":"https:\/\/vielhuber.de\/blog\/google-translation-api-hacking\/#breadcrumb"},"inLanguage":"de","potentialAction":[{"@type":"ReadAction","target":["https:\/\/vielhuber.de\/blog\/google-translation-api-hacking\/"]}]},{"@type":"ImageObject","inLanguage":"de","@id":"https:\/\/vielhuber.de\/blog\/google-translation-api-hacking\/#primaryimage","url":"https:\/\/vielhuber.de\/wp-content\/uploads\/image-5.png","contentUrl":"https:\/\/vielhuber.de\/wp-content\/uploads\/image-5.png","width":1109,"height":461},{"@type":"BreadcrumbList","@id":"https:\/\/vielhuber.de\/blog\/google-translation-api-hacking\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/vielhuber.de\/"},{"@type":"ListItem","position":2,"name":"Google Translation API Hacking"}]},{"@type":"WebSite","@id":"https:\/\/vielhuber.de\/#website","url":"https:\/\/vielhuber.de\/","name":"Vielhuber David","description":"Full-Stack Developer","publisher":{"@id":"https:\/\/vielhuber.de\/#\/schema\/person\/64d4ff14713d413ea4d9b210d0c2c6ef"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/vielhuber.de\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"de"},{"@type":["Person","Organization"],"@id":"https:\/\/vielhuber.de\/#\/schema\/person\/64d4ff14713d413ea4d9b210d0c2c6ef","name":"David","image":{"@type":"ImageObject","inLanguage":"de","@id":"https:\/\/vielhuber.de\/wp-content\/uploads\/about.jpg","url":"https:\/\/vielhuber.de\/wp-content\/uploads\/about.jpg","contentUrl":"https:\/\/vielhuber.de\/wp-content\/uploads\/about.jpg","width":700,"height":552,"caption":"David"},"logo":{"@id":"https:\/\/vielhuber.de\/wp-content\/uploads\/about.jpg"},"sameAs":["https:\/\/x.com\/vielhuber"]}]}},"_links":{"self":[{"href":"https:\/\/vielhuber.de\/hy\/wp-json\/wp\/v2\/posts\/2597","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/vielhuber.de\/hy\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/vielhuber.de\/hy\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/vielhuber.de\/hy\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/vielhuber.de\/hy\/wp-json\/wp\/v2\/comments?post=2597"}],"version-history":[{"count":71,"href":"https:\/\/vielhuber.de\/hy\/wp-json\/wp\/v2\/posts\/2597\/revisions"}],"predecessor-version":[{"id":3236,"href":"https:\/\/vielhuber.de\/hy\/wp-json\/wp\/v2\/posts\/2597\/revisions\/3236"}],"wp:attachment":[{"href":"https:\/\/vielhuber.de\/hy\/wp-json\/wp\/v2\/media?parent=2597"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/vielhuber.de\/hy\/wp-json\/wp\/v2\/categories?post=2597"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/vielhuber.de\/hy\/wp-json\/wp\/v2\/tags?post=2597"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}