Posts: 1,031
Threads: 246
Joined: Jul 2022
07-22-2022, 04:11 AM
(This post was last modified: 07-22-2022, 04:17 AM by Davider.)
Hi,
This is a practical, useful feature,
In a dialog, if the role of a control is STATICTEXT or CHECKBUTTON or PUSHBUTTON , I need to translate it
In order to make the translation faster, I thought of the following method:
1.get all the controls text that needs to be translated
2.Merge all text into one line, the delimiter is | , Replace some symbols like , : Like this below
hello world | hello friend | Father | Mother | my city | your country
3.Use Google Translate the line text, and re-split the string, delimiter is |
4.Set the translated text to the matching control
5.When I press the hotkey Ctrl+Q again, the controls text returns to its original
In the QM code below, I've only done part of it, I can only translate the text of one control at a time
My level of programming is not very good, some are not done
this is a very comprehensive example
Thanks for any advice and help
david
Macro Tran
str dd=
;BEGIN DIALOG
;0 "" 0x90C80AC8 0x0 0 0 224 136 "Press Ctrl+Q translate controls text" "4"
;3 Button 0x54032000 0x0 32 44 48 14 "Father"
;4 Button 0x54032000 0x0 144 44 48 14 "Mother"
;5 Static 0x54000000 0x0 40 16 48 13 "hello,world"
;6 Static 0x54000000 0x0 144 16 48 13 "hello: friend"
;7 Button 0x54012003 0x0 32 72 48 10 "my city"
;8 Button 0x54012003 0x0 144 72 64 10 "your country"
;9 Edit 0x54030080 0x200 8 96 72 12 ""
;10 Edit 0x54030080 0x200 144 96 72 12 ""
;1 Button 0x54030001 0x4 48 116 48 14 "OK"
;2 Button 0x54030000 0x4 120 116 48 14 "Cancel"
;END DIALOG
;DIALOG EDITOR: "" 0x2040C02 "*" "" "" ""
str controls = "7 8 9 10"
str c7my c8you e9 e10
if(!ShowDialog(dd &sub.DlgProc &controls)) ret
#sub DlgProc
function# hDlg message wParam lParam
sel message
,case WM_INITDIALOG
,DT_SetAccelerators(hDlg "401 Cq")
,case WM_DESTROY
,case WM_COMMAND goto messages2
ret
;messages2
sel wParam
,case IDOK
,case IDCANCEL
,case 401 ;;Ctrl+Q
,;Todo_1: Gets all the specific role controls text (STATICTEXT or CHECKBUTTON or PUSHBUTTON)
,
,;Todo_2: Merge all text into one line, the delimiter is | , Replace some symbols like , :
,
,;Todo_3: Use Google Translate text, and re-split the string, delimiter is | see below #sub tran
,
,;Todo_4: Set the translated text to the matching control
,;Todo_5: When I press the hotkey Ctrl+Q again, the controls text returns to its original
,_s.getwintext(id(5 hDlg))
,_s.replacerx(",|:" " ") ;;Replace , : with spaces
,_s=sub.tran(_s) ;;Translate control text
,_s.setwintext(id(5 hDlg))
ret 1
#sub tran
function'str str's
;Todo_3: change the following Powershell code to QM code
;sl=en -> Set the source language
;tl=zh-CN -> Set the target language
_s=
F
;function tran
;{{
;,$data = @('', '')
;,$objRet = Invoke-WebRequest "https://translate.googleapis.com/translate_a/single?client=gtx&sl=en&tl=zh-CN&dt=t&q=$args"
;,$data[0] = $objRet.Content
;,$objJson = ConvertFrom-Json $data[0]
;,$objJson[0][0][0]
;}
;tran "{s}"
PsCmd2 _s "" _s
ret _s.trim
Posts: 1,336
Threads: 61
Joined: Jul 2006
07-23-2022, 12:55 AM
(This post was last modified: 07-23-2022, 12:58 AM by Kevin.)
try this out
Function DialogTranslateControls
str dd=
;BEGIN DIALOG
;0 "" 0x90C80AC8 0x0 0 0 224 136 "Press Ctrl+Q translate controls text" "4"
;3 Button 0x54032000 0x0 32 44 48 14 "Father"
;4 Button 0x54032000 0x0 144 44 48 14 "Mother"
;5 Static 0x54000000 0x0 40 16 48 13 "hello,world"
;6 Static 0x54000000 0x0 144 16 48 13 "hello: friend"
;7 Button 0x54012003 0x0 32 72 48 10 "my city"
;8 Button 0x54012003 0x0 144 72 64 10 "your country"
;9 Edit 0x54030080 0x200 8 96 72 12 ""
;10 Edit 0x54030080 0x200 144 96 72 12 ""
;1 Button 0x54030001 0x4 48 116 48 14 "OK"
;2 Button 0x54030000 0x4 120 116 48 14 "Cancel"
;END DIALOG
;DIALOG EDITOR: "" 0x2040C02 "*" "" "" ""
str controls = "7 8 9 10"
str c7my c8you e9 e10
if(!ShowDialog(dd &sub.DlgProc &controls)) ret
#sub DlgProc
function# hDlg message wParam lParam
sel message
,case WM_INITDIALOG
,int- tn
,ARRAY(str)- translate original
,ARRAY(int)- a
,DT_SetAccelerators(hDlg "401 Cq")
,case WM_DESTROY
,case WM_COMMAND goto messages2
ret
;messages2
sel wParam
,case IDOK
,case IDCANCEL
,case 401 ;;Ctrl+Q
,tn=!tn
,if(translate.len =0)
,,ARRAY(str) roles="STATICTEXT[]PUSHBUTTON[]CHECKBUTTON"
,,Acc a1.Find(hDlg "DIALOG")
,,ARRAY(Acc) ac
,,a1.GetChildObjects(ac 1)
,,for _i 0 ac.len
,,,Acc& r1=ac[_i]
,,,str role name
,,,r1.Role(role); name=r1.Name
,,,for int'i 0 roles.len
,,,,if(!StrCompare(role roles[i]))
,,,,,a[]=r1.Hwnd
,,,,,original[]=name
,,,,,name.replacerx(",|:" " ") ;;Replace , : with spaces
,,,,,if _i !=ac.len-1
,,,,,,name+ " | "
,,,,,_s+name
,,sub.GetTranslation(_s "en" "zh-CN" &translate) ;;Translate control text
,int ii=0
,for i 0 a.len
,,if(tn)
,,,translate[ii].replacerx("\|" "");err
,,,translate[ii].rtrim;err
,,,translate[ii].setwintext(a[i]);err
,,else
,,,original[ii].setwintext(a[i]);err
,,ii+1
ret 1
#sub GetTranslation
function ~sourceText ~sourceLang ~targetLang ARRAY(str)&tl
sourceText.escape(11)
str s
IntGetFile F"https://translate.googleapis.com/translate_a/single?client=gtx&sl={sourceLang}&tl={targetLang}&dt=t&q={sourceText}" s
IXml x=JsonToXml(s)
IXmlNode r=x.RootElement
ARRAY(IXmlNode) a; r.Path("item/item/item[@type='string']" a 1)
for(int'i 0 a.len)
,,if(!(i&1))
,,,tl[]=a[i].Value
also need this function
Function JsonToXml
;/
function'IXml $JSON [flags] ;;flags: 1 display XML text in QM output
;Converts JSON text to XML and returns IXml object.
;REMARKS
;On Windows XP SP2 and Vista must be installed .NET 3.5 or later. Older OS are not supported.
;EXAMPLE
;out
;str JSON=
;;{
;;;;;"hello": "world",
;;;;;"t": true ,
;;;;;"f": false,
;;;;;"n": null,
;;;;;"i": 123,
;;;;;"pi": 3.14,
;;;;;"Address": { "City": "New York", "State": "NY" },
;;;;;"a": [1, 2, 3, 4]
;;}
;IXml x=JsonToXml(JSON 1)
;IXmlNode r=x.RootElement
;;get simple
;out r.Child("hello").Value
;;get with XPath
;out r.Path("Address/State").Value
;;get array
;ARRAY(IXmlNode) a; r.Path("a/*" a)
;int i; for(i 0 a.len) out a[i].Value
opt noerrorshere 1
CsScript x.SetOptions("references=System.Xml;System.Runtime.Serialization;System.ServiceModel.Web")
x.AddCode("")
_s=x.Call("ToXml" JSON)
if(flags&1) out _s
IXml k._create
k.FromString(_s)
ret k
#ret
using System;
using System.Text;
using System.Runtime.Serialization.Json;
using System.Xml;
using System.Xml.Linq;
public class Json
{
static public string ToXml(string JSON)
{
XmlDictionaryReader reader = JsonReaderWriterFactory.CreateJsonReader(Encoding.UTF8.GetBytes(JSON), new System.Xml.XmlDictionaryReaderQuotas());
return XElement.Load(reader).ToString();
}
}
Posts: 1,031
Threads: 246
Joined: Jul 2022
@ Kevin
Thank you so much, it worked perfectly
QM is too powerful, QM looks like it can do anything
Posts: 1,031
Threads: 246
Joined: Jul 2022
07-23-2022, 12:28 PM
(This post was last modified: 07-23-2022, 01:18 PM by Davider.)
@ Kevin
Hello, I put the Translate Control function, saved as a separate function (Tran_Ctrls) It assigns a hotkey Ctrl+Q
This way I can translate controls from other windows, but a lot of times it doesn't work,
is there a way to make the functions more generic? Thanks in advance
For example, I want to translate controls inside the custom dialog
Function Tran_Ctrls
Trigger Cq
int h = win
int tn
ARRAY(str) translate original
ARRAY(int) a
tn=!tn
if(translate.len =0)
,ARRAY(str) roles="STATICTEXT[]PUSHBUTTON[]CHECKBUTTON[]RADIOBUTTON[]LISTITEM[]CLIENT"
,Acc a1.Find(h "DIALOG") ;;WINDOW
,ARRAY(Acc) ac
,a1.GetChildObjects(ac 1)
,for _i 0 ac.len
,,Acc& r1=ac[_i]
,,str role name
,,r1.Role(role); name=r1.Name
,,for int'i 0 roles.len
,,,if(!StrCompare(role roles[i]))
,,,,a[]=r1.Hwnd
,,,,original[]=name
,,,,name.replacerx(",|:" " ") ;;Replace , : with spaces
,,,,if _i !=ac.len-1
,,,,,name+ " | "
,,,,_s+name
,sub.GetTranslation(_s "en" "zh-CN" &translate) ;;Translate control text
,_s=translate
,out _s
int ii=0
for i 0 a.len
,if(tn)
,,translate[ii].replacerx("\|" "");err
,,translate[ii].rtrim;err
,,translate[ii].setwintext(a[i]);err
,else
,,original[ii].setwintext(a[i]);err
,ii+1
#sub GetTranslation
function ~sourceText ~sourceLang ~targetLang ARRAY(str)&tl
sourceText.escape(11)
str s
IntGetFile F"https://translate.googleapis.com/translate_a/single?client=gtx&sl={sourceLang}&tl={targetLang}&dt=t&q={sourceText}" s
IXml x=JsonToXml(s)
IXmlNode r=x.RootElement
ARRAY(IXmlNode) a; r.Path("item/item/item[@type='string']" a 1)
for(int'i 0 a.len)
,,if(!(i&1))
,,,tl[]=a[i].Value
I found a more general way to locate:
Acc a.Find(w "RADIOBUTTON" "" "class=Button" 0x1004 1 1) -> Positioning of the first button
Acc a.Find(w "RADIOBUTTON" "" "class=Button" 0x1004 1 2) -> Positioning of the second button
If there is no third button, with the method above, an error will occur
Acc a1.Find(w1 "CHECKBUTTON" "" "class=Button" 0x1004 1 1) -> Positioning of the first checkbutton
If there is no second checkbutton, with the method above, an error will occur
...... Other control positioning is the same logic
But I don't know how to modify the code above
@ Gintaras
Currently, learning QM and Uiscripter, my biggest obstacle is poor English proficiency, at the same time, the software interface is not localized,
Can you use Uiscripter to create an assistant with a translation UI interface similar to the one above? Thanks in advance
With an assistant similar to the above, it is convenient for more people to use and learn QM and Uiscripter
Posts: 12,073
Threads: 140
Joined: Dec 2002
// script "Translate UI element text.cs" /// Gets UI element text and translates with Google Cloud Translation API. Displays the result as a tooltip.
/// Edit the langTo string. Optionally edit langFrom.
/// By default gets text of UI element from mouse (uses elm functions). If script command line contains "copy", gets the selected text (uses the clipboard).
/// Hotkey trigger example: hk["F4"] = o => script.run(@"Translate UI element text.cs");
/// Hotkey trigger example for translating the selected text: hk["Ctrl+F4"] = o => script.run(@"Translate UI element text.cs", "copy");
/// Bad: Uses a Google translation service URL that is undocumented and may stop working in the future. Good: don't need authentication, API key.
/// If the used URL will stop working or does not work well, try the official API instead. Also there are libraries.
/// Not tested: max allowed text length. It is passed in URL. The script does not limit the text. Not tested: try POST request instead of GET.
/*/ ifRunning restart; /*/
string langFrom = "en", langTo = "es"; //edit these strings
bool debug = !true; //if true, prints the source and translated texts and errors
if (debug) print.clear();
try {
string text;
if (args.Contains("copy")) clipboard.tryCopy(out text); else text = _GetMouseElementText();
if (text == null) return;
if (debug) print.it(text);
var s = _Translate(text, langFrom, langTo); if (s == null) return;
if (debug) print.it($"<><c green>{s}<>");
osdText.showText(s, xy: PopupXY.Mouse);
}
catch (Exception e1) { if (debug) print.it(e1); }
string _GetMouseElementText() {
var e = elm.fromMouse();
var name = e.Name;
if (name.NE() || !name.RxIsMatch(@"(?i)[a-z]")) name = null;
if (name != null && name.Contains('_') && e.WndContainer.ClassNameIs("HwndWrapper[*")) name = name.Replace("_", ""); //remove WPF accelerator prefix char
var help = e.Help; //tooltip
if (help.NE() || help == name || !help.RxIsMatch(@"(?i)[a-z]")) help = null;
var val = e.Value; //textbox text
if (val.NE() || val == name || val == help || !val.RxIsMatch(@"(?i)[a-z]")) val = null;
if (name == null && help == null && val == null) return null;
if ((help ?? val) == null) return name;
var sb = new StringBuilder(name);
if (help != null) sb.Append(sb.Length > 0 ? "\n" : null).Append("Help: ").Append(help);
if (val != null) sb.Append(sb.Length > 0 ? "\n" : null).Append("Text: ").Append(val);
return sb.ToString();
}
string _Translate(string sourceText, string sourceLang, string targetLang) {
var url = internet.urlAppend("https://translate.googleapis.com/translate_a/single", "client=gtx", "sl=" + sourceLang, "tl=" + targetLang, "dt=t", "q=" + sourceText);
var r = internet.http.Get(url);
if (!r.IsSuccessStatusCode) {
if (debug) print.it($"translate.googleapis.com error: {(int)r.StatusCode} {r.ReasonPhrase}");
return null;
}
var sb = new StringBuilder();
foreach (var v in r.Json()[0].AsArray()) sb.Append((string)v[0]);
return sb.ToString();
}
Posts: 1,031
Threads: 246
Joined: Jul 2022
07-26-2022, 11:51 PM
(This post was last modified: 07-27-2022, 12:07 AM by Davider.)
@ Gintaras
Worked well, thanks very much
I just implemented a similar feature in QM2, PS: May not be perfect, welcome to correct
QM2 is really like a legend, I can understand the code QM setting hotkeys is more convenient, Faster startup and execution
but for Uiscripter, it is still difficult to understand, maybe I need to learn more programming knowledge
Macro Macro14
Trigger F4
Acc ac = acc(mouse)
_s = ac.Name
if (empty(_s))
,int h = child(mouse)
,if h=0
,,h = win(mouse)
,_s.getwintext(h)
ARRAY(str) tl
sub.GetTranslation(_s "en" "zh_cn" tl)
str d=tl
OnScreenDisplay d.trim 1 0.5 0.5 "" 36 0xFF0000 4
#sub GetTranslation
function ~sourceText ~sourceLang ~targetLang ARRAY(str)&tl
sourceText.escape(11)
str s
IntGetFile F"https://translate.googleapis.com/translate_a/single?client=gtx&sl={sourceLang}&tl={targetLang}&dt=t&q={sourceText}" s
IXml x=JsonToXml(s)
IXmlNode r=x.RootElement
ARRAY(IXmlNode) a; r.Path("item/item/item[@type='string']" a 1)
for(int'i 0 a.len)
,,if(!(i&1))
,,,tl[]=a[i].Value
I found a problem, the flag 4 didn't work
4 click to hide
OnScreenDisplay d.trim 1 0.5 0.5 "" 36 0xFF0000 4
Posts: 12,073
Threads: 140
Joined: Dec 2002
Flag 4: the window is not transparent; users can click it to close.
Posts: 1,031
Threads: 246
Joined: Jul 2022
I changed the translation parameter [sourceLang] to "auto", and the output result has extra characters
I tried to make the following modifications, but it didn't work
ARRAY(IXmlNode) a; r.Path("item/item/item[@type='string'] [0]" a 1)
Function Test
Trigger K
_s="hello"
ARRAY(str) tl
sub.GetTranslation(_s "auto" "zh_cn" tl)
str d=tl
OnScreenDisplay d.trim 1 0.5 0.5 "" 36 0xFF0000 4
#sub GetTranslation
function ~sourceText ~sourceLang ~targetLang ARRAY(str)&tl
sourceText.escape(11)
str s
IntGetFile F"https://translate.googleapis.com/translate_a/single?client=gtx&sl={sourceLang}&tl={targetLang}&dt=t&q={sourceText}" s
IXml x=JsonToXml(s 1)
IXmlNode r=x.RootElement
ARRAY(IXmlNode) a; r.Path("item/item/item[@type='string']" a 1)
;ARRAY(IXmlNode) a; r.Path("item/item/item[@type='string'][0]" a 1)
for(int'i 0 a.len)
,,if(!(i&1))
,,,tl[]=a[i].Value
Posts: 12,073
Threads: 140
Joined: Dec 2002
[number] in QM strings is an escape sequence (character code). Need to escape [. To easily create text with escape sequences, use the Text dialog.
Posts: 1,336
Threads: 61
Joined: Jul 2006
08-03-2022, 03:35 PM
(This post was last modified: 08-03-2022, 03:40 PM by Kevin.)
Had some time to look at this again. I changed the translate subfunction to use c#(CsScript) in qm2 now for the translation. Much easier to handle the Json array returned using c#.
Function Trans2
Trigger K
out
Acc ac.FromMouse
str name = ac.Name
if(ac.Role =18)
,ret
if(empty(name))
,int h = child(mouse)
,if h=0
,,h = win(mouse)
,_s.getwintext(h)
,if _s.len=0
,,_s.getsel
,,if(_s.len =0)
,,,out "nothing selected"
,,,ret
,name=_s
out name
str d
sub.GetTranslation(name "auto" "zh_cn" d);;change values for other Languages
out d
OnScreenDisplay d -1 0 20 "" 24 0xFF0000 4|32 "TosdHwnd"
#sub GetTranslation
function ~sourceText ~sourceLang ~targetLang ~&tl
CsScript x
x.SetOptions("references=System.Web.Extensions.dll;System.Runtime.Serialization;Microsoft.CSharp.dll;System.Core.dll")
x.AddCode("")
tl=x.Call("Example.TranslateText" sourceText sourceLang targetLang)
#ret
using System.Net.Http;
using System.Collections;
using System.Web.Script.Serialization;
using System;
using System.Collections.Generic;
public class Example
{
,public static string TranslateText(string input , string sLang ,string tLang)
,{
,,string url = String.Format("https://translate.googleapis.com/translate_a/single?client=gtx&sl={0}&tl={1}&dt=t&q={2}", sLang, tLang, Uri.EscapeUriString(input));
,,HttpClient httpClient = new HttpClient();
,,string result = httpClient.GetStringAsync(url).Result;
,,var jsonData = new JavaScriptSerializer().Deserialize<List<dynamic>>(result);
,,var translationItems = jsonData[0];
,,string translation = "";
,,foreach (object item in translationItems)
,,{
,,,IEnumerable translationLineObject = item as IEnumerable;
,,,IEnumerator translationLineString = translationLineObject.GetEnumerator();
,,,translationLineString.MoveNext();
,,,translation += string.Format(" {0}", Convert.ToString(translationLineString.Current));
,,}
,,if (translation.Length > 1) { translation = translation.Substring(1); };
,,return translation;
,}
}
Posts: 1,031
Threads: 246
Joined: Jul 2022
@ Kevin
This time the effect is better, thanks again
Posts: 1,031
Threads: 246
Joined: Jul 2022
08-14-2022, 10:19 PM
(This post was last modified: 08-14-2022, 10:21 PM by Davider.)
Quote:// script "Translate UI element text.cs" /// Gets UI element text and translates with Google Cloud Translation API. Displays the result as a tooltip.
/// Edit the langTo string. Optionally edit langFrom.
/// By default gets text of UI element from mouse (uses elm functions). If script command line contains "copy", gets the selected text (uses the clipboard).
/// Hotkey trigger example: hk["F4"] = o => script.run(@"Translate UI element text.cs");
/// Hotkey trigger example for translating the selected text: hk["Ctrl+F4"] = o => script.run(@"Translate UI element text.cs", "copy");
/// Bad: Uses a Google translation service URL that is undocumented and may stop working in the future. Good: don't need authentication, API key.
/// If the used URL will stop working or does not work well, try the official API instead. Also there are libraries.
/// Not tested: max allowed text length. It is passed in URL. The script does not limit the text. Not tested: try POST request instead of GET.
/*/ ifRunning restart; /*/
string langFrom = "en", langTo = "es"; //edit these strings
bool debug = !true; //if true, prints the source and translated texts and errors
if (debug) print.clear();
try {
string text;
if (args.Contains("copy")) clipboard.tryCopy(out text); else text = _GetMouseElementText();
if (text == null) return;
if (debug) print.it(text);
var s = _Translate(text, langFrom, langTo); if (s == null) return;
if (debug) print.it($"<><c green>{s}<>");
osdText.showText(s, xy: PopupXY.Mouse);
}
catch (Exception e1) { if (debug) print.it(e1); }
string _GetMouseElementText() {
var e = elm.fromMouse();
var name = e.Name;
if (name.NE() || !name.RxIsMatch(@"(?i)[a-z]")) name = null;
if (name != null && name.Contains('_') && e.WndContainer.ClassNameIs("HwndWrapper[*")) name = name.Replace("_", ""); //remove WPF accelerator prefix char
var help = e.Help; //tooltip
if (help.NE() || help == name || !help.RxIsMatch(@"(?i)[a-z]")) help = null;
var val = e.Value; //textbox text
if (val.NE() || val == name || val == help || !val.RxIsMatch(@"(?i)[a-z]")) val = null;
if (name == null && help == null && val == null) return null;
if ((help ?? val) == null) return name;
var sb = new StringBuilder(name);
if (help != null) sb.Append(sb.Length > 0 ? "\n" : null).Append("Help: ").Append(help);
if (val != null) sb.Append(sb.Length > 0 ? "\n" : null).Append("Text: ").Append(val);
return sb.ToString();
}
string _Translate(string sourceText, string sourceLang, string targetLang) {
var url = internet.urlAppend("https://translate.googleapis.com/translate_a/single", "client=gtx", "sl=" + sourceLang, "tl=" + targetLang, "dt=t", "q=" + sourceText);
var r = internet.http.Get(url);
if (!r.IsSuccessStatusCode) {
if (debug) print.it($"translate.googleapis.com error: {(int)r.StatusCode} {r.ReasonPhrase}");
return null;
}
var sb = new StringBuilder();
foreach (var v in r.Json()[0].AsArray()) sb.Append((string)v[0]);
return sb.ToString();
}
How to improve the above code
Use a hotkey to implement the above two functions
e.g:
window: Limited to QM3 only
Hotkey: Press the ctrl key twice in a row
then: If there is selected text under the mouse pointer, the selected text is translated, otherwise the text of the control is translated
|