Can't get localization to work

I’m trying to understand how applescripts can be localized. I started with the material in the ASLG but, it did not work. I found a good example on MacScripter. But, that also did not work. No matter what I do, the script only displays the original key instead of the localised text.

This is the script example I tried:

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

This is the content of the localised.strings file:

I placed the localised.strings file into a folder inside a script bundle:

When I run the script, I get a dialog box with “CONTINUE”, “YES”, and “NO” instead of the localized text.

The localised.strings file is a UTF-8 file. I also tried saving it as UTF-16 with no better result. Telling Finder that it is an Xcode file changed the file type but was no improvement.

I’m clearly not understanding something simple but, can anyone point me to the solution ?

Thanks.

I’m not sure what you’re doing wrong. The name should be en.lproj these days, although I think English.lproj should still work.Otherwise I followed what you described and it works fine.

Here is an example.
Save the code below as an application:

set continue_loc to localized string "Continue" --> "Continuer"
set yes_loc to localized string "Yes" --> "Oui"
set no_loc to localized string "No" --> "Non"

display dialog (localized string "Computer") buttons {continue_loc, no_loc, yes_loc} default button yes_loc cancel button no_loc

In the editor of your choice, I use BBEdit,
create a file named “Localizable.strings” stored in:
myApp.app:Contents:Resources:en.lproj

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>Computer</key>
	<string>Computer</string>
	<key>Continue</key>
	<string>Continue</string>
	<key>No</key>
	<string>No</string>
	<key>Yes</key>
	<string>Yes</string>
</dict>
</plist>

create a file named “Localizable.strings” stored in:
myApp.app:Contents:Resources:fr.lproj

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>Computer</key>
	<string>Ordinateur</string>
	<key>Continue</key>
	<string>Continuer</string>
	<key>No</key>
	<string>Non</string>
	<key>Yes</key>
	<string>Oui</string>
</dict>
</plist>

Then execute the app.

On my machine running in French, the log history is:

tell current application
	localized string "Continue"
		--> "Continuer"
	localized string "Yes"
		--> "Oui"
	localized string "No"
		--> "Non"
	localized string "Computer"
		--> "Ordinateur"
end tell
tell application "Script Editor"
	display dialog "Ordinateur" buttons {"Continuer", "Non", "Oui"} default button "Oui" cancel button "Non"
		--> {button returned:"Oui"}
end tell

When it’s run in English the history is :

tell current application
	localized string "Continue"
		--> "Continue"
	localized string "Yes"
		--> "Yes"
	localized string "No"
		--> "No"
	localized string "Computer"
		--> "Computer"
end tell
tell application "Script Editor"
	display dialog "Computer" buttons {"Continue", "No", "Yes"} default button "Yes" cancel button "No"
		--> {button returned:"Yes"}
end tell

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) vendredi 17 avril 2020 11:37:07

Although you can use property lists, and they support some more complex options, you can still use simple text files like that posted by the original poster. Property lists are a bit of overkill, although they’re becoming more common because they can be semi-automatically machine generated.

Many thanks everyone, I’ll try out these ideas tomorrow. I’d rather use the plain text method as I’m planning to localize an applet that has a lot of dialogs and text.

One wrinkle I found early on is when I used Script Editor to create the folder structure inside the script bundle. I ended up with:

That was not visible from within Script Editor – I found it when I looked inside the bundle using Finder.

Cheers.

I apologize Shane but when I try to use text files it doesn’t work.

I tried with the app posted before but with alternate Localizable.strings files.

In en.lproj the file contains :

"Computer" = "Computer"
"Continue" = "Continue"
"No" = "No"
"Yes" = "Yes"

In fr.lproj, it contains :

"Computer" = "Ordinateur"
"Continue" = "Continuer"
"No" = "Non"
"Yes" = "Oui"

Alas, running on a French system or in an English one it behaves the same:

tell current application
	localized string "Continue"
		--> "Continue"
	localized string "Yes"
		--> "Yes"
	localized string "No"
		--> "No"
	localized string "Computer"
		--> "Computer"
end tell
tell application "Script Editor"
	display dialog "Computer" buttons {"Continue", "No", "Yes"} default button "Yes" cancel button "No"
		--> {button returned:"Yes"}

Have you tested on a machine which don’t speak English ?

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) vendredi 17 avril 2020 13:49:00

Yvan, the lines have to end with a semicolon (;).

Use Script Debugger…

Oops, I missed that.
With them it works.

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) vendredi 17 avril 2020 14:33:16

Thanks for all the contributions. Glad to know it does work. I got it working but, it was a 5 hour process.

Another wrinkle is that the ASLG refers to both “Localized.strings” files and to “Localizable.strings” files: Language Guide. The default is the latter but, in some tests I was using the former. So, I standardised to the latter. It is more than disappointing that a fault in coding the “localized string” command does not return an error – not even a “file not found” error.

Another mistake I made was that in Australia, we use "s"s more than "z"s. So, in some tests I had a file called “Localised.strings”. I fixed that but, still no joy. I tried pointing the script to the strings file with the “in bundle” and “from table” parameters instead of relying on the default location. I also tried passing a posix path to the strings file. For a while I changed my Mac’s language to French and tried to get a French strings file working. I changed the “English.lproj” folder to “en.lproj”. I varied the case in the strings file and lproj folder names. I did tests in script bundles and in applets. I’ve been working on a Mac running a patched copy of macOS 10.14. On the chance that patching caused the issue, I started from scratch on a Mac running macOS 10.13.6. I checked with Finder’s Get Info and BBEdit to make sure that I was using a plain text UTF-8 file to store the strings. I also checked the folder structure for the applets in my tests with that in the ASLG.

I was at a loss. The ASLG example could not be simpler. But, I couldn’t get it to work. I still thought that there could be a problem with the strings file. I opened the strings file and did a “Save As” in BBEdit. It didn’t seem to change any characteristics of the file (e.g. kind, encoding, line feeds) but, it fixed the problem and the script worked. I tried another test script and applied the same procedure and that script worked too. Then, I saved a third test which was in an scptd bundle as an application. It worked.

So, the clue is that localization only works (for me) if inside an applet bundle NOT an scptd file (despite what the ASLG says) and the strings file is saved by BBEdit !

I might go with the plist approach if there is more trouble and, OK, I’ll look at getting Script Debugger.

Cheers all.

You should report it as a bug (along with the fact that it’s using English.lproj rather than en.lproj).

it does, however, return the original key in such cases – you can test for that, and treat it as an error.

An easy mistake for some of us ;), but obviously file names need to be accurate.

Could the original file have been saved with CR instead of LF as line endings? It’s possible that BBEdit would silently fix such a problem because it knows about .strings files and their requirements.

I originally didn’t note that you used a .scptd file, so my first test was a .app. I then saved it as a .scptd file, and it still worked.

FWIW, all this works fine Script Debugger’s Lite (free) mode.

It struck me later that this is exactly the sort of menial task that scripting is meant for. So here’s a script to add .strings files for chosen localizations. It targets the front open document in Script Debugger, but you can change it to suit (for example, to work on the Finder selection).

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

-- set up basic template for Localizable.strings files
set templateString to "/* 
  Localizeable.strings file for '%@' locale. Line-breaks must be line-feeds, and format should be UTF-8 (no BOM) or UTF-16.
*/

/* Comments must be escaped like this line */
\"SampleEntry\" = \"This is what a sample entry looks like. Don't forget the trailing semicolon\";
"

-- get the file of the active document
tell application id "com.latenightsw.ScriptDebugger7" -- Script Debugger.app
	set theFile to file spec of document 1 -- careful when testing
end tell

-- get its bundle
set theBundle to current application's NSBundle's bundleWithURL:theFile
if theBundle is missing value then error "This script only works with .app and .scptd files."

-- get list of locale IDs and ask user to choose
set localeIDs to (current application's NSLocale's availableLocaleIdentifiers()'s sortedArrayUsingSelector:"localizedStandardCompare:") as list
set theIDs to choose from list localeIDs with prompt "Choose the locales to localize for:" with title "Localize Bundle" with multiple selections allowed

-- get URL of document's /Contents/Resources/ folder
set resourcesURL to theBundle's resourceURL()
set fileManager to current application's NSFileManager's defaultManager()

-- loop through IDs
repeat with anID in theIDs
	-- make .lproj folder if needed
	set folderURL to ((resourcesURL's URLByAppendingPathComponent:anID)'s URLByAppendingPathExtension:"lproj")
	(fileManager's createDirectoryAtURL:folderURL withIntermediateDirectories:true attributes:(missing value) |error|:(reference))
	-- check if Localizable.strings exists so we don't overwrite
	set fileURL to (folderURL's URLByAppendingPathComponent:"Localizable.strings")
	if not (fileURL's checkResourceIsReachableAndReturnError:(missing value)) as boolean then
		-- write Localizable.strings file
		set theString to current application's NSString's stringWithFormat_(templateString, anID)
		set theString to (theString's stringByReplacingOccurrencesOfString:return withString:linefeed) -- just in case
		(theString's writeToURL:fileURL atomically:true encoding:(current application's NSUTF8StringEncoding) |error|:(missing value))
	end if
end repeat

display dialog "Done" buttons {"OK"} default button "OK"

Many thanks for all this. The localization script will be useful as I have around 180 translations to add. I bought Script Debugger this morning – it was about time.

Cheers.

With that many translations, it’s worth considering how you can automate as much as possible.

One thing to keep in mind is that the .strings file format is technically a property list, of the original NeXT text format. This means you can read (but not write) one as a dictionary:

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

set thePath to "/Users/shane/Desktop/Local 2.app/Contents/Resources/en.lproj/Localizable.strings"
set theDict to current application's NSDictionary's dictionaryWithContentsOfFile:thePath

That means you can pretty easily automate things like checking that allKeys match, and even adding extra entries, without having to parse the files as text yourself.

And one hint: include the English version as a comment before each entry. So:

/* This will uninstall your Software. Do you want to continue? */
"CONTINUE" = "This will uninstall your Software. Do you want to continue?";

It makes it much easier for translators, and for checking things generally. BBEdit will color-code the formatting appropriately for you.