Reusable, and Translatable: Oracle APEX Text Messages

APEX Text Messages is a powerful feature to organize reusable and translatable text fragments. You can use them in PL/SQL, JavaScript, and Substitution Strings.

Text messages are a feature that I should utilize more in Oracle APEX. They are easy to understand and implement, but I have to remind myself that they exist, so I am writing this blog post.

They are basically translatable text fragments that you can reference almost anywhere. My rule of thumb is that if you need a specific text in multiple languages or places, you should create a text message.

Here are some scenarios where I use them:

  • Help text (e.g., for a page item)
  • Error messages
  • Success messages
  • Confirm dialogs (“do you really want to delete this record?“)

How to Create a Text Message

When you visit the shared components, you can navigate to text messages in the bottom-right section. You can see and edit existing ones, or create new ones.

The name field is the identifier for the text message. If you want to use the message in JavaScript, you have to explicitly check the “Use with JavaScript” checkbox. The language field is only relevant if your app supports multiple languages.

How to use them

When you create a text message, you can use it in your app in different ways.

Substitution String

You can use substitution strings in almost any place in your APEX app. They will automatically be replaced with the text message. You wrap the message ID in &. and put the prefix APP_TEXT$ in front of it:

1&APP_TEXT$GREETING.
2

You may have seen this already if you let APEX create a form page for you. It will by default use the substitution &APP_TEXT$DELETE_MSG!RAW. for the delete confirm message. You can create this text message and change the text to change the warning text on any page.

Note that the reference uses the !RAW suffix. Without it, the text will be escaped. I am not sure why the auto-generated form page uses it, as the message does not include any HTML.

PL/SQL

You can also use text messages in PL/SQL. The APEX_LANG package has the function message for this purpose:

1-- case insensitive, greeting would also work
2apex_lang.message(p_name => 'GREETING')
3

You can add additional parameters that will be replaced in the text message. The text message can include placeholders %0 to %9 which will be replaced by the corresponding parameter:

1-- Text message: "His name is %0 %1"
2apex_lang.message(p_name => 'HIS_NAME', p0 => 'Robert', p1 => 'Paulson')
3-- Result: "His name is Robert Paulson"
4

Additionally, we could pass a language and reference messages from another app in the same workspace:

1apex_lang.message(
2  p_name           => 'HIS_NAME'
3, p0               => 'Robert'
4, p1               => 'Paulson'
5, p_lang           => 'it'
6, p_application_id => :APP_ID
7)
8-- Result: "Il suo nome è Robert Paulson"
9

JavaScript

I never had a use case where I needed the text messages in a JavaScript context. But you can still use them, and this is really nice!

It would be inefficient when all text messages would be loaded on every page load, even when you don’t use them. That’s why we need to explicitly load them before usage. You can do this with the apex.lang.loadMessages function. It takes an array of message IDs and returns a promise. When the promise is fulfilled, you can use the messages. Remember that you need to check the “Use with JavaScript” checkbox when you create the text message.

Strangely, the API to access the messages is different. You can use the apex.lang.getMessage function to get a message and additionally apex.lang.formatMessage to format one with parameters. I am not sure why there are separate functions and why you can’t pass the parameters as an object with named parameters, like it works with the PL/SQL function. Instead, you have to pass the parameters as separate arguments, one after another.

There are additional functions like hasMessage, loadMessagesIfNeeded, and formatMessageNoEscape. You can go to apex.oracle.com/jsapi and choose apex.lang to find out more about them.

1(async () => {
2  try {
3    await apex.lang.loadMessages(['GREETING', 'HIS_NAME']);
4    let result = apex.lang.getMessage('GREETING');
5    result += ' ';
6    result += apex.lang.formatMessage('HIS_NAME', 'Robert', 'Paulson');
7    apex.items.P20_FROM_JS.setValue(result);
8  } catch (err) {
9    apex.debug.error('Could not load messages', err);
10  }
11})();
12

About the JS:

As loadMessages calls the database and this is asynchronous, the function returns a promise. From the perspective of the browser, the database is an entirely different system that it has no control over. We send a message to it and hope that after some time it fulfills our request and returns something (the messages). This is different from synchronous code, where the browser has full control over the execution and the tasks are completed line by line.

The await keyword is a way to wait for the promise to resolve (anything to come back from the DB). It is only allowed in async functions. You may have seen another syntax where you use .then(...) to handle the result of the promise. This is the old way to do it; I like the newer await syntax more.

To be able to use await we have to put our code into a function and label it as asynchronous by writing async before it. We could have wrapped the code into a named function and immediately called it like async function run() { ... } run();. I like to use IIFEs (Immediately Invoked Function Expression) where you don’t give a name to the function and call it directly by putting these strange sets of ”()” in there.

Auto-use of translated text messages

If you have a multi-language app, you can translate your text messages. When you start creating a new translation, your existing text messages will be copied, and you can translate the text. If you add a new text message, you have to manually create another one for the other language.

Substitution strings have the benefit that APEX automatically uses the text message in the user’s language if it exists. This is really convenient.

I haven’t worked a lot with translated apps, but Flows for APEX builds on it. You can check out their builder app to see it in action. Here is one example for the message “APP_VIEW”:

Difference between shortcuts

In the shared components under “Other Components” there are also shortcuts. The help text says: “Use shortcuts to write frequently used code once and then reference it in many places within your application. Shortcuts also allow for the dynamic generation of code in places that typically only support static text.”

They work similarly, as they also allow you to store text, and you reference them with some kind of substitution syntax. You need to wrap the name in parentheses, like "MY_SHORTCUT". They do not allow translations or parameters, and they don’t have a nice API like text messages do.

They allow for dynamic content, as you can call a PL/SQL function that returns a string. There is even a type for images, but I have no clue how or when to use it.

My take is that they are more focused on HTML content than basic text content. I have not yet found a use case for them in practice; if you have, please share it in the comments.