Oracle APEX 26.1 introduces a new text-message based translation approach. You can learn more about it in the APEX Developer’s Companion‘s Translating Your Application chapter.
After seeing multiple questions about how to migrate existing apps to the new approach, I explain the steps here with a simple example. It’s not yet an automated process, but the steps are not complicated.
Simple App with an Italian Translation
The simple_app_with_app_translation_in_italian_24_2.sql file is an APEX 24.2 export of a simple, one-page app with a page item and a button. It uses the application-based translation approach to cater to users in English and Italian.

Before, Literal Text Appears on Pages
As shown below, with the previous application-based translations, the app contains translatable text like Search Text below directly in pages. Translations exist in the Translation Repository, and a process of seeding and publishing copies the messages from the repository into a set of one or more “shadow apps.” At runtime, APEX uses the appropriate shadow app based on the current user’s language preference.

APEXlang Export Translation Repo Error
After importing the app into APEX 26.1, if we refresh the theme and then try to export the application in APEXlang, we receive an error:
This application contains translated text in the Translation Repository, which is not supported by the APEXlang format. Please continue using the SQL export format.
Overview of Manual Migration Process
A new application created in 26.1 uses the new Text Message-based app translations by default. However, an existing application will use the previous application-based approach.
The manual steps to change app translation approaches in APEX 26.1 are:
- Export each app translation as XLIFF file (from APEX 24.2 or 26.1)
- Delete 26.1 application-based language translations
- Change 26.1 app’s translation method to Text Message-Based
- Add back text-message-based app translations
- Export new text-message-based XLIFF file
- Merge existing app-based XLIFF translations into new XLIFF
- Import new XLIFF into new app translations
Exporting Translations as XLIFF Files
Either from APEX 24.2 or 26.1, export each of the existing app translations as an XLIFF file. This preserves your existing app translation efforts. Here I’m doing it from APEX 24.2 and choosing the single Italian translation my app has. If your application has multiple languages, you’d repeat this step once for each existing language.

This produces a file like f106_107_en_it.xlf that looks like the example below. The original app id is 106 in my example, and the id of the Italian shadow app is 107. That explains the file name f106_107_en_it.xlf.
Notice its trans‑unit elements each use a system-generated id attribute value. Notice as well that some text repeats multiple times, like Simple App and Home.
<!-- ****************** ** Source : 106 ** Source Lang: en ** Target : 107 ** Target Lang: it ** Filename: f106_107_en_it.xlf ** Generated By: SMUENCH ** Date: 16-JUL-2026 18:25:38 ****************** --><xliff version="1.0"> <file original="f106_107_en_it.xlf" source-language="en" target-language="it" datatype="html"> <header/> <body> <trans-unit id="S-5-1-106"> <source>Home</source> <target>Pagina iniziale</target> </trans-unit> <trans-unit id="S-6-1-106"> <source>Simple App</source> <target>App Semplice</target> </trans-unit> <trans-unit id="S-13-4077646825110622-106"> <source>Search</source> <target>Cerca</target> </trans-unit> <trans-unit id="S-20-4077809109110624-106"> <source>Container</source> <target>Contenitore</target> </trans-unit> <trans-unit id="S-20-4106849218854402-106"> <source>Simple App</source> <target>App Semplice</target> </trans-unit> <trans-unit id="S-28-4105966009854400-106"> <source>Home</source> <target>Pagina iniziale</target> </trans-unit> <trans-unit id="S-28-4107458760854411-106"> <source>&APP_USER.</source> <target>&APP_USER.</target> </trans-unit> <trans-unit id="S-28-4107976496854411-106"> <source>---</source> <target>---</target> </trans-unit> <trans-unit id="S-28-4108381575854412-106"> <source>Sign Out</source> <target>Disconnetti</target> </trans-unit> <trans-unit id="S-80-106-106"> <source>Simple App</source> <target>App Semplice</target> </trans-unit> <trans-unit id="S-100-4093906414854361-106"> <source>Home</source> <target>Pagina iniziale</target> </trans-unit> <trans-unit id="S-257-4098911142854384-106"> <source>Insufficient privileges, user is not an Administrator</source> <target>Privilegi insufficienti: l'utente non è un amministratore</target> </trans-unit> <trans-unit id="S-476-106-106"> <source>Simple App</source> <target>App Semplice</target> </trans-unit> <trans-unit id="S-14-4077769802110623-106"> <source>Search Text</source> <target>Testo da cercare</target> </trans-unit> </body> </file></xliff>
Deleting 26.1 App-Based Translations
Since the XLIFF files you exported above preserve your existing translation efforts, the next step is deleting the existing app-based translations. This is necessary to avoid the export-time error we saw above. For each app-based language-translation, edit its language mapping as shown below…

Then delete it…

Changing to Text-Message Translations
Under Shared Components > Globalization Attributes, you can now change the application’s Translation Method to Text Message-Based as shown below and apply the changes.

Adding Back Message-Based Translations
Once you’ve changed translation methods, you can add back the same set of application translation languages you had before. My simple app only had a single Italian translation, so I start by clicking (Add Language).

And choosing Italian from the Language select list.

When you add an application translation, if it’s the first language being added, App Builder creates a new shared component Text Message for each distinct translatable string and updates the application to reference the string using the new &{TEXTMSG}. substitution notation. You can observe this in Page Designer by studying the Label property of the P1_SEARCH_TEXT page item. As shown below, a new text message named SEARCH_TEXT got created, and the item’s Label is now supplied by the &{SEARCH_TEXT}. substitution. When using Text Message based translation, a text message picker “globe” button appears next to every translatable property and the base language text “Search Text” appears just below the field. If you don’t see the globe button, see Using the Text Messages Picker for more information on how to enable it.

Creating a new Text Message-based app translation also synchronizes the new app translation language’s text messages from the base language’s. Each gets tagged with the appropriate language code. You can see the effect in the Shared Components > Text Messages list page as shown below. Initially, the Italian language translations just use the English text.

Exporting Text Message-Based XLIFF file
Next, you export the XLIFF file for each of your Text Message-based application translations. Again, here I just have the single Italian one. You use the Export as XLIFF option on the “kebab” Actions menu as shown below.

This exports the f106_en_it.xlf file below. Notice the file name no longer has a second shadow app id in it. Also observe that the trans‑unit elements use the text message name as the id in the new approach, and that each distinct translatable string appears only once.
?xml version="1.0" encoding="UTF-8"?><xliff version="1.0"> <file original="f106_en_it.xlf" source-language="en" target-language="it" datatype="html"> <trans-unit id="SIMPLE_APP"> <source>Simple App</source> <target>Simple App</target> </trans-unit> <trans-unit id="HOME"> <source>Home</source> <target>Home</target> </trans-unit> <trans-unit id="SIGN_OUT"> <source>Sign Out</source> <target>Sign Out</target> </trans-unit> <trans-unit id="CONTAINER"> <source>Container</source> <target>Container</target> </trans-unit> <trans-unit id="ERROR_MESSAGE"> <source>Insufficient privileges, user is not an Administrator</source> <target>Insufficient privileges, user is not an Administrator</target> </trans-unit> <trans-unit id="SEARCH"> <source>Search</source> <target>Search</target> </trans-unit> <trans-unit id="SEARCH_TEXT"> <source>Search Text</source> <target>Search Text</target> </trans-unit> </file></xliff>
Merge Existing Translations to New XLIFF
So now we have our original target translated strings in f106_107_en_it.xlf and the new Text Message-based f106_en_it.xlf file. Both files have matching source elements containing the base language strings. What we wish existed is a utility to process each source element in the new file and replace its target string by the existing translation in the original application-based XLIFF file.
Rather than doing this by hand, I asked Codex to write me a Python program to save me time. I used the following prompt.
Create a python program that accepts two arguments. The first argument is the name of an XLIFF file from an apex application based on app-based message translation. The second argument is the name of an XLIFF file from a text-message-based translation application. The program will read the text-message-based XLIFF file and loop over the
/xliff/file/body/trans‑unit/sourceelements and lookup the first occurrence of the same/xliff/file/body/trans‑unit/sourceelement in the application based XLIFF file. Then it will update the target text of the text-message-based element to be the content it found from the application-based corresponding source entry. I’ve provided you two example files in the project directory.f106_107_en_it.xlfis the application-based XLIFF file.f106_en_it.xlfis the text-message-based XLIFF file. Notice they share the same source values in their elements, but the application-based message file already has been translated into Italian so its target elements have the data we want to propagate to the corresponding text-message-based XLIFF file. Notice, too, that the text-message-based file only has distinct entries and also uses text-message keys for the id instead of the generated ids. Let me know if you have any questions. The goal is that when I type:$ migrate_xlf_app2text f106_f107_en_it.xlf f106_en_it.xlf
The result is a new filef106_text_based_en_it.xlfthat merges the translations fromf106_f107_en_it.xlfinto the new text message trans-units off106_en_it.xlf, leaving the original files unchanged.
It produced the example migrate_xlf_app2text.py python script. This let me run the new command to produce a new f106_text_based_en_it.xlf file.
$ python3 migrate_xlf_app2text.py f106_f107_en_it.xlf f106_en_it.xlf
The f106_text_based_en_it.xlf file contains the Text Message based id values the new translation method expects, and now uses the existing target translations from the existing application-based XLIFF file as shown below:
<xliff version="1.0"> <file original="f106_en_it.xlf" source-language="en" target-language="it" datatype="html"> <trans-unit id="SIMPLE_APP"> <source>Simple App</source> <target>App Semplice</target> </trans-unit> <trans-unit id="HOME"> <source>Home</source> <target>Pagina iniziale</target> </trans-unit> <trans-unit id="SIGN_OUT"> <source>Sign Out</source> <target>Disconnetti</target> </trans-unit> <trans-unit id="CONTAINER"> <source>Container</source> <target>Contenitore</target> </trans-unit> <trans-unit id="ERROR_MESSAGE"> <source>Insufficient privileges, user is not an Administrator</source> <target>Privilegi insufficienti: l'utente non è un amministratore</target> </trans-unit> <trans-unit id="SEARCH"> <source>Search</source> <target>Cerca</target> </trans-unit> <trans-unit id="SEARCH_TEXT"> <source>Search Text</source> <target>Testo da cercare</target> </trans-unit> </file></xliff>
Import new XLIFF into App Translations
The last step is to import the now-translated text messages for the Text Message-based app translation languages back into APEX App Builder. Using the Import Text Messages link in the Text Message Translations section, as shown below you can pick the new f106_text_based_en_it.xlf file to apply.

Run the App to Text the Translations
You can now run the APEX 26.1 application from a browser that prefers Italian and validate the Italian translations using the new Text Message-based app translation.

App Now Exports Correctly to APEXlang
Since it no longer contains translation repository contents, the app now exports without error in APEXlang format. You can download my sample app and practice the steps on it before repeating them on your own application.
