Перейти на главную страничку сайта (список статей, файлы для скачивания)

ФОРУМ (здесь можно обсудить эту статью, а также саму программу AutoHotkey и проблемы её использования)

Проект перевода документации AutoHotkey: перечень переведённых статей и статей в работе.

AutoHotkey and Russian language are friends forever

Androgen Belkin
translated by Gavrikova V.


Note: this article is based on releases of AutoHotkey earlier than 1.0.44.03 - May 29, 2006.


AutoHotkey is a very powerful scripting language. With it, you can automate anything you like. (Although I do not advocate such generalizations, in this case it seems to be true.) For example, on my computer one singular constantly loaded script has substituted for at least five constantly loaded programs and made it easier to operate others. Now I cannot even imagine doing without AutoHotkey. But there is one drawback: AutoHotkey is not very friendly with Russian language. If you have not faced this problem before, unpleasant surprises are yet to come. It is easy to avoid all these surprises, though. You should only know the places where you might "slip" to "sprinkle sand over them". So here is what we’re going to do in this article – "to sprinkle sand".

(A part of what is written here has already been mentioned on this Forum, but The gray Cardinal suggested putting everything in one place, extending description and covering some undiscussed questions).

Before we begin, I would like to point out that the article considers a common situation for Russia, when two languages are installed on the system – English and Russian. In most cases, it is this bilingualism that causes malfunctions of hotkeys, hotstrings, auto-replace hotstrings and Send, SendRaw, ControlSend and ControlSendRaw commands (lack of skill is not counted).

It more than two languages are installed on your PC, you just need to make appropriate corrections.

Let’s begin. The main and the only theoretical basis of this topic is the fact that Windows has so-called "default input language". This language is used when you start your computer. The language is set in Control Panel > Regional and Language Options > Languages > Details… > Settings > Default Input Language.

Note: At the end of this article there is a script that allows changing the default input language quickly.

Attention: Default Input Language (or Default Keyboard Layout) is NOT a current language for some application that you can switch by Alt+Shift (or Ctrl+Shift, depending on settings). It is the language (keyboard layout) with which Windows starts and with which all the applications (programs, scripts etc.) are launched (either while the system is starting or at any other time).

Let’s keep it in mind: ALL THE APPLICATIONS ARE LAUNCHED WITH THE DEFAULT LANGUAGE.

Later I’ll explain what this means for us, but at first…

Hotkeys

… we ‘ll write and run a tiny script:

^!z:: ; Define a hotkey Ctrl+Alt+z
MsgBox, The hotkey is down. ; show a message
Return ; End of the hotkey action

Now press Ctrl+Alt+z. If you saw a message "the hotkey is down", it means that the Default Input Language in your Windows is English. If this message was displayed when you launched the script, before you pressed the hotkey, it means that the Default Input Language is Russian. In other words, the script failed to register the hotkey and continued executing, as if our hotkey did not exist at all. But what if instead of a mere message there would have been something more important? For example, I have a hotkey for a forced reboot and in this case, I would have lost all the information I had not save.

Hence, we have the first problem.

If a hotkey you assigned can also have other keyboard layout, it makes your script dependent on the default keyboard layout (input language).

Note: that a key is able to have another layout is easy to determine by colour. For example, on my keyboard all the layout independent keys have black colour, whereas all the keys of the Russian layout have also red characters.

The statement of the problem clearly implies that it does not arise if you assign "independent" key. Such assignment does not depend on the layout and always works:

^!F11:: ; Define a hotkey Ctrl+Alt+F11

Here is a solution to the first problem: if a key is "dependent" on the layout and if you would like to make sure that your script will work in spite of the default input language on your system, there are two variants of the solution:

  1. to define hotkeys both for English key and for its Russian "inmate" on a keyboard, or…
  2. to define hotkeys with the help of a scan code. How to determine the scan code is explained in this article: List of Keys and Mouse/Joystick Buttons. Though it describes the keys that do not belong to the list of standard keys, nobody prevents us from handling standard keys the same way.

For the first variant, add the following line immediately above the first line (or beneath it, it does not matter):

^!я:: ; Define a Ctrl+Alt+z hotkey

For the second variant, instead of the first line include the following one:

^!sc02C:: ; Define a Ctrl+Alt+z hotkey

Defining a hotkey with the help of a scan code (sc) is, certainly, far more universal. That is why I recommend this very method to you.

Note: if you do not plan to change the default input language and do not have intention to use your scripts on a PC other than your own, you can certainly define hotkeys only in one layout (for English language it is English by default, for Russian - Russian). But are you so sure that you will never change the default input language, or that you will not run your script on another system with another default input language? Maybe it is better to take it into account at once.

Now let us continue further.

Hotstrings и Auto-replace

Scan code is a good thing. But it will not work with hotstrings and auto-replace. They should only be defined according to the first variant, i.e. bilingually.

Whether you believe it or not, but hotstrings and auto-replace depend on the default keyboard layout the same way as hotkeys.

But let us test out another thing. We add a text in Russian into the following example of an auto-replace hotstring from AutoHotkey Help and run it:

::btw::by the way. И дополнение на русском. ; Define an auto-replace hotstring for English keyboard layout
::иец::by the way. И дополнение на русском. ; Define an auto-replace hotstring for Russian keyboard layout

Then we start Notepad and type there "btw". (Actually, if your keyboard layout is Russian by default, you will type иец, rather than btw, since these Russian letters are neighbours of btw letters on the keyboard. But we will not pay attention to it.) Now press Enter.

Hooray! We got exactly what we wanted.

Now we change the keyboard layout in Notepad, pressing a language hotkey such as LeftAlt+Shift (or Ctrl+Shift), then again type "btw", and press Enter.

Or dear! We got rubbish.

If your default keyboard layout is Russian, you will see: "by the way/ B ljgjkytybt yf heccrjv/", i.e. all the Russian letters (including a full stop) will be typed in their English keyboard "inmates".

If your default keyboard layout is English, you will see: "ин еру цфню И дополнение на русскомю". i.e. all the English letters (including a full stop) will be typed in their Russian keyboard "inmates".

Let us consider what has happened.

At first, everything was OK, as we had default input language both in the script and in Notepad. But after we had changed the keyboard layout in Notepad, there emerged a mismatch between the keyboard layout of notepad and that of our script (since we had not changed the keyboard layout of the script).

Hence, we have the second problem..

If the keyboard layout of the script does not match the layout of the application where a text is sent, the text will most likely be sent incorrectly.

It is what is written about in the FAQ that was translated by Дед Мазай ("I use multiple languages or keyboard layouts on my system. Why do Send and Hotstrings sometimes send the wrong characters?")

The solution suggested in the FAQ (IMHO) is rather clumsy.

Here we must say that Дед Мазай, certainly is not to blame. It is known that "a prose translator is a slave; a poetry translator is a rival". And though AutoHotkey itself, undoubtedly, is the poetry, still Help for it is the cynical English-language prose :)

So the FAQ suggests that having detected a wrong response to a hotstring or the Send command, one should delete-cancel all that these commands had done (there is nothing about it in the FAQ, but that’s for sure). Then one should either to call (using hotkeys) the fragment of the script that will open its window, switch its layout, and close its window, or carry this out manually. Not only are both variants inconvenient, but also it might be late to cancel. As the phrase goes, "OK has pressed, what is the use of sobbing".

If you make up your mind to try out the variant that automatically changes the language layout for AutoHotKey scripts (described in the FAQ), you should take into account that your keys for changing the layout might be different. But the main thing that you should take into account is the problem number one that has been already considered in this article. That is, until you have switched the layout in the way described here, one of the hotstrings will not work (depending on the layout, naturally). But after you have changed the layout, the other will stop working, though the first one will be working. So, I suggest another…

Solution to the second problem:

For hotstrings to work properly in spite of the layout, it is necessary that the layout of the script was in sync with the layout of the application where this hotkey is to be implemented. This condition should be fulfilled before the text of the hotstring is sent.

Unfortunately, I do not know any easy way to do such synchronization, but still there is a way. First of all, it is necessary to exclude auto-replace hotstrings (where both a typed text and a replacement string are in the same line) and use hotstrings instead.

::btw::by the way. И дополнение на русском. ; Define auto-replace for the English layout
::иец::by the way. И дополнение на русском. ; Define auto-replace for the Russian layout

The lines above are auto-replace. The lines below are hotstrings.

::btw:: ; Define a hotstring for the English layout
::иец:: ; Define a hotstring for the Russian layout
Send, by the way. И дополнение на русском. ; Send a text of the hotstring
Return ; End of the hotstrings

So, we exclude auto-replace and use Send command in hotstrings. This command has errors similar to the ones of the auto-replace. What is more, it itself will not put things right. But immediately before Send we can put something that will improve. I have written a function for this purpose:

;*****************************************************************************
; AutoHotkey Version:   1.0.41.01
; Author:               Androgen Belkin
; Name of the script:   Keyboard layout correction of a script.ahk (v.1.2)
;*****************************************************************************
; Function of script layout correction for hotstrings and the Send and SendRaw commands.
;*****************************************************************************
PreSend()
{
    ; Remember the current mode of hidden windows detection
    DetectHiddenWindows_Before := A_DetectHiddenWindows
    ; Switch to "operating with hidden windows" option
    DetectHiddenWindows, On
    ; Remember the current mode of searching windows by title
    TitleMatchMode_Before := A_TitleMatchMode
    ; Switch to "search in any part of the window title" mode
    SetTitleMatchMode, 2
    ; Remember the current numerical format
    FormatInteger_Before := A_FormatInteger
    ; Switch to the hexadecimal format
    SetFormat, integer, H
    WinGet, Active_Window_ID, ID, A ; Get ID of the active window
    Active_Window_Thread := DllCall("GetWindowThreadProcessId"
        , "UInt", Active_Window_ID, "UInt", 0)
    ; For the active window, get ID of its thread through Win API
    Lang_In_Window := DllCall("GetKeyboardLayout", "UInt", Active_Window_Thread)
    ; Get the current layout for a found thread of the active window through Win API
    ; Get ID for the window of the script
    WinGet, Script_ID, ID, %A_ScriptName% ahk_class AutoHotkey
    SendMessage, 0x50,, %Lang_In_Window%,, ahk_id %Script_ID%
    ; Switch the script keyboard layout to the layout identical to the active window
    ; 0x50 - WM_INPUTLANGCHANGEREQUEST
    ; Restore the "hidden windows detection" mode
    DetectHiddenWindows, %DetectHiddenWindows_Before%
    ; Restore "search windows by title" mode
    SetTitleMatchMode, %TitleMatchMode_Before%
    ; Restore numerical format
    SetFormat, integer, %FormatInteger_Before%
} ; End of the function

Note: we could certainly design this function as a usual subroutine and then just insert it in our script (after all this function does not even return any values). But I prefer to use a function just as a function, because I have about 20 different functions, and it surely is not convenient to insert them into each script where I am going to use them. Besides, just imagine such a situation: you wrote a subroutine and inserted it into one of your scripts (then into another one and another one…). One day you caught a glitch in this subroutine. Well, you put it right and were happy for a while. But now the subroutine should be put right everywhere you had inserted it. What if there are about a hundred of such scripts? I have already faced this problem, which is why I prefer a function. You need only to attach the function with the help of #Include and that’s all. If you catch a glitch, you should only correct it in one script rather than in 50 and continue to enjoy himself.

So we save the above function as a separate script and then attach it to our test script, for example, this way (you certainly set your own path):

#Include D:\Program_2\AutoHotkey\HScripts\-Projects\Keyboard layout correction of a script.ahk

Now you can use the function. It is done this way:

::btw:: ; Define a hotstring for the English layout
::иец:: ; Define a hotstring for the Russian layout
PreSend() ; Call the function for the script keyboard layout correction
Send, by the way. И дополнение на русском. ; Send a replacement text
Return ; End of the hotstring

Now you can switch the layout in a target application (Notepad in our case) as you wish. Your script will send the text correctly. Hooray!

Note: the Send command sends a text a bit more slowly than auto-replace hotstrings. If you do not like it, you can remove delay with the help of the SetKeyDelay, -1 command, though usually there is no need in it.

Let’s now consider the Send command separately from hotstrings

This command can be used for two goals (modes):

  1. to send a text, as it was in the previous cases, and…
  2. to send command keys.

Those who use a single (English) keyboard layout, use this command in both cases identically. As for us, we should make the above mentioned division, if we do not want to step on the same rake that we already learned how to avoid earlier.

The first mode sends a text. Well, everything is clear here. I have already told that the Send command is "ill" with the same errors as auto-replace hotstrings and therefore is "treated" accordingly: you just need to call the above-considered function for layout correction before the command.

The second mode is even simpler. If we need to send command keys, then:

Send, ^{sc02E} ; Press CTRL+С

etc.

I have not mentioned the SendRaw command here, only because it does not send command keys (since it was not designed to do so), that’s why, when we want to send something with the help of this command, we use it as in the first mode of the Send command.

And to complete the picture, I would like to add, that there is another way to send a text with the help of Send command "without any fear of layouts". I do not find it convenient, but still it exists. I mean sending a text by means of ASCII codes. It is not possible to "press" command keys this way, but a text can be sent. It looks like this:

Send, {ASC 143}{ASC 224}{ASC 168}{ASC 162}{ASC 165}{ASC 226} {ASC 97}{ASC 98}{ASC 99} ; "Привет abc" will be typed

OK, we have cleared up this case, let’s now consider the ControlSend (ControlSendRaw) command

Bad news: I have failed to "defeat" the ControlSend command in order for it to work with the "dependent" keys. I do not want to load you down with different combinations of conditions when either Russian or English letters do not want to be typed. You can work it out by yourself if you wish.

As for good news, there are two pieces of it. The first one is that independent keys work properly as usual. The second is that in most cases, the ControlSend command can be replaced with the Send command, which we have already coped with. The convenience of the ControlSend command is that it enables to send texts (keys) to window controls without making the window possessing these controls active. But almost always you can without problem activate the window, set focus to the required control and send it a text using the Send command. For example:

WinActivate, ahk_class Notepad; Activate a window of the Notepad
ControlFocus, Edit1, ahk_class Notepad ; Set focus to the required control
PreSend() ; Call the function of the script layout correction
Send, by the way. И дополнение на русском. ; Send the text

And now we consider an easier command – ControlSetText

This command works perfectly in all conditions, for any layout, any symbols and with no problems at all.

It replaces ALL THE TEXT of a control with the text you specified. It can be used not only to change, for example, a caption on a button (I do not see any beneficial use here), but also to fill any controls (boxes) or to insert paths leading to something, for example, while automatizing some programs. I have attached to this command to such an extent that when I write a script, I always look for the occasion to use it :)

We have worked out how to deal with the AutoHotkey commands that pose problems for us, now here is additional information

Now we will consider a couple of examples when we need to change the keyboard layout of a window with the help of a script. Let’s switch the layout of an active window to the English layout:

PostMessage, 0x50, 0, 0x4090409,, A ; Switch the layout of an active window to the English layout

In the next example, we will switch the layout of a particular window (it might as well be Notepad) to the Russian layout. Well, supposing we would also like to call the File menu using a script. We, certainly, could send Alt + Ф to Notepad, but if the layout of Notepad is currently English, it will not work. At first, the layout of Notepad should be switched to the Russian one. Let’s do the following (the window of Notepad should be active):

PostMessage, 0x50, 0, 0x4190419,, ahk_class Notepad ; Switch the layout of Notepad to the Russian layout
Sleep, 100 ; a small pause in order for Notepad to have time for switching
PreSend() ; Call the function of the script layout correction
;(i.e. switch the keyboard layout of the script to the Russian layout as well)
Send, !ф ; Press down Alt + Ф (to call the File menu)

The above example is given only as an illustration. It is better to call items of various menus – "File", "Edit", etc. (in any language) – using the WinMenuSelectItem command. The reason why I tell about it here is that if a program in which you want to call menu items, have a multi-language interface, then to make sure that you’ll be able to call the required menu item after switching the language (not the layout but a program interface), you should use numbers of menu items instead of their titles.

WinMenuSelectItem, ahk_class Notepad,, Help, About the Program
; Call "About Program" menu in Notepad, using titles of the items
WinMenuSelectItem, ahk_class Notepad,, 5&, 3&
; Call "About Program" menu in Notepad, using numbers of the items

Though the "About Program" item appears second in the Menu, there is a menu separator, which is also considered a menu item. Therefore, "About Program" is the third menu item.

It should be noted, that the WinMenuSelectItem command does not work with all the windows, in which case you can try to use menu call the way we did it for Notepad two paragraphs above. If it does not help either, then you will have to use a mouse (a script mouse, certainly).

But what if we want the keyboard layout of some window to be switched automatically to the required language? For example, the default keyboard layout on my computer is Russian, therefore Total Commander is launched with the Russian layout. It suits me fine. But I often use a quick search in TC, and, naturally, the quick search window also opens with the Russian layout, which does not suit me (as I usually make a search by English letters). It is possible, certainly, to switch the layout "by hand", but there is AutoHotkey! We can "ask" it. At first, we create a timer that will launch automation of windows, and insert it somewhere into the auto-executing section of a script.

#Persistent ; The script will be persistent
;(if you have any hotkeys or hotstrings after the auto-execution section,
;this directive is unnecessary)
SetTimer, Auto_Window, 200 ; Move to the specified subroutine every 0.2 seconds
Return ; Finish the auto-executing part

Now the subroutine of automation of windows itself:

Auto_Window:
;a label to call the window automation timer (this subroutine can be placed
;at any place of the script. I like to put subroutines at the end of a script, but it is not compulsory)

; TOTAL COMMANDER: TO SWITCH THE KEYBOARD LAYOUT FOR QUICK SEARCH
IfWinActive, ahk_class TQUICKSEARCH ; if the quick search window in TC is active, then...
{
    if Win_QS = ; if the window has just become active, then...
    {
        SendMessage, 0x50,, 0x4090409,, ahk_class TQUICKSEARCH
        ; Switch the layout to English in the quick search window
        Win_QS = 1 ; Flag that the window is already active 
    }
}
Else ; if the window is not active, then...
    Win_QS = ; Flag that the window is NOT active

; TOTAL COMMANDER: CLOSING THE "DRIVE NOT FOUND" WINDOW
IfWinExist, ahk_class TDRIVEDLG ; if the "drive not found" window exists in Total Commander, then...
WinClose, ahk_class TDRIVEDLG ; Close the "drive not found" window

Return ; The end of the subroutine that calls the timer

The script checks if the Quick Search window became active just now or it is active for a long time (for a longer time, than the previous timer fired). This is necessary, because if I would like to make a search in this window in Russian, I will switch the layout manually, and the script will not prevent me from doing so. Naturally, you can do that with any window.

I have added another piece of automation as an example – closing the "Drive is not found" window in Total Commander. This window appears when you choose, for example, CD, while there is no CD’s in the CD-drive. Total says that it cannot find the drive and offers to choose another one. Why should I look at this window?

The next script is designed to rapidly switch the default input language. It can help us to carry out experiments, described at the beginning of this article.

;*****************************************************************************
; AutoHotkey Version:  1.0.41.01
; Author:               Androgen
; Script name:         Switching default input language.ahk (v.1.3)
;*****************************************************************************
; Check and switch, if necessary, the default input language (that is set when Windows starts).
;*****************************************************************************

; ========== USER SETTINGS ==========
Name_Lang_Serv_Win = Text Services and Input Languages
; How do you call this window (I’ve met other names)?
; ========== END OF THE USER SETTINGS ==========

#NoTrayIcon ; Do not display a script icon in the tray
RegRead, Load_Lang, HKEY_CURRENT_USER, Keyboard Layout\Preload, 1
; Read which the default layout is
if Load_Lang = 00000419 ; If the default layout is Russian, then...
{
        MsgBox, 1, Attention.
        , The current default input language is Russian.`n`nWould you like to have it changed into English?
        ; Form a message
    IfMsgBox, OK ; If "OK", then...
        Key_Action = {Up} ; This key will be sent to a control
        ; to change the layout into the English one (correct it if necessary)
    else ; If a confirmation button is not down, then...
        Exit ; End the script
}
if Load_Lang = 00000409 ; If the default layout is English, then...
{
        MsgBox, 1, Attention.
        , The current default input language is English.`n`nWould you like to have it changed into Russian?
        ; Form a message
    IfMsgBox, OK ; If "OK", then...
        Key_Action = {Down} ; This key will be sent to a control
        ; to change the layout into the Russian one (correct it if necessary)
    else ; If a confirmation button is not down, then...
        Exit ; End the script
}
if (Load_Lang <> 00000419 AND Load_Lang <> 00000409)
; If the default layout is neither Russian nor English, then...
{
        MsgBox, 0, Attention!, %A_Space%The current language is NEITHER ENGLISH NOR RUSSIAN.`n`n
        , The script is designed to work with these layouts.`n`n
        , Change script settings. ; Form a message
        Exit ; End the script
}

Run, RunDll32.exe shell32.dll`,Control_RunDLL input.dll
; Run the "Text Services and Input Languages" applet
WinWait, %Name_Lang_Serv_Win%,, 5
; Wait until the Text Services and Input Languages window appear, but no more than 5 seconds
If ErrorLevel <> 0 ; If the window did not appear, then...
{
    MsgBox, "%Name_Lang_Serv_Win%"`n`n did not appear.
    , Check USER SETTINGS in the script ; Form a message
        Exit ; End the script
}
; If the window appeared, then...
ControlSend, ComboBox1, %Key_Action%, %Name_Lang_Serv_Win% ; Choose a language
Sleep, 100 ; Make a pause to make sure that the script layout was switched properly
;~ pause ; To control the layout switching, uncomment this line,
; and make comment for #NoTrayIcon (at the beginning of the script)
metka0: ; A label for a repetitive click, if there will be a need in any
ControlClick, Button10 ; Click "OK"
if ErrorLevel <> 0 ; If a control was not ready (it happens), then...
    goto, metka0 ; Repeat a click
Exit ; End the script

Here I have to say you good-bye. I tried to be brief, so a lot of nuances were set aside. However, we have considered the main problems of AutoHotkey and the Russian language. I hope you will find useful at least something of what was described in this article. I also hope that someday the developers of AutoHotkey will take into account that not only those who have a single layout use their remarkable product. And then we will not have to fall back on all those tricks, we have to use now.

Good luck!

Перейти на главную страничку сайта (список статей, файлы для скачивания)

© 2007 http://www.script-coding.com При любом использовании материалов сайта обязательна ссылка на него как на источник информации, а также сохранение целостности и авторства материалов.