Finder listview (arrange by: Kind & sort by: Name) MacOS Catalina

Having been looking at this myself this morning, I’ve found that neither Yvan’s or KniazidisR’s original scripts work on my Mojave system. Yvan’s tries to read the variable ‘byKind’, which hasn’t been set, and KniazidisR’s clicks a menu item called “Use Groups”, which doesn’t exist in that menu on my system.

Yvan’s Catalina-friendly version (post #6) does work on my machine, but uses more GUI Scripting than is strictly necessary (as I see KniazidisR’s now realised).

As chance would have it, Shane posted some handlers on the Late Night Software fora a day or two ago which do a fair job of translating strings for GUI Scripting. In particular, they work for the Finder term “Kind”. Here’s my entry into the fray. It uses Shane’s Library and two of the handlers I mentioned and uses Finder scripting to set the list view and sort column. GUI Scripting is only used for grouping. It seems to be slightly faster than Yvan’s post #6 script. Most of its bulk is the handler which translates “Kind” into the local language. :wink:

use AppleScript version "2.5" -- macOS 10.11 or later
use framework "Foundation"
use script "FileManagerLib"
use scripting additions

-- Get the local string for "Kind".
property dictCache : missing value -- we cache strings dictionaries here to speed multiple look-ups a bit
set byKind to my localizedStringForEnglish:"Kind" inBundle:(path to application "Finder")
-- The above handler returns missing value for "None" and "none" for "none" in Mojave. The following line's in case "Group by:" ever needs to be cancelled.
if ((byKind is missing value) or (byKind is "none")) then set byKind to 1

set theFolder to (choose folder)
set PosixPath to POSIX path of theFolder

set theFolders to objects of PosixPath result type files list with searching subfolders without include files and include invisible items -- Requires FileManagerLib.
set end of theFolders to theFolder

repeat with aFolder in theFolders
	tell application "Finder"
		set thisWindow to (make new Finder window to aFolder)
		tell thisWindow's list view options to set sort column to name column -- This won't take effect until the next time the window's opened.
		set thisWindow's current view to list view
	end tell
	tell application "System Events"
		tell application process "Finder"
			set frontmost to true
			keystroke "j" using {command down}
			repeat until (subrole of window 1 is "AXSystemFloatingWindow")
				delay 0.1
			end repeat
			tell pop up button 1 of window 1
				click
				repeat until menu 1 exists
					delay 0.1
				end repeat
				click menu item byKind of menu 1
			end tell
			keystroke "j" using {command down}
			--repeat while (subrole of window 1 is "AXSystemFloatingWindow")
			--	delay 0.1
			-- end repeat
		end tell
	end tell
	tell application "Finder" to close thisWindow
end repeat

-- Handlers by Shane Stanley <https://forum.latenightsw.com/t/localizing-gui-scripts/2246>:

-- use this if you have the English name and it is in the standard Localized.strings file
on localizedStringForEnglish:enString inBundle:fileOrNSURL
	set langID to current application's NSLocale's currentLocale()'s localeIdentifier()
	return my localizedStringFor:enString fromTable:"" inBundle:fileOrNSURL destLang:langID sourceLang:"en"
end localizedStringForEnglish:inBundle:
-- use this if you also want to specify a particular .strings file. A destLangCode of "" means the current locale, and a stringsFileName of "" means the default Localizable.strings file
on localizedStringFor:baseString fromTable:stringsFileName inBundle:fileOrNSURL destLang:destLangCode sourceLang:sourceLangCode
	if stringsFileName is "" then set stringsFileName to "Localizable"
	set theBundle to current application's NSBundle's bundleWithURL:fileOrNSURL
	set sourceLangString to current application's NSString's stringWithString:sourceLangCode
	-- make key to search for cached values
	set cacheKey to current application's NSString's stringWithFormat_("%@_%@_%@", theBundle's bundleIdentifier(), stringsFileName, sourceLangCode)
	if my dictCache is missing value then set my dictCache to current application's NSMutableDictionary's dictionary()
	-- get source strings values
	set sourceDict to dictCache's objectForKey:cacheKey
	if sourceDict = missing value then
		set sourceURL to theBundle's URLForResource:stringsFileName withExtension:"strings" subdirectory:"" localization:sourceLangString
		if sourceURL is missing value and (sourceLangString's containsString:"_") as boolean then
			-- try stripping off country-specific part
			set sourceLangString to sourceLangString's substringToIndex:2
			set sourceURL to theBundle's URLForResource:stringsFileName withExtension:"strings" subdirectory:"" localization:sourceLangString
		end if
		if sourceURL is missing value then
			-- try long name for localization
			set sourceLangString to (current application's NSLocale's localeWithLocaleIdentifier:"en")'s localizedStringForLocaleIdentifier:sourceLangString
			set sourceURL to theBundle's URLForResource:stringsFileName withExtension:"strings" subdirectory:"" localization:sourceLangString
		end if
		if sourceURL is missing value then error "No " & sourceLangCode & " localization found"
		set theData to current application's NSData's alloc()'s initWithContentsOfURL:sourceURL
		if theData is missing value then error "No " & sourceLangCode & " localization found"
		set sourceDict to (current application's NSPropertyListSerialization's propertyListWithData:theData options:0 format:0 |error|:(missing value))
		dictCache's setObject:sourceDict forKey:cacheKey
	end if
	-- make key to search for cached values
	set cacheKey to current application's NSString's stringWithFormat_("%@_%@_%@", theBundle's bundleIdentifier(), stringsFileName, destLangCode)
	-- get dest strings values
	set destDict to dictCache's objectForKey:cacheKey
	if destDict = missing value then
		if destLangCode is "" then
			set destLangString to current application's NSLocale's currentLocale()'s localeIdentifier()
		else
			set destLangString to current application's NSString's stringWithString:destLangCode
		end if
		set localURL to theBundle's URLForResource:stringsFileName withExtension:"strings" subdirectory:"" localization:destLangString
		if localURL is missing value and (destLangString's containsString:"_") as boolean then
			-- try stripping off country-specific part
			set destLangString to destLangString's substringToIndex:2
			set localURL to theBundle's URLForResource:stringsFileName withExtension:"strings" subdirectory:"" localization:destLangString
		end if
		if localURL is missing value then
			-- try long name for localization
			set destLangString to (current application's NSLocale's localeWithLocaleIdentifier:"en")'s localizedStringForLocaleIdentifier:destLangString
			set localURL to theBundle's URLForResource:stringsFileName withExtension:"strings" subdirectory:"" localization:destLangString
		end if
		if localURL is missing value then error "No " & destLangCode & " localization found"
		
		set theData to current application's NSData's alloc()'s initWithContentsOfURL:localURL
		if theData is missing value then error "No " & destLangCode & " localization found"
		set destDict to (current application's NSPropertyListSerialization's propertyListWithData:theData options:0 format:0 |error|:(missing value))
		dictCache's setObject:destDict forKey:cacheKey
	end if
	-- convert from source to dest
	set destValue to destDict's objectForKey:((sourceDict's allKeysForObject:baseString)'s firstObject())
	if destValue is not missing value then set destValue to destValue as text
	return destValue
end localizedStringFor:fromTable:inBundle:destLang:sourceLang:

Hello Nigel

I missed the fact that the key “N224” is not reachable from the English resources in Catalina.

I edited message #16 which contain now:

if (system attribute "sys2") < 14 then
		-- Here with High Sierra or older. Tested with 10.13.6
		set o's kind_loc to localized string "N224" in bundle theBundle
	else
		-- Here with Mojave or newer -- tested with Catalina
		set o's kind_loc to localized string "119.title" from table "ArrangeByMenu" in bundle theBundle
	end if

Would be glad to know if it’s Ok for Mojave.

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) jeudi 2 janvier 2020 16:15:10

Hi Yvan.

Both of those ‘localized string’ commands return “Kind” on my Mojave system. But I don’t know if that’s because both search locations are native to Mojave or because one of them wasn’t deleted during an upgrade from an earlier OS version.

My fault. I read too fast your previous message.

The problem wasn’t with “Kind” but with “Arrange By:”

if (system attribute "sys2") < 14 then
		-- Here with High Sierra or older systems
		set aKey to "974.title"
		set o's arrangeBy_loc to localized string aKey from table "ViewOptionsWindow" in bundle theBundle
		-- In English, the resource isn't available -> "974.title". Define it here.
		if o's arrangeBy_loc = aKey then set o's arrangeBy_loc to "Arrange By:"
		log o's arrangeBy_loc --> "Arrange By:"
	else
		-- Here under Mojave or newer (tested with Catalina)
		set aKey to "V026"
		set arrangeBy_loc to localized string aKey in bundle theBundle --> "Grouper par :" (Catalina)
	end if

Would be glad how it works under Mojave.

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) jeudi 2 janvier 2020 18:02:31

Hi Yvan.

The result is “V026” on my Mojave system.

If I change the first line to include Mojave systems too, the result is “Group By:”, which is the correct English label for the first pop-up menu in Mojave’s View Options window and for the equivalent button in Finder window toolbars.

On my El Capitan system, that label is “Arrange By:”. Your code returns the correct string there by virtue of the ‘if o’s arrangeBy_loc = aKey …’ line.

Thanks.

I will edit the test.

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) jeudi 2 janvier 2020 21:50:12

Sorry for the late reply!
On the earlier version (after post #42) the script did exactly what I wanted under Catalina.

That is so great. Thanks to all of YOU and a special thanks to Yvan.

Now I tested the script you posted a few hours ago.

  • Under HighSierra it works perfectly.
  • Under Catalina I get this error:

@ Flavour.

What gives the late version in message #16 ?

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) jeudi 2 janvier 2020 23:52:07

I’m an ass, the key is not supposed to be V026 but VO26.

The message #16 is edited accordingly.

You may run this simple test:

set theBundle to path to application "Finder" --> an alias
script o
property arrangeBy_loc : ""
end

if (system attribute "sys2") < 14 then
	set aKey to "974.title"
	set o's arrangeBy_loc to localized string aKey from table "ViewOptionsWindow" in bundle theBundle
	-- In English, the resource isn't available -> "974.title". Define it here.
	if o's arrangeBy_loc = aKey then set o's arrangeBy_loc to "Arrange By:"
	log o's arrangeBy_loc --> "Arrange By:"
else
	set aKey to "VO26" -- with the letter "O", not the digit "0"
	set o's arrangeBy_loc to localized string aKey in bundle theBundle --> "Grouper par :" (Catalina)
	log o's arrangeBy_loc --> "Arrange By:"
end if


(*
table Localizable.string in en.lproj (English)
	<key>VO26</key>
	<string>Group By:</string>
table Localizable.string in de.lproj (German)
	<key>VO26</key>
	<string>Gruppieren nach:</string>
table Localizable.string in fr.lproj (French)
<key>VO26</key>
	<string>Grouper par :</string>
*)

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) vendredi 3 janvier 2020 04:18:29

added the script objet named o

Thank you Yvan!

Now the script does exactly what I want under Catalina.
That is so great.

Now I have two questions:

  1. Where and how do you find the localized strings for the Finder-App?
    like: “VO26”, “6.title” or “N224” in
  1. If Apple decides to make some changes in future macOS versions like 10.16, 10.17, etc.
    may it will be necessary to make some changes in this script (for example: localized strings).
    Did I understand it right?

I ran the test and got this:

-- Don't click [Open this Scriplet in your Editor:]
tell application "Finder"
	path to current application
		--> error number -1708
	«event ascrgdut»
		--> error number -1708
	path to current application
		--> alias "macOS:System:Library:CoreServices:Finder.app:"
end tell
tell current application
	system attribute "sys2"
		--> 15
	localized string "VO26" in bundle alias "macOS:System:Library:CoreServices:Finder.app:"
		--> "Group By:"
Result:
error "The variable o is not defined." number -2753 from "o"

Oops I forgot the three instructions defining the script object.

message #49 is edited accordingly.

Some of these days, I will create a thread in which I will post the script which I use since I discovered that my good old friend EasyFind is unable to search in the resources files delivered by Apple.

I just wish to make some complementary tests because I found a case where it return a wrong result.

When I had access to my grand daughter Catalina macBook, I borrowed its folder “/System/Library/” so know I may also search in Catalina resources.
As I dislike wasting space, I am busy removing every files which aren’t relevant for this task.

Working upon your question was a good opportunity to check my script.
Now, I will be able to remove every code using GUI Scripting for tasks which may be achieved by plain Applescript code.

I already knew the cases wher the English resource related to a keyString is not reachable.
I already faced GUI changes introduced in 10.9, 10.10, 10.11, 10.12 and 10.13.
I assumed that change were introduced by 10.14 and 10.15 but I ignored which they are.

I would be glad if somebody is fair enough to send me a copy of the folder “/System/Library/” belonging to Mojave.

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) vendredi 3 janvier 2020 15:05:48

Thanks Yvan!
That sounds really great. I will look up for it. :slight_smile:

Today I ran the Test (post #49)

English Log History:

-- Don't click [Open this Scriplet in your Editor:]
tell application "Finder"
	path to current application
		--> alias "macOS:System:Library:CoreServices:Finder.app:"
end tell
tell current application
	system attribute "sys2"
		--> 15
	localized string "VO26" in bundle alias "macOS:System:Library:CoreServices:Finder.app:"
		--> "Group By:"
	(*Group By:*)
end tell

German Log History:

-- Don't click [Open this Scriplet in your Editor:]
tell application "Finder"
    path to current application
        --> alias "macOS:System:Library:CoreServices:Finder.app:"
end tell
tell current application
    system attribute "sys2"
        --> 15
    localized string "VO26" in bundle alias "macOS:System:Library:CoreServices:Finder.app:"
        --> "Gruppieren nach:"
    (*Gruppieren nach:*)
end tell

I sent you an email with the “/System/Library/” folder from Mojave (10.14.6)

I again had to put things in order in my folders, and I remembered this already old topic. It offered 3 solutions: from me, from @Yvan Koenig and from @Nigel Garvey.

@Yvan Koenig’s solutions (many posts…) often work as they do here, but traditionally contain horribly redundant code, excessive GUI scripting, and verbiage.

@Nigel Garvey’s solution (post #41) works on Catalina too, and I would take it as the best option, but it uses a third party library, AsObjC (load time kills me), additional complexity in getting the localized string, and does not set the Always Open in List View flag.

My solution (post #5) also works on Catalina, but also has a number of drawbacks: nested tell blocks, non-localization, excessive GUI scripting, and does not set the Always Open in List View flag.

Therefore, in the following script, I tried to collect all the best from the topic to solve the problem:


-- script: Group selected folder's and its subfolder's contents by Kind,
-- sort by Name, set Alwais Open in List View, set Use as Defaults
-- written: by me, right now

property pathList : {}

tell application "Finder"
	set kindString to localized string "N224"
	set alwaysOpenInListViewString to localized string "VO15"
	set useAsDefaultsString to localized string "6.title" from table "ViewOptionsWindow"
	set theFolders to selection as alias list
	activate
end tell

repeat with i from 1 to count theFolders
	set aFolder to item i of theFolders
	set pathList to pathList & aFolder
	my get_All_Folders_of_Folder(aFolder)
end repeat

repeat with aFolder in the pathList
	tell application "Finder"
		set thisWindow to (make new Finder window to folder aFolder)
		set current view of thisWindow to list view
		tell thisWindow to tell its list view options to set its sort column to column id name column
	end tell
	tell application "System Events" to tell process "Finder"
		key code 29 using {control down, command down} -- "Use Groups"
		keystroke "j" using command down -- "Show View Optons"
		tell window 1
			set isAlwaysOpenInListView to value of checkbox alwaysOpenInListViewString
			if isAlwaysOpenInListView is 0 then click checkbox alwaysOpenInListViewString
			click pop up button 1
			click menu item kindString of menu 1 of pop up button 1 -- "Kind"
			click UI element useAsDefaultsString -- "Use As Defaults"
		end tell
		keystroke "j" using command down -- "Hide View Optons"
	end tell
	tell application "Finder" to close thisWindow
end repeat

on get_All_Folders_of_Folder(aFolder)
	tell application "System Events"
		set foldersList to folders of aFolder whose visible is true
		if foldersList is {} then
			set the end of pathList to (path of aFolder)
		else
			repeat with aFolder in foldersList
				my get_All_Folders_of_Folder(aFolder)
			end repeat
		end if
	end tell
end get_All_Folders_of_Folder