Using Google Translate to automate text translation

Post your working scripts, libraries and tools for AHK v1.1 and older
UnReALiTyy
Posts: 223
Joined: 06 Jun 2017, 11:38

Re: Using Google Translate to automate text translation

04 Mar 2020, 10:58

rommmcek wrote:
04 Mar 2020, 06:05
I extracted additional translation data:
ok these are the individual intermediate steps that take place during translation. good to know but i don't know what to do with it.
User avatar
rommmcek
Posts: 1475
Joined: 15 Aug 2014, 15:18

Re: Using Google Translate to automate text translation

07 Mar 2020, 09:30

All important data are now extracted. Since for some translations the data can be abondant MsgBox is no longer suitable for display.
F1 or Ctrl + 5 - copy marked text and traslate it. Esc - hide Gui, F2 - show hidden Gui, #Esc - Exit
Edit: Added Phonetic transcription and "Did you mean..."

Code: Select all

#NoEnv  ; Recommended for performance and compatibility with future AutoHotkey releases.
; #Warn  ; Enable warnings to assist with detecting common errors.
SendMode Input  ; Recommended for new scripts due to its superior speed and reliability.
SetWorkingDir %A_ScriptDir%  ; Ensures a consistent starting directory.

#SingleInstance, Force
DetectHiddenWindows, On
SoundBeep, 2500

F1::
^5::
    While Clipboard && A_Index<10 {
        Clipboard:= ""
        Sleep, 50
    }
    While !Clipboard && A_Index<5 {
        Sleep, 100
        SendInput, ^c
    }
    if !Clipboard {
        MsgBox,,, No copied text to translate!, 2.5
        Return
    }

    r:= GoogleTranslate(c:= RTrim(Clipboard, "`n`r"), , "de")
    
    loop, 7
        if StrLen(r[A_Index])>(l:=10000)
            r[A_Index]:= SubStr(r[A_Index], 1, l)
    ;[int, lng, Main, trn, syn, def, exp]
    d:= (r.1? r.1 "`n": "") (r.2? r.2 "`n`n": "") r.3 (r.4||r.5||r.6||r.7? "`n`n": "")
      . (r.4? r.4 (r.5||r.6||r.7? "`n`n": ""): "") (r.5? r.5 (r.6||r.7? "`n`n": ""): "")
      . (r.6? r.6 (r.7? "`n`n": ""): "") (r.7? r.7: ""), ww:=wh:=g:= 0
            
    Gosub doGui
    gui show, Hide
    WinGetPos,,, ww,, ahk_id %hGui%
    ww:= ww>A_ScreenWidth*.8? A_ScreenWidth*.8: ww
    Gosub doGui
    gui show, Hide
    WinGetPos,,,, wh, ahk_id %hGui%
    wh>=A_ScreenHeight*.9? (wh:=A_ScreenHeight*.9, ww+=Ceil(15*A_ScreenDPI/96) ): ""
    Gosub doGui
    gui Show
    SendMessage, 0xB1, 0, 0,, ahk_id %hEdt%
Return

doGui:
    gui, Destroy
    gui, Margin, 0, 0
    ;gui font, s12
    gui color,, BEC8BE
    gui, % "+hWndhGui -DPIScale " (++g<3? "-Caption": "")
    gui add, Edit, % (ww? "w" ww: "") (wh? " h" wh: "") (wh<A_ScreenHeight*.9? " -VScroll": "") " +hwndhEdt", % d
    GroupAdd, TrGui, % "ahk_id " hGui
Return

#IfWinActive, ahk_group TrGui
GuiClose:
Esc:: Gui, Hide
#IfWinActive
#Esc::
    KeyWait, Esc
    KeyWait, LWin
    SoundBeep, 1200
ExitApp

F2:: Gui, show

GoogleTranslate(str, from := "auto", to := "en")  { ;function by RRR based on teadrinkers GoogleTranslate()
   static JS := CreateScriptObj(), _ := JS.( GetJScript() ) := JS.("delete ActiveXObject; delete GetObject;")
   
    json := SendRequest(JS, str, to, from, proxy := "")
    oJSON := JS.("(" . json . ")")

    step1:= oJSON[0][0][8][0][0][1], step2:= oJSON[0][0][8][1][0][1]
    int:= (step1? "1: " step1: "") (step2? "   2: " step2: "")
    
    loop, % lln:=oJSON[8][0].length
        lng1.= oJSON[8][0][A_Index-1] (A_Index<lln? ", ": "")
    loop, % lln:=oJSON[8][0].length
        lng2.= oJSON[8][3][A_Index-1] (A_Index<lln? ", ": "")
    lng := "a: " oJSON[2] "   b: " lng1 "   c: " lng2

    Loop % oJSON[0].length
        Src.= oJSON[0][A_Index-1][1] ((Prn:= ojson[0][1][3])&&A_Index=1? "`n[" Prn "]": "")
    Src!=(Dym:= ojson[7][1])? Src.= (Dym? "`n►►►Did you mean: ▌" Dym "?": "")
                            : Src.= (Dym? "`n►►►Showing result for ▌" Dym "`n►►►Translate instead ▌" (Src:=Str): "")
    Loop % oJSON[0].length
        mTr .= oJSON[0][A_Index-1][0]
        
    Main:= Src "`n----------------------`n" mTr

    loop, % ojson[1].length
    {
        trn.= "♦ " ojson[1][cW:=A_Index-1][0] "`n"
        Loop, % ojson[1][cW][2].length
        {
            Loop, % ln:=ojson[1][cW][2][tW:=A_Index-1][1].length
                tr.= ojson[1][cW][2][tW][1][A_Index-1] (A_Index<ln? ", ": "")
                  ;. (Mod(A_Index, 5)=0&&A_Index<ln? "`n       ": "")
            trn.= "    ▪ " ((ar:=ojson[1][cW][2][A_Index-1][4])? ar " ": "")
                 . ojson[1][cW][2][A_Index-1][0] "  ► " tr "`n",  tr:= ""
        }
    }    trn:= (trn? "▬ Translations of  ►" oJSON[1][0][3] "◄`n" RTrim(trn, "`n"): "")
   
    loop, % ojson[11].length
    {
        syn.= "♦ " ojson[11][cW:=A_Index-1][0] "  ►" ojson[11][cW][2] "◄`n"
        Loop, % ojson[11][cw].length
            Loop, % ojson[11][cW][cN:=A_Index].length
                Loop, % ojson[11][cW][cN][sW:=A_Index-1].length
                    Loop, % ln:=ojson[11][cW][cN][sW][tW:=A_Index-1].length
                        syn.= (A_Index=1? "    ▪ ": "") ojson[11][cW][cN][sW][tW][A_Index-1] (A_Index<ln? ", ": "`n")
                           ;. (Mod(A_Index, 5)=0&&A_Index<ln? "`n       ": "")
    }   syn:= (syn? "▬ Synonims`n" RTrim(syn, "`n"): "")
   
    loop, % ojson[12].length
    {
        def.= "♦ " ojson[12][cW:=A_Index-1][0] "  ►" ojson[12][cW][2] "◄`n"
        loop, % ojson[12][cW][1].length
            qu:= ojson[12][0][1][A_Index-1][2]
          , def.= "    ▪ " ojson[12][cW][1][A_Index-1][0] (qu? "`n     """ qu """" "`n": "`n")
    }  def:= (def? "▬ Definitions`n" RTrim(def, "`n "): "")

    loop, % ln:=ojson[13][0].length
        exp.= "    ♦ " ojson[13][0][A_Index-1][0] (A_Index<ln? "`n": "")
    exp:= (exp? "▬ Examples`n" StrReplace(StrReplace(exp, "<b>", "▌"), "</b>"): "")

   Return [int, lng, Main, trn, syn, def, exp]
}

SendRequest(JS, str, tl, sl, proxy) {
   ComObjError(false)
   http := ComObjCreate("WinHttp.WinHttpRequest.5.1")
¬:=( proxy && http.SetProxy(2, proxy) )
   http.open( "POST", "https://translate.google.com/translate_a/single?client=webapp&sl="
      . sl . "&tl=" . tl . "&hl=" . sl                                              ; sl in place of tl by RRR
      . "&dt=at&dt=bd&dt=ex&dt=ld&dt=md&dt=qca&dt=rw&dt=rm&dt=ss&dt=t&ie=UTF-8&oe=UTF-8&otf=0&ssel=0&tsel=0&pc=1&kc=1"
      . "&tk=" . JS.("tk").(str), 1 )

   http.SetRequestHeader("Content-Type", "application/x-www-form-urlencoded;charset=utf-8")
   http.SetRequestHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:47.0) Gecko/20100101 Firefox/47.0")
   http.send("q=" . URIEncode(str))
   http.WaitForResponse(-1)
   Return http.responsetext
}

URIEncode(str, encoding := "UTF-8")  {
   VarSetCapacity(var, StrPut(str, encoding))
   StrPut(str, &var, encoding)

   While code := NumGet(Var, A_Index - 1, "UChar")  {
      bool := (code > 0x7F || code < 0x30 || code = 0x3D)
      UrlStr .= bool ? "%" . Format("{:02X}", code) : Chr(code)
   }
   Return UrlStr
}

GetJScript()
{
   script =
   (
      var TKK = ((function() {
        var a = 561666268;
        var b = 1526272306;
        return 406398 + '.' + (a + b);
      })());

      function b(a, b) {
        for (var d = 0; d < b.length - 2; d += 3) {
            var c = b.charAt(d + 2),
                c = "a" <= c ? c.charCodeAt(0) - 87 : Number(c),
                c = "+" == b.charAt(d + 1) ? a >>> c : a << c;
            a = "+" == b.charAt(d) ? a + c & 4294967295 : a ^ c
        }
        return a
      }

      function tk(a) {
          for (var e = TKK.split("."), h = Number(e[0]) || 0, g = [], d = 0, f = 0; f < a.length; f++) {
              var c = a.charCodeAt(f);
              128 > c ? g[d++] = c : (2048 > c ? g[d++] = c >> 6 | 192 : (55296 == (c & 64512) && f + 1 < a.length && 56320 == (a.charCodeAt(f + 1) & 64512) ?
              (c = 65536 + ((c & 1023) << 10) + (a.charCodeAt(++f) & 1023), g[d++] = c >> 18 | 240,
              g[d++] = c >> 12 & 63 | 128) : g[d++] = c >> 12 | 224, g[d++] = c >> 6 & 63 | 128), g[d++] = c & 63 | 128)
          }
          a = h;
          for (d = 0; d < g.length; d++) a += g[d], a = b(a, "+-a^+6");
          a = b(a, "+-3^+b+-f");
          a ^= Number(e[1]) || 0;
          0 > a && (a = (a & 2147483647) + 2147483648);
          a `%= 1E6;
          return a.toString() + "." + (a ^ h)
      }
   )
   Return script
}

CreateScriptObj() {
   static doc
   doc := ComObjCreate("htmlfile")
   doc.write("<meta http-equiv='X-UA-Compatible' content='IE=9'>")
   Return ObjBindMethod(doc.parentWindow, "eval")
}
@teadrinker do you have a hint how to trigger pronunciation if its possible at all?
Last edited by rommmcek on 11 Mar 2020, 05:14, edited 7 times in total.
teadrinker
Posts: 4331
Joined: 29 Mar 2015, 09:41
Contact:

Re: Using Google Translate to automate text translation

07 Mar 2020, 10:06

@rommmcek
You can get an mp3 file from Google like this:

Code: Select all

MsgBox, % GetAudioFromGoogle("Hello World", "en", A_Desktop . "\test.mp3")

GetAudioFromGoogle(text, lng, mp3filePath)  {
   url := CreateUrl(text, lng)
   data := SendRequest(url)
   if SubStr(data, 1, 6) = "Error!"
      Return data
   
   Return SaveDataToFile(data, mp3filePath)
}

CreateUrl(text, lng)  {
   JS := CreateScriptObj(), JS.( GetJScript() )
   url := "https://translate.google.ru/translate_tts?ie=UTF-8&tl="
         . lng . "&total=1&idx=0&client=t&prev=input&textlen="
         . StrLen(text) . "&tk=" . JS.("tk").(text) . "&q=" . URIEncode(text)
   Return url
}

SendRequest(url) {
   whr := ComObjCreate("Msxml2.XMLHTTP.6.0")
   whr.Open("GET", url, false)
   whr.Send()
   
   if (whr.Status != 200)
      Return "Error! Status: " . whr.Status . "`n`n" . whr.responseBody
   Return whr.responseBody
}

SaveDataToFile(data, filePath) {
   stream := ComObjCreate("ADODB.Stream")
   stream.type := 1  ; Binary data
   stream.Open
   stream.Write(data)
   stream.SaveToFile(filePath, 2)
   stream.Close
   Return true
}

GetJScript()
{
   script =
   (
      var TKK = ((function() {
        var a = 561666268;
        var b = 1526272306;
        return 406398 + '.' + (a + b);
      })());

      function b(a, b) {
        for (var d = 0; d < b.length - 2; d += 3) {
            var c = b.charAt(d + 2),
                c = "a" <= c ? c.charCodeAt(0) - 87 : Number(c),
                c = "+" == b.charAt(d + 1) ? a >>> c : a << c;
            a = "+" == b.charAt(d) ? a + c & 4294967295 : a ^ c
        }
        return a
      }

      function tk(a) {
          for (var e = TKK.split("."), h = Number(e[0]) || 0, g = [], d = 0, f = 0; f < a.length; f++) {
              var c = a.charCodeAt(f);
              128 > c ? g[d++] = c : (2048 > c ? g[d++] = c >> 6 | 192 : (55296 == (c & 64512) && f + 1 < a.length && 56320 == (a.charCodeAt(f + 1) & 64512) ?
              (c = 65536 + ((c & 1023) << 10) + (a.charCodeAt(++f) & 1023), g[d++] = c >> 18 | 240,
              g[d++] = c >> 12 & 63 | 128) : g[d++] = c >> 12 | 224, g[d++] = c >> 6 & 63 | 128), g[d++] = c & 63 | 128)
          }
          a = h;
          for (d = 0; d < g.length; d++) a += g[d], a = b(a, "+-a^+6");
          a = b(a, "+-3^+b+-f");
          a ^= Number(e[1]) || 0;
          0 > a && (a = (a & 2147483647) + 2147483648);
          a `%= 1E6;
          return a.toString() + "." + (a ^ h)
      }
   )
   Return script
}

URIEncode(str, encoding := "UTF-8")  {
   VarSetCapacity(var, StrPut(str, encoding))
   StrPut(str, &var, encoding)

   While code := NumGet(Var, A_Index - 1, "UChar")  {
      bool := (code > 0x7F || code < 0x30 || code = 0x3D)
      UrlStr .= bool ? "%" . Format("{:02X}", code) : Chr(code)
   }
   Return UrlStr
}

CreateScriptObj() {
   static doc
   doc := ComObjCreate("htmlfile")
   doc.write("<meta http-equiv='X-UA-Compatible' content='IE=9'>")
   Return ObjBindMethod(doc.parentWindow, "eval")
}
User avatar
rommmcek
Posts: 1475
Joined: 15 Aug 2014, 15:18

Re: Using Google Translate to automate text translation

07 Mar 2020, 10:57

You are amazing! Thank you very much! Maybe is now time for a tiny repayment!
garry
Posts: 3770
Joined: 22 Dec 2013, 12:50

Re: Using Google Translate to automate text translation

07 Mar 2020, 15:53

thank you teadrinker, I'm an idiot
how translate this english text to portuguese ?
( I hear english text in audio file )

Code: Select all

f1:= a_desktop . "\test.mp3"
lng:="pt"
text:="Good evening , how are you ?"
aa:=GetAudioFromGoogle(text, lng, F1)
global aa
runwait,%f1%
filedelete,%f1%
exitapp
;----------------------------------------------------------------------------
GetAudioFromGoogle(text,lng, mp3filePath)  {
   url := CreateUrl(text,lng)
   data := SendRequest(url)
   if SubStr(data, 1, 6) = "Error!"
      Return data
   
   Return SaveDataToFile(data, mp3filePath)
}

CreateUrl(text, lng)  {
   JS := CreateScriptObj(), JS.( GetJScript() )
   url := "https://translate.google.ru/translate_tts?ie=UTF-8&tl="
         . lng . "&total=1&idx=0&client=t&prev=input&textlen="
         . StrLen(text) . "&tk=" . JS.("tk").(text) . "&q=" . URIEncode(text)
   Return url
}

SendRequest(url) {
   whr := ComObjCreate("Msxml2.XMLHTTP.6.0")
   whr.Open("GET", url, false)
   whr.Send()
   
   if (whr.Status != 200)
      Return "Error! Status: " . whr.Status . "`n`n" . whr.responseBody
   Return whr.responseBody
}
SaveDataToFile(data, filePath) {
   stream := ComObjCreate("ADODB.Stream")
   stream.type := 1  ; Binary data
   stream.Open
   stream.Write(data)
   stream.SaveToFile(filePath, 2)
   stream.Close
   Return true
}
GetJScript()
{
   script =
   (
      var TKK = ((function() {
        var a = 561666268;
        var b = 1526272306;
        return 406398 + '.' + (a + b);
      })());
      function b(a, b) {
        for (var d = 0; d < b.length - 2; d += 3) {
            var c = b.charAt(d + 2),
                c = "a" <= c ? c.charCodeAt(0) - 87 : Number(c),
                c = "+" == b.charAt(d + 1) ? a >>> c : a << c;
            a = "+" == b.charAt(d) ? a + c & 4294967295 : a ^ c
        }
        return a
      }
      function tk(a) {
          for (var e = TKK.split("."), h = Number(e[0]) || 0, g = [], d = 0, f = 0; f < a.length; f++) {
              var c = a.charCodeAt(f);
              128 > c ? g[d++] = c : (2048 > c ? g[d++] = c >> 6 | 192 : (55296 == (c & 64512) && f + 1 < a.length && 56320 == (a.charCodeAt(f + 1) & 64512) ?
              (c = 65536 + ((c & 1023) << 10) + (a.charCodeAt(++f) & 1023), g[d++] = c >> 18 | 240,
              g[d++] = c >> 12 & 63 | 128) : g[d++] = c >> 12 | 224, g[d++] = c >> 6 & 63 | 128), g[d++] = c & 63 | 128)
          }
          a = h;
          for (d = 0; d < g.length; d++) a += g[d], a = b(a, "+-a^+6");
          a = b(a, "+-3^+b+-f");
          a ^= Number(e[1]) || 0;
          0 > a && (a = (a & 2147483647) + 2147483648);
          a `%= 1E6;
          return a.toString() + "." + (a ^ h)
      }
   )
   Return script
}
URIEncode(str, encoding := "UTF-8")  {
   VarSetCapacity(var, StrPut(str, encoding))
   StrPut(str, &var, encoding)
   While code := NumGet(Var, A_Index - 1, "UChar")  {
      bool := (code > 0x7F || code < 0x30 || code = 0x3D)
      UrlStr .= bool ? "%" . Format("{:02X}", code) : Chr(code)
   }
   Return UrlStr
}
CreateScriptObj() {
   static doc
   doc := ComObjCreate("htmlfile")
   doc.write("<meta http-equiv='X-UA-Compatible' content='IE=9'>")
   Return ObjBindMethod(doc.parentWindow, "eval")
}
UnReALiTyy
Posts: 223
Joined: 06 Jun 2017, 11:38

Re: Using Google Translate to automate text translation

07 Mar 2020, 16:35

@garry Translate before you create the audio

Code: Select all

f1 := a_desktop . "\test.mp3"
lng := "pt"
text := GoogleTranslate("Good evening , how are you ?",, lng)
aa := GetAudioFromGoogle(text, lng, F1)
return
garry
Posts: 3770
Joined: 22 Dec 2013, 12:50

Re: Using Google Translate to automate text translation

08 Mar 2020, 05:13

thank you... I've lost the overview ...
teadrinker
Posts: 4331
Joined: 29 Mar 2015, 09:41
Contact:

Re: Using Google Translate to automate text translation

08 Mar 2020, 08:08

Tried to play mp3 from memory. It turned out a bit cumbersome. :)

Code: Select all

; https://docs.microsoft.com/en-us/windows/win32/medfound/player-cpp

#NoEnv
SetBatchLines, -1

text := "not`r`nsignificant      """

if !pIStream := GetMp3StreamFromGoogle(text, "en") {
   MsgBox, Failed to create stream!
   ExitApp
}
Session := new MediaSession()
Session.InitializeWithStream(pIStream)
ObjRelease(pIStream)
Session.Start()
Return

$F1:: Session.Start()
$F2:: Session.Pause()
$F3:: Session.Stop()

Esc:: ExitApp

class MediaSession {
   __New() {
      static MF_VERSION := 0x2, MFSTARTUP_NOSOCKET := 0x1
      DllCall("Mfplat\MFStartup", "UInt", MF_VERSION, "UInt", MFSTARTUP_NOSOCKET, "UInt")
      this.MFSession := IMFMediaSession.Create()
   }
   __Delete() {
      this.Topology := ""
      this.PresentationDescriptor := ""
      this.MediaSource := ""

      this.MFSession.Shutdown()
      this.MFSession := ""
      DllCall("Mfplat\MFShutdown")
   }
   InitializeWithStream(pIStream, fileType := "mp3") {
      static MF_RESOLUTION_MEDIASOURCE := 1
      SourceResolver := IMFSourceResolver.Create()
      ByteStream := IMFByteStream.CreateFromStream(pIStream)

      this.MediaSource := SourceResolver.CreateObjectFromByteStream( ByteStream.ptr, "file." . fileType
                                                                   , MF_RESOLUTION_MEDIASOURCE, 0, MF_OBJECT_TYPE )
      SourceResolver := ByteStream := ""

      this.PresentationDescriptor := this.MediaSource.CreatePresentationDescriptor()
      this.Topology := IMFTopology.Create()
      descriptorCount := this.PresentationDescriptor.GetStreamDescriptorCount()
      Loop % descriptorCount
         this._AddBranchToPartialTopology(A_Index - 1)
      this.MFSession.SetTopology(0, this.Topology.ptr)
   }
   Start(atBeginning := true) {
      VarSetCapacity(GUID_NULL, 16, 0)
      VarSetCapacity(PROPVARIANT, 8 + A_PtrSize*2, 0)
      state := this.GetState()
      if atBeginning
         NumPut(VT_I8 := 0x14, PROPVARIANT, "UShort")
      this.MFSession.Start(&GUID_NULL, &PROPVARIANT)
   }
   Pause() {
      state := this.GetState()
      if (state != 1)
         this.Start(false)
      else
         this.MFSession.Pause()
   }
   Stop() {
      this.MFSession.Stop()
   }
   GetState() {
      Clock := this.MFSession.GetClock()
      Return Clock.GetState()
   }
   _AddBranchToPartialTopology(i) {
      static MFMediaType_Audio := "{73647561-0000-0010-8000-00AA00389B71}"
      StreamDescriptor := this.PresentationDescriptor.GetStreamDescriptorByIndex(i, selected)
      if selected {
         MediaTypeHandler := StreamDescriptor.GetMediaTypeHandler()
         VarSetCapacity(GUID, 16, 0)
         MediaTypeHandler.GetMajorType(GUID)
         if StringFromGUID(GUID) = MFMediaType_Audio
            MFActivate := IMFActivate.Create()
         SourceTopologyNode := this._AddSourceNode(StreamDescriptor)
         OutputTopologyNode := this._AddOutputNode(MFActivate)
         SourceTopologyNode.ConnectOutput(0, OutputTopologyNode.ptr, 0)
      }
   }
   _AddSourceNode(StreamDescriptor) {
      static MF_TOPOLOGY_SOURCESTREAM_NODE := 1
           , MF_TOPONODE_SOURCE                  := "{835C58EC-E075-4BC7-BCBA-4DE000DF9AE6}"
           , MF_TOPONODE_PRESENTATION_DESCRIPTOR := "{835C58ED-E075-4BC7-BCBA-4DE000DF9AE6}"
           , MF_TOPONODE_STREAM_DESCRIPTOR       := "{835C58EE-E075-4BC7-BCBA-4DE000DF9AE6}"
      SourceTopologyNode := IMFTopologyNode.Create(MF_TOPOLOGY_SOURCESTREAM_NODE)
      SourceTopologyNode.SetUnknown( CLSIDFromString(MF_TOPONODE_SOURCE                 , GUID), this.MediaSource.ptr )
      SourceTopologyNode.SetUnknown( CLSIDFromString(MF_TOPONODE_PRESENTATION_DESCRIPTOR, GUID), this.PresentationDescriptor.ptr )
      SourceTopologyNode.SetUnknown( CLSIDFromString(MF_TOPONODE_STREAM_DESCRIPTOR      , GUID), StreamDescriptor.ptr )
      this.Topology.AddNode(SourceTopologyNode.ptr)
      Return SourceTopologyNode
   }
   _AddOutputNode(MFActivate) {
      static MF_TOPOLOGY_OUTPUT_NODE := 0
           , MF_TOPONODE_STREAMID             := "{14932F9B-9087-4BB4-8412-5167145CBE04}"
           , MF_TOPONODE_NOSHUTDOWN_ON_REMOVE := "{14932F9C-9087-4BB4-8412-5167145CBE04}"
      OutputTopologyNode := IMFTopologyNode.Create(MF_TOPOLOGY_OUTPUT_NODE)
      OutputTopologyNode.SetObject(MFActivate.ptr)
      OutputTopologyNode.SetUINT32( CLSIDFromString(MF_TOPONODE_STREAMID            , GUID), 0 )
      OutputTopologyNode.SetUINT32( CLSIDFromString(MF_TOPONODE_NOSHUTDOWN_ON_REMOVE, GUID), 0 )
      this.Topology.AddNode(OutputTopologyNode.ptr)
      Return OutputTopologyNode
   }
}

class IMFMediaSession extends InterfaceBase {
   Create() {
      DllCall("Mf\MFCreateMediaSession", "Ptr", 0, "PtrP", pIMFMediaSession)
      Return new IMFMediaSession(pIMFMediaSession)
   }
   SetTopology(flags, pIMFTopology) {
      hr := DllCall(this.VTable(7), "Ptr", this.ptr, "UInt", flags, "Ptr", pIMFTopology)
      this.IsError("IMFMediaSession::SetTopology", hr)
   }
   Start(pGUID, pVariant) {
      hr := DllCall(this.VTable(9), "Ptr", this.ptr, "Ptr", pGUID, "Ptr", pVariant)
      this.IsError("IMFMediaSession::Start", hr)
   }
   Pause() {
      hr := DllCall(this.VTable(10), "Ptr", this.ptr)
      this.IsError("IMFMediaSession::Pause", hr)
   }
   Stop() {
      hr := DllCall(this.VTable(11), "Ptr", this.ptr)
      this.IsError("IMFMediaSession::Stop", hr)
   }
   Shutdown() {
      hr := DllCall(this.VTable(13), "Ptr", this.ptr)
      this.IsError("IMFMediaSession::Shutdown", hr)
   }
   GetClock() {
      hr := DllCall(this.VTable(14), "Ptr", this.ptr, "PtrP", pIMFClock)
      this.IsError("IMFMediaSession::GetClock", hr)
      Return new IMFClock(pIMFClock)
   }
}

class IMFClock extends InterfaceBase {
   GetState() {
      hr := DllCall(this.VTable(6), "Ptr", this.ptr, "UInt", 0, "UIntP", state)
      this.IsError("IMFClock::GetState", hr)
      Return state
   }
}

class IMFSourceResolver extends InterfaceBase {
   Create() {
      DllCall("Mfplat\MFCreateSourceResolver", "PtrP", pIMFSourceResolver)
      Return new IMFSourceResolver(pIMFSourceResolver)
   }
   CreateObjectFromByteStream(pIMFByteStream, URL, flags, pIPropertyStore, ByRef MF_OBJECT_TYPE) {
      static IID_IMFMediaSource := "{279A808D-AEC7-40C8-9C6B-A6B492C78A66}"
      hr := DllCall(this.VTable(4), "Ptr", this.ptr, "Ptr", pIMFByteStream, "WStr", URL, "UInt", flags
                                  , "Ptr", pIPropertyStore, "UIntP", MF_OBJECT_TYPE, "PtrP", pIUnknown)
      this.IsError("IMFSourceResolver::CreateObjectFromByteStream", hr)
      pIMFMediaSource := ComObjQuery(pIUnknown, IID_IMFMediaSource)
      ObjRelease(pIUnknown)
      Return new IMFMediaSource(pIMFMediaSource)
   }
}

class IMFByteStream extends InterfaceBase {
   CreateFromStream(pIStream) {
      hr := DllCall("Mfplat\MFCreateMFByteStreamOnStream", "Ptr", pIStream, "PtrP", pIMFByteStream)
      IMFByteStream.IsError("IMFByteStream::MFCreateMFByteStreamOnStream", hr)
      Return new IMFByteStream(pIMFByteStream)
   }
}

class IMFMediaSource extends InterfaceBase {
   CreatePresentationDescriptor() {
      hr := DllCall(this.VTable(8), "Ptr", this.ptr, "PtrP", pIMFPresentationDescriptor)
      this.IsError("IMFMediaSource::CreatePresentationDescriptor", hr)
      Return new IMFPresentationDescriptor(pIMFPresentationDescriptor)
   }
}

class IMFPresentationDescriptor extends InterfaceBase {
   GetStreamDescriptorCount() {
      hr := DllCall(this.VTable(33), "Ptr", this.ptr, "UIntP", descriptorCount)
      this.IsError("IMFPresentationDescriptor::GetStreamDescriptorCount", hr)
      Return descriptorCount
   }
   GetStreamDescriptorByIndex(idx, ByRef selected) {
      hr := DllCall(this.VTable(34), "Ptr", this.ptr, "UInt", idx, "UIntP", selected, "PtrP", pIMFStreamDescriptor)
      this.IsError("IMFPresentationDescriptor::GetStreamDescriptorByIndex", hr)
      Return new IMFStreamDescriptor(pIMFStreamDescriptor)
   }
}

class IMFTopology extends InterfaceBase {
   Create() {
      DllCall("Mf\MFCreateTopology", "PtrP", pIMFTopology)
      Return new IMFTopology(pIMFTopology)
   }
   AddNode(pNode) {
      hr := DllCall(this.VTable(34), "Ptr", this.ptr, "Ptr", pNode)
      this.IsError("IMFTopology::AddNode", hr)
   }
}

class IMFStreamDescriptor extends InterfaceBase {
   GetMediaTypeHandler() {
      hr := DllCall(this.VTable(34), "Ptr", this.ptr, "PtrP", pIMFMediaTypeHandler)
      this.IsError("IMFStreamDescriptor::GetMediaTypeHandler", hr)
      Return new IMFMediaTypeHandler(pIMFMediaTypeHandler)
   }
}

class IMFMediaTypeHandler extends InterfaceBase {
   GetMajorType(ByRef GUID) {
      hr := DllCall(this.VTable(8), "Ptr", this.ptr, "Ptr", &GUID)
      this.IsError("IMFMediaTypeHandler::GetMajorType", hr)
   }
}

class IMFActivate extends InterfaceBase {
   Create() {
      hr := DllCall("Mf\MFCreateAudioRendererActivate", "PtrP", pIMFActivate)
      Return new IMFActivate(pIMFActivate)
   }
}

class IMFTopologyNode extends InterfaceBase {
   Create(type) {
      hr := DllCall("Mf\MFCreateTopologyNode", "UInt", type, "PtrP", pIMFTopologyNode)
      IMFTopologyNode.IsError("IMFTopologyNode::Create", hr)
      Return new IMFTopologyNode(pIMFTopologyNode)
   }
   SetUINT32(riid, value) {
      hr := DllCall(this.VTable(21), "Ptr", this.ptr, "Ptr", riid, "UInt", value)
      this.IsError("IMFTopologyNode::SetUINT32", hr)
   }
   SetUnknown(riid, pi) {
      hr := DllCall(this.VTable(27), "Ptr", this.ptr, "Ptr", riid, "Ptr", pi)
      this.IsError("IMFTopologyNode::SetUnknown", hr)
   }
   SetObject(pi) {
      hr := DllCall(this.VTable(33), "Ptr", this.ptr, "Ptr", pi)
      this.IsError("IMFTopologyNode::SetObject", hr)
   }
   ConnectOutput(OutputIndex, pDownstreamNode, dwInputIndexOnDownstreamNode) {
      hr := DllCall(this.VTable(40), "Ptr", this.ptr, "UInt", OutputIndex
                                   , "Ptr", pDownstreamNode, "UInt", dwInputIndexOnDownstreamNode)
      this.IsError("IMFTopologyNode::ConnectOutput", hr)
   }
}

class InterfaceBase {
   __New(ptr) {
      this.ptr := ptr
   }
   __Delete() {
      ObjRelease(this.ptr)
   }
   VTable(idx) {
      Return NumGet(NumGet(this.ptr + 0) + A_PtrSize*idx)
   }
   IsError(method, result, exc := true) {
      if (result = 0)
         Return 0
      this.error := method . " failed. Result: " . ( result = "" ? "No result" : Format("{:#x}", result & 0xFFFFFFFF) )
                              . "`nErrorLevel: " . ErrorLevel
      if !exc
         Return this.error
      throw Exception(this.error)
   }
}

StringFromGUID(ByRef VarOrAddress) {
   pGuid := IsByRef(VarOrAddress) ? &VarOrAddress : VarOrAddress
   VarSetCapacity(sGuid, 78) ; (38 + 1) * 2
   if !DllCall("ole32\StringFromGUID2", "Ptr", pGuid, "Ptr", &sGuid, "Int", 39)
      throw Exception("Invalid GUID", -1, Format("<at {1:p}>", pGuid))
   return StrGet(&sGuid, "UTF-16")
}

CLSIDFromString(IID, ByRef CLSID) {
   VarSetCapacity(CLSID, 16, 0)
   if res := DllCall("ole32\CLSIDFromString", "WStr", IID, "Ptr", &CLSID, "UInt")
      throw Exception("CLSIDFromString failed. Error: " . Format("{:#x}", res))
   Return &CLSID
}

GetMp3StreamFromGoogle(text, lng) {
   static hHeap := DllCall("GetProcessHeap", "Ptr")
        , flags := (HEAP_NO_SERIALIZE := 0x1) | (HEAP_ZERO_MEMORY := 0x8)
   if ( (text := Trim(text, " `t`n`r")) = "" ) {
      MsgBox, String is empty
      Return
   }
   chunks := [], text .= ".", pos := 1
   Loop {
      for k, v in ["\.", "!", "\?", ";", ",", ":", "\(", "\)", " ", "$"]
         RegExMatch(SubStr(text, pos, 200), "sO).+" . v . "+", m)
      until m.Len
      chunks.Push(m[0])
      pos += m.Len
   } until pos > StrLen(text)
   last := chunks.Pop()
   chunks.Push( SubStr(last, 1, -1) )

   size := offset := 0
   for k, v in chunks {
      url := CreateUrl(v, lng)
      arr := SendRequest(url)
      pData := NumGet(ComObjValue(arr) + 8 + A_PtrSize)
      length := arr.MaxIndex() + 1
      size += length
      if (A_Index = 1)
         pHeap := DllCall("HeapAlloc", "Ptr", hHeap, "UInt", flags, "Ptr", size, "Ptr")
      else
         pHeap := DllCall("HeapReAlloc", "Ptr", hHeap, "UInt", flags, "Ptr", pHeap, "Ptr", size, "Ptr")
      DllCall("RtlMoveMemory", "Ptr", pHeap + offset, "Ptr", pData, "Ptr", length)
      offset := size
   }
   pIStream := DllCall("Shlwapi\SHCreateMemStream", "Ptr", pHeap, "UInt", offset, "Ptr")
   DllCall("HeapFree", "Ptr", hHeap, "UInt", 0, "Ptr", pHeap)
   Return pIStream
}

CreateUrl(text, lng)  {
   JS := GetJS(), JS.( GetJScript() )
   url := "https://translate.google.ru/translate_tts?ie=UTF-8&tl="
         . lng . "&total=1&idx=0&client=t&prev=input&textlen="
         . StrLen(text) . "&tk=" . JS.("tk").(text) . "&q=" . URIEncode(text)
   Return url
}

SendRequest(url) {
   whr := ComObjCreate("Msxml2.XMLHTTP.6.0")
   whr.Open("GET", url, false)
   whr.Send()
   
   if (whr.Status != 200)
      Return "Error! Status: " . whr.Status . "`n`n" . whr.responseBody
   Return whr.responseBody
}

GetJS() {
   static doc := ComObjCreate("htmlfile")
        , __ := doc.write("<meta http-equiv='X-UA-Compatible' content='IE=9'>")
        , JS := ObjBindMethod(doc.parentWindow, "eval")
   Return JS
}

URIEncode(str, encoding := "UTF-8")  {
   VarSetCapacity(var, StrPut(str, encoding))
   StrPut(str, &var, encoding)
   While code := NumGet(Var, A_Index - 1, "UChar")  {
      bool := (code > 0x7F || code < 0x30 || code = 0x3D)
      UrlStr .= bool ? "%" . Format("{:02X}", code) : Chr(code)
   }
   Return UrlStr
}

GetJScript()
{
   script =
   (
      var TKK = ((function() {
        var a = 561666268;
        var b = 1526272306;
        return 406398 + '.' + (a + b);
      })());

      function b(a, b) {
        for (var d = 0; d < b.length - 2; d += 3) {
            var c = b.charAt(d + 2),
                c = "a" <= c ? c.charCodeAt(0) - 87 : Number(c),
                c = "+" == b.charAt(d + 1) ? a >>> c : a << c;
            a = "+" == b.charAt(d) ? a + c & 4294967295 : a ^ c
        }
        return a
      }

      function tk(a) {
          for (var e = TKK.split("."), h = Number(e[0]) || 0, g = [], d = 0, f = 0; f < a.length; f++) {
              var c = a.charCodeAt(f);
              128 > c ? g[d++] = c : (2048 > c ? g[d++] = c >> 6 | 192 : (55296 == (c & 64512) && f + 1 < a.length && 56320 == (a.charCodeAt(f + 1) & 64512) ?
              (c = 65536 + ((c & 1023) << 10) + (a.charCodeAt(++f) & 1023), g[d++] = c >> 18 | 240,
              g[d++] = c >> 12 & 63 | 128) : g[d++] = c >> 12 | 224, g[d++] = c >> 6 & 63 | 128), g[d++] = c & 63 | 128)
          }
          a = h;
          for (d = 0; d < g.length; d++) a += g[d], a = b(a, "+-a^+6");
          a = b(a, "+-3^+b+-f");
          a ^= Number(e[1]) || 0;
          0 > a && (a = (a & 2147483647) + 2147483648);
          a `%= 1E6;
          return a.toString() + "." + (a ^ h)
      }
   )
   Return script
}
Last edited by teadrinker on 12 Mar 2020, 04:05, edited 7 times in total.
garry
Posts: 3770
Joined: 22 Dec 2013, 12:50

Re: Using Google Translate to automate text translation

08 Mar 2020, 09:38

thank you teadrinker, works fine , I'll never understand, just few basic , like if ... then ... goto
User avatar
rommmcek
Posts: 1475
Joined: 15 Aug 2014, 15:18

Re: Using Google Translate to automate text translation

09 Mar 2020, 05:55

Hi teadrinker!
Thank you very much again! You fixed the bug for long chunks of text (present in mp3 code - which I never noticed) too.
I think neither original Google translator site nor your code manage chunks for optimal pronunciation (always tending to get them as big as possible up to 200 characters).

I'm not very familiar with RegEx, but I propose using While StartPos := RegExMatch(Text, ".+?(\.|;|!|\?)", Found, StartPos) + StrLen(Found) (didn't noticed any difference if $ sign is present or not), but there is much less chance to break outside of listed punctuations, hence less chance for "hesitation" in the middle of the sentence.

Besides I think there is a tiny bug in your second while loop for "chunks". It should brake at comma (as Google traslator does), instead it breaks in the middle of the sentence for the given example:

Code: Select all

...well as
any manner...
Don't know how to fix it concisely!

Added Phonetic transcription and "Did you mean..." to my previous code!
teadrinker
Posts: 4331
Joined: 29 Mar 2015, 09:41
Contact:

Re: Using Google Translate to automate text translation

09 Mar 2020, 13:44

@rommmcek
I've added some improvements, try it now, please.
User avatar
rommmcek
Posts: 1475
Joined: 15 Aug 2014, 15:18

Re: Using Google Translate to automate text translation

09 Mar 2020, 16:07

Very good "chunks" code!
Ha, ha, you append a period, if not present, to make it working (at least now I understand (condition && expression) - where can I read this in docs?).
For the given text example: Better then Google translator!
You didn't follow my observations entirely though. If you replace all semicolons with e.g. exclamation marks then you drop to Google level again. I suggest for k, v in ["\.", "!", "\?", ":", ";", ",", " "] to reduce that (maybe still some punctuation to add Wikipedia [][!"#$%&'()*+,./:;<=>?@\^_`|{}~-]). Btw., I changed the order (not sure if your code use order of appearance). Edit: Sure it take precedence! Awesome!
All in all, not so important, but instructive anyway!
Last edited by rommmcek on 09 Mar 2020, 16:45, edited 2 times in total.
teadrinker
Posts: 4331
Joined: 29 Mar 2015, 09:41
Contact:

Re: Using Google Translate to automate text translation

09 Mar 2020, 16:43

rommmcek wrote: If you replace all semicolons with e.g. exclamation marks then you drop to Google level again.
Yes, you are right, thanks! Added a few more punctuation marks.
teadrinker
Posts: 4331
Joined: 29 Mar 2015, 09:41
Contact:

Re: Using Google Translate to automate text translation

09 Mar 2020, 17:18

rommmcek wrote: at least now I understand (condition && expression) - where can I read this in docs?
I've not found in the docs a description of such using && operator. Some people consider this «a bad pactice». :)
User avatar
rommmcek
Posts: 1475
Joined: 15 Aug 2014, 15:18

Re: Using Google Translate to automate text translation

09 Mar 2020, 19:59

Thanks! But it works! However I hate it a bit, because it triggers an AutoGUI highlight bug. Nevertheless I have a cure for it: ¬:=(condition && expression). And no, it's not some new kind of emoji (it could be any variable name)!
Edit: Nested example due to Ternary bad practice:

Code: Select all

(("can you do it" != "" && a := " Yes you can!") && r:= "It worked!")
MsgBox % "Can you do it? " a " and " r
P.s.: Was personally involved in that discussion, but "your way" didn't remember because didn't understand nada!
User avatar
rommmcek
Posts: 1475
Joined: 15 Aug 2014, 15:18

Re: Using Google Translate to automate text translation

10 Mar 2020, 18:55

Newest chunking code seems to have serious bug. E.g. not
significant
is strangely pronounced. Apparently adding option s cures the problem so I use RegExMatch(SubStr(text, pos, 200), "sO).+" . v, m) for now.

Edit: Besides appending period at the end of the text chunk could be unfortunate if at the end is at least one white space, causing it to be pronounced literally (dot). Working solution would be to Trim it: chunks.Push( RTrim( m[0], ".") ), but then I got the idea to append an inert character, unlikely to appear in any text and then Trim it afterwards:

Code: Select all

chunks := [], Text.="ǁ", pos := 1
   Loop {
      for k, v in ["ǁ", "\.", "!", "\?", ";", ",", ":", "\(", "\)", " ", "$"]
         RegExMatch(SubStr(text, pos, 200), "sO).+" v, m)
      until m.Len
¬:=   ( m[0] != "" && chunks.Push( RTrim(m[0], "ǁ") ) ), pos += m.Len
   } until pos > StrLen(text)
P.s.: Sorry for my ignorance, didn't find option O in docs. On stackoverflow I read o (minuscule) makes RegEx perform only once...

Edit2. Sign + (plus) anywhere in text causes error (not the case in previous code): Error: IMFSourceResolver::CreateObjectFromByteStream failed. Result: 0xc00d36c4
ErrorLevel: 0
---> 315: Throw,Exception(this.error)
User avatar
rommmcek
Posts: 1475
Joined: 15 Aug 2014, 15:18

Re: Using Google Translate to automate text translation

11 Mar 2020, 02:35

When the entry text is not what Google translate would expect and Google is pretty sure what it should be then alert the user with the message: "Did you mean: ….?".
If Google is not so sure the alert is: Showing result for …. / Translate instead ….
I think I solved this now! Updated my previous code!
teadrinker
Posts: 4331
Joined: 29 Mar 2015, 09:41
Contact:

Re: Using Google Translate to automate text translation

11 Mar 2020, 10:02

@rommmcek
Thanks for testing!
rommmcek wrote: Sign + (plus) anywhere in text causes error
Fixed.
rommmcek wrote: adding option s cures the problem
Added :)
rommmcek wrote: at the end is at least one white space, causing it to be pronounced literally
Now all spaces at the end are trimmed.
Last edited by teadrinker on 11 Mar 2020, 14:44, edited 1 time in total.

Return to “Scripts and Functions (v1)”

Who is online

Users browsing this forum: No registered users and 197 guests