# Oracle APEX - apex_lang.message vs apex_lang.lang

This is a complicated subject because apex\_lang.message (`message`) and apex\_lang.lang (`lang`) are very similar and yet the differences are meaningful. Both `message` and `lang` allow you to do substitutions into the string and translate the string to another language. The devil, as they say, is in the details. Deciding when to use one or the other is complicated.

# TLDR

If you are translating your application to another language, use `message`.

If you want to store all of your translations (or substitutions) in a single application and use them in other applications, use `message`.

If you want to create translations dynamically (using an API) use `message`.

<mark>NOTE: </mark> `message` <mark>requires you to register a message in order to do substitutions or translations.</mark>

If none of the above apply, you just want to do substitutions, and you don't plan to translate to another language, use `lang`. (Yes, despite the name, `lang` should only be used for substitutions, not translations. And yes...`lang` can do translations, but `message` is just the better choice.)

<mark>Note: </mark> `lang` <mark>does not require you to register a dynamic translation to do substitutions, but it does require a dynamic translation to do translations. </mark> `lang` <mark>is case sensitive.</mark>

# The Details

The easiest way to show how each behaves is to show a few examples. These examples assume I have 2 APEX applications (app 111 and app 222). Both applications have two languages, English and Spanish. App 111 has translations defined for both `message` and `lang`. **App 222 does not have any translations defined.**

## apex\_lang.message

**App 111** has the following "Text Messages" defined in Shared Components &gt; Translate &gt; Text Messages:

| Name | English (en) | Spanish (es) |
| --- | --- | --- |
| GREETING | Hello | Hola |
| APPROVED | %0 approved expense report number %1. | %0 aprobó el informe de gastos número %1. |

Let's look at the results you get for certain input bind variables. Assume we have a classic report with the following query:

```sql
select apex_lang.message(p_name           => :P1_NAME,
                         p0               => :P1_PARAM0,
                         p1               => :P1_PARAM1,
                         p_lang           => :P1_LANG,
                         p_application_id => :P1_APP_ID) the_result
  from dual
```

If :P1\_LANG is null, apex\_lang.message assumes the current application language.

If :P1\_APP\_ID is null, apex\_lang.message assumes the current application id (:APP\_ID).

Sorry, you have to scroll left and right on the table below. I haven't figured out how to improve the format of this blog yet.

| The application you are running | Application Language | P1\_NAME | P1\_PARAM0 | P1\_PARAM1 | P1\_LANG | P1\_APP\_ID | the\_result | Notes about this result |
| --- | --- | --- | --- | --- | --- | --- | --- | --- |
| 111 | en | GREETING | any value | any value |  |  | Hello |  |
| 111 | es | GREETING | any value | any value |  |  | Hola |  |
| 111 | en | greeting | any value | any value |  |  | Hello | p\_name always converts to upper case in order to find the message. |
| 111 | en | grEeTing | any value | any value |  |  | Hola | p\_name always converts to upper case in order to find the message. |
| 111 | en | GREETING | any value | any value | es |  | Hola | because we specified the language, it did the translation |
| 222 | en | grEEting | any value | any value |  |  | APEX Version Dependent: Greeting (v22.1), grEEting (v19.2), GREETING (other versions) | because app 222 does not have messages defined, `message` converts to upper case, and can't find a value, so it returns "Greeting" in some manner, case depending on version. |
| 222 | en | greETing | any value | any value | es | 111 | Hola | Because we provided both p\_lang and p\_application\_id, `message` was able to find the translation in app 11 and convert to Spanish. |
| 111 | en | approved | Velimir | 17 |  |  | Velimir approved expense report number 17. |  |
| 111 | es | APPROVED | Velimir | 17 |  |  | Velimir aprobó el informe de gastos número 17. |  |
| 222 | en | Approved | Velimir | 17 |  |  | Approved | Case depends on APEX Version |
| 111 or 222 | en | %0 Approved expense report number %1 | Velimir | 17 |  |  | %0 APPROVED EXPENSE REPORT NUMBER %1 | `message` will not do substitutions if the string has not been registered. |

## apex\_lang.lang

**App 111** has the following "Dynamic Translations" defined in Shared Components &gt; Translate &gt; Dynamic Translations:

| Translation From | Language | Translate To |
| --- | --- | --- |
| Hello | es | Hola |
| %0 approved expense report number %1. | es | %0 aprobó el informe de gastos número %1. |

Let's look at the results you get for certain input bind variables. Assume we have a classic report with the following query:

```sql
select apex_lang.lang(p_primary_text_string    => :P1_NAME,
                      p0                       => :P1_PARAM0,
                      p1                       => :P1_PARAM1,
                      p_primary_language       => :P1_LANG) the_result
  from dual
```

If :P1\_LANG is null, apex\_lang.lang assumes the current application language.

apex\_lang.lang does not have the ability to look for a Dynamic Translation in another application. It must be in the current application.

Sorry, you have to scroll left and right on the table below. I haven't figured out how to improve the format of this blog yet.

| The application you are running | Application Session Language | P1\_NAME | P1\_PARAM0 | P1\_PARAM1 | P1\_LANG | the\_result | Notes about this result |
| --- | --- | --- | --- | --- | --- | --- | --- |
| 111 | en | Hello | any value | any value |  | Hello |  |
| 111 | es | Hello | any value | any value |  | Hola |  |
| 111 | en | hello | any value | any value |  | hello | hello, lower case, is not registered. |
| 222 | es | Hello | any value | any value |  | Hello | Hello, is not registered in app 222 and will not translate. |
| 111 | en | %0 approved expense report number %1. | Velimir | 17 |  | Velimir approved expense report number 17. |  |
| 111 | es | %0 approved expense report number %1. | Velimir | 17 |  | Velimir aprobó el informe de gastos número 17. |  |
| 111 | en | %0 approved expense report number %1. | Velimir | 17 | es | Velimir aprobó el informe de gastos número 17. | because we specified the language it translated. |
| 111 | es | %0 Approved expense report number %1. | Velimir | 17 |  | Velimir Approved expense report number 17. | apex\_lang.lang is case sensitive. Because "Approved" has a capital A it did not translate. |
| 222 | en | %0 approved expense report number %1. | Velimer | 17 |  | Velimir approved expense report number 17. | apex\_lang.lang does not require a registration to do subsitutions. |
| 222 | es | %0 approved expense report number %1. | Velimer | 17 |  | Velimir approved expense report number 17. | apex\_lang.lang does not require a registration to do subsitutions, but it will not translate without a registered translation. |
| 111 or 222 | en | Hi %0, have a great %1. | Michael | day |  | Hi Michael, have a great day. | apex\_lang.lang does not require a registration to do substitutions. |
