Leaderboard
Popular Content
Showing content with the highest reputation on 08/20/2024 in all areas
-
problem solved I Unchecked the following checkbox1 point
-
Google Translation with GUI
dmob reacted to pixelsearch for a topic
Hi everybody With the help of this thread, @AspirinJunkie @Nine and @Musashi , I was able to add a GUI to Google translation with AutoIt. Please note the 3 colors possible (green / yellow / red) found in these 3 pics, we'll discuss them later (need some sleep now) The maximum number of characters allowed for the original text is 65535, but imho it's too much. If you experience some slowness with 65535, then you can modify this variable at the very beginning of the script and indicate a lower value : Local $iMaxInputLength = 65535 There are different ways to import the text to translate : * Paste it directly from the clipboard (Ctrl+V) inside the upper edit control * Click the "Paste ClipBoard" button in the GUI * Drag a text file inside the upper edit control * Click the "Open File" button in the GUI (only .txt files for the moment) Then choose in each combo box the language you want ("Detect language" is useful for the Original) and click Translate. After the translation is done, if you want to copy the translated text, then a click on the "Copy Translated" button is possible. During the translation process, you have the possibility to press the "Escape" key if you want to stop the translation. ; Version 11 (June 30, 2024) ; This script requires AutoIt 3.3.16.1 (which includes Maps, as Json.au3 requires Maps for some translations) ; It also requires the file "Google Translate (languages list #3).txt" to be placed in same folder as the script ; To translate pdf files, it requests "pdftotext.exe" (a free command line tool from Xpdf downloadable at https://www.xpdfreader.com) #include "Json.au3" ; by @AspirinJunkie, download from https://github.com/Sylvan86/autoit-json-udf #include <Array.au3> #include <ButtonConstants.au3> #include <ComboConstants.au3> #include <EditConstants.au3> #include <FileConstants.au3> #include <GUIConstantsEx.au3> #include <GUIEdit.au3> #Include <GUIMenu.au3> #include <MsgBoxConstants.au3> #include <StaticConstants.au3> #include <StringConstants.au3> #include <WinAPISysWin.au3> #include <WindowsConstants.au3> Opt("MustDeclareVars", 1) ;0=no, 1=require pre-declaration Opt("GUICloseOnESC", 0) ;1=ESC closes (default), 0=ESC won't close Global $iMaxInputLength = 65535 ; In theory, 65535 is the maximum length of the original encoded string, when it is only composed of ; letters, digits, etc... which have an ascii code < 128 and coded on 1 byte only. ; But in practice, this variable should probably be lower (5000 as Google Translate site ?) or 10000, 15000 ... ; to avoid the warning message that will appear when the original encoded string becomes > 65535 bytes length. ; Also _JSON_Parse() may take some time on slow computers with "big" original encoded strings, we'll see... ; Global $iMaxInputLength = 5460 ; this value allows any encoded string to be < 65536 length, even if each character is a Unicode character ; requiring 4 bytes. In this case, each character will be coded on 12 bytes in the string "%..%..%..%.." ; 5460 * 12 = 65520 bytes (which is < 65536) ; user can choose amongst 39 GUI languages (this has nothing to do with 133 translatable languages) Global $aLangGui = _FillArrayLangGui("Google Translate (languages list #3).txt") Global $sIniFile = StringTrimRight(@ScriptFullPath, 4) & ".ini" ; ".au3" | ".a3x" | ".exe" => ".ini" Global $aLang, $aLink, $sLang = "", $iLangGui = -1, $bRTL Global $aIni = _ReadIniFile($sIniFile, $aLang, $aLink, $sLang) ; _ArrayDisplay($aLang, Ubound($aLang) & " codes & languages", Default, $ARRAYDISPLAY_NOROW, Default, "Code|Language|In Combo ?") #Region ### START Koda GUI section ### Form=C:\Temp\koda_1.7.3.0\Forms\google translate #4.kxf Global $sGUI_Title, $hGUI, $idPasteClip, $idOpenFile, $idLabelFrom, $idLangFrom, $idFontSizeFrom, $idWordWrapFrom, $idLanguageFilter Global $idLabelTo, $idLangTo, $idFontSizeTo, $idWordWrapTo, $idTranslate, $idCopyTrans, $idTextFrom, $idTextTo, $idCharStatus Global $idMsg, $sFileName, $sFileExt, $sEmpty = "" ; variable $sEmpty created only because ByRef doesn't accept literals _CreateGui() GUISetState(@SW_SHOW) #EndRegion ### END Koda GUI section ### GUIRegisterMsg($WM_COMMAND, "WM_COMMAND") While 1 $idMsg = GUIGetMsg() Switch $idMsg Case $GUI_EVENT_CLOSE _WriteIniFile($sIniFile, $aLink) ExitLoop Case $idPasteClip ; in edit control containing the original text _UpdateGUI(StringLeft(ClipGet(), $iMaxInputLength)) Case $idOpenFile, $GUI_EVENT_DROPPED ; in edit control containing the original text $sFileName = ($idMsg = $idOpenFile) ? _OpenFile() : @GUI_DragFile If $sFileName Then $sFileExt = StringMid($sFileName, 1 + StringInStr($sFileName, ".", 0, -1)) ; -1 to start search from the right If StringLen($sFileExt) = StringLen($sFileName) Then $sFileExt = "no dot found" ; no "." found in $sFileName If StringInStr(FileGetAttrib($sFileName), "D") Then $sFileExt = "this is a folder" Select Case $sFileExt = "txt" ; no problem with txt extension Case $sFileExt = "pdf" ; extract pdf content to txt file using pdftotext.exe $sFileName = _PdfToText($sFileName) ; $sFileName (xxx.pdf) => $sFileName (TextExtractedFromPDF.txt) if success. If Not $sFileName Then ContinueLoop ; ... if extraction of txt fails, then $sFileName becomes "" (0 in fact) Case Else MsgBox($MB_TOPMOST, "File extension error ", _ "File extension '" & $sFileExt & "' can't be processed" & @crlf & _ "Only .txt or .pdf file can be processed", 0, $hGUI) _UpdateGUI($sEmpty) ContinueLoop EndSelect _UpdateGUI(StringLeft(FileRead($sFileName), $iMaxInputLength)) EndIf Case $idFontSizeFrom GUICtrlSetFont($idTextFrom, Int(GUICtrlRead($idFontSizeFrom))) GUICtrlSetState($idTextFrom, $GUI_FOCUS) ; keep showing an eventual selection Case $idWordWrapFrom $idTextFrom = _EditRecreate_From(Int(GUICtrlRead($idFontSizeFrom)), GUICtrlRead($idWordWrapFrom)) ; Font size, WordWrap GUICtrlSetState($idTextFrom, $GUI_FOCUS) Case $idFontSizeTo GUICtrlSetFont($idTextTo, Int(GUICtrlRead($idFontSizeTo))) GUICtrlSetState($idTextTo, $GUI_FOCUS) Case $idWordWrapTo $idTextTo = _EditRecreate_To(Int(GUICtrlRead($idFontSizeTo)), GUICtrlRead($idWordWrapTo)) ; Font size, WordWrap GUICtrlSetState($idTextTo, $GUI_FOCUS) Case $idTranslate GUICtrlSetState($idTranslate, $GUI_DISABLE) ; prevent accidental double click on the button GUICtrlSetData($idTextTo, "") WinSetTitle($hGUI, "", _GuiTitle()) HotKeySet("{ESC}", "_Wanna_Quit") ; in case user keys Esc during the translation process _Translate() HotKeySet("{ESC}") GUICtrlSetState($idTranslate, $GUI_ENABLE) Case $idCopyTrans ClipPut(GUICtrlRead($idTextTo)) MsgBox($MB_TOPMOST, "Copy", "Done", 1, $hGUI) ; timeout 1s Case $idLanguageFilter GUIRegisterMsg($WM_COMMAND, "") GUISetState(@SW_DISABLE, $hGUI) _LanguageFilter($aLang, $aLink, $sLang) ; in a new temporary GUI ($hGUI2) GUISetState(@SW_RESTORE, $hGUI) ; only flag that seems to do the job correctly, even on a non-minimized window !? GUISetState(@SW_ENABLE, $hGUI) GUIRegisterMsg($WM_COMMAND, "WM_COMMAND") Case $idLabelFrom ; clickable label to decrease both combobox font size (some GUI languages appear much smaller than others) If $aIni[8] - 1 < 8.3 Then ContinueLoop $aIni[8] -= 1 GUICtrlSetFont($idLangFrom, $aIni[8]) GUICtrlSetFont($idLangTo, $aIni[8]) ; if a language is displayed as squares in the combobox, try instead the 2 following lines ; GUICtrlSetFont($idLangFrom, $aIni[8], 400, 0, "Ms Shell Dlg", 2) ; GUICtrlSetFont($idLangTo, $aIni[8], 400, 0, "Ms Shell Dlg", 2) Case $idLabelTo ; clickable label to increase both combobox font size (some GUI languages appear much smaller than others) If $aIni[8] + 1 > 16.3 Then ContinueLoop $aIni[8] += 1 GUICtrlSetFont($idLangFrom, $aIni[8]) GUICtrlSetFont($idLangTo, $aIni[8]) ; if a language is displayed as squares in the combobox, try instead the 2 following lines ; GUICtrlSetFont($idLangFrom, $aIni[8], 400, 0, "Ms Shell Dlg", 2) ; GUICtrlSetFont($idLangTo, $aIni[8], 400, 0, "Ms Shell Dlg", 2) EndSwitch WEnd GUIDelete($hGUI) ;============================================== Func _UpdateGUI(ByRef $sText) GUICtrlSetData($idTextFrom, $sText) _UpdateCharStatus() GUICtrlSetData($idTextTo, "") WinSetTitle($hGUI, "", _GuiTitle()) EndFunc ;==>_UpdateGUI ;============================================== Func _CreateGui() $sGUI_Title = "Google Translation (v11)" $hGUI = GUICreate(_GuiTitle(), 900, 700, -1, -1, $WS_OVERLAPPEDWINDOW, $WS_EX_ACCEPTFILES) $idPasteClip = GUICtrlCreateButton("Paste Clipboard", 10, 10, 100, 30) $idOpenFile = GUICtrlCreateButton("Open File", 10, 50, 100, 30) GUICtrlCreateGroup(" Original text options ", 120, 10, 310, 70) $idLabelFrom = GUICtrlCreateLabel("Language From", 133, 32, 110, 17) GUICtrlSetTip(-1, "click to decrease both combobox font size", "", 0, 1) $idLangFrom = GUICtrlCreateCombo("", 128, 50, 140, 25, BitOR($CBS_DROPDOWNLIST, $WS_VSCROLL), _ ; $CBS_HASSTRINGS always forced ? $bRTL _ ? BitOr($WS_EX_LEFTSCROLLBAR, $WS_EX_RTLREADING, $WS_EX_RIGHT) _ : -1) GUICtrlSetFont(-1, $aIni[8]) ; if a language is displayed as squares in the combobox, try instead the following line ; GUICtrlSetFont(-1, $aIni[8], 400, 0, "Ms Shell Dlg", 2) GUICtrlSetData(-1, $aLangGui[$iLangGui + 2] & "|" & $sLang) ; ex. item 0 is "Detect language" or "Sprache erkennen" or etc... GUICtrlSendMsg($idLangFrom, $CB_SETCURSEL, $aIni[0], 0) GUICtrlCreateLabel("Font size", 290, 32, 54, 17) $idFontSizeFrom = GUICtrlCreateInput($aIni[1], 288, 50, 57, 21, BitOR($GUI_SS_DEFAULT_INPUT, $ES_CENTER, $ES_NUMBER)) GUICtrlSetLimit(-1, 2) GUICtrlCreateUpdown($idFontSizeFrom) GUICtrlSetLimit(-1, 72, 8) GUICtrlCreateLabel("Word wrap", 364, 32, 64, 17) $idWordWrapFrom = GUICtrlCreateCheckbox("", 382, 46, 17, 25) GUICtrlSetState(-1, $aIni[2]) ; $GUI_CHECKED = 1 , $GUI_UNCHECKED = 4 GUICtrlCreateGroup("", -99, -99, 1, 1) $idLanguageFilter = GUICtrlCreateButton("?", 437, 48, 25, 25) GUICtrlCreateGroup(" Translated text options ", 470, 10, 310, 70) $idLabelTo = GUICtrlCreateLabel("Language To", 483, 32, 110, 17) GUICtrlSetTip(-1, "click to increase both combobox font size", "", 0, 1) $idLangTo = GUICtrlCreateCombo("", 478, 50, 140, 25, BitOR($CBS_DROPDOWNLIST, $WS_VSCROLL), _ ; $CBS_HASSTRINGS always forced ? $bRTL _ ? BitOr($WS_EX_LEFTSCROLLBAR, $WS_EX_RTLREADING, $WS_EX_RIGHT) _ : -1) GUICtrlSetFont(-1, $aIni[8]) ; if a language is displayed as squares in the combobox, try instead the following line ; GUICtrlSetFont(-1, $aIni[8], 400, 0, "Ms Shell Dlg", 2) GUICtrlSetData(-1, $sLang) GUICtrlSendMsg(-1, $CB_SETCURSEL, $aIni[3], 0) GUICtrlCreateLabel("Font size", 640, 32, 54, 17) $idFontSizeTo = GUICtrlCreateInput($aIni[4], 638, 50, 57, 21, BitOR($GUI_SS_DEFAULT_INPUT, $ES_CENTER, $ES_NUMBER)) GUICtrlSetLimit(-1, 2) GUICtrlCreateUpdown($idFontSizeTo) GUICtrlSetLimit(-1, 72, 8) GUICtrlCreateLabel("Word wrap", 714, 32, 64, 17) $idWordWrapTo = GUICtrlCreateCheckbox("", 732, 46, 17, 25) GUICtrlSetState(-1, $aIni[5]) ; $GUI_CHECKED = 1 , $GUI_UNCHECKED = 4 GUICtrlCreateGroup("", -99, -99, 1, 1) $idTranslate = GUICtrlCreateButton("Translate", 790, 10, 100, 30) $idCopyTrans = GUICtrlCreateButton("Copy Translated", 790, 50, 100, 30) $idTextFrom = _EditRecreate_From($aIni[1], $aIni[2]) ; Font size, WordWrap (for edit control containing the original text) $idTextTo = _EditRecreate_To ($aIni[4], $aIni[5]) ; same, for edit control containing the translated text. $idCharStatus = GUICtrlCreateLabel("", 790, 380, 80, 25, BitOR($SS_CENTER,$SS_CENTERIMAGE)) _UpdateCharStatus() EndFunc ;==>_CreateGui ;============================================== Func _Translate() Local $iItemTo = GUICtrlSendMsg($idLangTo, $CB_GETCURSEL, 0, 0) If $iItemTo = - 1 Then ; no "Language To" selected WinSetTitle($hGUI, "", _GuiTitle()) MsgBox($MB_TOPMOST, "'Language To' is undefined", "Please choose a target language from the list", 0, $hGUI) Return EndIf Local $sEncoded = _URIEncode(GUICtrlRead($idTextFrom)) If StringLen($sEncoded) = 0 Then WinSetTitle($hGUI, "", _GuiTitle()) MsgBox($MB_TOPMOST, "No original text", "Nothing to translate", 0, $hGUI) Return EndIf ; ConsoleWrite(BinaryToString(StringToBinary("$sEncoded: " & $sEncoded, $SB_UTF8), $SB_ANSI) & @crlf) Local $iBytes = StringLen($sEncoded) ; ConsoleWrite("Length of $sEncoded : " & $iBytes & @crlf & @crlf) If $iBytes > 65535 Then ; tested (not 65536 !) Local $iYesNo = MsgBox(BitOr($MB_TOPMOST, $MB_YESNO, $MB_DEFBUTTON2, $MB_ICONWARNING), _ "Warning : Encoded text is too big", _ "Encoded text size: " & @Tab & $iBytes & @crlf & _ "Max. allowed:" & @Tab & " 65535" & @crlf & _ "" & @crlf & _ "Do you want to truncate the original text ?", 0, $hGUI) If $iYesNo = $IDNO Then Return $sEncoded = _URIEncode_Truncate(GUICtrlRead($idTextFrom)) EndIf Local $iItemFrom = GUICtrlSendMsg($idLangFrom, $CB_GETCURSEL, 0, 0) Local $sLangFrom = ($iItemFrom = 0) ? "auto" : $aLang[$aLink[$iItemFrom - 1]][0] ; ex. "auto", "en", de", ... ; Local $iItemTo = GUICtrlSendMsg($idLangTo, $CB_GETCURSEL, 0, 0) ; moved at beginning of function, to test if a "Language To" is selected Local $sLangTo = $aLang[$aLink[$iItemTo]][0] ; ex. "en", "de", ... Local $sLangUsed, $sTranslated = _GoogleAPITranslate($sEncoded, $sLangFrom, $sLangTo, $sLangUsed) If $sLangUsed Then ; translation has succeeded and $sLangUsed always contains the code that Google used for its last translation ; ConsoleWrite("$sLangUsed: >>>" & $sLangUsed & "<<<" & @crlf) _GUICtrlEdit_SetText($idTextTo, $sTranslated) If $sLangFrom = "auto" Then Local $sLangUsedFull For $i = 0 To Ubound($aLang) - 1 If $sLangUsed = $aLang[$i][0] Then $sLangUsedFull = $aLang[$i][1] ; ex. "English", "German" , ... ExitLoop EndIf Next WinSetTitle($hGUI, "", "Translated From " & _ ($sLangUsedFull ? $sLangUsedFull : $sLangUsed & " (???)") & _ " To " & $aLang[$aLink[$iItemTo]][1]) Else WinSetTitle($hGUI, "", "Translated From " & $aLang[$aLink[$iItemFrom - 1]][1] & " To " & $aLang[$aLink[$iItemTo]][1]) EndIf Else ; translation failed as $sLangUsed is still empty WinSetTitle($hGUI, "", _GuiTitle()) MsgBox($MB_TOPMOST, "Error", _ "Something went wrong during translation" & @crlf & _ "(Internet connection ok ?)", 0, $hGUI) EndIf EndFunc ;==>_Translate ;============================================== Func _GoogleAPITranslate(Const ByRef $sEncoded, $sFrom, $sTo, ByRef $sLangUsed) Local $oLocalCOMErrorHandler = ObjEvent("AutoIt.Error", "_ErrFuncLocal") ; format and send request With ObjCreate("WinHttp.WinHttpRequest.5.1") ; thanks AspirinJunkie for correct syntax of the Object methods below. .Open("POST", "https://translate.googleapis.com/translate_a/single", False) .SetRequestHeader("Content-Type", "application/x-www-form-urlencoded") .Send(StringFormat("client=gtx&sl=%s&tl=%s&dt=t&q=%s", $sFrom, $sTo, $sEncoded)) Local $sResponse = .ResponseText EndWith ; ConsoleWrite(BinaryToString(StringToBinary("$sResponse: " & $sResponse, $SB_UTF8), $SB_ANSI) & @crlf) Local $vResponse = _JSON_Parse($sResponse) ; process return Local $sOutput = "" If VarGetType($vResponse) = 'Array' Then ; _ArrayDisplay($vResponse, "$vResponse") Local $aData = $vResponse[0] If VarGetType($aData) = 'Array' Then For $i = 0 To UBound($aData) -1 $sOutput &= ($aData[$i])[0] Next EndIf $sLangUsed = $vResponse[2] ; ex. "en" , "de" , ... to display which language Google used for translation, in case user choosed "Detect language" EndIf Return $sOutput EndFunc ;==>_GoogleAPITranslate ;============================================== Func _URIEncode(Const ByRef $sData) ; code below from Prog@ndy (minor changes) : https://www.autoitscript.com/forum/topic/95850-url-encoding/?do=findComment&comment=689060 ; maybe Chr(32) should always be encoded as "%20" and not "+" to be compatible with all browsers (?) we'll see... Local $aData = StringSplit(BinaryToString(StringToBinary($sData, $SB_UTF8), $SB_ANSI), "") ; "" => each character returns as an element Local $nChar, $sEncoded = "" For $i = 1 To $aData[0] $nChar = Asc($aData[$i]) Switch $nChar Case 45, 46, 48 To 57, 65 To 90, 95, 97 To 122, 126 $sEncoded &= $aData[$i] Case 32 $sEncoded &= "+" Case Else $sEncoded &= "%" & Hex($nChar, 2) EndSwitch Next Return $sEncoded EndFunc ;==>_URIEncode ;============================================== Func _URIEncode_Truncate(Const ByRef $sData) Local $dBinary, $nChar, $sEncoded = "", $iGoodLen = 0 For $j = 1 To StringLen($sData) $iGoodLen = StringLen($sEncoded) ; from the previous loop $dBinary = StringToBinary(StringMid($sData, $j, 1), $SB_UTF8) ; for example ะป (russian) becomes 0xD0BB For $i = 1 To BinaryLen($dBinary) $nChar = Number(BinaryMid($dBinary, $i, 1)) Switch $nChar Case 45, 46, 48 To 57, 65 To 90, 95, 97 To 122, 126 $sEncoded &= Chr($nChar) Case 32 $sEncoded &= "+" Case Else $sEncoded &= "%" & Hex($nChar, 2) EndSwitch Next If StringLen($sEncoded) > 65535 Then ; stop encoding... GUICtrlSetData($idTextFrom, StringLeft($sData, $j - 1)) ; ...truncate original text... $sEncoded = StringLeft($sEncoded, $iGoodLen) ;...delete the very last byte(s) in $sEncoded... ExitLoop ; ...return the corresponding encoded string (which will never exceed 65535 bytes) EndIf Next Return $sEncoded EndFunc ;==>_URIEncode_Truncate ;============================================== Func _LanguageFilter(ByRef $aLang, ByRef $aLink, ByRef $sLang) ; Backup 3 arrays (quick), $iLangGui, $bRTL : in case user makes changes in GUI2 then presses Cancel button in GUI2 => revert Local $aLang_backup = $aLang, $aLink_backup = $aLink, $sLang_backup = $sLang, $iLangGui_backup = $iLangGui, $bRTL_backup = $bRTL ; Keep status of combo "Language From" (to select it again, even with another GUI language... if user keeps the language checked) Local $iOld_ItemFrom = GUICtrlSendMsg($idLangFrom, $CB_GETCURSEL, 0, 0) ; always 0+ (0 <=> "Detect language" or "Sprache erkennen" or etc...) Local $sOld_CodeFrom = ($iOld_ItemFrom = 0) ? "auto" : $aLang[$aLink[$iOld_ItemFrom - 1]][0] ; ex. "en" , "de" , ... Local $bCheck_ItemFrom = ($iOld_ItemFrom = 0) ? False : True ; Keep status of combo "Language To" (to select it again, even with another GUI language... if user keeps the language checked) Local $iOld_ItemTo = GUICtrlSendMsg($idLangTo, $CB_GETCURSEL, 0, 0) ; may be -1 if no selected "Language To" . 0+ if selected Local $sOld_CodeTo = ($iOld_ItemTo = -1) ? "" : $aLang[$aLink[$iOld_ItemTo]][0] ; ex. "en" , "de" , ... Local $bCheck_ItemTo = ($iOld_ItemTo = -1) ? False : True Local $iNumLi = -1, $iLeft = 15, $iTop = 49, $iMaxLanguages = Ubound($aLang), $idCheckbox[$iMaxLanguages] Local $iCountGui_Lang = Ubound($aLangGui) / 4, $idMenuItem[$iCountGui_Lang] Local $hGUI2 = GUICreate(_GuiTitle(2), 900, 700, -1, -1, BitOR($WS_CAPTION, $WS_POPUP, $WS_SYSMENU, $WS_MAXIMIZEBOX)) Local $hMenu = _GUICtrlMenu_GetSystemMenu($hGUI2) _GUICtrlMenu_EnableMenuItem($hMenu, $SC_CLOSE, $MF_GRAYED, False) ; "disable red [X] and System Menu 'Close' item" (Melba23, Dec 14 2009) Local $idMenu_Lang = GUICtrlCreateMenu("GUI language") For $i = 0 To Ubound($aLangGui) - 1 Step 4 ; 0, 4, 8, 12, ... , 152 when 39 Gui languages $idMenuItem[$i / 4] = GUICtrlCreateMenuItem($aLangGui[$i + 1], $idMenu_Lang, -1, 1) ; 1 = menuradioitem If $i = $iLangGui Then GUICtrlSetState(-1, $GUI_CHECKED) Next GUICtrlCreateGroup("", 5, 39, 890, 639) ; just to display a thin rectangle around all Checkbox controls For $i = 0 To $iMaxLanguages - 1 $iNumLi += 1 ; 0+ If $iNumLi > 24 Then ; 0 to 24 = 25 checkbox controls per column (x 7 columns => 175 languages max. with these coords. Actually 133) $iLeft += 125 $iNumLi = 0 EndIf $idCheckbox[$i] = GUICtrlCreateCheckbox($aLang[$i][1], $iLeft, $iTop + $iNumLi * 25, 115, 25, _ $bRTL _ ? BitOr($BS_RIGHTBUTTON, $BS_RIGHT) _ : -1) If $aLang[$i][2] = 1 Then ; display this language in combobox GUICtrlSetState($idCheckbox[$i], $GUI_CHECKED) GUICtrlSetBkColor($idCheckbox[$i], 0xC0FFFF) ; light blue when checked EndIf Next GUICtrlCreateGroup("", -99, -99, 1, 1) Local $idCheckAll = GUICtrlCreateButton("Check All", 150, 7, 100, 30) Local $idUncheckAll = GUICtrlCreateButton("Uncheck All", 270, 7, 100, 30) Local $idOk = GUICtrlCreateButton("Ok", 520, 7, 100, 30) Local $idCancel = GUICtrlCreateButton("Cancel", 640, 7, 100, 30) GUISetState(@SW_SHOW) Local $idMsg, $iLangGuiNew While 1 $idMsg = GUIGetMsg() Switch $idMsg Case $idCancel ; no $GUI_EVENT_CLOSE here, as GUI close button disabled just after GUI creation. Also Opt("GUICloseOnESC", 0) $aLang = $aLang_backup $aLink = $aLink_backup $sLang = $sLang_backup $iLangGui = $iLangGui_backup $bRTL = $bRTL_backup ExitLoop Case $idMenuItem[0] To $idMenuItem[$iCountGui_Lang - 1] ; ex. 30 To 68 when 39 GUI languages $iLangGuiNew = 4 * ($idMsg - $idMenuItem[0]) ; 0, 4, 8, 12, ... , 152 (when 39 GUI languages) If $iLangGuiNew <> $iLangGui Then _ChangeGuiLang($hGUI2, $iLangGuiNew, $idCheckbox) Case $idCheckbox[0] To $idCheckbox[$iMaxLanguages - 1] ; ex. 70 To 202 when 133 translatable languages... ; ...surrounded by 2 GUICtrlCreateGroup("") i.e. controls #69 & #203 If GUICtrlRead($idMsg) = $GUI_CHECKED Then GUICtrlSetBkColor($idMsg, 0xC0FFFF) ; light blue when checked $aLang[$idMsg - $idCheckbox[0]][2] = 1 ; display this language in combobox Else GUICtrlSetBkColor($idMsg, $GUI_BKCOLOR_DEFAULT) $aLang[$idMsg - $idCheckbox[0]][2] = "" ; do not display this language in combobox EndIf Case $idCheckAll GUISetState(@SW_LOCK, $hGui2) ; "Lock the window to avoid repainting" For $i = 0 To $iMaxLanguages - 1 GUICtrlSetState($idCheckbox[$i], $GUI_CHECKED) GUICtrlSetBkColor($idCheckbox[$i], 0xC0FFFF) ; light blue when checked $aLang[$i][2] = 1 ; display this language in combobox Next GUISetState(@SW_UNLOCK, $hGui2) ; "Unlock the window to allow repainting" Case $idUncheckAll GUISetState(@SW_LOCK, $hGui2) For $i = 0 To $iMaxLanguages - 1 GUICtrlSetState($idCheckbox[$i], $GUI_UNCHECKED) GUICtrlSetBkColor($idCheckbox[$i], $GUI_BKCOLOR_DEFAULT) $aLang[$i][2] = "" ; do not display this language in combobox Next GUISetState(@SW_UNLOCK, $hGui2) Case $idOk For $i = 0 To $iMaxLanguages - 1 ; test if one language (at least) is checked If GUICtrlRead($idCheckbox[$i]) = $GUI_CHECKED Then ExitLoop Next If $i = $iMaxLanguages Then ; no language checked MsgBox(BitOr($MB_TOPMOST, $MB_ICONWARNING), "Warning", "Please check at least 1 language", 0, $hGUI2) ContinueLoop EndIf ; at this stage $aLang[][2] Col 2 is fully up to date (1 = display this language in combobox, or "" = do not display) Local $iCountChecked = 0, $iNew_ItemFrom = -1, $iNew_ItemTo = -1 Dim $aLink[$iMaxLanguages] $sLang = "" For $i = 0 To $iMaxLanguages - 1 If GUICtrlRead($idCheckbox[$i]) = $GUI_CHECKED Then $iCountChecked += 1 ; 1+ $aLink[$iCountChecked - 1] = $i ; ex. $aLink[0] = 10 (1st combobox item links to row 10 in $aLang) , $aLink[1] = 27 etc... $sLang &= $aLang[$i][1] & "|" If $bCheck_ItemFrom And $aLang[$i][0] = $sOld_CodeFrom Then $iNew_ItemFrom = $iCountChecked ; 1+ (remember item 0 <=> "Detect language" or "Sprache erkennen" or etc...) $bCheck_ItemFrom = False EndIf If $bCheck_ItemTo And $aLang[$i][0] = $sOld_CodeTo Then $iNew_ItemTo = $iCountChecked - 1 ; 0+ $bCheck_ItemTo = False EndIf EndIf Next Redim $aLink[$iCountChecked] ; delete all superfluous empty rows (quick) GUICtrlSetData($idLangFrom, "|" & $aLangGui[$iLangGui + 2] & "|" & $sLang) ; "|" at beginning destroys the previous list (help file) GUICtrlSetData($idLangTo, "|" & $sLang) If $bRTL <> $bRTL_backup Then ; redraw both combo (RTL or LTR) If $bRTL Then _WinAPI_SetWindowLong(GUICtrlGetHandle($idLangFrom), $GWL_EXSTYLE, _ BitOr($WS_EX_LEFTSCROLLBAR, $WS_EX_RTLREADING, $WS_EX_RIGHT)) _WinAPI_SetWindowLong(GUICtrlGetHandle($idLangTo), $GWL_EXSTYLE, _ BitOr($WS_EX_LEFTSCROLLBAR, $WS_EX_RTLREADING, $WS_EX_RIGHT)) Else _WinAPI_SetWindowLong(GUICtrlGetHandle($idLangFrom), $GWL_EXSTYLE, 0) ; combobox don't have default exstyles (help file error) _WinAPI_SetWindowLong(GUICtrlGetHandle($idLangTo) , $GWL_EXSTYLE, 0) EndIf EndIf If $iNew_ItemFrom = - 1 Then ; old language From has been unchecked OR $iOld_ItemFrom = 0 GUICtrlSendMsg($idLangFrom, $CB_SETCURSEL, 0, 0) ; ex. "Detect language" or "Sprache erkennen" or etc... _WinAPI_SetWindowLong(GUICtrlGetHandle($idTextFrom), $GWL_EXSTYLE, $WS_EX_CLIENTEDGE) ; default extended style value in Edit field Else ; 1+ GUICtrlSendMsg($idLangFrom, $CB_SETCURSEL, $iNew_ItemFrom, 0) EndIf If $iNew_ItemTo = - 1 Then ; old language To has been unchecked OR $iOld_ItemTo = -1 GUICtrlSendMsg($idLangTo, $CB_SETCURSEL, -1, 0) ; blank the "Language To" selection GUICtrlSetData($idTextTo, "") ; clear the eventual translated text _WinAPI_SetWindowLong(GUICtrlGetHandle($idTextTo), $GWL_EXSTYLE, $WS_EX_CLIENTEDGE) ; default extended style value in Edit field Else ; 0+ GUICtrlSendMsg($idLangTo, $CB_SETCURSEL, $iNew_ItemTo, 0) EndIf If $iNew_ItemFrom > 0 And $iNew_ItemTo > -1 Then WinSetTitle($hGUI, "", "Translated From " & _ $aLang[$aLink[$iNew_ItemFrom - 1]][1] & " To " & $aLang[$aLink[$iNew_ItemTo]][1]) Else WinSetTitle($hGUI, "", _GuiTitle()) ; display the generic GUI title (which doesn't mention any translation language) EndIf ExitLoop EndSwitch WEnd GUIDelete($hGUI2) EndFunc ;==>_LanguageFilter ;============================================== Func _ChangeGuiLang(Const $hGUI2, Const $iLangGuiNew, Const ByRef $idCheckbox) ; ConsoleWrite($iLangGui & " " & $iLangGuiNew & @crlf) ; both can be 0, 4, 8, 12, ..., 152 (when 39 Gui languages) Local $bRTL_old = $bRTL ; define this variable before _FillArrayLang() ; Remap the links between the 2 language arrays (before redrawing all checkbox) Local $iIndex, $aLangNew = _FillArrayLang($aLangGui[$iLangGuiNew]) ; 3rd column of $aLangNew is empty at this stage ; $iLangGui & $bRTL redefined during _FillArrayLang() ... ; ... which means, from now on, $iLangGui = $iLangGuiNew For $i = 0 To Ubound($aLang) - 1 If Not $aLang[$i][2] Then ContinueLoop $iIndex = _ArraySearch($aLangNew, $aLang[$i][0], 0, 0, 1, 2, 1, 0) ; 2 = exact match, last 0 = search in col 0 of a 2D array If $iIndex > -1 Then $aLangNew[$iIndex][2] = 1 ; same language code (displayed in combobox) will very often have a different index in the new array. Else MsgBox(BitOr($MB_TOPMOST, $MB_ICONWARNING), "Serious warning", _ "Code language '" & $aLang[$i][0] & "' not found in " & $aLangGui[$iLangGui + 1] & @crlf & _ "Language text file is corrupted", 0, $hGUI2) EndIf Next ; redraw all checkbox with their captions reflecting the new language, tick the ones that need to be ticked, take care of LTR / RTL GUISetState(@SW_LOCK, $hGui2) ; Lock the window to avoid repainting Local $iStyle = _WinAPI_GetWindowLong(GUICtrlGetHandle($idCheckbox[0]), $GWL_STYLE) ; all 133 checkbox got the same style, pick any one. For $i = 0 To Ubound($aLang) - 1 If $bRTL <> $bRTL_old Then _WinAPI_SetWindowLong(GUICtrlGetHandle($idCheckbox[$i]), $GWL_STYLE, _ $bRTL _ ? BitOr ($iStyle, $BS_RIGHTBUTTON, $BS_RIGHT) _ : BitXOr($iStyle, $BS_RIGHTBUTTON, $BS_RIGHT)) EndIf GUICtrlSetData($idCheckbox[$i], $aLangNew[$i][1]) If $aLangNew[$i][2] = 1 Then ; display this language in combobox GUICtrlSetState($idCheckbox[$i], $GUI_CHECKED) GUICtrlSetBkColor($idCheckbox[$i], 0xC0FFFF) ; light blue when checked Else GUICtrlSetState($idCheckbox[$i], $GUI_UNCHECKED) GUICtrlSetBkColor($idCheckbox[$i], $GUI_BKCOLOR_DEFAULT) EndIf Next WinSetTitle($hGUI2, "", _GuiTitle(2)) GUISetState(@SW_UNLOCK, $hGui2) ; Unlock the window to allow repainting $aLang = $aLangNew ; note that $iLangGui and $bRTL have been updated during _FillArrayLang() EndFunc ;==>_ChangeGuiLang ;============================================== Func _ReadIniFile($sIniFile, ByRef $aLang, ByRef $aLink, ByRef $sLang) If FileExists($sIniFile) Then Local $aIni = FileReadToArray($sIniFile) If @extended <> 9 Then MsgBox($MB_TOPMOST, "_FileReadToArray: extended = " & @extended, _ "Ini file should have exactly 9 lines, not " & @extended & @crlf & _ "(this message is normal if you just upgraded the script to a new version.)" & @crlf & _ "" & @crlf & _ "Incompatible .ini file will be deleted now, please run the script again.") FileDelete($sIniFile) Exit EndIf For $i = 0 To 5 $aIni[$i] = Number($aIni[$i]) ; Number, or serious issues later with GUICtrlSendMsg() Next $aLink = StringSplit($aIni[6], ",", $STR_NOCOUNT) $aLang = _FillArrayLang(StringStripWS($aIni[7], $STR_STRIPLEADING + $STR_STRIPTRAILING)) ; ex. $aIni[7] = "en" or "de" or etc... ; (GUI interface for the language names) For $i = 0 To Ubound($aLink) - 1 $aLang[$aLink[$i]][2] = 1 ; display this language in ComboBox $sLang &= $aLang[$aLink[$i]][1] & "|" ; "a trailing GUIDataSeparatorChar is ignored" (help file, topic GUICtrlSetData for combo & list control) Next $aIni[8] = Number($aIni[8]) ; _ArrayDisplay($aIni, "Existing ini file") $aIni[6] = "" ; string not needed anymore (no big deal, just to match same behavior when ini file doesn't exist) Else ; ini file doesn't exist Local $aIni[9] $aIni[0] = 0 ; Language From (combobox) (force 'Detect Language' when .ini file doesn't exist) $aIni[1] = 11 ; Font Size for edit control original text $aIni[2] = 1 ; Word wrap for edit control original text (1 = checked, 4 = unchecked) $aIni[3] = -1 ; Language To (combobox) (forced to -1 [unselected] when .ini file doesn't exist) $aIni[4] = 11 ; Font Size for edit control translated text $aIni[5] = 1 ; Word wrap for edit control translated text (1 = checked, 4 = unchecked) ; $aIni[6] : nothing special about $aIni[6] when ini file doesn't exist. $aIni[7] = "en" ; Suggest english GUI interface when no ini file (i.e. language names in english) though it's changeable here $aIni[8] = 9.3 ; Font Size for both combobox (decrease by clicking LABEL $idLabelFrom, increase by clicking LABEL $idLabelTo) $aLang = _FillArrayLang($aIni[7]) Dim $aLink[Ubound($aLang)] For $i = 0 To Ubound($aLang) - 1 ; add all languages in $sLang when ini file doesn't exist (user will keep those he needs, later) $aLang[$i][2] = 1 ; 1 = display this language in ComboBox ("" = don't display it) $aLink[$i] = $i ; link between combobox item # and row in $aLang . This will be useful if user unchecks some languages he doesn't need $sLang &= $aLang[$i][1] & "|" ; "a trailing GUIDataSeparatorChar is ignored" (help file, topic GUICtrlSetData for combo & list control) Next ; _ArrayDisplay($aIni, "New ini file") EndIf Return $aIni EndFunc ;==>_ReadIniFile ;============================================== Func _WriteIniFile($sIniFile, Const ByRef $aLink) Local $hIni = FileOpen($sIniFile, $FO_OVERWRITE) If $hIni = -1 Then MsgBox($MB_TOPMOST, "FileOpen error", $sIniFile, 0, $hGUI) ; 5th param prevents clicking inside the GUI Else FileWrite($hIni, _ GUICtrlSendMsg($idLangFrom, $CB_GETCURSEL, 0, 0) & @crlf & _ Int(GUICtrlRead($idFontSizeFrom)) & @crlf & _ GUICtrlRead($idWordWrapFrom) & @crlf & _ GUICtrlSendMsg($idLangTo, $CB_GETCURSEL, 0, 0) & @crlf & _ Int(GUICtrlRead($idFontSizeTo)) & @crlf & _ GUICtrlRead($idWordWrapTo) & @crlf & _ _ArrayToString($aLink, ",") & @crlf & _ $aLangGui[$iLangGui] & @crlf & _ ; ex. "en" or "de" etc... (it will be the GUI interface for the language names, on next launch) $aIni[8]) ; So there are 9 lines in ini file (no need of a last @crlf , though it is allowed but not mandatory) FileClose($hIni) EndIf EndFunc ;==>_WriteIniFile ;============================================== Func _EditRecreate_From($iFontSize, $iWordWrap) Local $idEdit_Old = $idTextFrom ; 0 at first passage Local $iExStyle If $idEdit_Old Then Local $aPos = ControlGetPos($hGUI, "", $idEdit_Old) Local $sText = GUICtrlRead($idEdit_Old) Local $aSel = _GUICtrlEdit_GetSel($idEdit_Old) $iExStyle = _WinAPI_GetWindowLong(GUICtrlGetHandle($idEdit_Old), $GWL_EXSTYLE) GUICtrlDelete($idEdit_Old) Else ; 1st passage Local $aPos[4] = [10, 100, 880, 275] Local $iItemFrom = GUICtrlSendMsg($idLangFrom, $CB_GETCURSEL, 0, 0) Local $sLangFrom = ($iItemFrom = 0) ? "auto" : $aLang[$aLink[$iItemFrom - 1]][0] ; ex. "auto", "en", de", ... $iExStyle = _IsRTL($sLangFrom) _ ? BitOR($WS_EX_LEFTSCROLLBAR, $WS_EX_RTLREADING, $WS_EX_RIGHT, $WS_EX_CLIENTEDGE) _ : -1 ; -1 is the default value ($WS_EX_CLIENTEDGE) used by GUICtrlCreateEdit below EndIf Local $idTextFrom = GUICtrlCreateEdit("", $aPos[0], $aPos[1], $aPos[2], $aPos[3], _ (($iWordWrap = 1) _ ; Word wrap (1 = unchecked, 4 = checked) ? BitOr($ES_WANTRETURN, $WS_VSCROLL, $ES_AUTOVSCROLL) _ : -1), _ $iExStyle) ; new control ID (1st passage) or same control ID as the deleted edit control (2nd+ passage) => no gap in id's controls list GUICtrlSetFont(-1, $iFontSize) GUICtrlSetState(-1, $GUI_DROPACCEPTED) GUICtrlSendMsg(-1, $EM_LIMITTEXT, $iMaxInputLength, 0) If $idEdit_Old Then _GUICtrlEdit_SetText($idTextFrom, $sText) If $aSel[0] <> $aSel[1] Then _GUICtrlEdit_SetSel($idTextFrom, $aSel[0], $aSel[1]) EndIf Return $idTextFrom EndFunc ;==>_EditRecreate_From ;============================================== Func _EditRecreate_To($iFontSize, $iWordWrap) Local $idEdit_Old = $idTextTo ; 0 at first passage Local $iExStyle If $idEdit_Old Then Local $aPos = ControlGetPos($hGUI, "", $idEdit_Old) Local $sText = GUICtrlRead($idEdit_Old) Local $aSel = _GUICtrlEdit_GetSel($idEdit_Old) $iExStyle = _WinAPI_GetWindowLong(GUICtrlGetHandle($idEdit_Old), $GWL_EXSTYLE) GUICtrlDelete($idEdit_Old) Else ; 1st passage Local $aPos[4] = [10, 410, 880, 275] Local $iItemTo = GUICtrlSendMsg($idLangTo, $CB_GETCURSEL, 0, 0) If $iItemTo = -1 Then ; no "Language To" selected $iExStyle = -1 ; -1 is the default value ($WS_EX_CLIENTEDGE) used by GUICtrlCreateEdit below Else Local $sLangTo = $aLang[$aLink[$iItemTo]][0] ; ex. "en", "de", ... $iExStyle = _IsRTL($sLangTo) _ ? BitOR($WS_EX_LEFTSCROLLBAR, $WS_EX_RTLREADING, $WS_EX_RIGHT, $WS_EX_CLIENTEDGE) _ : -1 ; -1 is the default value ($WS_EX_CLIENTEDGE) EndIf EndIf Local $idTextTo = GUICtrlCreateEdit("", $aPos[0], $aPos[1], $aPos[2], $aPos[3], _ (($iWordWrap = 1) _ ; Word wrap (1 = unchecked, 4 = checked) ? BitOr($ES_WANTRETURN, $WS_VSCROLL, $ES_AUTOVSCROLL) _ : -1), _ $iExStyle) ; new control ID (1st passage) or same control ID as the deleted edit control (2nd+ passage) => no gap in id's controls list GUICtrlSetFont(-1, $iFontSize) GUICtrlSendMsg(-1, $EM_LIMITTEXT, -1, 0) ; allow unlimited text size in edit control (translated text) If $idEdit_Old Then _GUICtrlEdit_SetText($idTextTo, $sText) If $aSel[0] <> $aSel[1] Then _GUICtrlEdit_SetSel($idTextTo, $aSel[0], $aSel[1]) EndIf Return $idTextTo EndFunc ;==>_EditRecreate_To ;============================================== Func _OpenFile() Local $sFileName = FileOpenDialog( _ "Open Text or PDF file", _ @ScriptDir, _ "Text or PDF (*.txt;*.pdf)", _ BitOR($FD_FILEMUSTEXIST, $FD_PATHMUSTEXIST), _ "", _ $hGUI) If @error Then ;~ MsgBox($MB_TOPMOST, "Open File", _ ;~ "No file selected ", 0, $hGUI) ; FileOpenDialog() was exited by Cancel/Red X button, or by Esc key Return ; 0 EndIf ; Change the working directory (@WorkingDir) back to the location of the script directory as FileOpenDialog sets it to the last accessed folder (on successful return) ; FileChangeDir(@ScriptDir) Return $sFileName EndFunc ;==>_OpenFile ;============================================== Func _PdfToText(Const $sInputFileName) Local Static $sPdfToText = @ScriptDir & ((@OSVersion <> "WIN_XP") ? "\pdftotext.exe" : "\pdftotext40.exe") Local Static $sOutputFileName = @ScriptDir & "\TextExtractedFromPDF.txt" If Not FileExists($sPdfToText) Then MsgBox($MB_TOPMOST, "PDF : missing extractor file", _ "To translate a pdf, you need the file 'pdftotext.exe'" & @crlf & _ "" & @crlf & _ "It can be downloaded at https://www.xpdfreader.com" & @crlf & _ "then place it in same folder as the script", 0, $hGUI) _UpdateGUI($sEmpty) Return ; 0 EndIf Local $sCommand = " /c" & " " & _ chr(34) & chr(34) & $sPdfToText & chr(34) & " " & _ "-enc UTF-8" & " " & _ chr(34) & $sInputFileName & chr(34) & " " & _ chr(34) & $sOutputFileName & chr(34) & chr(34) Local $iRet = RunWait(@ComSpec & $sCommand, @ScriptDir, @SW_HIDE) Select Case @error MsgBox($MB_TOPMOST, "RunWait: error " & @error, _ "AutoIt error, please check AutoIt code", 0, $hGUI) Return ; 0 Case $iRet Local $sErr = "" ; following exit codes (from 0 To 99) are found in PdfToText documentation, except the personal "Case Else" Switch $iRet ;~ Case 0 ;~ $sErr = "No error." Case 1 $sErr = "Error opening a PDF file." Case 2 $sErr = "Error opening an output file." Case 3 $sErr = "Error related to PDF permissions." Case 98 $sErr = "Out of memory." ; this exit code 98 was added in last version 4.05 of pdftotxt.exe (released on Feb 8, 2024) Case 99 $sErr = "Other error." Case Else ; should never happen $sErr = "This error isn't found in PdfToText documentation !" EndSwitch MsgBox($MB_TOPMOST, "PdfToText exit code: error " & $iRet, _ $sErr, 0, $hGUI) _UpdateGUI($sEmpty) Return ; 0 EndSelect ; all is good from now on ($iRet = 0, which means no error returned from pdftotext.exe) Return $sOutputFileName EndFunc ;==>_PdfToText ;============================================== Func WM_COMMAND($hWnd, $iMsg, $wParam, $lParam) ; Local $hWndFrom = $lParam ; Local $iIDFrom = _WinAPI_LoWord($wParam) Local $iIDFrom = BitAND($wParam, 0xFFFF) ; same (cf WinAPIConv.au3) ; Local $iCode = _WinAPI_HiWord($wParam) Local $iCode = BitShift($wParam, 16) ; same (cf WinAPIConv.au3) Switch $iIDFrom Case $idTextFrom If $iCode = $EN_UPDATE Then ; Sent when an edit control is about to redraw itself _UpdateCharStatus() EndIf Case $idLangFrom If $iCode = $CBN_CLOSEUP Or $iCode = $CBN_KILLFOCUS Then Local $iExStyle_Old = _WinAPI_GetWindowLong(GUICtrlGetHandle($idTextFrom), $GWL_EXSTYLE) ; Edit field From Local $iItemFrom = GUICtrlSendMsg($idLangFrom, $CB_GETCURSEL, 0, 0) Local $sLangFrom = ($iItemFrom = 0) ? "auto" : $aLang[$aLink[$iItemFrom - 1]][0] ; ex. "auto", "en", de", ... Local $iExStyle_New = _IsRTL($sLangFrom) _ ? BitOR($WS_EX_LEFTSCROLLBAR, $WS_EX_RTLREADING, $WS_EX_RIGHT, $WS_EX_CLIENTEDGE) _ : $WS_EX_CLIENTEDGE ; $WS_EX_CLIENTEDGE is the default extended style value in Edit field If $iExStyle_New <> $iExStyle_Old Then _WinAPI_SetWindowLong(GUICtrlGetHandle($idTextFrom), $GWL_EXSTYLE, $iExStyle_New) EndIf EndIf Case $idLangTo If $iCode = $CBN_CLOSEUP Or $iCode = $CBN_KILLFOCUS Then Local $iExStyle_Old = _WinAPI_GetWindowLong(GUICtrlGetHandle($idTextTo), $GWL_EXSTYLE) ; Edit field To Local $iItemTo = GUICtrlSendMsg($idLangTo, $CB_GETCURSEL, 0, 0) If $iItemTo = -1 Then ; no "Language To" selected Local $iExStyle_New = $WS_EX_CLIENTEDGE ; $WS_EX_CLIENTEDGE is the default extended style value in Edit field Else Local $sLangTo = $aLang[$aLink[$iItemTo]][0] ; ex. "en", "de", ... Local $iExStyle_New = _IsRTL($sLangTo) _ ? BitOR($WS_EX_LEFTSCROLLBAR, $WS_EX_RTLREADING, $WS_EX_RIGHT, $WS_EX_CLIENTEDGE) _ : $WS_EX_CLIENTEDGE ; $WS_EX_CLIENTEDGE is the default extended style value in Edit field EndIf If $iExStyle_New <> $iExStyle_Old Then _WinAPI_SetWindowLong(GUICtrlGetHandle($idTextTo), $GWL_EXSTYLE, $iExStyle_New) EndIf EndIf EndSwitch Return $GUI_RUNDEFMSG EndFunc ;==>WM_COMMAND ;============================================== Func _UpdateCharStatus() Local $iChars = GUICtrlSendMsg($idTextFrom, $WM_GETTEXTLENGTH, 0, 0) ; same as _GUICtrlEdit_GetTextLen($idTextFrom) ? Select Case $iChars > $iMaxInputLength GUICtrlSetData($idTextFrom, StringLeft(GUICtrlRead($idTextFrom), $iMaxInputLength)) ContinueCase Case $iChars = $iMaxInputLength GUICtrlSetData($idCharStatus, $iMaxInputLength & " / " & $iMaxInputLength) GUICtrlSetBkColor($idCharStatus, 0xFF4040) ; reddish Case Else ; i.e. $iChars < $iMaxInputLength GUICtrlSetData($idCharStatus, $iChars & " / " & $iMaxInputLength) GUICtrlSetBkColor($idCharStatus, ($iChars <= 5460) ? 0x80FF80 : 0xFFFF00) ; greenish => ok, yellow => encoded string may become too long. EndSelect EndFunc ;==>_UpdateCharStatus ;============================================== Func _ErrFuncLocal($oError) ; https://www.autoitscript.com/forum/topic/191401-com-error-handling-in-a-udf-best-practice/?do=findComment&comment=1373102 MsgBox($MB_TOPMOST, @ScriptName & " (" & $oError.scriptline & ") : ==> Local COM error handler - COM Error intercepted !", _ @Tab & "err.number is: " & @Tab & @Tab & "0x" & Hex($oError.number) & @crlf & _ @Tab & "err.windescription:" & @Tab & @Tab & StringStripWS($oError.windescription, 2) & @crlf & _ @Tab & "err.description is: " & @Tab & @Tab & StringStripWS($oError.description, 2) & @crlf & _ @Tab & "err.source is: " & @Tab & @Tab & $oError.source & @crlf & _ @Tab & "err.helpfile is: " & @Tab & @Tab & $oError.helpfile & @crlf & _ @Tab & "err.helpcontext is: " & @Tab & @Tab & $oError.helpcontext & @crlf & _ @Tab & "err.lastdllerror is: " & @Tab & @Tab & $oError.lastdllerror & @crlf & _ @Tab & "err.scriptline is: " & @Tab & @Tab & $oError.scriptline & @crlf & _ @Tab & "err.retcode is: " & @Tab & @Tab & "0x" & Hex($oError.retcode), 0, $hGUI) EndFunc ;==>_ErrFuncLocal ;============================================== Func _Wanna_Quit() ; in case user keys Esc during the translation process HotKeySet("{ESC}") If WinActive($hGUI) Then MouseMove(@DesktopWidth / 2, @DesktopHeight / 2, 0) ; 0 = fastest move If MsgBox(BitOr($MB_TOPMOST, $MB_OKCANCEL, $MB_DEFBUTTON2), _ "Esc was keyed", "Quit translation script ?", 0, $hGUI) = $IDOK Then GUIDelete($hGUI) Exit EndIf Else ; Esc was keyed when another application was on top Send("{ESC}") EndIf HotKeySet("{ESC}", "_Wanna_Quit") EndFunc ;_Wanna_Quit ;============================================== Func _FillArrayLangGUI($sFileName) ; Text file containing a list of 39 languages & codes (for GUI) found patiently on Google translate web site, one by one... Local $sFilePath = @ScriptDir & "\" & $sFileName If Not FileExists($sFilePath) Then Exit MsgBox($MB_TOPMOST, "File not found", $sFilePath) Local $s_String = FileRead($sFilePath) If @error Then Exit MsgBox($MB_TOPMOST, "FileRead : error " & @error, $sFilePath) ; this shouldn't happen, but who knows ? Local $sPattern = '(?i)Language code\h*:?\h*(.+?)\h*\R+\h*Language name\h*:?\h*(.+?)\h*\R+\h*\["auto","(.+?)"],(\[".+"])\h*(?:\R+|$)' Local $aArray = StringRegExp($s_String, $sPattern, 3) If @error Then Exit MsgBox($MB_TOPMOST, "StringRegExp", _ "error = " & @error & (@error = 1 ? " (no matches)" : " (bad pattern)") & @crlf & _ "" & @crlf & _ $sFilePath) If Mod(Ubound($aArray), 4) Then _ArrayDisplay($aArray, "Malformed txt file : number of rows should be a multiple of 4") Exit MsgBox($MB_TOPMOST, "error", _ "Please install a correct language text file" & @crlf & _ "" & @crlf & _ $sFilePath) EndIf ; _ArrayDisplay($aArray, (Ubound($aArray) / 4) & " GUI languages codes & names") Return $aArray EndFunc ;==>_FillArrayLangGUI ;============================================== Func _FillArrayLang($sCode) For $i = 0 To Ubound($aLangGui) - 1 Step 4 ; 4 consecutive rows (in $aLangGui) contain the characteristics of each GUI language If $aLangGui[$i] = $sCode Then ExitLoop ; ex. "en" or "de" etc... Next If $i > Ubound($aLangGui) - 1 Then Exit MsgBox($MB_TOPMOST, 'error', _ 'Language code "' & $sCode & '" not retrieved in language text file' & @crlf & _ 'Please install a correct language text file (and delete your eventual ini file)') $iLangGui = $i ; important global variable indicating the row where starts the characteristics of the actual Gui language. ; its value is 0, 4, 8, 12...152 (when 39 Gui languages) Local $s_String = $aLangGui[$iLangGui + 3] ; string of 133 languages, for ex. ["af","Afrikaans"],["sq","Albanian"],[...],["zu","Zulu"] ; suppress eventual leading & trailing whitespaces ; Regexp Pattern in _FillArrayLangGUI() has already checked this => comment out the test below ; $s_String = StringStripWS($s_String, $STR_STRIPLEADING + $STR_STRIPTRAILING) ; check the string starts with an opening bracket followed by a double quote, then ends with a double quote followed by a closing bracket ; Regexp Pattern in _FillArrayLangGUI() has already checked this => comment out the test below ; If StringLeft($s_String, 2) <> '["' Or StringRight($s_String, 2) <> '"]' Then Exit MsgBox($MB_TOPMOST, _ ; 'Language code "' & $sCode & '" : bracket and double quote error inside language text file', _ ; 'The first 2 characters of the string must be ["' & @crlf & _ ; 'The last 2 characters of the string must be "]') ; beginning of string : suppress leading opening bracket and first double quote ; end of string : suppress last double quote and trailing closing bracket $s_String = StringMid($s_String, 3, StringLen($s_String) - 4) ; Split the string in a 2D array (2 columns) with a 3rd empty column added for further usage (5th param. of _StringSplit2D_EX) Local $aArray = _StringSplit2D_EX($s_String, '","', '"],["', False, 1) If @error Then Exit MsgBox($MB_TOPMOST, "_StringSplit2D_EX : error " & @error, _ "All rows don't have the same number of fields" & @crlf & _ "Please install a correct language text file") If Ubound($aArray, $UBOUND_COLUMNS) <> 3 Then Exit MsgBox($MB_TOPMOST, _ "error : number of columns = " & Ubound($aArray, $UBOUND_COLUMNS), _ "It should be 3 (last column being empty for further usage)" & @crlf & _ "Please install a correct language text file") $bRTL = _IsRTL($sCode) ; True if language is RTL, False if not Return $aArray EndFunc ;==>_FillArrayLang ;============================================== Func _IsRTL($sCode) ; June 2024 : 10 languages are RTL amongst the 133 traductable languages (tested) ; Place them all in the string below, though only 3 of them are accessible from the 39 GUI languages (Arabic, Hebrew, Persian) ; Arabic (ar) , Dhivehi (dv), Hebrew (iw), Kurdish (Sorani) (ckb), Pashto (ps) ; Persian (fa), Sindhi (sd), Urdu (ur), Uyghur (ug), Yiddish (yi) ; Note : in version 10+ the whole list (10 RTL languages) is checked in case of 1 or 2 edit fields are RTL (in edit creation & WM_COMMAND) Local Static $sRTL = "ar,dv,iw,ckb,ps,fa,sd,ur,ug,yi," ; end the string with a comma (to make the search easy on next line) Return (StringInStr($sRTL, $sCode & ",") > 0) ? True : False EndFunc ;==>_IsRTL ;============================================== Func _GuiTitle($iGui = 0) Local $sTitle Switch $iGui Case 0 ; add GUI language code (which may change during the script) to the string, so user knows what GUI language is actually set. ; ex. "Google Translation (v10 - en)" $sTitle = StringTrimRight($sGUI_Title, 1) & " - " & $aLangGui[$iLangGui] & ")" Case 2 ; add GUI language name to the string ; ex. "GUI language (English) & Languages filter" $sTitle = "GUI language (" & $aLangGui[$iLangGui + 1] & ") & Languages filter" Case Else $sTitle = "Scripter, where are you ?" EndSwitch Return $sTitle EndFunc ;==>_GuiTitle ;============================================== Func _StringSplit2D_EX(ByRef $sString, $sDelim_Col = "|", $sDelim_Row = @CRLF, $bExpand = False, $iAdd_EmptyCol = 0) ; based on AspirinJunkie's function _StringSplit2D found at https://autoit.de/thread/85380-1d-array-in-2d-array-splitten/ ; Thanks Nine for suggesting the 4th parameter $bExpand, to allow or not the same number of fields per row (as in _FileReadToArray) Local $a_FirstDim = StringSplit($sString, $sDelim_Row, $STR_ENTIRESPLIT + $STR_NOCOUNT) Local $iKeep_NbCol = Ubound(StringSplit($a_FirstDim[0], $sDelim_Col, $STR_ENTIRESPLIT + $STR_NOCOUNT)) ; keep nb cols row 0 Local $a_Out[UBound($a_FirstDim)][1 + $iAdd_EmptyCol], $a_Line, $i_2DMax = 1 For $i = 0 To UBound($a_FirstDim) - 1 $a_Line = StringSplit($a_FirstDim[$i], $sDelim_Col, $STR_ENTIRESPLIT + $STR_NOCOUNT) If (Not $bExpand) And (Ubound($a_Line) <> $iKeep_NbCol) Then Return SetError(3, 0, 0) ; same error # as _FileReadToArray If UBound($a_Line) > $i_2DMax Then ; when $bExpand = False, this test will be True maximum 1 time, never more. $i_2DMax = UBound($a_Line) ReDim $a_Out[UBound($a_Out)][$i_2DMax + $iAdd_EmptyCol] EndIf For $j = 0 To UBound($a_Line) - 1 $a_Out[$i][$j] = $a_Line[$j] Next Next Return $a_Out EndFunc ;==>_StringSplit2D_EX Please indicate if something doesn't work correctly as I just finished the script a few minutes ago. Have a great day * Update June 7, 2024 (version 7) If you don't need all 133 language names (in both combobox) and prefer to display less names for a better visibility, then it's easily doable as explained in my post just below * Update June 23, 2024 (version 9) list of changes below in this thread, here * Update June 27, 2024 (version 10) this will be probably my final update (unless a bug is discovered in the script) + Added code to take care of possible RTL edit fields (10 languages amongst 133) during Edit controls creation, or during the registered WM_COMMAND function (test on $CBN_CLOSEUP, $CBN_KILLFOCUS etc...) + Minor cosmetic changes * Update June 30, 2024 (version 11) Pdf files can now be opened for translation. Instructions to download the free utility "pdftotext" are found in the post below Google Translate (languages list #3).zip1 point