Author Archive
Erin Vang facilitates again—Localization World, Santa Clara pre-conference session
by Erin Vang on Sep.20, 2011 , under facilitative leadership, localization, program management
Erin Vang will moderate a panel discussion on “Leading Globalized Software Development in Your Company” presented on Monday, October 10, 2011, 2:30-5pm at the Network Meeting Center at Techmart.
Developing Software for the World – Internationalization, Localization and Beyond
This event is by invitation only and limited to 50 participants. Register online and join us on Monday, October 10th at 2:30pm in Santa Clara, CA for an expert presentation and panel discuss on leading globalized software development.
What: Leading Globalized Software Development Presentation and Expert Panel Discussion
When: Monday, October 10, 2011; 2:30-5pm
Where: Network Meeting Center at Techmart
5201 Great America Parkway
Santa Clara, California 95054
Cost: Complimentary
Open to: Lingoport customers and friends (space is limited; approval required)
Registration: https://www1.gotomeeting.com/register/308146424
Panelists: Tex Texin, Chief Globalization Architect at Rearden Commerce, Andrew Bredenkamp, CEO at Acrolinx, Loic Dufresne de Virel, Localization Strategist at Intel, Richard Faubert, Manager, Software Development QA at Cisco, and Adam Asnes, Founder & CEO of Lingoport.
Panel Facilitator: Erin Vang, Global Pragmatica LLC®
This event is by invitation only and limited to 50 participants. Register online and join us on Monday, October 10th at 2:30pm in Santa Clara, CA for an expert presentation and panel discuss on leading globalized software development.
Customers & friends of Lingoport and Acrolinx are cordially invited to join us for a special event on the eve of Localization World in Santa Clara on Monday, October 10th at 2:30pm. Join us at TechMart for an interactive presentation and expert panel discuss on how to lead globalized software development at your company.
Together, industry experts from Rearden Commerce, Acrolinx, Intel, Cisco and Lingoport will present and discuss:
- Developing software for the world
- Closing the loop between internationalization and localization
- Content authoring with localization in mind
- Measuring software development for globalization
- How to justify and gaining approval for software globalization (i18n and L10n) from management
- Measuring ROI on your globalization projects
- Agile software development best practices, and much more
The event is open to Lingoport customers & friends and registration is requested. The event targets customer-side internationalization, localization, and globalization managers, software developers and engineers, content developers and technical writer, and anyone interested in understanding and promoting the software globalization process and the effects i18n and L10n have on an organization as a whole.
Agenda
2:30-3:00pm: Introductions and networking.
Coffee and cookies will be provided.
3:00-3:50pm: Presentation and Case Studies: Leading Globalized Development in Your Company
Tex Texin, Chief Globalization Architect at Rearden Commerce and Adam Asnes, Founder & CEO of Lingoport, will discuss how to lead globalized development within a company. Tex and Adam will also showcase real-life case studies and many best practices.
3:50-4:10pm: Break
4:10-5:00pm: Expert panel discuss – To Globalize, or Not. That is the Questions!
We’ll continue the afternoon with an expert panel discussion featuring some of the most experienced industry experts from Rearden Commerce, Acrolinx, Intel, Lingoport and Cisco. Developing software for the world has unique challenges and can add tremendous growth and value to a company’s bottom line. In today’s fast-paced and economically challenging business environment, software companies have very little room to make costly mistakes or to miss out on global opportunities. The goal of the panel discussion is to stimulate debate on a variety of software development, globalization, internationalization and localization related issues. Panel members will discuss real-world best practices and answer and discuss questions from the audience.
5:00-6:00pm: Open Bar – Networking and Discussion
We’ll conclude the afternoon with a networking session and drinks sponsored by Acrolinx and Lingoport. Many of us will then probably head over to the LocWorld opening reception dinner.
Round Table Facilitator
Erin Vang
Erin Vang, PMP, is Principal Pragmatist with Global Pragmatica LLC®, which offers facilitative leadership for technical audiences. She has over twenty years of experience in statistical software documentation, quality assurance, project management, and localization, most recently as International Program Manager for the JMP Research and Development at SAS, and previously with Abacus Concepts and SYSTAT. Vang holds degrees in music performance and math, is a PMI-certified Project Management Professional, and has extensive training in facilitative leadership and conflict resolution. She writes a regular column for Multilingual magazine and is in much demand as a speaker, event moderator, and facilitator.
Panelists
Tex Texin
Chief Globalization Architect at Rearden Commerce
Tex Texin, chief globalization architect at Rearden Commerce, has been providing globalization services including architecture, strategy, training and implementation to the software industry for many years. Tex has created numerous globalize products, managed internationalization development teams, developed internationalization and localization tools, and guided companies in taking business to new regional markets. Tex is also an advocate for internationalization standards in software and on the web. He is a representative to the Unicode Consortium and the World Wide Web Consortium.
Andrew Bredenkamp
CEO at Acrolinx
Andrew Bredenkamp is cofounder and CEO of Acrolinx. Andrew has over 20 years’ experience in multilingual information development. Before starting Acrolinx, Andrew was head of the Technology Transfer Centre at the German Research Centre for Artificial Intelligence language technology lab. Andrew holds degrees in technical translation and linguistics and a Ph.D. in computational linguistics. He is on the advisory board of a number of organizations, including Translators without Borders and The Centre for Next Generation Localisation.
Loïc Dufresne de Virel
Localization Strategist at Intel Corporation
Loïc Dufresne de Virel is currently a localization strategist within Intel’s in-house localization team. In this role, his main activities include overseeing the use of Intel’s translation management system and deployment of other localization tools, constantly advocating for proper and improved internationalization and localization practices and processes for web, software and “print” collateral, as well as defining the training roadmap for localization and internationalization. Prior to moving to Oregon and joining Intel, where he has been involved in localization for the past 12 years, Loïc spent a few years in Costa Rica, working as a regional technical adviser for the United Nations Conference on Trade and Development.
Richard Faubert
Manager, Software Development QA at Cisco
Richard Faubert has over 20 years of experience in telecommunications technical support for ROLM/IBM/Siemens. Richard joined Cisco Systems in 2000 and has worked in a number of capacities, including Software Development Manager and Program Manager. He is currently the QA Manager of Cisco’s TelePresence. Richard is an alumni of Washington State University.
Adam Asnes
President & CEO of Lingoport
Adam Asnes founded Lingoport in 2001 after seeing firsthand that the niche for software globalization engineering products and services was underserved in the localization industry. Lingoport helps globally focused technology companies adapt their software for worldwide markets with expert internationalization and localization consulting and Globalyzer software. Globalyzer, a market leading software internationalization tool, helps entire enterprises and development teams to effectively internationalize existing and newly developed source code and to prepare their applications for localization.
This event is by invitation only and limited to 50 participants. Register online and join us on Monday, October 10th at 2:30pm in Santa Clara, CA for an expert presentation and panel discuss on leading globalized software development.
Free sample of Erin Vang’s facilitation
by Erin Vang on Aug.04, 2011 , under facilitative leadership, localization, program management
A recording of the webinar panel discussion I moderated yesterday is now available here.
Bridging the gap between software development and localization
If you’re involved in software development, localization, internationalization, or globalization, you should watch this for a great introduction to the issues particularly with regard to stakeholder awareness, education, commitment, and communication—it was a lively panel with more than a few “hot button” topics getting lively debate, featuring a number of industry experts:
- Val Swisher, Founder & CEO of Content Rules
- Danica Brinton, Senior Director of International at Zynga
- Dale Schultz, Globalization Test Architect at IBM
- Edwin Hoogerbeets, Senior Internationalization Engineer at Palm
- Adam Asnes, CEO & President of Lingoport
- Erin Vang, Principal Pragmatist of Global Pragmatica LLC®
Free sample of Erin Vang’s work as a moderator
If you’re in charge of setting up panel discussions or conferences for your company and you’re wondering whether engaging a professional facilitator as your moderator for the event is worthwhile, you might want to watch this as a free sample—see how I work to make the conversation more valuable for the audience members and panel participants. I work as an audience advocate to ensure that the event delivers the content that was promised, that it’s lively and interesting, and that we get past the buzzwords, spin, and hot air right away—we get right into the content.
Bridging the Gap Between Software Development and Localization
by Erin Vang on Jul.28, 2011 , under facilitative leadership, localization, program management
Erin Vang moderates panel discussion on software l10n
Cross-posted from Lingoport.com
So, you’ve developed a new software application, and have high aspirations in terms of selling your application to a global audience. Now what? Problems often arise between developers, localization managers, and business managers due to perceived lack of support, time, and money.
This lack of understanding can lead to great frustration within the development tiers. Join us for an hour long online panel discussion and learn how some of the best known industry thought leaders are contributing to bridging the gap between software development and localization.
The panel features the following industry thought leaders and experts from the software development, content development, internationalization, and localization industries:
- Val Swisher, Founder & CEO of Content Rules
- Danica Brinton, Senior Director of International at Zynga
- Dale Schultz, Globalization Test Architect at IBM
- Edwin Hoogerbeets, Senior Internationalization Engineer at Palm
- Adam Asnes, CEO & President of Lingoport
Online Panel Discussion: “Bridging the Gap Between Software Development and Localization”
Date and Times: Wednesday, August 3rd at 9:30am PT / 10:30am MT / 11:30am CT / 12:30pm ET
Registration: Register for free @ https://www1.gotomeeting.com/register/964415249
Where: Your desktop
Erin Vang, Owner of GlobalPragmatica will be facilitating the online panel discussion. Erin has over twenty years of experience in statistical software documentation, quality assurance, project management, and localization, most recently as International Program Manager for the JMP Research and Development at SAS, and previously with Abacus Concepts and SYSTAT. She is currently designing a localization program for Dolby Laboratories.
This presentation is intended for technical managers, software engineers, test engineering managers, QA managers, internationalization and localization managers, technical writers, content developers, and anyone wanting to learn more on how to optimize their global software releases.
We’d love to hear from you. Please send any questions or topics you’d like to have discussed during this panel to Chris Raulf @ chris (at) lingoport.com.
Update 4 August 2011
The recording of our panel discussion is now available here.
Lessons from smart JSL mistakes
by Erin Vang on Jan.11, 2011 , under JMP & JSL
This is a follow-up to my previous post, “A script to check and change a JMP preference ($2).”
Background
Marianne Toft asked the JMP scripting group on LinkedIn a question about managing preferences with JSL:
Does anybody know how to read a specific setting from the Preferences? I need to read the status of ‘Use JMP locale settings’ and if 0 then change to 1 and give the user a message to restart JMP.
A $2 solution
I wrote a $2 script that solves the problem, and I sent Marianne a coupon code for a free copy, in thanks for posting such a thought-provoking JSL challenge.
My strategy is described in detail in that previous post, if you’d like to try writing it yourself with a bunch of pointers.
It’s also available as a $2 download if you’d rather just get the final code, fully commented and tested on JMP 8 and 9 on both Windows and Mac. One reason to consider buying this script instead of a tall drip is that the script is abstracted to check and change any preference, not just Use JMP Locale Settings( 0|1 ). That abstraction reveals some scripting tricks that might be helpful in your other scripting projects.
I’ll be delighted if someone else comes up with a more elegant solution, because mine is brute force. That’s because JMP doesn’t return the current preferences as an expression or a string or anything else you could potentially operate on with JSL. It just echoes the preferences to the Log window, so you need to read the text of the Log window and deal with that. If I’m missing something here, someone please enlighten me!
Several smart mistakes and lessons to learn from them
Several other people wrote helpful suggestions. Unfortunately, their code didn’t work, and we can all learn some important things about JSL by studying their smart mistakes.
Mark Anawi suggested using Show Preferences(Use JMP Locale Settings) )
to see the setting for just that preference. It’s a good guess at an argument for the Show Preferences()
command, but it’s not actually a supported argument. It doesn’t work. You’ll just get the usual output for Show Preferences() without an argument.
Lesson: when you supply an argument JMP doesn’t understand, sometimes you get errors, but a lot of the time it’s just ignored, silently. You get no indication that you did something that doesn’t work. The only way you can be sure it was ignored is to try it again without the argument, or with a different argument, and check whether anything changed.
Peter Wiebe wrote an elegant solution that uses a For()
loop to step through each Arg()
of an expression, looking for and if necessary changing the preference in question. His code is concise and beautiful:
jmp_rulz = getpreferences(); For( i = 1, i <= N Arg( jmp_rulz ), i++, If( Contains( Char( Arg( jmp_rulz, i ) ), "Use JMP Locale Settings(0)" ), Preferences( Use JMP Locale Settings( 1 ) ); New Window( "Restart JMP", Panel Box( "Restart JMP", Button Box( "OK", << closewindow ) ) ) ) );
His code is far more elegant than mine. It’s an efficient strategy! Unfortunately, it doesn’t work, for several reasons.
Get Preferences()
isn’t a command in JMP 8 or 9, so the first line returns an error:
Name Unresolved: get preferences in access or evaluation of 'get preferences' , get preferences() In the following script, error marked by /*###*/ ::jmp_rulz = get preferences() /*###*/
I assume that was just a typo, though. Peter meant Show Preferences()
, but as I warned at the outset, that command doesn’t return an expression. It just echoes text to the Log window. So, after running the first line (with “get” changed to “show”), jmp_rulz contains . (missing).
Therefore, N Arg(jmp_rulz)
i
is never < 0, so the For()
loop never runs.
I like Peter’s script better than mine. It should have worked—Show Preferences()
really ought to return an expression so that his For()
loop can step through it and look for the specific preference in question. Unfortunately it doesn’t. I hope our heroes in JMP R&D will consider improving this in a future version.
Why did Peter think this worked? Probably because when he ran it, he didn’t get any error messages. I didn’t either, and at first I was trying to figure out why his script worked when I’d already proven to myself that Show Preferences()
doesn’t return anything into the jmp_rulz
global.
He probably also checked the Log window after running it to confirm that the preference was on, saw that it was, and figured that it must have worked. He wouldn’t have expected to see his “Restart JMP” window unless it the setting was off to begin with, so he probably figured it had already been on.
Peter’s a clever scripter, by the way—whenever he posts something on LinkedIn, I make sure to read it, because I know I’ll learn something.
Lesson: Not getting error messages in the Log window doesn’t mean your script works.
Why should we care more than “Oops!”?
OK, we all make mistakes, especially when we’re writing JSL. (We do, right? Please tell me it’s not just me!) No big deal.
So why am I writing about these mistakes? Believe me, I’m not doing this to make anyone feel bad! They were both great suggestions, and they looked like they worked. Both Mark and Peter had good reasons to think they were correct.
I see some important lessons for JMP scripters.
Be aware that JMP has a lot of silent error conditions
Mark’s command Show Preferences(Use JMP Locale Settings)
didn’t produce any error messages, and it did echo the current settings to the Log, so it was reasonable for Mark to assume that putting a specific preference as an argument to the command was a valid syntax variation that would show the setting of that specific preference.
This is a type of mistake I make all the time: I come up with a syntax improvement in my imagination or in my failing memory, one that isn’t real, and it seems to work, so I think it is real.
But it’s not. There are a lot of situations where JMP is silent about errors in JSL. Sending an unrecognized argument to a command is a typical example. Another is when you put something that needs to be evaluated—say, an arithmetic expression to set a parameter from a global—inside an object message or platform launch command, and it turns out that the particular message or command you’re using is not actually capable of evaluating its arguments. (JMP is inconsistent that way, unfortunately. Sometimes you can get away with it, sometimes you can’t, and you can only find out by testing.)
Be sure you know which kinds of errors are silent (and deadly)
This article would go on for a hundred pages if I tried to list every possible error and whether JMP will report an error message or not, but here are some of the most important things to know:
Some typical mistakes that usually produce error messages:
- Operators that don’t exist, e.g.
Until()
instead ofWhile()
- Globals that don’t exist or haven’t been assigned values yet (or have had their values cleared with
Clear Globals()
- Using
=
instead of==
as a conditional test, e.g.If(a=1, write("a"), write("not a"))
would assign 1 toa
, but JMP alerts you to the fact that you probably meant to test whethera
equals 1 witha==1
Some typical mistakes that do not produce error messages:
- Messages that don’t exist, e.g.
Distribution[1]<<close;
instead ofDistribution[1]<<close window;
just silently pretends to run, returning the name of the object (in this example,Distribution[]
) as if it had done something to it - Arguments that don’t exist, e.g.
Show Preferences(Initial Splash Window)
instead ofShow Preferences(All)
simply runs as if you hadn’t included the unrecognized argument - Scoping mistakes, e.g. inside
Formula()
, any variable that you don’t explicitly scope, such as by putting::
before a global’s name, is going to be interpreted as a column name, and unless that leads to an error condition or some kind of unexpected behavior that you notice, you will never realize your mistake - Conditional mistakes, e.g.
For()
loops or other loops that never need to iterate simply don’t iterate, and they don’t tell you that they haven’t—unless you write some code to report on the iteration, e.g. you could set some global inside theFor()
loop and then check the global after theFor()
loop
Is it a bug when JMP doesn’t report an error?
Not necessarily.
In the case of invented messages not being flagged, my opinion is yes, that should be reported as an error. It’s too dangerous to think you’re accomplishing something and never realize JMP’s just ignoring you. But consider what the JMP developers are up against: JSL is so flexible that you can send lists of messages like obj << {mesg1, mesg2, …, mesgN}
, and you can send messages to an obj << mesg
group, and that’s just for starters. It would be hard to make the error-checking and reporting bulletproof.
What about invented arguments? Well, again, I’d say yes, if JMP can’t interpret something, it should say so. But again, consider what the developers are up against: most, but not all, commands in JSL can take arbitrarily complex expressions inside their parentheses. For example, myList={}; InsertInto(myList, 4)
is a straightforward way to put something in a list, but you could also have it calculate the thing it needs to insert, e.g. myList={}; InsertInto(myList, 1+3)
. That calculation could be incredibly complex, involving If()
tests and For()
loops and all kinds of insanity. Any number of things could go wrong inside those calculations. How is JMP’s InsertInto()
supposed to know that the problem is you made up an argument and not that you attempted a calculation that didn’t work out?
What about unscoped, mis-scoped, or insufficiently scoped variables? Hard to say. The name-resolution and scoping details of JSL keep improving with each new version of JMP, so that careful scripters can get better and better control of these kinds of problems, but for most scripters (and I include myself), the bigger problem is that we don’t realize when we’re not scoping carefully enough. For example, inside a Formula
expression, any variables that aren’t scoped otherwise will get interpreted as operators (if one by that name exists) or as columns (if one by that name exists). If either of those exist, then the global you thought you were calling is not going to come into play. For example:
x=1; column("Age Next Year") << set formula(x + :Age)
That should add 1 to each Age column’s value and put it in the Age Next Year column, right? Right. As long as you don’t also have a column named “x”! If you do, it’s going to add the value in the Age column to the value in the x column and put it in the Age Next Year column; more precisely, it will do that for each row.
Or what about this:
For=1; column("Age Next Year") << set formula(x + :Age)
Will that work? Well, if you want to play along, open good old Big Class.jmp, create an Age Next Year column, and try these two scripts. First try the one with x=1
. That’ll work fine since Big Class doesn’t have an x column. Now try the one with For=1
.
What happens?
Do you think it worked? Well, you might—after all, the Age Next Year column has the right answers in it, and you didn’t get an error message in the Log.
But did it? Let’s check more carefully. Change the first bit to For=5
and run it again. Do you get values like 17, 18, 19 as you should?
No!
Do you get error messages?
No!
What’s going on? Remember, For
is an operator! JMP sees a For()
operator even though its ()
parentheses and arguments aren’t there. Since For
has no arguments, it doesn’t do anything, and then it can’t add :Age to it because there’s no “it” to add it to, so the column is unchanged. JMP doesn’t think anything is wrong, because it didn’t see anything it couldn’t interpret. It knows For()
! It just didn’t realize that by For
what you really meant was 5!
The first time, when For=1
, we didn’t realize the problem, because the Age Next Year column already had the answers we expected and we didn’t have any way of knowing they didn’t change because of an error rather than because the answers were the same. The second time, when we were expecting different answers, we had a chance of noticing our mistake—but only if we checked those answers!
What do you do if you actually do want to use a global that shares a name with an operator or a column? You scope it. Change it to ::For
inside the Formula()
, and you’re back in business.
So, it’s not clear that JMP is in the wrong when errors don’t get messages. It’s not necessarily a bug, although it certainly can be frustrating! We can all hope that these kinds of errors will be trapped better in future versions of JMP, but in the meantime, we need to take that responsibility ourselves.
Tips on interpreting error messages
JMP’s error reporting is quite helpful, but it can be frustrating at times. Here are a few tips:
- Sometimes it’s hard to find the
/*###*/
in the Log to see where your error is. Don’t forget about Edit/Find, or the keyboard shortcut Ctrl-F or Command-F! - Sometimes the
/*###*/
will be in a misleading place. Start by investigating the thing that comes right before it, but if you’re sure that’s not in error, see if you can find some other problem that’s causing that thing to get read in some way other than what you meant, e.g. if you had a comma instead of a semicolon, it might be getting read as the “else” action in anIf()
, where you meant it to be an extra command in the “then” action. - When you get error messages about an unexpected or a missing
)
or,
(parentheses or commas), if the/*###*/
marker doesn’t help you, try getting some help from the Reformat Script utility. Try reformatting the whole script, and see where the cursor sits after you get the error message again. If you don’t see the problem nearby, try selecting chunks of the script and reformatting those. Keep going until you find the chunk of script that won’t format; your problem is inside that chunk somewhere. - Notice that the Reformat Script utility does helpful auto-indenting of your code, so that things happening inside other things will be indented further than their containers, and the closing
)
will appear vertically-aligned to the command and opening(
that JMP thinks it corresponds to. Make sure that JMP’s alignment of)
s matches what you think you’re doing. Getting messed up inside the parentheses is an especially common mistake when you’re writing custom display boxes. - Also try using the fence-matching feature: double-click on a parenthesis, and the Script Editor should highlight everything up to the matching parenthesis. See if it matches what you think it should; if it doesn’t, you’ve got one missing or extra somewhere. Note: one problem with the Script Editor’s fence-matching is that it won’t always select all the way up to a matching parenthesis; it’ll just do nothing when you double-click. This seems to happen when the chunk is too big—if the matching parenthesis is scrolled too far off-screen, JMP doesn’t even try. I hope this will be improved in a future version of JMP. In this situation, using an external programmer’s editor (such as jedit on Windows or BBEdit on Mac) can be helpful.
Be sure to test that your code is doing something
The smart JSL mistakes we saw above didn’t report errors, so the scripters didn’t realize anything was wrong. This brings up an important point: you need to test not only that the results you get are correct, but also that you’re getting results!
Mark’s error was to invent an argument. JMP’s response was to ignore the fictional argument. So how did he know it was a nonexistent argument and not just an undocumented one? The only way to be sure is to try the command twice—first without the argument, and again with the argument—and see if the results actually differ. If they don’t, that argument isn’t doing anything. Either it doesn’t exist, or you’re making some kind of a mistake in how you’re using it.
Peter’s mistake was not checking that his For()
loop actually did any iterations. Since NArg()
was zero, his i
counter that started at 1 was never less than zero, so the loop didn’t do a single iteration. JMP skipped right over it. There are several ways to test for this kind of problem. The easiest way is just to put Write()
statements inside the For()
loop that will report what’s going on to the Log window. As long as you make sure to look at the Log and see that the messages you expect are showing up, that’s good enough.
But if your For()
loop is happening as part of a larger program, you might not think to look for the absence of a message, right? In that case it would be better to lay a trap. For example, you could set a global right before the For()
loop, do something to that global inside the For()
loop, and then test the global after the For()
loop. For example:
test=0; For( i=1, i<6, i++, [ do whatever you need to do here ]; test++; ); if( test==0, throw( "The For loop did not iterate" ));
(Note: it wouldn’t necessarily be a mistake for a For()
loop never to iterate. There might come a situation where you want to write an iteration test that takes advantage of For()
‘s ability to decide whether to iterate or not.)
What are your issues with JSL errors?
What kinds of silent-but-deadly errors have you noticed? What kind of errors have you found it hard to track down? What tips do you have for trapping silent errors, or figuring out reported errors?
Please ask your questions and share your experiences in the Comments below. We’ll be happy to follow-up with replies to your comments or at greater length in future blog posts.
A script to check and change a JMP preference
by Erin Vang on Dec.23, 2010 , under JMP & JSL
Marianne Toft asked the JMP scripting group on LinkedIn a question about managing preferences with JSL:
Does anybody know how to read a specific setting from the Preferences? I need to read the status of ‘Use JMP locale settings’ and if 0 then change to 1 and give the user a message to restart JMP.
Several people had helpful suggestions, but unfortunately it’s just not a trivial problem. It’s a surprisingly tricky problem.
Challenges
First, by default Show Preferences()
only shows the preferences that you’ve taken to change. You need to use Show Preferences(all)
to see the defaults listed, too.
Second, Show Preferences(SomeSpecificPreferenceHere)
, although a good guess, doesn’t actually work. You’ll just get the usual output for Show Preferences() without an argument.
Lesson: when you supply an argument JMP doesn’t understand, sometimes you get errors, but a lot of the time it’s just ignored, silently. You get no indication that you did something that doesn’t work. The only way you can be sure it was ignored is to try it again without the argument, or with a different argument, and check whether anything changed.
Third, Show Preferences( [all] )
doesn’t return an expression. It returns missing. The information you need is echoed to the Log window, but it’s not returned as a string or an expression or a list or anything else you’d be able to do some work on. So you need to overcome that.
How to solve the basic problem
In JMP 9, you can Get Log(), which returns a list of strings, one for each line of whatever’s in the log. So to work efficiently, you need to Clear Log()
first, then Show Preferences(all)
, then do Get Log()
.
Well! Now you have a list of strings, and you need to find the item in that list that is relevant, so you need to put a Contains()
test inside a For()
loop.
It’s a string, so you have to transform it into an expression with Parse()
and some other bookkeeping, so that you can probe its argument with Arg()
.
Then check whether the argument is the desired one, and if not, change it, by editing the expression with Substitute()
and then immediately doing Eval()
on the result of that.
Now we need to let the user know we did something, and perhaps that they need to restart. So, put up a New Window()
with a Text Box()
saying that a preference has changed and they need to restart. Include a Button Box()
for “OK” that closes the alert window.
So, that’s the gist of the strategy.
Abstract the script
But, why write all this just for this one specific preference? Why not write it to check and if necessary change any preference?
So, start out with a couple globals that set the preference and the desired value.
But wait! Marianne’s example involves a numeric argument, but other preferences have text arguments; consider Use JMP Locale Settings(0)
vs. Report Table Style("Plain")
. So we need to make sure our script can work properly whether the particular preference takes a numeric or string value.
And while Marianne’s example requires a restart for the changed preference to take effect, most preferences do not. Might as well set a restartNeeded=1
if it’s important, or 0
if it’s not.
In that alert window, we only need to tell them to restart if it’s important, not if it’s not, so let’s add an If Box()
around the Text Box()
that only shows the instruction if it’s needed.
If the preference was what we wanted in the first place, we should probably echo something to the Log window, just so we know it ran, if nothing else. We need a Write()
statement for that.
Which version of JMP?
But wait, there’s more! The Get Log()
command didn’t come along until JMP version 9. If you haven’t been able to upgrade yet, then you need to take another strategy to this whole problem.
There might be a better way to do this, but the only way I could find was to Save Log()
to a text file, then Load Text File()
to a string, then use Substr()
and a bunch of arguments with Contains()
to chop out the single preference we care about. From there, the script runs about the same way.
And we need to put all this inside a test for JMP Version()
, which returns a string, and usually we need to Trim()
it and then do an inequality test, e.g. If( Trim( JMP Version() ) > "9", …)
, where the first argument is the Get Log()
strategy described first, and the second argument uses the Save Log()
strategy.
What about nested preferences?
Yet another problem! The script so far deals with top-level preferences with simple, single arguments, either numeric or character. But some preferences are trickier, e.g.,
Preferences( Fonts( English( Monospaced Font("Courier", 10) ) ) );
In this case, the Parse()
line will need to be more elaborate, and you’ll have to nest more Arg()
s around it to dig down to the particular setting value of interest. Conditioning for such a situation is left as an exercise for the reader, primarily because it’s a hassle, it’s not what Marianne needs, and the syntax of the Preferences()
command in JMP is arbitrary enough that we can’t really write code today that will be future-proof; we don’t know how deeply nested a given preference might be, nor what kind of arguments it might expect.
Buy now!
Contact me if you’re interested in purchasing this script or commissioning one like it.