Applescript for changing the keyboard layout?

My first message :slight_smile:

Is there a way to applescript to select a keyboard layout such as “U.S.” , “Dvorak”, “Hiragana”, “Katakana” and so forth. It drives me mad to have to rotate through them

In OS9 it was just:

set keyboard layout to "U.S."

Many thanks.

That will require GUI scripting the “Language” pane of the “International” window of the Sytem Preferences, a process I have never succeeded in doing. Perhaps others will know more. I answer here at all, just to let you know that it’s tricky.

Thanks for the info. Off topic, but do you know of another approach using the terminal. It doesn’t really matter how I do it!

Thanks.

A quick way to enable assistive services is like this:

tell application "System Events" to set (UI elements enabled) to true

You’ll be asked for a password.

I don’t think the plist file is read again until you log out and in. The menu works immediately.

Hello

I tried Jacques’s scriptlet and it trigger the Displays menu.

To show it at work, I introduced a small change:

tell application “System Events” to tell process “SystemUIServer”
tell (get first menu bar item of menu bar 1 whose value of attribute “AXDescription” is “text input menu extra”)
click
delay 0.1
(click menu item “U.S.” of menu 1)
get name of every menu item of menu 1
end tell
end tell

The report is:
tell application “System Events”
get menu bar item 1 of menu bar 1 of process “SystemUIServer” whose value of attribute “AXDescription” = “text input menu extra”
menu bar item 4 of menu bar 1 of application process “SystemUIServer”
click menu bar item 4 of menu bar 1 of application process “SystemUIServer”
menu bar item 4 of menu bar 1 of application process “SystemUIServer”
get name of every menu item of menu 1 of menu bar item 4 of menu bar 1 of application process “SystemUIServer”
{“Détecter les moniteurs”, missing value, “DPro930SB”, “800 x 600, 85 Hz, Millions”, “1280 x 1024, 85 Hz, Millions”, “1600 x 1200, 85 Hz, Millions”, missing value, “Nombre d’éléments récents”, “Préférences Moniteurs…”}
end tell

Which demonstrates that we are really not trigerring the keyboards menu item :wink:

I get a workaround.

tell application “System Events” to tell process “SystemUIServer”
set _ to get value of attribute “AXDescription” of every menu bar item of menu bar 1
set {k, j} to {count of _, 0}
repeat with i from 1 to k
if _'s item i is “text input menu extra” then
set j to i
exit repeat
end if
end repeat
if j > 0 then
tell menu bar item i of menu bar 1
click
delay 0.1
(click menu item “U.S.” of menu 1)
get name of every menu item of menu 1
end tell
end if
end tell

the report is:

tell application “System Events”
get value of attribute “AXDescription” of every menu bar item of menu bar 1 of process “SystemUIServer”
{“Classic menu extra”, missing value, “volume menu extra”, “displays menu extra”, “AppleScript menu extra”, “text input menu extra”, “clock menu extra”}
click menu bar item 6 of menu bar 1 of process “SystemUIServer”
menu bar item 6 of menu bar 1 of application process “SystemUIServer”
end tell

and the correct menu is triggered.

Yvan KOENIG (from FRANCE mercredi 20 septembre 2006 10:14:44)

This one worked just fine for me. Thanks so much. However, I am having one problem:

One of my keyboards is Dvorak - Qwerty - [clover-leaf symbol]

If I copy paste the referenced keyboard the clover-leaf symbol (i.e. command) appears in the script editor but disappears when I try to run it. Is there a way to reference that correctly. I tried an escape as a guess but without luck.

Hello

I apologize MacGruber but, whith this config:

{“Classic menu extra”, missing value, “volume menu extra”, “displays menu extra”, “AppleScript menu extra”, “text input menu extra”, “clock menu extra”}
(the missing value is the Stuffit MagicMenu), the instruction using whose DOESN’T works.

I don’t understand why but it DOESN’T.
Happily the described workaround works.

tell application “System Events” to tell process “SystemUIServer”
set _ to get value of attribute “AXDescription” of every menu bar item of menu bar 1
set {k, j} to {count of _, 0}
repeat with i from 1 to k
if _'s item i is “text input menu extra” then
set j to i
exit repeat
end if
end repeat
if j > 0 then
tell menu bar item i of menu bar 1
click
delay 0.1
get name of every menu item of menu 1
(*
{“Français”,
“Américain”,
“Dvorak”,
missing value,
“Afficher Palette de caractères”,
“Afficher Visualiseur de clavier”,
missing value,
“Afficher le nom de la source de saisie”,
missing value,
“Ouvrir International.”}
*)
click menu item “Dvorak” of menu 1
end tell
end if
end tell

Yvan KOENIG (from FRANCE mercredi 20 septembre 2006 15:16:05)

Yvan’s script works beautifully for me. I have only two languages: “U.S.” and “Canadian French - CSA” on my International menu. How can I find out which is checked in the menu? (the items don’t have a “checked” property) Then it would be easy to modify Yvan’s script to toggle between them with a simple if statement. I don’t want to rely on a property storing the last chosen which could be incorrect if the menu had been used directly.

If you only have two anyway then you should be able to use:

System Prefs > International > Input Menu and choose Input menu shortcuts

For example, if you set Select next input source in menu to F9 or something that should work.

Hello

We may grab the active one with this code:

tell application “System Events” to tell process “SystemUIServer”
set _ to get value of attribute “AXDescription” of every menu bar item of menu bar 1
set {k, j} to {count of _, 0}
repeat with i from 1 to k
if _'s item i is “text input menu extra” then
set j to i
exit repeat
end if
end repeat
if j > 0 then
tell menu bar item i of menu bar 1
click
delay 0.1
set lk to get name of every menu item of menu 1
set {kl, i} to {{}, 0}
repeat
set i to i + 1
if lk’s item i is missing value then exit repeat
set end of kl to lk’s item i
end repeat
repeat with i from 1 to count of kl
if (value of attribute “AXMenuItemMarkChar” of menu item (kl’s item i) of menu 1 is not “”) then

				my affiche(kl's item i)
				exit repeat
			end if
			
		end repeat
		(*

{“Français”,
“Américain”,
“Dvorak”,
missing value,
“Afficher Palette de caractères”,
“Afficher Visualiseur de clavier”,
missing value,
“Afficher le nom de la source de saisie”,
missing value,
“Ouvrir International.”}
*)
click menu item “Français” of menu 1
end tell
end if
end tell

on affiche(m)
tell application (path to frontmost application as string) to display dialog “” & m & " is the active layout !"
end affiche

Yvan KOENIG (from FRANCE mercredi 20 septembre 2006 17:03:08)

Hello

This late one does the full trick.

If there are two installed layouts it swaps between both of them.
If they are more, it “rotates” from one to the next one.


tell application "System Events" to tell process "SystemUIServer"
	set _ to get value of attribute "AXDescription" of every menu bar item of menu bar 1
	set {k, j} to {count of _, 0}
	repeat with i from 1 to k
		if _'s item i is "text input menu extra" then
			set j to i
			exit repeat
		end if
	end repeat
	
	if j > 0 then
		tell menu bar item j of menu bar 1
			click
			tell menu 1
				set {lk, kl, i} to {get name of every menu item, {}, 0}
				repeat
					set i to i + 1
					if lk's item i is missing value then exit repeat
					set end of kl to lk's item i
				end repeat
				(* now we have a list of installed layouts *)
				
				set knt to count of kl
				if knt > 1 then
					repeat with i from 1 to knt
						if (value of attribute "AXMenuItemMarkChar" of menu item (kl's item i) is not "") then
							if i = knt then
								set i to 1 (* back to top of list *)
							else
								set i to i + 1 (* switch to next layout *)
							end if
							exit repeat
						end if
					end repeat
					click menu item (kl's item i)
				end if --  knt > 1
			end tell -- to menu 1
		end tell -- to menu bar item.
	end if --  j > 0
end tell -- to process and System Events

Yvan KOENIG (from FRANCE mercredi 20 septembre 2006 17:44:43)

Thanks a lot Yvan. I just estimated how long I’d need to sit in front of my computer to work out the above, and I think I’ve come up with a good estimate: forever. :slight_smile:

Impressionnant! :lol:

Digne, en fait, de la signalisation dans l’échange de code.

[Worthy, in fact, of posting in Code Exchange]

Thanks to both of you.

I just hope that someone (perhaps kai) would be able to explain why the instruction using whose give a wrong result.

If it appears that it is a bug, it would be useful to report it to Apple guys.

Yvan KOENIG

Here’s a simple toggle for when there are just a couple of keyboard layouts:


to |toggle input menu| between l
	tell application "System Events" to tell menu bar 1 of process "SystemUIServer" to repeat with i in l
		tell (first menu bar item whose value is i) to if exists then return {click, click (menu 1's first menu item whose name is not i)}
	end repeat
end |toggle input menu|

|toggle input menu| between {"U.S.", "Canadian French - CSA"} (* specify the input menu pair required *)


This gives me a slight sense of déjà vu, Yvan… :wink: (items listed from most recent to earliest.)

The problem appears to be a basic error in SystemUIServer’s filter mechanism for attributes, which results in confusion between the objects to be filtered and the filter itself. So, rather than determining the index number of the appropriate menu bar item, the position of the attribute (within the attribute list) is applied to the menu bar items.

For example, attribute “AXDescription” normally happens to be item 4 in the attributes returned for SystemUIServer’s menu bar items:

Filtering for this attribute, as attempted earlier, actually results in menu bar item 4 being returned ” regardless of the value of its attribute “AXDescription”.

So this behaviour depends not so much on which menu bar items are present, but more on which attribute is being used as a filter ” and which menu bar item occupies the corresponding position in the menu bar. (With no corresponding menu bar item, a “NSReceiverEvaluationScriptError: 4” error is likely to occur.)

To demonstrate:

tell application "System Events" to tell menu bar 1 of process "SystemUIServer"
	set l to name of attributes of menu bar item 1
	set c to count menu bar items
end tell
tell l
	repeat with i from 1 to c
		set item i to (i as Unicode text) & tab & item i
	end repeat
	repeat with i from i + 1 to count
		set item i to (i as Unicode text) & "*" & tab & item i
	end repeat
end tell
tell (choose from list l with prompt "Choose an attribute to test
(items marked * will error):")
	if it is false then return
	set {i, a} to beginning's words
end tell
try
	tell application "System Events" to {first menu bar item of menu bar 1 of process "SystemUIServer" whose name of attribute a is a}'s e
on error r number n
	if n is -1728 then
		set text item delimiters to "{"
		set r to r's text from text item 2 to end
		set text item delimiters to "}"
		set r to r's text from beginning to text item -2
		set text item delimiters to {""}
		set e to "Filtering on this attribute [index: " & i & "] results in menu bar item " & i & " being returned. (If the required menu bar item happened to be at position " & i & " in the menu bar, then the correct result would be returned ” but for the wrong reason.)"
	else if n is 1 then
		set e to "The chosen attribute [" & i & "] is greater than the total number of menu bar items currently available [" & c & "]. In other words, no menu bar item " & i & " exists ” so an error occurs."
	else
		set e to "n/a"
	end if
end try
display alert "Chosen Attribute: \"" & a & "\"" message "Attribute index: " & i & return & "Total menu bar items: " & c & return & return & "Result:" & return & r & return & return & "Explanation:" & return & e

Does anyone have any idea how I type my Dvorak - Qwerty layout into the the script? The full name is

“Dvorak - Qwerty - [command-key clover symbol]”

I have no idea have to put the [command-key clover symbol] in there.

Thanks.

Hello

Read the given script.
There is no need to know the menu name as it is not used to grab it.

The menu itself is identified thru its attribute “AXDescription” whose value is: “text input menu extra”.
After that we must grab the name of the menu items.
When done, to select the Dvorak one, we just need to search in the menu item list one which contains “Dvorak”.

If the active layout is Dvorak, getting the properties of the Layout menu will return:

{position:{1244, 0}, maximum:missing value, name:missing value, size:{30, 22}, subrole:“AXMenuExtra”, class:menu bar item, minimum value:missing value, enabled:true, selected:false, role:“AXMenuBarItem”, help:missing value, title:missing value, value:“Dvorak”, entire contents:{}, description:“menu extra”, focused:missing value, orientation:missing value}

But, as explained in detail by kai (thank you kai) we may not use this value in a search.

Here is a variation on the script which activates the Dvorak layout:

tell application "System Events" to tell process "SystemUIServer"
	set _ to get value of attribute "AXDescription" of every menu bar item of menu bar 1
	set {k, j} to {count of _, 0}
	repeat with i from 1 to k
		if _'s item i is "text input menu extra" then
			set j to i
			exit repeat
		end if
	end repeat
	
	if j > 0 then
		--	get properties of menu bar item j of menu bar 1
		
		tell menu bar item j of menu bar 1
			click
			tell menu 1
				set {lk, kl, i} to {get name of every menu item, {}, 0}
				repeat
					set i to i + 1
					if lk's item i is missing value then exit repeat
					set end of kl to lk's item i
				end repeat
				(* now we have a list of installed layouts *)
				
				set knt to count of kl
				if knt > 1 then
					repeat with i from 1 to knt
						if kl's item i contains "Dvorak" then
							(* here we met the "Dvorak" menu item *)
							click menu item (kl's item i)
							exit repeat
						end if
					end repeat
				end if --  knt > 1
			end tell -- to menu 1
		end tell -- to menu bar item.
	end if --  j > 0
end tell -- to process and System Events

Yvan KOENIG (from FRANCE vendredi 22 septembre 2006 07:26:36)

Sorry, Yvan. I’m not talking about the Dvorak menu. I’m talking about the special “Dvorak - Qwerty - [apple-command]” menu. The name of that menu is not “Dvorak”, but
“Dvorak - Qwerty - [apple-command]”

Ah, I get it.

Sorry, Yvan! “contains Dvorak” . That’s great. It all works perfectly now!

Thanks.

I’m receiving an error sometimes:

System Events got an error: NSReceiverEvaluationScriptError: 4

and this is selected:

set {lk, kl, i} to {get name of every menu item, {}, 0}

The weird thing is that it works sometimes, but not other times. I can see no pattern. I manually select Hiragana, run the script it works, I try again and I get the error.


tell application "System Events" to tell process "SystemUIServer"
	set _ to get value of attribute "AXDescription" of every menu bar item of menu bar 1
	set {k, j} to {count of _, 0}
	repeat with i from 1 to k
		if _'s item i is "text input menu extra" then
			set j to i
			exit repeat
		end if
	end repeat
	
	if j > 0 then
		--    get properties of menu bar item j of menu bar 1
		
		tell menu bar item j of menu bar 1
			click
			tell menu 1
				set {lk, kl, i} to {get name of every menu item, {}, 0}
				repeat
					set i to i + 1
					if lk's item i is missing value then exit repeat
					set end of kl to lk's item i
				end repeat
				(* now we have a list of installed layouts *)
				
				set knt to count of kl
				if knt > 1 then
					repeat with i from 1 to knt
						if kl's item i contains "Dvorak" then
							(* here we met the "Dvorak" menu item *)
							click menu item (kl's item i)
							exit repeat
						end if
					end repeat
				end if -- knt > 1
			end tell -- to menu 1
		end tell -- to menu bar item.
	end if -- j > 0
end tell -- to process and System Events