Placeholders for variables in a localized string?

Dear MacScripters,

I’m trying to take my Weather AppleScripts beyond English thanks to the help of some users of the software they work with WeatherCat. However, this requires being able to insert the value of a variable into the middle of a localized string. My German translator asks this way:

So what I need from you to build a correct German sentence is a placeholder for the variables you are using (in this case a placeholder for and ).

Something like:

/* German localized text for WC Temperature Falling */

“ISO_639-1Language_code”=“en”; //DO NOT LOCALISE THIS!!!

/* Growl notifications */
“Temperature has fallen below $vtemp;”=“Die Temperatur ist unter $vtemp; gesunken”;
“Outside temperature has fallen below the threshold of: $vtemp; at $vtime;”=“Die Außentemperatur ist um $vtime; unter den Grenzwert von $vtemp; gesunken”;
“at”=“um”;
“The current temperature is: $vtemp;”=“Die momentane Temperatur ist: $vtemp;”;

I’ve looked around and I don’t see any way to create placeholders in this way using AppleScript. Have I not looked hard enough? Is this a feature that simply universal to the Mac development environment so I have been looking in the wrong place? Is this a deficiency in the AppleScript environment and it is simply impossible to create localized strings in this way?

Any pearls of wisdom on this matter would be greatly appreciated!! :slight_smile:

Thanks in advance, Edouard :slight_smile:

I doubt that placeholders in localized strings work in AppleScript.

In Cocoa placeholders are used in conjunction with the method stringWithFormat: of NSString,
which replaces the placeholder tokens (%@, %d, %f etc.) with the appropriate parameter values.

There is no equivalent in AppleScript, even in AppleScriptObjC stringWithFormat_() doesn’t work with non-object parameters

Thanks Stefan for the confirmation,

It is rather what I suspected, but only someone like you would know for sure.

Oh well, time to contemplate what are the possible "plan-B"s

Cheers, Edouard

You may create a resource containing :
begOfString1
endOfString1
begOfString2
endOfString2
begOfString3
endOfString3

In the script you may re-create the complete string with :
set theCompleteString to localized_begOfString1 & variableItem & localized_endOfString1

I do that in scripts building formulas for Numbers spreadsheets.

Yvan KOENIG (VALLAURIS, France) mercredi 28 mars 2012 15:52:55

Dear Yvan,

Thanks for the suggestion. I hadn’t thought about trying to provide enough string segments so that translators would have multiple venues to insert appropriate text for their language. It clearly would be awkward, but perhaps it is workable so long as each sentence of phrase containing a variable is handled separately and the output paragraphs are assembled within the AppleScript itself.

The other possibility that occurred to me was to avail myself of the extensions within Satimage’s Smile AppleScript extensions and implement a placeholder substitution within AppleScript using change extension. My hope was that would be reasonably efficient and cleaner. However, I haven’t had any time to try to mock something one to see how this work.

So, we’ll see!

Thanks again for your suggestion! :slight_smile:

Cheers, Edouard

Placeholders can not be used, like Stefan said, but still, I think that concept is the best way. Write some simple text search and replace handler (there are probably 1000 suggestions for such handlers on this site). Variables in strings could be expressed like <> or something similar. Thinking about text fragments for all possible grammar rules will drive you crazy.

First, you take the localized string, second, you replace the variable string.

Jürgen

Dear Jürgen and MacScripters,

Yes I agree with you.

I’m hoping to use an AppleScript extension that would be more efficient than doing it all in AppleScript, but even the text substitution could be handled complete in AppleScript.

I’m trying to clean up other things right now, but I’ll try to mock this up soon and send it back to my German translator to see if it can be made practical and easy for translators to use.

Thanks again!! :slight_smile:

Cheers, Edouard

Elegache,

printf is also an terminal command so I use it myself like when a user is asked to confirm a text like ‘Are you sure you want to delete the 5 selected items?’ where the number 5 is variable.

Then in applescript I use a small handler to get the localized strings. Give up an empty array or use just the localized string command when there are no variables in the string.

localizedString("teststring", {10, "string value"})

on localizedString(stringKey, stringValues)
	set command to "printf " & quoted form of (localized string stringKey)
	repeat with p in stringValues
		set command to command & space & quoted form of (contents of p as string)
	end repeat
	return do shell script command
end localizedString

EDIT:
The reason I use it with these placeholders is that localized string files can be easily used with Objective-C as well. You should keep that in mind if you want to translate the project slowly to Objective-C in the future and maybe Objective-C and AppleScript need the same string from the localized file.

Dear DJ Bazzie Wazzie and MacScripters,

Your point is well taken, but . . . as others have belly-ached . . sure hate to spawn a new process every time I need to translate a string. :frowning:

I definitely agree on this one. Sure don’t want to implement printf in AppleScript, but was hoping to find a way to allow my strings to be usable with the Xcode implementation of localized.strings. Hope to look into this soon.

Thanks for the tips! :slight_smile:

Cheers, Edouard

Dear MacScripters,

As you all will soon discover - I’m a little slow then it comes to reading the documentation . . . :lol:

So imagine my surprise when I investigated the change verb that is supplied by the Satimage.osax . . . .

Just a little further down is:printf! :cool:

Works just as advertised and should be lightening fast compared using the UNIX command printf.

Is there some reason why folks don’t avail themselves of these extensions? I realize that commercial developers can’t easily ask their clients to install this. However I’m giving away my “toys” for free. Is there some other reason why AppleScript developers shy away from using these extensions that - I should be worrying about! :expressionless:

Thanks for all the help!! :slight_smile:

Cheers, Edouard

Two reasons, 1) every mac is installed with printf and not with satimage. 2) you don’t show (normally) 100 - 1000s of localized strings within scripts “ not even with applications “ simultaneous, so performance of localized strings is never an issue for me. Since printf is older than do shell script command itself, I’m sure that every OS can run the command. I’m not so sure about satimage (the same addition version)

This one I can only speak for my self but I never use scripting additions except for Mac OS X’s default but also use those as less a possible as well.

  1. They take away my ‘freedom’ on the mac through all systems.
  2. When I want real performance I write an utility in C, C++, Objective-C or even combine all those languages.
  3. Easier with version control when embedding a command line tool in the script bundle
  4. Don’t forget you’re using a UNIX system so there is nothing wrong with using native features (like opening a shell)
  5. Using shell commands makes your script much less OS or addition version depended
  6. Support for third party scripting additions are unsure.
  7. Using POSIX tools gives also a certain quality of your script.

I think I can name even more arguments why not using it.

Dear MacScripters,

Okay, so call me greedy, but I decided to try to write a single AppleScript handler that would be able to localize strings, then run printf. If the Satimage osax was installed, it would use that printf extension, otherwise it would create a UNIX command and do that. So I naively used a try-block and used the UNIX as the fall-back. However, I realized that this couldn’t possibly work. If the Satimage osax wasn’t installed - then there would be a compile-time, not run-time error.

Gulp, that’s the trouble. :expressionless: Because, even with this obvious impossibility - it works!?!? Here is my code as it appears on my Mac which has Satimage osax installed:

set convertedString to "Outside temperature has exceeded the threshold of: %dËš at %s."
set variableList to {50, "2:19pm"}

localizeVarStrings(convertedString, variableList)

on localizeVarStrings(localString, variableList)
	-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
	-- Routine to localize a string that includes printf formatting codes and then
	-- substitute the values for the format codes.
	-- uses Satimage osax when installed and UNIX printf command
	-- as fall-back
	--
	-- Thanks to Adam Bell for nifty list to string concatenation.
	-- http://macscripter.net/viewtopic.php?pid=53342#p53342
	-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
	local convertedString, variableString, tdl, quotedVariableList, variableValueStr
	
	-- First get translated string from localizable.strings file
	set convertedString to (localized string localString)
	
	try
		-- try Satimage printf extension
		set convertedString to printf convertedString parameters variableList
	on error
		-- Convert values for printf into shell-friendly form.
		set quotedVariableList to {}
		repeat with variableValue in variableList
			set variableValueStr to contents of variableValue as string
			set end of quotedVariableList to quoted form of variableValueStr
		end repeat
		
		-- Now make printf values into a single  string
		set {tdl, text item delimiters} to {text item delimiters, space}
		set variableString to quotedVariableList as string
		set text item delimiters to tdl
		set convertedString to quoted form of convertedString
		-- Finally let UNIX do the printf work.
		set convertedString to do shell script ("printf " & convertedString & space & variableString)
	end try
	
	return (convertedString)
end localizeVarStrings

If I copy this script to a Mac that doesn’t have Satimage osax installed, the line:

set convertedString to printf convertedString parameters variableList

looks instead like this:

set convertedString to «event SATIprtf» convertedString «class para» variableList

However, that doesn’t generate a compile-time error. Now I recognize that these are raw Apple events and my understanding is that this should be reasonable. The «event SATIprtf» is unavailable on Macs without the Satimage osax installed, so the handler goes on to do the error code which uses the UNIX printf just as I want to.

However, . . . . this sure look a little weird to me. Am I doing something crazy here that only a AppleScript guru would properly understand? Can code like this be distributed without risking problems for “innocent users”?

Curious minds want to know!! :smiley:

Cheers, Edouard :slight_smile:

P.S. Here is a link to printf localized string test.scptd file complete with the localized string resources for folks to play with. According to the tests I’ve run, this file should work on any Mac, even if just cutting and pasting the code above will not work unless you have the Satimage osax installed.

That doesn’t look right – I’d expect something more like:

set convertedString to «event SATIprtf» convertedString given «class para»:variableList

But the reason it won’t give a compile error is because it can’t find any dictionary to check those codes with, so it has no way of knowing whether it will work or not.

Dear Shane and MacScripters,

Oops, :confused: you are correct:

I typed in that line “by hand” since it was coming from another Mac and in my haste I forgot the given.

However, you left my key question unanswered. Can such code be used “safely” in general? It is certainly very kludgy, :frowning: but AppleScript is hardly the elegant scripting language that I suppose the originators at Apple first intended. At some point you’ve gotta compromise and make your scripts usable to the folks who want to use them!!

Cheers, Edouard

If you want to distribute your scripts I recommend to avoid third party Scripting Additions at all,
because they are required on any machine which uses the scripts and it’s quite tricky to install them on-the-fly.
Furthermore I guess that many users (including myself) don’t like to get components installed just for support of a single AppleScript

Solutions like DJ’s in post #8 are preferable

I don’t see why not – it compiles fine, and you trap the error. I can see where Stefan is coming from – although I’m amazed at how AppleScript beggars are often such choosers – but it seems to me that doing it as you propose covers that objection.

And unless I’m misunderstanding, your script by definition is designed to be used in app packages, so the option of adding a scripting addition is, at least technically, simple in this case.

Dear Stefan, Shane, and MacScripters,

I agree with Shane that your point is very appropriate in general. However, this isn’t scripts intended for general distribution. They are instead incomplete tools provided for hobbyists who are expected be comfortable using the AppleScript Editor and dealing with surprises. These are folks with a weather station who are interested in going beyond what commercial software can do for them and are willing to learning something about scripting in order to gain those advantages.

I made a test script available to the community who would be using them and indeed no one has encountered any problems. Given this very limited objective, it doesn’t seem like a bad idea. Anything I decide to make more polished will have to graduate from AppleScript to Objective-C. Then having the Localizable.strings already completed will be one less thing to do.

Thank you Stefan for reminding me of this post of DJ’'s. When I looked at it, I realized that I had repeatedly modified test code until it had all the features I wanted - but in doing, it so had gotten very clumsy. D.J.'s solution is much more elegant and I’ve incorporated that instead into my handler. How easily we make the same mistake of reinventing the wheel! :frowning:

Cheers, Edouard