Applescript for changing the keyboard layout?

It seems that the above problem is related to having a Japanese keyboard selected. In other words, the line

“get name of every menu item, {}, 0” seems not to work when a Japanese keyboard is selected.

I have a very limited understanding of Applescript so I have no idea what would cause that.

Hello

I didn’t install eastern resources so I can’t make complete tests.

I just dicover a “surprising” behaviour.
When Hiragana is installed the test upon missing value fails.

Here is a modified code which seems to work well, even with Hiragana installed.


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
			delay 2
			tell menu 1
				set {lk, kl, i} to {{}, {}, 0}
				repeat
					set i to i + 1
					try
						set yy to name of menu item i
						if yy is missing value then exit repeat
						set end of kl to yy
					on error
						exit repeat
					end try
				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


I wait for your responce and will post the new script in the “Code Exchange” area if it works well.

Yvan KOENIG (from FRANCE vendredi 22 septembre 2006 08:49:11)

Excellent!

It now seems to work :slight_smile:

Yd’bto

Eg. yr yd’y C ecojrk.p.e ogplpcocbi x.d’kcrgpW

Oops, the keyboard was set on Dvorak :wink:

Thanks.

Due to this problem I discovered an odd behaviour.

If an eastern item is selected the returned values are not the same as the one returned when they aren’t.
When they aren’t. the name of the separator line is missing value.
When an eastern item is installed, the name is “”.

Here I post an alternate code checking for {missing value,“”}

At this time I am unable to choose between the two working versions but better two working codes that none :wink:


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
				-- get properties of every menu item
				set {lk, kl, i} to {get name of every menu item, {}, 0}
				repeat
					set i to i + 1
					if lk's item i is in {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 09:09:54)

Yes, I think the Eastern input is a different engine so to speak. For example, when you switch to Hiragana then the whole menu changes, so that you get the options needed for Japanese input, such as saving particular Kanji inputs etc, plus the preferences for your Hiragana/Katakana input etc.

Macg

Hello

I was unable to choose between the late two scripts . so I wrote a third one :wink:
I commented with the returned value to help you to understand what appears to be an oddity.
Of course you may remove these comments.


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
				-- get properties of every menu item
				(*
{position:{1245, 26}, maximum:missing value, name:"Français", size:{295, 21}, subrole:missing value, class:menu item, minimum value:missing value, enabled:true, selected:false, role:"AXMenuItem", help:missing value, title:"Français", value:missing value, entire contents:{}, description:"commande de menu", focused:missing value, orientation:missing value}, 
{position:{1245, 47}, maximum:missing value, name:"Américain", size:{295, 21}, subrole:missing value, class:menu item, minimum value:missing value, enabled:true, selected:false, role:"AXMenuItem", help:missing value, title:"Américain", value:missing value, entire contents:{}, description:"commande de menu", focused:missing value, orientation:missing value},
{position:{1245, 68}, maximum:missing value, name:"Dvorak", size:{295, 21}, subrole:missing value, class:menu item, minimum value:missing value, enabled:true, selected:false, role:"AXMenuItem", help:missing value, title:"Dvorak", value:missing value, entire contents:{}, description:"commande de menu", focused:missing value, orientation:missing value},
{position:{1245, 89}, maximum:missing value, name:"Romaji", size:{295, 21}, subrole:missing value, class:menu item, minimum value:missing value, enabled:true, selected:false, role:"AXMenuItem", help:missing value, title:"Romaji", value:missing value, entire contents:{}, description:"commande de menu", focused:missing value, orientation:missing value},
{position:{1245, 110}, maximum:missing value, name:"Hiragana", size:{295, 21}, subrole:missing value, class:menu item, minimum value:missing value, enabled:true, selected:false, role:"AXMenuItem", help:missing value, title:"Hiragana", value:missing value, entire contents:{}, description:"commande de menu", focused:missing value, orientation:missing value},
{position:{1245, 131}, maximum:missing value, name:missing value, size:{295, 12}, subrole:missing value, class:menu item, minimum value:missing value, enabled:false, selected:false, role:"AXMenuItem", help:missing value, title:"", value:missing value, entire contents:{}, description:"commande de menu", focused:missing value, orientation:missing value},
{position:{1245, 143}, maximum:missing value, name:"Afficher Palette de caractères", size:{295, 21}, subrole:missing value, class:menu item, minimum value:missing value, enabled:true, selected:false, role:"AXMenuItem", help:missing value, title:"Afficher Palette de caractères", value:missing value, entire contents:{}, description:"commande de menu", focused:missing value, orientation:missing value},
{position:{1245, 164}, maximum:missing value, name:"Afficher Palette pour le japonais", size:{295, 21}, subrole:missing value, class:menu item, minimum value:missing value, enabled:true, selected:false, role:"AXMenuItem", help:missing value, title:"Afficher Palette pour le japonais", value:missing value, entire contents:{}, description:"commande de menu", focused:missing value, orientation:missing value},
{position:{1245, 185}, maximum:missing value, name:"Afficher Visualiseur de clavier", size:{295, 21}, subrole:missing value, class:menu item, minimum value:missing value, enabled:true, selected:false, role:"AXMenuItem", help:missing value, title:"Afficher Visualiseur de clavier", value:missing value, entire contents:{}, description:"commande de menu", focused:missing value, orientation:missing value},
{position:{1245, 206}, maximum:missing value, name:missing value, size:{295, 12}, subrole:missing value, class:menu item, minimum value:missing value, enabled:false, selected:false, role:"AXMenuItem", help:missing value, title:"", value:missing value, entire contents:{}, description:"commande de menu", focused:missing value, orientation:missing value},
{position:{1245, 218}, maximum:missing value, name:"Afficher le nom de la source de saisie", size:{295, 19}, subrole:missing value, class:menu item, minimum value:missing value, enabled:true, selected:false, role:"AXMenuItem", help:missing value, title:"Afficher le nom de la source de saisie", value:missing value, entire contents:{}, description:"commande de menu", focused:missing value, orientation:missing value},
{position:{1245, 237}, maximum:missing value, name:missing value, size:{295, 12}, subrole:missing value, class:menu item, minimum value:missing value, enabled:false, selected:false, role:"AXMenuItem", help:missing value, title:"", value:missing value, entire contents:{}, description:"commande de menu", focused:missing value, orientation:missing value},
{position:{1245, 249}, maximum:missing value, name:"Ouvrir International.", size:{295, 19}, subrole:missing value, class:menu item, minimum value:missing value, enabled:true, selected:false, role:"AXMenuItem", help:missing value, title:"Ouvrir International.", value:missing value, entire contents:{}, description:"commande de menu", focused:missing value, orientation:missing value}}
				*)
				-- get name of every menu item
				(*
{"Français", "Américain", "Dvorak", "Romaji", "Hiragana", missing value, "Afficher Palette de caractères", "Afficher Palette pour le japonais", "Afficher Visualiseur de clavier", missing value, "Afficher le nom de la source de saisie", missing value, "Ouvrir International."}
				*)
				-- get title of every menu item
				(*
{"Français", "Américain", "Dvorak", "Romaji", "Hiragana", "", "Afficher Palette de caractères", "Afficher Palette pour le japonais", "Afficher Visualiseur de clavier", "", "Afficher le nom de la source de saisie", "", "Ouvrir International."}
				*)
				set {kl, i} to {{}, 0}
				repeat
					set i to i + 1
					set yy to title of menu item i
					(* 
when eastern layout(s) is(are) available, 
title returns "" for a line separator
while name returns an error ! 
*)
					if yy is in {missing value, ""} then exit repeat
					set end of kl to yy
				end repeat
				(* now we have a list of installed layouts *)
				
				if (count of kl) > 1 then
					repeat with nmi in kl
						if nmi contains "Dvorak" then
							(* here we met the "Dvorak xx" menu item *)
							click menu item nmi
							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 10:16:46)

This last one throws up an error

Do this:
Select “Hiragana” THEN Select “Katakana”. Then run the script.

tell application “System Events”
get value of attribute “AXDescription” of every menu bar item of menu bar 1 of process “SystemUIServer”
{missing value, “AppleScript menu extra”, “iChat menu extra”, “text input menu extra”, “volume menu extra”, “clock menu extra”, “user menu extra”, “bluetooth menu extra”}
click menu bar item 4 of menu bar 1 of process “SystemUIServer”
menu bar item 4 of menu bar 1 of application process “SystemUIServer”
get title of menu item 1 of menu 1 of menu bar item 4 of menu bar 1 of process “SystemUIServer”
“System Events got an error: NSReceiverEvaluationScriptError: 4”

Hello

As I don’t get this error after doing what you wrote, I think that it would be better to continue in a private exchange then return here when the problem would be solved.

Yvan KOENIG

Hi.

Obviously I’ve come into this thread a little late. :slight_smile:

I just wanted to say I don’t get this discussion at all. On my machine, filtering for the ‘first menu bar item of menu bar 1 whose value of attribute “AXDescription” is “text input menu extra”’ returns the keyboard menu “ if it exists “ regardless of its index number. There must be some other reason why it’s not working for Yvan, Jacques, and kai.

This makes a perfectly good “layout rotator” on my machine (G5, OS 10.4.8).

tell application "System Events"
	tell application process "SystemUIServer"
		tell (get first menu bar item of menu bar 1 whose value of attribute "AXDescription" is "text input menu extra")
			set currentKeyboard to its value
			perform action "AXPress"
			(*repeat until (menu 1 exists)
				delay 0.2
			end repeat*)
			set menuItems to name of menu items of menu 1
			repeat with i from 1 to (count menuItems)
				if (item i of menuItems is currentKeyboard) then
					set n to i + 1
					if (item n of menuItems is missing value) then set n to 1
					perform action "AXPress" of (menu item n of menu 1)
					exit repeat
				end if
			end repeat
		end tell
	end tell
end tell

Hello Nigel Garvey

I tried your script and, on my system (French one), it triggers the Sound Menu ;-((

Yvan KOENIG (from FRANCE dimanche 26 novembre 2006 07:00:39)

Hi, Yvan.

I guessed that would probably be the case, given your previous experience. I’m beginning to believe that GUI Scripting is inherently unreliable, as several people have reported that GUI scripts of mine that should work “ and do work on my machine “ don’t work for them. I’ve no reason to believe that there’s anything wrong with my system or with theirs.

Interestingly, although ‘first menu bar item of menu bar 1 whose value of attribute “AXDescription” is “text input menu extra”’ works perfectly well for me in the “keyboard layout” script, the same construction doesn’t work for me in some other scripts. For instance, one way to identify the “Copy” item in an “Edit” menu is to find the menu item whose attribute “AXMenuItemCmdChar” has a value of “C”. (This indicates the character used for the menu item’s Command-keystroke equivalent. For completeness, we should also check that its attribute “AXMenuItemCmdModifiers” has a value of 0, but let’s ignore that for now.) Take this script:

tell application "System Events"
	tell application process "TextEdit"
		set frontmost to true
		name of first menu item of menu 1 of menu bar item 4 of menu bar 1 whose value of attribute "AXMenuItemCmdChar" is "C"
	end tell
end tell

Since the filter ‘. whose value of attribute “AXDescription” is “text input menu extra”’ works for me in the keyboard layout script, you’d expect ‘. whose value of attribute “AXMenuItemCmdChar” is “C”’ to work for me too. But in fact it doesn’t. The above script returns “Select All” instead of “Copy”. This accords with kai’s explanation, since “Select All” is the tenth menu item in the “Edit” menu and “AXMenuItemCmdChar” is the tenth attribute in each menu item’s list of attributes. However, if I replace the attribute’s name with its index number, the script returns the result I want:

tell application "System Events"
	tell application process "TextEdit"
		set frontmost to true
		name of first menu item of menu 1 of menu bar item 4 of menu bar 1 whose value of attribute 10 is "C"
	end tell
end tell

Since I don’t quite trust attributes to have particular positions in attribute lists, I currently prefer this:

tell application "System Events"
	tell application process "TextEdit"
		set frontmost to true
		name of first menu item of menu 1 of menu bar item 4 of menu bar 1 whose value of attributes contains "C" and value of attributes contains 0
	end tell
end tell

Feeding these ideas back into the “keyboard layout rotation” script, do either of these work for you? (Only their third lines differ.)

tell application "System Events"
	tell application process "SystemUIServer"
		tell (first menu bar item of menu bar 1 whose value of attribute 4 is "text input menu extra")
			set currentKeyboard to its value
			perform action "AXPress"
			(*repeat until (menu 1 exists)
				delay 0.2
			end repeat*)
			set menuItems to name of menu items of menu 1
			repeat with i from 1 to (count menuItems)
				if (item i of menuItems is currentKeyboard) then
					set n to i + 1
					if (item n of menuItems is missing value) then set n to 1
					perform action "AXPress" of (menu item n of menu 1)
					exit repeat
				end if
			end repeat
		end tell
	end tell
end tell
tell application "System Events"
	tell application process "SystemUIServer"
		tell (first menu bar item of menu bar 1 whose value of attributes contains "text input menu extra")
			set currentKeyboard to its value
			perform action "AXPress"
			(*repeat until (menu 1 exists)
				delay 0.2
			end repeat*)
			set menuItems to name of menu items of menu 1
			repeat with i from 1 to (count menuItems)
				if (item i of menuItems is currentKeyboard) then
					set n to i + 1
					if (item n of menuItems is missing value) then set n to 1
					perform action "AXPress" of (menu item n of menu 1)
					exit repeat
				end if
			end repeat
		end tell
	end tell
end tell

Just FYI, all three of your “discovery” scripts work for me; the first revealing that my current language is set to “US” and the last two revealing that I have choices between “US” and “Canadian French CSA”, and my current setting is “US”.

Adam

Hello

I tested the two Nigel’s proposals and they failed.

I have installed:

French
American
Dvorak
Romaji
Hiragana
Kotakana

With both scripts, it seems to switch correctly but, when Kotakana is reached once, it returns to Dvoak, not to French which is at the top of the menu and so, only the lst four layouts are “used”.

I remember that I got this behaviour with some codes but I can’t say which at this time because my “script in progress” folder is gone with my HD crash ;-((

I discovered this feature when I was asked about this script by someone using eastern layouts.

You may test as you want, I am ready to switch off now.

I will be back tomorrow :wink:

Yvan KOENIG (from FRANCE dimanche 26 novembre 2006 21:45:50)

That’s a nice way to talk about a couple of scripts that actually succeeded in finding the right menu for you. :wink:

I get the same result as you, though, when the Katakana keyboard is active and is the last one in the menu: the second keyboard in the menu is then activated instead of the first, even though the first is correctly identified and clicked. This appears to be due to some fault in the system and sometimes happens when I click manually too.

What’s working for me at the moment is to give the menu itself a little “flick” before going back to the first item in it:

tell application "System Events"
	tell application process "SystemUIServer"
		tell (first menu bar item of menu bar 1 whose value of attribute 4 is "text input menu extra") -- Try this.
			-- tell (first menu bar item of menu bar 1 whose value of attributes contains "text input menu extra") -- . or this.
			set currentKeyboard to its value
			perform action "AXPress"
			(* repeat until (menu 1 exists)
				delay 0.2
			end repeat *)
			set menuItems to name of menu items of menu 1
			repeat with i from 1 to (count menuItems)
				if (item i of menuItems is currentKeyboard) then
					set n to i + 1
					if (item n of menuItems is missing value) then
						set n to 1
						-- If going back to the beginning, close and reopen the menu first.
						perform action "AXCancel"
						perform action "AXPress"
					end if
					perform action "AXPress" of menu item n of menu 1
					exit repeat
				end if
			end repeat
		end tell
	end tell
end tell

Hello

The late N.G. 's script works flawlessly on my machine.

I apologize if my late message was not correctly worded, remember that English is not my native language.

Yvan KOENIG (from FRANCE lundi 27 novembre 2006 07:20:39)

Hi everybody,

amazing thread :slight_smile:

I tried to figure out a solution without UI scripting, but nothing worked.
Here my thoughts:

Reading the enabled keyboard layouts is easy, the information is stored in ~/Library/Preferences/ByHost/com.apple.HIToolbox.[myMACaddress}.plist

set myMACaddress to words 2 thru 7 of (do shell script "ifconfig en0 ether | grep -i ether" as string) as string
set pListPath to ((path to preferences folder) as string) & "ByHost:com.apple.HIToolbox." & myMACaddress & ".plist"

tell application "System Events"
	set pList to property list file pListPath
	set a to property list items of property list item "AppleEnabledInputSources" of contents of pList
	set theList to {}
	set dialogText to "enabled keyboard layouts:" & return & return
	repeat with i in a
		set v to value of i
		if |InputSourceKind| of v is "Keyboard Layout" then
			set end of theList to v
			set dialogText to dialogText & |KeyboardLayout Name| of v & return
		end if
	end repeat
end tell

display dialog dialogText

Writing is possible with defaults write, but nothing happens.
The following keys change when you change the keyboard layout manually from the menu bar:

in ~/Library/Preferences/com.apple.HIToolbox.plist
AppleItlbKeys
smRoman (0 = U.S.)

in ~/Library/Preferences/ByHost/com.apple.HIToolbox.[myMACaddress}.plist
AppleCurrentAsciiInputSource
KeyboardLayout ID (0 = U.S.)
KeyboardLayout Name

AppleCurrentInputSource
KeyboardLayout ID (0 = U.S.)
KeyboardLayout Name

AppleItlbKeys
smRoman (0 = U.S.)

AppleSelectedInputSources
1
KeyboardLayout ID (0 = U.S.)
KeyboardLayout Name

and ApplePreviousInputSource changes to what it’s name says.

The defaults command provides writing also into dictionaries and arrays of plist files.
I tried this (my default layout is german with ID 3):

set defaultsPath to ((path to preferences folder) as string) & "com.apple.HIToolbox"
set defwrite to "defaults write " & POSIX path of defaultsPath & " AppleItlbKeys -dict smCyrillic -int 19456 smRoman -int 0 smUnicodeScript -int -1"
do shell script defwrite

Even changing all values listed above with defaults write lines doesn’t change the keyboard layout :frowning:
Any Ideas?

Hello

after a default write it seems that it must be “validated”.

Here is a sample code:

do shell script "defaults write -g AppleICUTimeFormatStrings -dict-add 2 \"EEEE d MMMM YYYY HH':'mm':'ss\" ; killall SystemUIServer"

Yvan KOENIG (from FRANCE lundi 27 novembre 2006 20:49:22)

Been following this thread.

I have Spell Catcher installed, with prefs and settings accessible through the “text input menu extra” . When you click on the menu icon, a drop down list of options appears. I have tried the last posted routine in this thread, but it cannot identify anything in the Spell Catcher drop down lists beyond “Spell Catcher” (the first clickable option) - this is the only itme in the list kl.

I want to select languages (which are further down the Spell Catcher list) and wondering if anyone can help. Someone who uses Spell Catcher?

Cheers

Just a short follow up before I turn in.

I have this so far:

tell application "System Events"
	tell application process "SystemUIServer"
		tell menu bar item 7 of menu bar 1
			click
			delay 1
			tell menu 1
				click menu item 29
			end tell
		end tell		
	end tell
end tell

Which clicks the right item, EXCEPT, that I need to then select a sub item of the correctly clicked item (ie the particular language I am seeking to select). What needs to come after the click menu item 29 to access the sub menu? Any suggestions…

Cheers

In Xcode, I tried the following to get the current layout, but it didn’t return any result. Why?

on launched theObject
	set a to call method "KLGetCurrentKeyboardLayout"
	log a
end launched