Using dictionaries (key/value pairs) in Applescript....

Hi guys,

I’ve been creating an application using Applescript that uninstalls a software off a Mac. It’s supposed to be multi-platform (Panther, Tiger, and Leopard), and I have finished it. The only problem is that I have to make it more efficient, so I thought of using the dictionaries to reduce my localization part of the code (as my code is very lengthy due to this). Currently, this is how I handle localization:

on displayEnglish()
	display dialog "This will uninstall your Software. Do you want to continue?" buttons {"Yes", "No"} default button "Yes" with icon caution
	if button returned of result is "No" then error number -128
end displayEnglish

on displayFrench()
	display dialog "This is French!" buttons {"Yes", "No"} default button "Yes" with icon caution
	if button returned of result is "No" then error number -128
end displayFrench

So I thought of using dictionaries to minimize this code to something like:

set key “en” to value “This is English”,
set key “fr” to value “This is French”.

And so on. Then I would have to probably loop through it and query through my if statements, so if the current language is “en” then display the value for that key which is" Uninstall software…".

Can you guys help me achieve this?

Thanks so much and God Bless you all! :slight_smile:

Ben.

Hi,

there is a quite easy way to localize AppleScripts without AppleScript Studio.
The only requirement is, that the script must be saved as bundle

¢ Open the bundle with the contextual menu > Show Package Contents
¢ Open the folder Contents > Resources
¢ Create a folder English.lproj in the folder Resources and further localized folders, in the following example I’ll describe an additional folder German.lproj
¢ Open TextEdit (or another text editor) and create a plain text file with the contents

"CONTINUE" = "This will uninstall your Software. Do you want to continue?"; "YES" = "Yes"; "NO" = "No";
Notes: The syntax “Label” = “the text”; is important
The preferred text encoding is UTF-8

¢ save the file as Localizable.strings in yourApp/Contents/Resources/English.lproj
¢ create a second plain text file with the contents

"CONTINUE" = "Das script wird Ihre Software entfernen. Wollen Sie fortfahren?"; "YES" = "Ja"; "NO" = "Nein";
¢ save the file as Localizable.strings in yourApp/Contents/Resources/German.lproj

Use this kind of AppleScript code. The literal string after localized string represents the label in the localizable.strings file.
If there is an equivalent .lproj folder with a Localizable.strings file in it, the script will always display the messages in the current system language


displayLocalized()

on displayLocalized()
    display dialog (localized string "CONTINUE") buttons {localized string "YES", localized string "NO"} default button 1 with icon caution
    if button returned of result is (localized string "NO") then error number -128
end displayLocalized

If you’d like an example, my Screenshot Settings app has a French localization. Also, note that localized string is only available on Tiger or later.

See also: localized string

So guys, this method provided by Stefan doesn’t work on Panther? It only works on Tiger and Leopard?

Is there a universal method between Panther, Tiger, and Leopard, or should I use dictionaries?

Alternatively use different scripts for the message handlers containing the localized parts, which will be loaded as script (object) depending on the language.
This example works in Panther, Tiger and Leopard, the localized scripts are located in the bundle /Contents/Resources/Scripts/


set SystemLanguage to word 1 of (do shell script "defaults read -g AppleLanguages")
--> returns en for english, de for german, fr for french etc.
set localizedFolder to ((path to me as string) & "Contents:Resources:Scripts:")
try
	set localizedScript to load script alias (localizedFolder & "localized_" & SystemLanguages & ".scpt")
on error
	set localizedScript to load script alias (localizedFolder & "localized_en.scpt")
end try
tell localizedScript to displayMessage()

¢ create a script for each language named localized_[lang].scpt ([lang] represents the two character language abbreviation)
for example, if no localized script will be found, the main script uses always the english version.

script myApp.app/Contents/Resources/Scripts/localized_en.scpt


on displayMessage()
   display dialog "This will uninstall your Software. Do you want to continue?" buttons {"Yes", "No"} default button 1 with icon caution
   if button returned of result is "No" then error number -128
end displayMessage

script myApp.app/Contents/Resources/Scripts/localized_de.scpt


on displayMessage()
   display dialog "Das script wird Ihre Software entfernen. Wollen Sie fortfahren?" buttons {"Ja, "Nein"} default button 1 with icon caution
   if button returned of result is "Nein" then error number -128
end displayMessage

Cool, I’ll definitely try that. Also, I use this Bash command:

set SystemLanguage to do shell script "defaults read .GlobalPreferences AppleLanguages | tr -d [:space:] | cut -c2-3"

As I couldn’t truncate the first word, however, word1 does that for me instead of this line correct?

I’ve tested my version successfully on all of the three system versions, but the tr / cut version should also work

Hey thanks again you guys are helping a lot…but I just did a code review and the lead wants dictionaries (key/ value pairs) used for localization. So I have to do this…whats the syntax for using dictionaries on Applescript…back to my original question and also, how can I use it for my localization stuff. Sorry for the long list of questions, but I have to meet a deadline in two days! :frowning:

Thanks and God Bless.

Ben.

I need a solution for what the subject asks for - i.e. I do not need to do localization, I really need key-value pairs in AppleScript.

I am scanning a large set of data and need to find matching names in it. Imagine I want to scan a large folder with subfolders and want to find all files of the same name, appearing in different folders.

So I need to use a dictionary-like variable in which I can put the found items with their names. I also need to see if the item’s name is already in there, and react on that.

I know that there’s a “contains” verb, but that’s not enough for me. I need something like “index of”, or “item with name”.

Basically, I imagine writing this:


set mySet to {}
repeat with theItem ...
set itsName to name of theItem
set itemFromSet to (index of itsName in mySet's keys)
if itemFromSet is nil then
  -- not in the set yet -> store this item now
  set mySet to mySet & { key: itsName, value: theItem }
else
  -- found a match
end
end

I understand that I could write my own “findItemInList” function performing a loop over all items in the set - but that would be quite slow and getting quadradically worse the more items I have. Not good. I need hash-like lookup function. I’m surprised I can’t find any postings on this. My AppleScript book also doesn’t discuss this. And I thought such lookup functions would be quite popular.

There are some very fast sort algorithms (mostly by Nigel Garvey under Code Exchange). My usual approach to your problem is to get the list of entire contents, sort them, and then run through the list looking for duplicates which will be adjacent.

All you would need to do is keep track of the names you have added in a separate list. Then you check against that list before adding the item to your list…

set mySet to {}
set namesList to {}

repeat with theItem in theItems
	tell application "Finder" to set itsName to name of theItem
	
	if itsName is not in namesList then
		set end of namesList to itsName
		set mySet to mySet & {key:itsName, value:theItem}
	else
		-- found a match
	end if
end repeat

Hello.

The use of localized string, doesn’t work well in Mavericks, not for me anyway, so I have tried to make a general handler for returning the correct localized string.

In order to return the correct localized string, I have to get at the correct Lproj bundle, that contains the localized string for either my language or AppleLocale.

Algorithm:

see if the first of languages is in one of the *big* languages[1], if it is, then return correct bundle name.

Else see if the appleLocale, is among the *very detailed bundles* 

check and see if the language, has a related, *normal region specific bundle*

if all else fails, use English.lproj as fall back.

[1] Big Languages:

Dutch.lproj, English.lproj, French.lproj, German.lproj, Italian.lproj, Japanese.lproj, Spanish.lproj
The "big languages " are collected up front.

[2] Very detailed bundles:
pt_PT.lproj, zh_CN.lproj zh_TW.lproj

[3]normal region specific bundle:
ar.lproj, ca.lproj, cs.lproj, da.lproj, el.lproj, fi.lproj, and so on . . .

Documentation:

This handler is for returning a localized string, on some machine from some app, according to the current users language and locale settings.

How to preparate input for this handler:

You have to open the resources folder of the app that you inted to retrieve the localized string from, and other than assure that you have got the correct “key” for your localized string, you have to make a list with any spelled out language names, and a related list with the language codes, this page may help when contstructing the lists: iso-639-2 list.

Here comes the handler with a demonstration: Figure out the local string for screenshots of ScreenCapture app.

set hfsAppName to "Macintosh HD:System:Library:CoreServices:SystemUIServer.app:"
# I have figured out the big languages list, by peeking into the SystemUIServer apps Resources folder . . . 
set langList to {"nl", "en", "fr", "de", "it", "jp", "es"}
set lProjList to {"Dutch.lproj", "English.lproj", "French.lproj", "German.lproj", "Italian.lproj", "Japanese.lproj", "Spanish.lproj"}
set rscPath to POSIX path of ((hfsAppName & "Contents:Resources") as alias)

set correctLProj to getTheLProjBundle(rscPath, langList, lProjList)

set correctResource to path to resource "no.lproj" in bundle file hfsAppName

set locString to localized string of "%@ %@ at %@" from table "ScreenCapture" in bundle correctResource

log locString
--> "%@ %@ kl. %@"

on getTheLProjBundle(pxPathToResourcesFolder, langList, lProjList)
	# http://macscripter.net/viewtopic.php?pid=179383#p179383
	set sysLang to word 1 of (do shell script "defaults read -g AppleLanguages")
	# StefanK
	if (count langList) ≠ (count lProjList) then error "getTheLProjBundle: langList is not as long as lProjList"
	set pass to 1
	repeat
		
		if pass = 1 then
			-- check if the first language is amongst the "big" languages.
			if sysLang is in langList then
				repeat with i from 1 to (count langList)
					if sysLang is item i of langList then return item i of lProjList
				end repeat
			else
				set pass to 2
			end if
		else if pass = 2 then
			-- check if the current AppleLocales are among the very detailed localization bundles.
			try
				set curLocale to do shell script "/usr/bin/defaults read -g AppleLocale"
			on error
				set pass to 3
			end try
			if pass = 2 then -- still
				try
					tell application id "MACS"
						set locBundleName to name of first folder of folder (POSIX file pxPathToResourcesFolder as text) whose name begins with curLocale
					end tell
					return locBundleName
				on error
					set pass to 3
				end try
			end if
		else if pass = 3 then
			-- check and see if the language, has a related, *normal region specific bundle*
			try
				set region to (do shell script "/usr/bin/grep  \"" & sysLang & "_*\"  /usr/share/locale/locale.alias |/usr/bin/head -1 |/usr/bin/sed -En 's/([[:alpha:]]+[[:blank:]]+)([^_]+)_([^.]+)(.*)/\\3/p' |/usr/bin/tr 'A-Z' 'a-z'")
			on error
				set pass to 4
			end try
			if pass = 3 then -- still . . . 
				try
					tell application id "MACS"
						set locBundleName to name of first folder of folder (POSIX file pxPathToResourcesFolder as text) whose name begins with region
					end tell
					return locBundleName
				on error
					set pass to 4
				end try
			end if
		else if pass = 4 then
			-- fall back
			-- Thanks to Shane Stanley
			tell application id "MACS"
				set rcsFol to folder (POSIX file pxPathToResourcesFolder as text)
				if exists folder "English.lproj" of rcsFol then
					return "English.lproj"
				else if exists folder "en.lproj" of rcsFol then
					return "en.lproj"
				else
					error "getTheLProjBundle: No english localization bundle in this App!!"
				end if
			end tell
		end if
	end repeat
end getTheLProjBundle

You should be aware that Apple recommends using the ISO identifiers instead these days – en.lproj, fr.lproj, &c. That’s what you will see in newer apps.

Yes, I should have communicated better that in order to use this handle, you have to use what is in the apps resorce folder as a vantage point when assembling the “Big Languages” list.

This is something of the past, since Apple nowadays recommends en.lproj over English.lproj, in the case of SystemUIServer, I guess they can’t change it, or in any app for that matter for backwards compatibility.

Again, this handler deals with what is there, not what should be there. :slight_smile:

Edit

Thank you Shane for pointing out the bug in my code, I have changed the fallback now, to en.lproj, if there isn’t any English.lproj there.