Hard-coded strings are bad, mmmkay - ServiceNow Community
Alex Coope - SN
ServiceNow Employee
ServiceNow Employee

When I started my ServiceNow journey back in a chilly February of 2012, I was learning so many new things. At the time I remember trying to be super conscious of not picking up any bad habits, so I would always make special notes of things that might come and haunt me in the future.

For example, we've all heard that age old piece of advice of "comment your code". I find it fascinating that there's pros and cons to this, in that the con side is that it could impact performance - well if it's just a single line to remind you either what the function does, or what type of parameters it's expecting are, in reality it's going to be absolutely fine.

If it's an essay, then sure it might but in reality even with a 100k executions in a minute it would be so negligible that you wouldn't perceivably notice it. Therefore the pro is that in the future if you need to debug it, you can quickly catch-up to speed with what-ever the note states potentially saving you time and unnecessary troubleshooting steps.

The same is also true for single letter variables;

var a = b-c;


Best-practice over the decades of all technology implementations has taught us that variables should be meaningful, because if we (or someone else) has to come back to that bit of code in the distant future that person shouldn't need to unpick the code (no matter how skilled they are) in order to understand it's intent and thus fix it;

var RemainingFruit = Oranges - Bananas; // this is what's left in our shopping basket


So, being considerate in our code is a good thing. Everyone has to start their coding journey somewhere and no-one wants to be that person who stays until 1am on a Friday trying to unpick someone else's code where a single comment or a meaningful variable could have saved a lot of time, effort, stress and unnecessary work.

What if I said message strings were no different?

 

Just because we can, should we?
So let's take the idea of adding a simple info message to a form;

g_form.addInfoMessage('This is my message');

Now, lets imagine a few years later there is now a need to provide the services in another language. If there's lots of code snippets like this it would mean that:

  1. We'd need to find them - no small task
  2. We'd need to modify the scripts - again, no small task because it could cause feature inter-dependencies if it contradicts our development cycles.
  3. Because we can't easily identify, we don't know how many message's we need to consider for translations until 1 and 2 are actioned.

 

So, going back to our "Scripting in ServiceNow" course and remember what our tutors would say - "it's best to call getMessage". Putting the language aspect aside for a moment, the reason is actually quite simple. If the process owner ever wants to change the wording of the message then it doesn't require changing the code. Instead, the "message" in the [sys_ui_message] record needs updating which is just data.

What does the code amendment look like?

g_form.addInfoMessage(getMessage('This is my message'));

On it's own like this it doesn't do a whole lot. It is calling the message API and it will show "This is my message" but there's still one more thing we need to do. Which is to make a record in the [sys_ui_message] table:

Key - "This is my message"
Language - "en"
Message - "This is my message"

This means in the future if we want to change the text to "This might be my message", we can just update the record.

When it comes to the languages aspect, and if our coding standards have been thorough enough to rigorously follow this concept since the beginning, we therefore know that all we have to do is export our custom made records in [sys_ui_message] for translation and re-import in our additional desired target language.

There are some subtle nuances for different types of scripts but as general rule they follow this concept:
find_real_file.png

In that very bottom example, if we created a message record for a key of "show more", it absolutely could have a message of "this is my text" when called in the widget. This is to show and convey that the "key" is a unique entity meaning if you create it once, then it can be re-used in multiple places, saving you time, effort and potential future expense.

 

Wait, there's one more thing
When it comes to other languages, we need to be mindful that we also try and help the translators. Those translators (machine or humans) need some help aka context, to what they are translating:
find_real_file.png

The above is an example of a "concatenated string". This means that the sentence is made up of multiple objects within a script. 
Going back to our idea of "just because we can, should we?", yes it might be easy to write and do in English sadly it does not make it equally as easy in other languages due to differences in grammar and typical sentence structure.

In the first example saying "you have" with a number followed by "items in your shopping cart" might not work. If you don't speak English as your native tongue, try translating the 3 above examples and see what it comes across as.

 

Summary
What have we learned and what's our take away? Well, when designing and building a solution, always think about the future. Think about the ease of expansion in the future, think about how someone else could troubleshoot the design or add to the design. 

When I'm designing and building something I personally always ask my-self - "what would it be like for someone else looking at my code who doesn't know how to code as well as myself?" and "how easy is it to add to this?"

 

As usual, if you liked this, please like, share and subscribe as it always helps

 

 

Comments
Chuck Tomasi
Tera Patron

Great stuff @Alex Coope. A quick "couple of lesson learned the hard way" - My preference when using gs.getMessage() is to NOT pass nice human readable messages, but keys that look like something the system would use. For example:

gs.addInfoMessage(gs.getMessage('myapp_welcome_message'));

Why? Two reasons:

  1. It makes it EASY to spot if it's missing from sys_ui_message because the user will be greeted with 'welcome_message' instead of something nice. This ensures the developer isn't lazy and rely on a messing entry from sys_ui_message as a default. Which leads me to #2.
  2. If/when it comes time to translate, it's much easier to look for all those keys that start with 'myapp_' and export them for the translators than to wonder if anything is missing. Of course, this implies you have a naming standard on all your custom messages. 😉

When it comes to passing the parameters, the second argument is an array of arguments, so with the above tip on keys involved, the call should look more like this:

gs.addInfoMessage(gs.getMessage('myapp_fruit_message', [bananaCount, orangeCount, appleCount]));

In which case, the Message field on sys_ui_message would be:

You have {0} bananas, {1} oranges, and {2} apples.

If if there is only one arg to pass, pass an array and let getMessage() do the hard work.

EXTRA TIP: When using getMessage() in client scripts, add the keys to the Messages field in the Client Script to ensure they are pre-loaded rather than fetched at runtime to improve performance. It's easy to miss that Messages field.

find_real_file.png

Alex Coope - SN
ServiceNow Employee
ServiceNow Employee

Thanks @Chuck Tomasi,

Indeed, there's for sure further levels of API awesomeness. I'm actually now wondering if I should write a NowLearning course just on the Message API and all it's nuances 🙂

Chuck Tomasi
Tera Patron

Localization/translation would be a great topic. Include how to find missing translations, the four basic translation tables, debugging, gs.getMessage() etc. 🙂

Alex Coope - SN
ServiceNow Employee
ServiceNow Employee

@Chuck Tomasi, have you seen this that we launched recently?

David Arbour
Tera Guru

@Alex Coope I like to use system messages for catalog task short descriptions and descriptions. I put this script in a script include somewhere (typically a class I create just to be used in workflows) and call it in the catalog task script. You have to create a message for each type of catalog task, but you can be super dynamic with how those are setup because you can pass parameters based on logic.

function setTaskFields(task, key, parms) {
  var message = gs.getMessage('customer_prefix.' + key, parms);

  task.short_description = message.split('|')[0];
  task.description = message.split('|')[1];
}

An example of a message would be:

Receive {0}|Please receive {1} then close this task so the workflow can continue.

And in code you would call this like (this is just for demonstration purposes so not sure all of the syntax is correct):

new SomeScriptInclude().setTaskFields(task, 'receive_hardware', [
  current.variables.asset.model.name,
  'quantity ' + current.quantity + ' ' + current.variables.asset.model.display_name
]);
Alex Coope - SN
ServiceNow Employee
ServiceNow Employee

@David Arbour 

Indeed you can, however I would always urge consideration on keeping the key and the call as simple as possible, 

Imagine in a UAT cycle, how hard would it be to find a mis-translation's source for:

"quantity 7 servers"

 

Now imagine if your call was:

message = gs.getMessage("Number of servers remaining ") + current.quantity;

Also, your snippet is a prime example of a concatenated string, which is where the string to be translated will be constructed from multiple different objects making it extremely difficult to translate in other languages whilst maintaining its meaning. For example, your sentence won't work as intended in Japanese, Korean or German (and quite possibly other languages as well). This is because the word order is very different compared to English.

The takeaway is essentially this - keep the message string as simple as possible. The simpler the better so as its meaning in other languages is clear, concise and contextually correct (which will also help you find them should they need tweaking, updating, correcting etc). It goes back to my point in the post "Just because we can, should we?" no matter how tempting it is from a coding stand point 🙂

David Arbour
Tera Guru

I probably should have been more specific in my initial comment. The use case here is a bit different as I have never actually used this method for translations. I started doing this to give customers the ability to apply minimal updates to (dynamically generated) verbiage without having to update the workflow or change code. If translations were part of the implementations, I would have approached this differently. With Flow Designer gaining functionality with every release lately, I can't imagine keeping my method around much longer since it cannot be used to set task object properties as it can in Workflow.

Version history
Last update:
‎11-17-2021 03:04 AM
Updated by: