Is it possible to switch keyboard layouts programmatically?

In a program I’m writing, I’d like to switch keyboard layouts for the user programmatically. Is there some way to do that? For instance, I’d like to be able to call an executable like this:

keyman.exe "qaa-Latn"

My keyboard layouts have associated shortcut keys. I’ve tried to simulate those keypresses with an AutoHotKey script, but so far without success.

Several tools, like FLEx and Paratext have features to switch Languages and Keyboards automatically, so, yes, it is possible. These tools can handle both Keyman and Microsoft Keyboards. I see you were trying AHK, but have you looked in the documentation for the programming language of the program you are writing?

I’ve only done this for Android, but you can embed Keyman fully in your app, and have even more control over Keyman,
https://help.keyman.com/developer/engine/desktop/

Hi, thanks for your quick reply. I’m using C++ with Qt.

Thank you for the reference to the Visual Basic API. I’ve gotten as far as this:

dim Keyman, language
Set Keyman = CreateObject("keymanapi.Keyman")
Set language = Keyman.Languages.Items(3) ' for example
Keyman.Control.ActiveLanguage = language
Keyman.Apply()

'' With this I can see that it works within the script
' Dim sInput
' sInput = InputBox("Enter your name")
' MsgBox "You entered:" & sInput

So that changes the input language for the current thread (in this case, just for the script itself), but I can’t see that it’s possible to change it for another thread with this interface.

I notice from the docs that IKeymanControl::ActiveLanguage is just a wrapper for the Windows ITfInputProcessorProfileMgr::ActivateProfile function. That makes me think that perhaps it’ll be just as easy to use the Windows API as to use Keyman. If anyone has a simpler approach I would welcome it. :slight_smile:

Okay, this isn’t exactly a Keyman solution, but Keyman ended up being helpful, so I’ll record it here for posterity.

Here is an AutoHotKey script that changes the current input language:

PostMessage, 0x50, 0, 0x2000,, A

In the line above 0x2000 is what identifies the input language. How might one find that identifier? Well, perhaps by consulting this list. Or you can run this Visual Basic script to get the identifiers for your installed keyboards.

dim Keyman
Set Keyman = CreateObject("keymanapi.Keyman")
For Each lang In Keyman.Languages
    Wscript.Echo lang.LayoutName & "    0x" &  Hex(lang.LangID)
Next

(Call the file keyman-list.vbs and run it from the command line as cscript keyman-list.vbs).

This is handy because I’m not sure how else I would have learned, e.g., that SIL’s IPA keyboard’s identifier is 0x2000.

It’s a mild irritant not to be able to switch to different layouts for the same language, but that’s another story.

Good work with your research.

There are some tricky things when it comes to switching languages on Windows. Much of this is due to the legacy of multiple input method management systems (ActivateKeyboardLayout, IMM, TSF, and the newish PowerShell APIs for managing layouts (e.g. Set-WinUserLanguageList) with no clear winner in the Windows API. Particularly frustrating is the reality that there is no Win32-accessible API that reliably enumerates input method BCP 47 tag associations.

  1. Language IDs are not necessarily constant – 0x2000 is allocated to the first keyboard installed that does not have a Windows language identifer pre-allocated. So if you move your app between systems, you may find your results vary.

  2. As you noted, it is possible to use COM to switch languages, and between input methods within the one language, with the ITfInputProcessorProfileMgr::ActivateProfile method, along with ITfInputProcessorProfiles::ChangeCurrentLanguage. These take somewhat more development but are the way I’d recommend switching languages.

  3. Keyman’s IKeymanLanguage interface, accessible through keymanapi.Keyman.Languages as you already found, also lists the ProfileGUID that you need to pass to the ITfInputProcessorProfileMgr::ActivateProfile method. This also helpfully gives you the BCP 47 tag for each installed input method.

Using the WIndows API to switch languages is recommended because it then works with any input method that is registered, rather than being limited to Keyman’s keyboards.

You cannot change the input method for a specific thread other than the current thread using the Windows APIs either – unless you use the big hammer flags TF_IPPMF_FORPROCESS or TF_IPPMF_FORSESSION. These are a little fraught with Windows 10 as Windows already by default switches languages for all threads in the session (so if a user has changed that preference, they probably don’t want an app changing languages for other threads in the session).

Of course, PostMessage 0x50 (WM_INPUTLANGCHANGEREQUEST) allows you to change input methods for another thread, but it’s a little naughty to do so.