Als onderdeel van de Google Cloud biedt Google de Google Translation API met een op gebruik gebaseerde kostenstructuur . Er is ook een niet- gedocumenteerde API die zonder sleutel kan worden gebruikt, maar die weigert te werken na slechts een paar verzoeken. Bij gebruik van de websitevertaalfunctie van Google Chrome valt op dat pagina's zonder merkbare beperking in zeer goede kwaliteit kunnen worden vertaald.
Blijkbaar wordt hier al gebruik gemaakt van het geavanceerde NMT-model . Maar welke API gebruikt Google Chrome intern om de content te vertalen en kan deze API ook direct worden aangesproken - ook aan de serverkant? Om netwerkverkeer te analyseren, worden tools zoals Wireshark of Telerik Fiddler , die ook versleuteld verkeer kunnen analyseren, aanbevolen. Maar Chrome levert zelfs de verzoeken die het verzendt voor de vertaling van de pagina gratis: ze kunnen eenvoudig worden bekeken met de Chrome DevTools:
Als u een vertaling uitvoert, vang dan het cruciale POST-verzoek naar https://translate.googleapis.com via "Kopiëren> Kopiëren als cURL (bash)" en voer het uit in een tool als Postman , u kunt het verzoek bijvoorbeeld zonder problemen opnieuw verzenden:
De betekenis van de URL-parameters is ook grotendeels duidelijk:
Sleutel | Voorbeeldwaarde | Betekenis |
anno | 3 | Annotatiemodus (beïnvloedt het retourformaat) |
cliënt | te_lib | Klantinformatie (varieert, de waarde is 'webapp' via de webinterface van Google Translate; heeft een effect op het retourformaat en snelheidsbeperking) |
formaat | html | Tekenreeksformaat (belangrijk voor het vertalen van HTML-tags) |
v | 1.0 | Versienummer van Google Translate |
sleutel | AIzaSyBOti4mM-6x9WDnZIjIeyEU21OpBXqWBgw | API-sleutel (zie hieronder) |
logld | vTE_20200210_00 | Protocolversie |
sl | de | Brontaal |
tl | nl | Doel taal |
sp | nmt | ML-model |
tc | 1 | onbekend |
sr | 1 | onbekend |
tk | 709408.812158 | Token (zie hieronder) |
Mode | 1 | onbekend |
Sommige verzoekheaders zijn ook ingesteld, maar deze kunnen meestal worden genegeerd. Na het handmatig deselecteren van alle headers, inclusief die van de user-agent , wordt een coderingsprobleem ontdekt bij het invoeren van speciale tekens (hier bij het vertalen van " Hallo wereld "):
Als je de user-agent opnieuw activeert (dat kan over het algemeen geen kwaad), levert de API UTF-8-gecodeerde tekens:
Zijn we er al en hebben we alle informatie om deze API buiten Google Chrome te gebruiken? Als u de te vertalen tekenreeks (gegevensveld q van het POST-verzoek) wijzigt van bijvoorbeeld "Hallo wereld" naar "Hallo wereld ! “, We krijgen een foutmelding:
We vertalen deze gewijzigde nu opnieuw in Google Chrome met behulp van de vertaalfunctie van de website en stellen vast dat, naast de parameter q , ook de parameter tk is veranderd (alle andere parameters zijn hetzelfde gebleven):
Blijkbaar is het een token die afhankelijk is van de string, waarvan de structuur niet gemakkelijk te zien is. Wanneer u de websitevertaling start, worden de volgende bestanden geladen:
- 1 CSS-bestand: translateelement.css
- 4 afbeeldingen: translate_24dp.png (2x), gen204 (2x)
- 2 JS-bestanden: main_de.js , element_main.js
De twee JavaScript-bestanden zijn versluierd en verkleind. Tools zoals JS Nice en de4js helpen ons nu om deze bestanden leesbaarder te maken. Om ze live te debuggen, raden we de Chrome Extension Requestly aan, die externe bestanden direct lokaal tunnelt:
Nu kunnen we de code debuggen ( CORS moet eerst op de lokale server worden geactiveerd). De relevante codesectie voor het genereren van het token lijkt verborgen te zijn in deze sectie in het bestand element_main.js:
b7739bf50b2edcf636c43a8f8910def9
Hier wordt de tekst gehasht met behulp van enkele bitverschuivingen . Maar helaas missen we nog een stukje van de puzzel: naast het argument a (dat is de te vertalen tekst) wordt een ander argument b doorgegeven aan de functie Bp () - een soort zaadje dat van tijd tot tijd lijkt te veranderen en dat ook omvat stroomt in hashing. Maar waar komt hij vandaan? Als we naar de functieaanroep van Bp () springen, vinden we de volgende codesectie:
b7739bf50b2edcf636c43a8f8910def9
De functie Hq wordt vooraf als volgt gedeclareerd:
b7739bf50b2edcf636c43a8f8910def9
Hier liet de Deobfuscater wat rotzooi achter; Nadat we String.fromCharCode ('...') hebben vervangen door de respectievelijke tekenreeksen, verwijder de verouderde a () en voeg de functieaanroepen [c (), c ()] samen , het resultaat is:
b7739bf50b2edcf636c43a8f8910def9
Of nog makkelijker:
b7739bf50b2edcf636c43a8f8910def9
De functie yq is eerder gedefinieerd als:
b7739bf50b2edcf636c43a8f8910def9
De seed lijkt te zitten in het globale object google.translate._const._ctkk , dat beschikbaar is tijdens runtime. Maar waar staat het? In het andere, eerder geladen JS-bestand main_de.js, is het tenminste ook aan het begin beschikbaar. Aan het begin voegen we het volgende toe:
b7739bf50b2edcf636c43a8f8910def9
In de console krijgen we eigenlijk het huidige zaad:
Hierdoor blijft Google Chrome zelf, dat blijkbaar voor het zaad zorgt, als laatste optie. Gelukkig is de broncode (Chromium, inclusief de Translate-component) open source en daarom publiekelijk beschikbaar. We halen de repository lokaal op en vinden de aanroep van de functie TranslateScript :: GetTranslateScriptURL in het bestand translate_script.cc in de map components / translate / core / browser:
b7739bf50b2edcf636c43a8f8910def9
De variabele met de URL is vast gedefinieerd in hetzelfde bestand:
b7739bf50b2edcf636c43a8f8910def9
Als we nu het bestand element.js nauwkeuriger bekijken (na opnieuw verduidelijken), vinden we de hard-set entry c._ctkk - het google.translate- object wordt ook dienovereenkomstig ingesteld en het laden van alle relevante items (die we al eerder hebben ontdekt) wordt geactiveerd:
b7739bf50b2edcf636c43a8f8910def9
Nu is de parameter sleutel blijft voor de behandeling (met de waarde AIzaSyBOti4mM-6x9WDnZIjIeyEU21OpBXqWBgw). Dat lijkt een generieke browser API-sleutel te zijn (die ook in sommige Google-resultaten te vinden is ). Het is ingesteld in Chromium in het bestand translate_url_util.cc in de map componenten / translate / core / browser:
b7739bf50b2edcf636c43a8f8910def9
De sleutel wordt gegenereerd in google_apis / google_api_keys.cc op basis van een dummy-waarde:
b7739bf50b2edcf636c43a8f8910def9
Een test toont echter aan dat de API-aanroepen hetzelfde werken zonder deze sleutelparameter. Als je experimenteert met de API, krijg je de statuscode 200 terug als je succesvol bent. Als je dan tegen een limiet aanloopt, krijg je de statuscode 411 terug met de melding " POST-verzoeken vereisen een inhoudslengte header ". Het is daarom aan te raden om deze header (die in Postman automatisch als tijdelijke header wordt ingesteld) op te nemen.
Het retourformaat van de vertaalde strings is ongebruikelijk als er meerdere zinnen in één verzoek staan. De afzonderlijke zinnen worden omsloten door de i- / b-HTML-tags:
Google Chrome stuurt ook niet de volledige HTML naar de API, maar slaat attribuutwaarden zoals href op in het verzoek (en stelt in plaats daarvan indices in zodat de tags later aan de clientzijde kunnen worden toegewezen):
Als u de waarde van de POST key client te veranderen van te_lib (Google Chrome) op webapp ( Google Translation-website ), krijg je de laatste vertaalde string:
Het probleem is dat je veel meer kans loopt om snelheidsbeperking tegen te komen dan via te_lib (ter vergelijking: bij webapp wordt dit bereikt na 40.000 tekens, bij te_lib is er geen snelheidsbeperking). We moeten dus nader bekijken hoe Chrome het resultaat parseert. We vinden het hier in element_main.js:
b7739bf50b2edcf636c43a8f8910def9
Als u de volledige HTML-code naar de API stuurt, blijven de attributen in het vertaalde antwoord. We hoeven daarom niet het hele parse-gedrag te imiteren, maar alleen de laatste, vertaalde string uit het antwoord te extraheren. Om dit te doen, bouwen we een kleine HTML-tag-parser die de buitenste <i>-tags inclusief hun inhoud verwijdert en de buitenste <b>-tags verwijdert. Met deze kennis kunnen we nu (na het installeren van afhankelijkheden met componist fzaninotto / faker vielhuber / stringhelper nodig hebben ) een server-side versie van de vertaal-API bouwen:
b7739bf50b2edcf636c43a8f8910def9
Hieronder volgen de resultaten van een eerste test die is uitgevoerd op vijf verschillende systemen met verschillende bandbreedtes en IP-adressen:
Karakter | Tekens per verzoek | Looptijd | Foutenpercentage | Kosten via officiële API |
13.064.662 | ~250 | 03: 36: 17 uur | 0% | 237,78€ |
24.530.510 | ~250 | 11: 09: 13 uur | 0% | 446,46€ |
49.060.211 | ~250 | 20: 39: 10 uur | 0% | 892,90€ |
99.074.487 | ~1000 | 61: 24: 37 uur | 0% | 1803,16€ |
99.072.896 | ~1000 | 62: 22: 20 uur | 0% | 1803,13€ |
Σ284,802,766 | ~ Ø550 | Σ159: 11:37 uur | 0% | Σ € 5183,41 |
Opmerking: deze blogpost inclusief alle scripts is alleen voor testdoeleinden geschreven. Laat de scripts voor productief gebruik niet gebruiken, in plaats daarvan te werken met de officiële Google-vertaling API .