Applescript for changing the keyboard layout?

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

I tried kiwilegal’s script.
I got this report:

tell application “System Events”
click menu bar item 7 of menu bar 1 of application process “SystemUIServer”
“Erreur dans System Events : NSReceiverEvaluationScriptError: 4”

So I’m asking if as is the script is running for him.

Yvan KOENIG (from FRANCE mercredi 30 mai 2007 14:17:23)

Yvan

I have developed a script to switch languages in Entourage and Spell Catcher. I invoke it using Quicksilver as required (separate script for each preferred language). Script is as follows (may need adjustments to the number of key code 125s used for any particular language - I have not worked out how to directly select a language from the sub menu of the Spell Catcher menu bar drop down menu). I also have a version for Word 2004. If you would like that, just post a reply.

Bien à vous


try
	-- Routine to switch language in Entourage
	tell application "System Events"
		tell application process "Microsoft Entourage"
			click menu item "English (UK)" of menu 1 of menu item "Change Dictionary" of menu 1 of menu bar item "Tools" of menu bar 1
		end tell
	end tell
	-- Following two lines needed to switch in and out of Entourage before changinf language in Spell Catcher
	tell application "System Events" to activate
	tell application "Microsoft Entourage" to activate
	
	
	-- Routine to switch language in Spell Catcher
	
	set mnu to "text input menu extra"
	set mnuItem to "Set Language"
	
	tell application "System Events" to tell process "SystemUIServer"
		set trk to get value of attribute "AXDescription" of every menu bar item of menu bar 1
		set {k, j} to {count of trk, 0}
		repeat with i from 1 to k
			if trk's item i is mnu as text then
				set j to i
				exit repeat
			end if
		end repeat
	end tell
	
	if j > 0 then
		try
			tell application "System Events"
				tell application process "SystemUIServer"
					tell menu bar item j of menu bar 1
						click
						delay 2
						tell menu 1
							click menu item mnuItem
							delay 0.2
							-- key down to set British English
							key code 125
							delay 0.5
							key code 125
							delay 0.5
							key code 36
						end tell
					end tell
				end tell
			end tell
		on error
			tell application "System Events" to key code 36
			display alert mnuItem & " menu item not found. Try again" giving up after 2
		end try
	end if
	
	display alert "Entourage/Spell Catcher language is now English...." giving up after 2
	
on error
	display alert "Operation failed" giving up after 2
end try

Thanks

I doesn’t use the named app: Entourage, Spell Catcher and QuickSilver so, I will simply save the passed script in my archives.

Yvan KOENIG

Hello. I’m trying to find an Apple Script to set me input method to Devanagari-QWERTY and then back to U.S. Extended. (Actually two scripts!)

Anyways a nice person over on the apple forums linked me to this thread and it seems VERY promising. But I’m having a small problem with Yvan KOENIG’s scripts. They all give me the following error:

System Events got an error: NSReceiverEvaluationScriptError: 4

And then highlight the word “value.” Any idea what could be causing that and how I deal with it? I am a complete novice at AppleScript so I don’t even know how to begin trying to figure this out. Thanks for any help/hints you can provide.

Hello

May you check that UI element scripting is enabled. which means that access for assistive devices is enabled in the Universal Access System Preference ?

Yvan KOENIG

That would be it! Sweet that seems to work great. Thank you very much.

I’ve been following this great thread and have finally come to a solution that works on my setup:
12 menu bar items including displays and third party menu extras
keyboard layouts:
Dvorak
Dvorak - QWERTY ‘clover leaf’
U.S.
U.S. Extended
Swiss German
(not tested but will also have to work on various flavours of Dvorak and Swiss layouts, for the final implementation should work)

key codes and keystrokes are not an option on my system - don’t ask, I’ve given up on that issue

the rough script:


set theMenuName to "text input menu extra"
set theLabelToClick to 1 --""

tell application "System Events" to tell application process "SystemUIServer"
	set menuBar to menu bar 1
	--> causes trouble: menu bar items of menuBar whose value of attribute "AXDescription" is theMenuName
	--set trk to get value of attribute "AXDescription" of every menu bar item of menu bar 1
	--set trk to value of attribute "AXDescription" of every menu bar item of menu bar 1
	set menuBarItemsList to value of attribute "AXDescription" of menu bar items of menuBar
	log "get the nuumber for: text input menu extra"
	set theMenuNum to my positionOfAinListB(theMenuName, value of attribute "AXDescription" of menuBar's menu bar items)
	if -1 ≠ theMenuNum then
		set theMenu to menuBar's menu bar item theMenuNum
		tell theMenu --the icon on menu bar
			repeat 3 times -- just handy for debugging
				log "the loop"
				-- can only get menu items if menu dropped
				if not selected then click
				set namesOfMenuItems to name of menu items of menu 1
				-- which layout do we have now? NOTE: the clover leaf doesn't seem to bother here!
				if "Dvorak - Qwerty " = value then
					if "Swiss German" is in namesOfMenuItems then
						-- click and make sure the value changed!
						my clickThisMenuItem(theMenu, "Swiss G", first menu item of menu 1 whose name is "Swiss German")
						-- NOTE: in the above line the filter works on "Swiss German" but not on clover leaf
					else
						beep
						log "Didn't  find Swiss German"
					end if
				else if "Swiss German" = value then
					-- this loop is ugly way to find out which item's name begins with to avoid having to deal with symbols and keyboard shortcuts
					-- can't use the positionOfAinListB routine because of the name's last character 
					-- if "Dvorak -" isn't found, targetMenuItem is the last item -> in my case: Open International
					repeat with i from 1 to count (menu items of menu 1)
						set targetMenuItem to menu item i of menu 1
						if targetMenuItem's name begins with "Dvorak -" then exit repeat
					end repeat
					-- click and make sure the value changed!
					my clickThisMenuItem(theMenu, "Dvorak -", targetMenuItem)
				end if -- SG current
				log "loop"
			end repeat -- debugging
		end tell -- theMenu
	else
		log "couldn't find menu bar item"
	end if -- theMenuNum
end tell

to clickThisMenuItem(theMenu, menuItemNameStart, Menuitem)
	-- using timeout  might be a good thing... to avoid an infinite loop
	tell application "System Events" to tell application process "SystemUIServer" to tell theMenu
		repeat until value begins with menuItemNameStart
			-- can't click empty space
			if not selected then click
			-- finally click the correct layout
			tell Menuitem to click
			delay 0.2
		end repeat
	end tell -- theMenu
	--end
end clickThisMenuItem

to positionOfAinListB(itemA, listB)
	-- this doesn't work on all layouts -> clover leaf
	if (itemA is in listB) then
		-- going backwards as I have my kyb layout menu extra far to the right, at the end of listB
		repeat with i from (count listB) to 1 by -1
			if itemA = listB's item i then return i
		end repeat
	else
		return -1
	end if
end positionOfAinListB

Thanks to all participants :lol:

Resurrecting this thread since now things seem to have changed with Leopard, and all the previous messages seem broken (on my system at least). So is there an easy way to select a keyboard with Applescript or Automator?

[edit: I’ve just spotted that changing
“text input menu extra” to “text input” tends to fix things!
]

Many thanks,
Macgruder