Using getMessage("message key") in ServiceNow Client Scripts
- nathanlee142
- Mar 18
- 18 min read
Updated: Apr 1

In ServiceNow development, hard-coding text strings in scripts can lead to maintenance challenges and poor user experience for non-English users. This is where the getMessage() function comes into play. The getMessage("message key") function is a client-side API that retrieves a localized message based on a key. Its primary purpose is to support internationalization (i18n) by pulling the correct language-specific text from the Messages table (sys_ui_message).
In client scripts, getMessage() allows developers to display dynamic messages (like instructions, alerts, or error messages) that automatically appear in the user's preferred language. This not only makes your ServiceNow application multilingual-ready but also centralizes message management – if the text needs to change, you update it in one place (the Messages table) rather than editing multiple scripts.
In summary, getMessage() is an essential tool for any ServiceNow developer who wants to write user-friendly, maintainable, and globally accessible client-side code.
Developers should use getMessage() because it improves reusability and consistency. Instead of duplicating literal strings across scripts, you define a message once and reference it by key. This means fewer errors (no typos in multiple places), easier updates, and seamless translation. Whether you're showing a simple confirmation note or a complex status message with dynamic values, getMessage() ensures the text is pulled from a single source of truth and presented appropriately for each user's locale. By leveraging this function, you adhere to best practices in ServiceNow client scripting and provide a more professional, localized experience to end users.
How getMessage() Works in Client Scripts
On the client side (in the browser), getMessage("message_key") looks up a message by its key and returns the corresponding text value. Under the hood, ServiceNow uses the message key to find a matching record in the Messages (sys_ui_message) table for the current user’s language. If a localized version exists for that language, it will return that; otherwise, it may fall back to a default (often English) message. In practical terms, when you write a client script and call getMessage("some.key"), you will get back a string that can be used in your script (for example, shown in an alert or info message on a form).
It’s important to note that in standard ServiceNow forms, getMessage() is readily available as a global function – you do not need to import or instantiate anything. However, to ensure the message is available on the client when your script runs, you typically pre-load it. The Client Script form in ServiceNow has a field called "Messages" where you can list message keys (one per line) that the script will use. ServiceNow will preload those messages to the client when the form loads. This means that by the time your client script executes, calling getMessage("your.key") will instantly return the localized string.
If you do not preload the message key via that field, the first time you call getMessage() the client may not yet have the translation. In older UI versions, this might result in the function returning the key itself (or an English default) on the first call, because it has to fetch the translation behind the scenes. In the modern platform, getMessage() will perform an asynchronous lookup if needed. For example, there's an option to call getMessage("your.key", callbackFunction), which will fetch the translation from the server and then execute your callback with the translated value once it’s available. Using the callback form ensures your script waits for the message to be retrieved before using it. Nonetheless, the best practice to avoid this complexity is to preload the messages using the Messages field whenever possible.
In summary, on the client side getMessage("message key") functions as a localized dictionary lookup: you provide a key, and it gives you the user-specific language string. By preparing your client script with the relevant message keys, you make this lookup instantaneous and seamless.
Client-Side vs. Server-Side getMessage()
ServiceNow provides getMessage() functionality on both client and server, but there are differences in how they work and are used:
Server-Side (gs.getMessage): On the server (e.g. in Business Rules, Script Includes, or any server script), you use the GlideSystem method gs.getMessage(). This method can retrieve messages similarly by key from the Messages table. One key difference is that on server side you can pass parameters directly for placeholders in the message. For instance, on the server you might write:
var msg = gs.getMessage("Hello {0}, you have {1} tasks.", ["John", 5]);
// msg would be "Hello John, you have 5 tasks." in the user's language
The server-side API allows either a second argument that is a single value or an array of values to replace {0}, {1}, etc. placeholders in the message string. Another form is passing multiple arguments after the key.
gs.getMessage("message_key", param1, param2, ...);
The GlideSystem will handle looking up the message by key and injecting the parameters. On the server, this is done synchronously (the code waits for the result), and since the server has direct access to the database, the lookup is straightforward. If the key is not found, often gs.getMessage() will simply return the key or the first argument string as-is (perhaps with the placeholders replaced if you provided a literal string instead of a key).
Client-Side (getMessage): In client scripts (browser side), you use getMessage() without a prefix (no gs. since GlideSystem is server-only). The client-side getMessage does not accept additional parameters to replace placeholders directly.
getMessage("my.key", "value")
If you try to call the code above on the client, it will likely ignore the second argument or throw an error – that usage is not supported on the client.
Instead, you retrieve the message text first, then handle any dynamic values in a second step (we will discuss how to do this with placeholders in the next section). Another difference is that the client must have the message available; it doesn't have automatic access to the database at will. As mentioned, you should preload messages via the Client Script Messages field to ensure the client-side has them. If not preloaded, the platform might fetch them asynchronously, which is why there's a callback option.
In summary, server-side gs.getMessage is more flexible in one call (supports parameter substitution and immediate availability), while client-side getMessage requires a bit more preparation (preloading and manually handling placeholders). Both serve the same goal: delivering the right message in the right language. When writing scripts, use gs.getMessage on server scripts and plain getMessage on client scripts; do not mix them up (e.g., there's no g_form.getMessage – g_form is a form API, whereas getMessage is a global function on the client).
Example: Using getMessage() in a Client Script
To illustrate how getMessage("message key") is used in a client script, let's walk through a step-by-step example. Suppose we want to show a friendly greeting to the user when a form loads, and that greeting should be translated based on the user's language. We will include a dynamic value (the user's name) in the message to demonstrate placeholders as well.
Scenario: On the Incident form, when the form loads, show an info message saying "Welcome [UserName], you have X open tickets." This message should be translated for users who use the platform in other languages.
Steps:
Define the Message in sys_ui_message: Navigate to System UI > Messages (the Messages table) and create a new message entry.
Key: welcome.greeting
(This is an arbitrary key we choose to identify the message. Often, using a descriptive key or even the English sentence as the key is common. We'll use welcome.greeting for clarity).
Message: (English) Welcome {0}, you have {1} open tickets.
(This is the default message text with placeholders {0} for the user's name and {1} for the number of tickets).
Language: English (en). (By default, we enter the English version. If your instance base language is English, this could be the default entry).
Save the record.
Optionally, create additional Message records for the same key welcome.greeting for other languages
For example, a French entry with Message: Bienvenue {0}, vous avez {1} tickets ouverts.). This will allow translation when a user’s language is French.
Create a Client Script on the Incident table (or whichever form you want this to run on).
Go to the Incident table's form (e.g., open any Incident record),
right-click the header, choose Configure > Client Scripts, and click New to create a client script.
Give it a name like "Show Welcome Message".
Set Type to onLoad (so it runs when the form loads).
In the Messages field of the client script, add the key welcome.greeting.
(This is crucial – it tells ServiceNow to preload the message for that key to the client. You can list multiple keys on separate lines if needed for larger scripts.)
In the Script field, write something like the following:
function onLoad() {
// Get the user's display name from the g_user
// object (provided by ServiceNow on client side)
// Retrieve the localized message by key
var userName = g_user.getFullName();
// Substitute the placeholders {0} and {1} with actual values
var rawMessage = getMessage("welcome.greeting");
// Display the message on the form as an info message
var finalMessage = rawMessage.withValues(userName, getOpenTicketCount());
// Helper function to get number of open tickets
// (for demo, returns a static number or could call GlideAjax)
g_form.addInfoMessage(finalMessage);
}
function getOpenTicketCount() {
return 5;
// In a real scenario,
// you might retrieve this via a GlideAjax call to count incidents
}
(We are assuming .withValues() is available on the returned message, which in recent versions and in Service Portal is true. If .withValues is not available in the particular client context, an alternative is shown below.)
Save the client script.
Test the Script: Open an Incident form as a user. When the form loads, you should see an information message at the top of the form that says, for example: "Welcome John Doe, you have 5 open tickets." This message came from the sys_ui_message entry. Now, to test localization, change your user’s language (for example, impersonate a user who has language set to French, and ensure you added a French translation for the key). Reload the form. The message should appear in French, with the placeholders replaced by the French user's name and the number of tickets. This confirms that getMessage pulled the correct translated message.
Understanding the Code: Let's break down what we did in the script:
We used g_user.getFullName() to get the current user's name. (g_user is a global object in ServiceNow client scripts that provides info about the logged-in user.)
We called getMessage("welcome.greeting") which looked up the message. Because we preloaded welcome.greeting in the Messages field, the platform already sent the translated text to the browser. rawMessage now holds a string like "Welcome {0}, you have {1} open tickets." in English, or the equivalent in the user's language.
We then called rawMessage.withValues(userName, getOpenTicketCount()). The withValues() function replaces the placeholders {0} and {1} in the message string with the values we passed (userName and the count). So if userName is "John Doe" and the count is 5, finalMessage becomes "Welcome John Doe, you have 5 open tickets.".
Finally, g_form.addInfoMessage(finalMessage) displays that message to the user as an info message on the form. (This is a typical way to show non-intrusive messages on forms. We could also use alert(finalMessage) for a pop-up, but that’s not as user-friendly in forms.)
Alternate Placeholder Handling: If for some reason withValues() was not available (some older versions or certain client contexts might not have it), we could achieve the same result by using a formatting approach.
For example:
var template = getMessage("welcome.greeting");
var finalMsg = template.replace('{0}', userName).replace('{1}', getOpenTicketCount());
g_form.addInfoMessage(finalMsg);
This manually replaces the placeholders in the string. Another approach is to use a custom format function similar to ServiceNow’s server logic. The key point is that on the client we must do this ourselves – getMessage alone only fetches the template.
Through this example, we saw how to properly define and use getMessage() in a client script. The steps highlight the need for setting up the message in the system, preloading it, and then retrieving and using it. This pattern can be used for any scenario where you need to display dynamic, translatable messages to the user.
Placeholders and the withValues() Function
Often, messages need to include dynamic data – like a user’s name, a number, a record identifier, etc. Hardcoding those values into a message would defeat the purpose of using a message key, and concatenating strings manually can lead to incorrect ordering in other languages. That’s why ServiceNow messages support placeholders. Placeholders are tokens like {0}, {1}, {2}, etc., placed within the message text to mark where dynamic values should be inserted.
For example, you might have a message defined as:
Key: task.update.notice
Message: Task {0} has been updated by {1}.
Here {0} and {1} are placeholders. In English, a usage might be intended as: "Task 12345 has been updated by Alice." In another language, the order might change but using placeholders ensures each value goes to the correct spot as defined by that language's message template.
On the server side, using placeholders is straightforward: gs.getMessage() allows passing the values.
For instance:
gs.getMessage("Task {0} has been updated by {1}.", [current.number, gs.getUserDisplayName()]);
If current.number is INC0010001 and the user is "Alice", the result might be "Task INC0010001 has been updated by Alice." (or the equivalent in the user's language if a translation exists for that message pattern). The server automatically replaces {0} with the first array element, {1} with the second, and so on.
On the client side, as discussed, getMessage() by itself does not take those values. This is where the withValues() function comes in handy. The withValues() function can be called on the object (or string) returned by getMessage() to perform placeholder substitution. In many client-side contexts (including the Service Portal's i18n service or even in the main UI if supported), you can do:
var messageText = getMessage("task.update.notice");
var finalText = messageText.withValues(taskNumber, updaterName);
After this, finalText would be "Task INC0010001 has been updated by Alice." given the earlier example values.
The withValues() function essentially mirrors what the server does: it takes the message string and replaces {0} with the first argument, {1} with the second argument, etc. It makes handling multiple placeholders much cleaner than doing a bunch of .replace() calls manually. It’s also less error-prone (you don't risk accidentally replacing the wrong part of the string since it targets {0}, {1} patterns specifically).
A few important notes on using placeholders and withValues():
Make sure the number of values you pass to withValues() matches the placeholders in the message. If your message has {0} and {1}, provide exactly two values. If you provide fewer, the untouched placeholder may remain in the output; if you provide more, the extras will be ignored.
The type of values: you can pass strings or numbers (they'll be converted to string in the output). If any value might be undefined or null, handle that beforehand to avoid literal "undefined" showing up in your message.
In some contexts, instead of using .withValues(), ServiceNow might have a global utility (for example, formatMessage() as shown in some ServiceNow community posts) that you can use to format the string. This custom function takes a string and a set of values and performs the placeholder substitution. You can either write your own or use the pattern:
function formatMessage(msg) {
if (arguments.length < 2) return msg;
// If second argument is an array, use it; otherwise use arguments
var args = Array.isArray(arguments[1])
? arguments[1] : Array.prototype.slice.call(arguments, 1);
for (var i = 0; i < args.length; i++) {
msg = msg.replace('{' + i + '}', args[i]);
}
return msg;
}
// Usage: var output = formatMessage(getMessage("task.update.notice"), taskNumber, updaterName);
This is an example for understanding; often, simply using withValues() is more concise if it is available in your client-side scope.
Service Portal / Mobile clients: In Service Portal (which uses AngularJS), there is an i18n Angular provider that offers methods like i18n.getMessage() and a similar .withValues() or i18n.format() to handle translations. In those environments, you typically ensure messages are preloaded using i18n.loadMessage() or by having them in the widget's server script context, and then call the client-side formatting. The concept is the same: placeholders in message templates get replaced with runtime values.
Using placeholders in your messages is highly recommended whenever a piece of the message might change (user names, counts, item names, etc.). It keeps the message template clean, and translations can rearrange those placeholders as needed for natural language. The withValues() function (or equivalent formatting method) makes it easy to plug in the current values without constructing strings manually.
Common Issues and Troubleshooting
When working with getMessage() in client scripts, developers might encounter a few common issues.
Here are those issues and how to troubleshoot them:
Message not translating (shows the key or English text): If you call getMessage("my.key") and the user sees what looks like the raw key or the default English text instead of a translated message, it usually means the translation wasn’t loaded or found.
First, ensure that you have a message record for that key in the Messages table for the user's language.
If the translation exists, then check if your client script listed the key in the Messages field.
Forgetting to preload the message is the number one reason for this issue. Add the key to the Messages field and try again.
If you cannot preload (perhaps because the message is dynamic or unknown until runtime), use the asynchronous callback form:
getMessage("my.key", function(translatedValue) {
g_form.addErrorMessage(translatedValue);
// use translatedValue here
});
This ensures the platform fetches the translation and then executes your code with the result. Keep in mind that using the callback makes the operation asynchronous, so any code dependent on that message should reside inside the callback function to execute in the right order.
Using the wrong function (e.g., g_form.getMessage): A common mistake for newcomers is to assume g_form (the client-side form API) has a method for messages. It does not. The correct usage is the global getMessage() function. So if you see something like g_form.getMessage("key") in your code, it will not work. Change it to getMessage("key"). The global function is provided by the platform and doesn’t need the g_form object.
Placeholders not replaced: If your message contains placeholders like {0} but after using getMessage the output still shows {0}, it means you didn't replace them on the client side.
Remember, getMessage() (client-side) by itself won't replace placeholders. You must use .withValues() or another method to insert actual values.
Double-check that this step is implemented. If you did use .withValues() but it’s still not working, ensure that the method is recognized in your context.
Perhaps the client API in older versions might not have had withValues() on a plain string. In that case, use the manual replace or format function approach. Essentially, if you see placeholders in the final message, the substitution step was missed or failed – add it or debug it.
Message key not found (typos or case sensitivity): The message lookup is usually case-sensitive, and it must match exactly the key in the Messages table. If you have a typo in the key name in your script or an extra space, it will not find the message and often returns the key itself.
For example, getMessage("welcome.greeting") is different from getMessage("Welcome.Greeting") (capitalization differences) unless you coincidentally created both keys.
Always double-check the spelling and case of your message keys in the script against what’s in the Messages table.
Service Portal or Workspace issues: In Service Portal, you might notice that getMessage() behaves differently. In fact, the Service Portal often uses a different mechanism for translations (like the $scope.i18n or i18n service). If you try to use getMessage() inside a Service Portal widget client script without preloading messages, it might not work as expected or may only return the English text. The solution is to use the Service Portal’s provided translation mechanisms:
Use the widget server script to add data.messages = getMessage("key") for keys you need, or
Use i18n.getMessage("key") in the client controller (but ensure the message is loaded via i18n.loadMessage("key","Translated text")).
Similarly, in the new Agent Workspace or other interfaces, there could be slight differences in how messages are retrieved. Consult the specific documentation for those interfaces if getMessage doesn’t behave as expected. Often, the cause is again about loading the message into the client context.
Multiple messages performance: If your client script uses many different messages, and especially if those might not all be used every time, you might worry about performance. Preloading dozens of messages is usually fine (they are small text strings), but if you have an extremely large number of them, consider whether all are necessary for every user action. You could load a core set on form load, and load others on demand with callbacks if needed. However, in most cases the performance impact is minimal, and it’s better to preload than to cause many server round-trips for messages. If you suspect that messages are not loading fast enough, you can verify by checking network requests: the platform may batch message fetches if not preloaded. The best practice is still to include the keys in the Messages field to avoid any extra network calls during user interaction.
Troubleshooting tips: Use jslog or console.log (in a scoped app, maybe gs.info from server-side for initial testing) to debug the values.
For example,
console.log("Message is: " + getMessage("some.key"));
This code can show you what the client received. If you see the key name in the log, you know the client didn't have the translation. If you see an English string while expecting another language, that means the translation for that language might be missing. Impersonate users of different locales to test that your messages switch languages correctly. Also, search the System Logs for any warnings about messages; sometimes ServiceNow logs a warning if a message key is requested but not found in the table.
By being aware of these issues and solutions, you can quickly fix problems with getMessage() usage and ensure your client scripts run smoothly in all languages.
Best Practices for Optimizing Message Retrieval
To get the most out of getMessage() in client scripts and optimize both development and performance, consider the following best practices:
Always Externalize User-Facing Text: As a rule, avoid putting raw user-facing strings in your client script code. Instead, define them in the Messages table and use getMessage() to retrieve them. This makes your script more maintainable (text can be changed without editing code) and translatable. It also signals to other developers that a given string is a message key, not literal text.
Use the Messages Field for Preloading: Take advantage of the Messages field on the Client Script form. List every message key your script will need. This ensures all those messages are sent to the client when the form loads, eliminating delays. It also avoids multiple server calls – the platform can send down a batch of messages in one go. If you have a Catalog Client Script or on a Service Portal widget, look for similar ways to preload messages (for example, in Catalog Client Scripts there's also a Messages field, and in Service Portal you might use $sp.getCatalogItem with messages).
Choose Consistent Message Keys: Many developers use the English phrase itself as the message key for simplicity (e.g., key = "Welcome {0}, you have {1} open tickets."). This works because if the key is not found in another language, it will default to showing the English (since that was literally the key). However, you might prefer shorter or more code-like keys (like "welcome.greeting"). Either approach is fine, but be consistent within your project or organization. If you use English sentences as keys, ensure they are exactly the sentence (to avoid mismatches). If you use abstract keys, ensure every script developer knows to add new keys to the Messages table with proper values.
Reuse Message Keys When Appropriate: If the same message or phrase is used in multiple places, reuse the same key everywhere. This way, you only need one entry per language in the Messages table. It reduces duplication and the chance of inconsistent translations. For example, if several forms show "Record saved successfully." you can have one message key for that and use getMessage("Record saved successfully.") in all scripts. This not only saves time for translators but also slightly improves performance by caching that message once per session.
Handle Dynamic Values with Placeholders: Plan your message strings to include placeholders for any dynamic data instead of concatenating strings in scripts. This is not just for translation reasons, but also for clarity. It's easier to manage one template string than pieces of strings in code. Plus, as discussed earlier, different languages might need a different word order – placeholders allow translators to position the variable parts correctly in the sentence. Always test that your substitution works and covers all placeholders.
Test with Multiple Languages: If your instance has internationalization enabled or if you plan to deploy to users in multiple locales, test your client script with at least one non-default language. Impersonate a user with a different language or change your own language preference, and verify that your messages appear translated. This is the best way to catch missing translations or keys that were not loaded properly. It’s much easier to fix these issues early than after deployment.
Use Callback When Preloading Isn’t Feasible: In some advanced cases, you might not know which message you need until runtime (for example, maybe you decide the key based on some data). In these cases, preloading every possible message is impractical. Instead, fetch on demand using getMessage(key, callback). Make sure to code the callback to gracefully handle the result. This approach should be the exception, not the norm, because excessive on-demand fetching can slow down the user experience. But it’s good to know the option exists for special scenarios.
Clean and Commented Code: When using message keys in script, consider adding a comment with the expected English text for clarity. For example:
// "welcome.greeting"-> expected output "Welcome {0}, you have {1} open tickets."
var msgTemplate = getMessage("welcome.greeting");
This helps future maintainers (or yourself) to know what that key was supposed to represent without having to look it up. It’s a small documentation touch that can save time.
Keep the Messages Table Organized: Over time, your sys_ui_message table can grow large. Use a naming convention for keys (perhaps prefix keys with app or module name like incident. or portal.) to group them logically. This isn’t directly related to performance, but it aids manageability. Also, periodically review for any obsolete messages that are no longer used by any script, especially if keys were changed – removing those can avoid confusion.
By following these best practices, you optimize both the performance (fewer unnecessary lookups, faster loads) and the maintainability (clear structure, easy updates, consistent translation) of your ServiceNow client scripts. Essentially, treat getMessage() usage as an integral part of client script design, not an afterthought. A well-planned use of messages makes your application robust and ready for a global audience.
Conclusion
In conclusion, the getMessage("message key") function is a powerful ally for ServiceNow client-side development. It enables you to write cleaner scripts by externalizing text, and it ensures your application’s messages can reach users in their own language without additional coding. We’ve learned that on the client side, getMessage() fetches a localized string by key, and with the help of placeholders and withValues(), it can dynamically inject variable data into those messages. Comparing client vs server, we saw that while the server-side gs.getMessage is straightforward with parameters, the client-side requires a bit of planning (preloading and formatting) to achieve the same result.
Key takeaways:
Always use getMessage() for any string that might be shown to an end user, rather than hardcoding the string in your script.
Prepare your client script by listing needed message keys in the Messages field to preload them, ensuring immediate availability.
Utilize placeholders in message text and handle them with .withValues() or similar logic to incorporate dynamic values.
Be mindful of potential issues like missing translations or not-found keys, and test your script in different languages.
Follow best practices such as reusing keys and keeping message keys organized for a maintainable codebase.
Next steps for developers: Try refactoring one of your existing client scripts to use getMessage(). Identify a message you’ve hardcoded (for example, an error message string in a script) and move it to the Messages table. Add the key to the client script’s Messages field, use getMessage() to retrieve it, and test it out. Notice how easy it becomes to change that message or add a translation without touching the script again. As you build new functionalities, design your client scripts with localization in mind from the start. Over time, you’ll build a library of message keys that can be reused and a reputation for creating user-friendly, global-ready ServiceNow apps.
By using getMessage("message key") effectively, you ensure that your ServiceNow client scripts are not only technically sound but also culturally adaptable, providing a smooth experience for users around the world. Happy scripting!