How to Handle Errors, Debug, and Translate Messages Using APEX_ERROR and APEX_DEBUG

A tested guide to APEX_ERROR, APEX_DEBUG, and APEX_LANG in Oracle APEX, from inline errors and error handling to debug logs and translations.

Three things separate a polished Oracle APEX application from a prototype: friendly error messages, a debug trail when something goes wrong, and text that speaks the user's language. APEX_ERROR adds errors from code and powers an application's error handling function. APEX_DEBUG writes to the debug log at levels you control. APEX_LANG reads and maintains text messages and runs the whole translation of an application, from language mapping to publishing.

This guide covers all three with tested examples and their real output.

Quick Reference

TaskSubprogram
Add errors without stopping processingAPEX_ERROR.ADD_ERROR, HAVE_ERRORS_OCCURRED
Write an error handling functionINIT_ERROR_RESULT, EXTRACT_CONSTRAINT_NAME, GET_FIRST_ORA_ERROR_TEXT, AUTO_SET_ASSOCIATED_ITEM
Turn debugging on and offAPEX_DEBUG.ENABLE, DISABLE
Write debug messagesERROR, WARN, INFO, TRACE, MESSAGE, ENTER, TOCHAR
See debug output in scriptsENABLE_DBMS_OUTPUT, DISABLE_DBMS_OUTPUT, LOG_DBMS_OUTPUT
Log long text and session stateLOG_LONG_MESSAGE, LOG_PAGE_SESSION_STATE, GET_LAST_MESSAGE_ID, GET_PAGE_VIEW_ID
Clean up the debug logREMOVE_SESSION_MESSAGES, REMOVE_DEBUG_BY_VIEW, REMOVE_DEBUG_BY_APP, REMOVE_DEBUG_BY_AGE
Read text messagesAPEX_LANG.GET_MESSAGE, LANG
Maintain, export, and import text messagesCREATE_MESSAGE, UPDATE_MESSAGE, DELETE_MESSAGE, EXPORT_TEXT_MESSAGES, IMPORT_TEXT_MESSAGES
Translate a whole applicationCREATE_LANGUAGE_MAPPING, SEED_TRANSLATIONS, APPLY_XLIFF_DOCUMENT, PUBLISH_APPLICATION, and related subprograms

How to Run These Examples

The examples ran in Oracle APEX 26.1 against a test application with ID 200 that has English and German text messages. Most need an APEX session of that application, created with APEX_SESSION.CREATE_SESSION as shown in the guide to creating APEX sessions and managing session state from PL/SQL; the translation example instead sets the workspace. The error handling example inserts into the orb_promotions table of the Orbit Outfitters sample schema, from the orb_tables repository on GitHub. Run the examples as your workspace schema with server output switched on. The output under each example is exactly what the database printed.

Errors: APEX_ERROR

APEX collects the errors of a request, from validations, processes, and code, and shows them after processing: next to an item, in the notification area, or on an error page. APEX_ERROR adds errors from code and is the toolkit for an application's error handling function, which can rewrite every message before the user sees it.

ADD_ERROR and HAVE_ERRORS_OCCURRED

ADD_ERROR adds an error and lets processing continue, unlike raising an exception, which stops it, so a process can report several problems at once. The message is p_message, or the text message p_error_code with the values p0 to p9, escaped unless p_escape_placeholders is false. p_display_location is apex_error.c_inline_in_notification, c_inline_with_field, c_inline_with_field_and_notif, or c_on_error_page. The error can belong to a page item (p_page_item_name) or to a column and row of an interactive grid (p_region_id, p_column_alias, p_row_num). p_additional_info appears on the error page only. HAVE_ERRORS_OCCURRED returns whether the request has errors so far, for example to skip the rest of a process.

Syntax:

apex_error.add_error(p_message in varchar2 | p_error_code in varchar2, p0 .. p9 in varchar2 default null,
    p_escape_placeholders in boolean default true, p_additional_info in varchar2 default null,
    p_display_location in varchar2, [p_page_item_name in varchar2 | p_region_id in number,
    p_column_alias in varchar2 default null, p_row_num in number,] p_ignore_ora_error in boolean default false)
apex_error.have_errors_occurred return boolean

This example needs a session of application 200, page 20.

Example:

begin
    dbms_output.put_line('errors: ' || case when apex_error.have_errors_occurred then 'yes' else 'no' end);

    -- in a validation or process: the message goes to the notification area ...
    apex_error.add_error(p_message          => 'The order has no lines.',
                         p_display_location => apex_error.c_inline_in_notification);
    -- ... or next to a page item (and into the notification)
    apex_error.add_error(p_message          => 'Enter a quantity between 1 and 99.',
                         p_display_location => apex_error.c_inline_with_field_and_notif,
                         p_page_item_name   => 'P20_NUMBER');
    -- ... from a text message of the application, with placeholders
    apex_error.add_error(p_error_code       => 'APEX.PAGE_ITEM_IS_REQUIRED',
                         p0                 => 'Product',
                         p_display_location => apex_error.c_inline_with_field_and_notif,
                         p_page_item_name   => 'P20_PRODUCT');

    dbms_output.put_line('errors: ' || case when apex_error.have_errors_occurred then 'yes' else 'no' end);
end;
/

Output:

errors: no
errors: yes

On the page, these three errors look exactly like the ones validations raise: the notification lists all three, and the two item errors also appear below P20_NUMBER and P20_PRODUCT. When ADD_ERROR is called inside an exception handler, p_ignore_ora_error true stops the ORA error being handled from appearing as a second error. For older techniques, compare displaying custom error messages from a PL/SQL process.

The Error Handling Function

An application's Error Handling Function, under Application Definition, Error Handling, takes an apex_error.t_error and returns an apex_error.t_error_result. APEX calls it for every error, including internal ones, constraint violations, and unhandled exceptions, so it can change the message, where it appears, or which item it belongs to. t_error describes the error with message, additional_info, display_location, association_type, page_item_name, region_id, column_alias, row_num, apex_error_code, is_internal_error, is_common_runtime_error, ora_sqlcode, ora_sqlerrm, error_backtrace, error_statement, and component.

HelperPurpose
INIT_ERROR_RESULT(p_error)Returns a t_error_result with the error's message, additional info, display location, and item: the starting point.
EXTRACT_CONSTRAINT_NAME(p_error, p_include_schema)The name of the violated constraint, for ORA-00001, ORA-02091, ORA-02290, ORA-02291, and ORA-02292.
GET_FIRST_ORA_ERROR_TEXT(p_error, p_include_error_no)The text of the first ORA error, without the error stack.
AUTO_SET_ASSOCIATED_ITEM(p_error_result, p_error)Finds the page item or column for the violated constraint's column and sets it in the result, so the message shows next to the field.

This example needs a session of application 200, page 1. It violates a check constraint on purpose and runs the error through a handling function defined inside the block.

Example:

declare
    l_error  apex_error.t_error;
    l_result apex_error.t_error_result;

    -- an error handling function, as the application's Error Handling Function setting calls it
    function handle_error(p_error in apex_error.t_error) return apex_error.t_error_result is
        l_result apex_error.t_error_result := apex_error.init_error_result(p_error => p_error);
    begin
        if p_error.ora_sqlcode in (-1, -2091, -2290, -2291, -2292) then
            -- constraint violations: a message per constraint
            l_result.message := case apex_error.extract_constraint_name(p_error => p_error)
                                    when 'ORB_PROMOTIONS_DATES_CK' then 'A promotion must end after it starts.'
                                    else apex_error.get_first_ora_error_text(p_error => p_error) end;
            l_result.additional_info := null;
        end if;
        apex_error.auto_set_associated_item(p_error_result => l_result, p_error => p_error);
        return l_result;
    end;
begin
    begin
        insert into orb_promotions (promotion_id, promotion_name, start_date, end_date, discount_pct)
        values (999, 'Test', date '2026-06-30', date '2026-06-01', 10);
    exception when others then
        l_error.message          := sqlerrm;
        l_error.ora_sqlcode      := sqlcode;
        l_error.ora_sqlerrm      := sqlerrm;
        l_error.display_location := apex_error.c_inline_in_notification;
    end;

    dbms_output.put_line('ORA text:   ' || apex_error.get_first_ora_error_text(p_error => l_error));
    dbms_output.put_line('with code:  ' || apex_error.get_first_ora_error_text(p_error => l_error, p_include_error_no => true));
    dbms_output.put_line('constraint: ' || apex_error.extract_constraint_name(p_error => l_error, p_include_schema => true));
    l_result := handle_error(l_error);
    dbms_output.put_line('shown:      ' || l_result.message || ' (' || l_result.display_location || ')');
end;
/

Output:

ORA text:   check constraint (ORBIT.ORB_PROMOTIONS_DATES_CK) violated
with code:  ORA-02290: check constraint (ORBIT.ORB_PROMOTIONS_DATES_CK) violated
constraint: ORBIT.ORB_PROMOTIONS_DATES_CK
shown:      A promotion must end after it starts. (INLINE_IN_NOTIFICATION)

A common production pattern maps constraint names to text messages with apex_lang.get_message, hides the details of internal errors from users by checking p_error.is_internal_error, and logs them with a reference number the user can quote to support. A complete walkthrough is in the Oracle APEX error handling function example.

Debugging: APEX_DEBUG

APEX writes a debug log for requests with debugging on, through DEBUG=YES in the URL, the Developer Toolbar, or APEX_DEBUG.ENABLE. The Developer Toolbar's View Debug and the APEX_DEBUG_MESSAGES view show it. Each message has a level: apex_debug.c_log_level_error (1), c_log_level_warn (2), c_log_level_info (4, what DEBUG=YES logs), c_log_level_app_enter (5), c_log_level_app_trace (6), c_log_level_engine_enter (8), and c_log_level_engine_trace (9). A message is logged when its level is at most the enabled level, except ERROR messages, which are always logged, even with debugging off. The messages of a request are written when it ends.

ENABLE and DISABLE

Turn debugging on for the rest of the session at a level, c_log_level_info by default, or off again. In a job or automation, enable it in the code to see what happened afterwards.

ERROR, WARN, INFO, TRACE, MESSAGE, and ENTER

ERROR, WARN, INFO, and TRACE log a message at their level; %s in the message takes p0 to p9 in turn, and %0 to %9 by position. ERROR and WARN add the call stack. MESSAGE logs at p_level (c_log_level_info by default) and takes up to 20 values, and p_force true logs even with debugging off. ENTER logs the start of a procedure with up to ten parameter names and values at c_log_level_app_enter, which makes it the natural first line of every procedure worth tracing. TOCHAR turns a Boolean into true, false, or null for messages.

Syntax:

apex_debug.error | warn | info | trace(p_message in varchar2, p0 .. p9 in varchar2 default null,
    p_max_length in pls_integer default 1000)
apex_debug.message(p_message in varchar2, p0 .. p19 in varchar2 default null, p_max_length in pls_integer default 1000,
    p_level in t_log_level default c_log_level_info, p_force in boolean default false)
apex_debug.enter(p_routine_name in varchar2, p_name01 in varchar2 default null, p_value01 in varchar2 default null,
    ... p_name10, p_value10, p_value_max_length in pls_integer default 1000)
apex_debug.tochar(p_value in boolean) return varchar2

ENABLE_DBMS_OUTPUT, DISABLE_DBMS_OUTPUT, and LOG_DBMS_OUTPUT

ENABLE_DBMS_OUTPUT also writes every logged message to DBMS_OUTPUT with a prefix, so you can watch APEX code run in SQL*Plus, SQLcl, or a unit test. DISABLE_DBMS_OUTPUT stops it. LOG_DBMS_OUTPUT works the other way round: it copies what code wrote with DBMS_OUTPUT into the debug log, for older code that prints its tracing.

LOG_LONG_MESSAGE, LOG_PAGE_SESSION_STATE, GET_LAST_MESSAGE_ID, and GET_PAGE_VIEW_ID

LOG_LONG_MESSAGE logs text longer than 4,000 characters as several messages. LOG_PAGE_SESSION_STATE logs the items of a page, or of all pages, with the length of their values; in 26.1 the values themselves appear as ***. GET_LAST_MESSAGE_ID returns the ID of the last message, and GET_PAGE_VIEW_ID the ID of the page view the messages belong to, which is null until the first message of a request.

This example needs a session of application 200, page 20.

Example:

begin
    apex_debug.enable(p_level => apex_debug.c_log_level_info);   -- like DEBUG=YES
    apex_debug.info('Start');
    -- also write the messages to DBMS_OUTPUT: handy in scripts and tests
    apex_debug.enable_dbms_output(p_prefix => '[debug] ');

    apex_debug.error('Order %s: payment failed (%s)', 'ORD-10042', 'card declined');
    apex_debug.warn('Stock of %0 below %1', 'TNT-1002', 5);
    apex_debug.info('Recalculating %s lines, forced: %s', 3, apex_debug.tochar(true));
    apex_debug.message(p_message => 'Total: %s', p0 => to_char(1047.30), p_level => apex_debug.c_log_level_info);
    apex_debug.trace('Line %s: %s', 1, 'TNT-1002');            -- level 6: below the enabled level
    apex_debug.enter('orb_orders_pkg.recalc', 'p_order_id', 42);  -- level 5: not logged either

    apex_debug.disable_dbms_output;
    apex_debug.log_long_message(p_message => rpad('x', 5000, 'x'), p_level => apex_debug.c_log_level_info);
    apex_debug.log_page_session_state(p_page_id => 20, p_level => apex_debug.c_log_level_info);
    dbms_output.put_line('last message ID: ' || case when apex_debug.get_last_message_id > 0 then 'set' end);
    dbms_output.put_line('page view ID:    ' || case when apex_debug.get_page_view_id > 0 then 'set' end);
end;
/

Output:

######
###### State changes to: app=>200, page=>20, session=>799643294676, workspace=>36520348764463069, user=>ADMIN
######
[debug]  ERR Order ORD-10042: payment failed (card declined)                                            %DEBUG_API.error:140<:7
[debug]  WRN Stock of TNT-1002 below 5                                                                  %DEBUG_API.warn:176<:8
[debug]  INF Recalculating 3 lines, forced: true
[debug]  INF Total: 1047.3
last message ID: set
page view ID:    set

Only the messages at or below the INFO level reached DBMS_OUTPUT; the TRACE and ENTER lines sat above the enabled level and were skipped. The ERROR and WARN lines carry their call stack at the end. Leave these calls in your code: they cost almost nothing when debugging is off. The deprecated LOG_MESSAGE, with p_enabled and p_level, is replaced by MESSAGE and the level procedures. For plain DBMS_OUTPUT techniques, see using DBMS_OUTPUT to debug PL/SQL.

REMOVE_SESSION_MESSAGES, REMOVE_DEBUG_BY_VIEW, REMOVE_DEBUG_BY_APP, and REMOVE_DEBUG_BY_AGE

Delete debug messages of a session (the current one by default), of a page view, of an application, or of an application older than a number of days. APEX also purges old messages by itself, according to the instance setting Delete Debug Data After.

Syntax:

apex_debug.remove_session_messages(p_session in number default null)
apex_debug.remove_debug_by_view(p_application_id in number, p_view_id in number)
apex_debug.remove_debug_by_app(p_application_id in number)
apex_debug.remove_debug_by_age(p_application_id in number, p_older_than_days in number)

Reading the debug log in the Builder is covered in the guide to debugging, source control, and going to production.

Translation: APEX_LANG

APEX translates an application in two ways. Text messages, under Shared Components, Text Messages, are named strings per language that code and substitutions such as &{NAME} look up at runtime. Translated applications are copies of the application, one per language, generated from a translation repository that holds every label, heading, and help text, and chosen at runtime by the Application Language Derived From setting. The overall approach is covered in the guide to globalization and translation in Oracle APEX.

GET_MESSAGE and LANG

GET_MESSAGE returns a text message in the session's language, or in p_lang, from the current application or p_application_id. Its placeholders are named, %name, with %0 counting as the name 0, and p_params is a list of names and values in turn. An unknown name returns the name itself, and a message missing in the requested language falls back to the application's primary language, which is new in 26.1. LANG looks up a string in the translation repository of the published translation for the session's language, with placeholders %0 to %9, and returns the string unchanged when there is no translation. MESSAGE, with p0 to p9, is deprecated.

Syntax:

apex_lang.get_message(p_name in varchar2, p_params in apex_t_varchar2 default apex_t_varchar2(),
    p_lang in varchar2 default null, p_application_id in number default null) return varchar2
apex_lang.lang(p_primary_text_string in varchar2 default null, p0 .. p9 in varchar2 default null,
    p_primary_language in varchar2 default null) return varchar2

This example needs a session of application 200, page 1.

Example:

begin
    -- text messages of the application (Shared Components > Text Messages), in the session's language ...
    dbms_output.put_line(apex_lang.get_message('ORDER_DATE'));
    -- ... or another one; p_params are name/value pairs for the placeholders (%0 has the name 0)
    dbms_output.put_line(apex_lang.get_message(p_name => 'ORDER_DATE', p_lang => 'de'));
    dbms_output.put_line(apex_lang.get_message(p_name => 'ORBIT_DISCOUNT_APPROVAL', p_params => apex_t_varchar2('0', '20')));
    dbms_output.put_line('[' || apex_lang.get_message('NO_SUCH_MESSAGE') || ']');   -- the name itself

    -- a string of the translation repository, in the session's language
    dbms_output.put_line(apex_lang.lang(p_primary_text_string => 'Orders over %0 need approval', p0 => '$5,000'));
end;
/

Output:

Order Date
Bestelldatum
A discount above 20% needs approval. Set the status to Pending Approval.
[NO_SUCH_MESSAGE]
Orders over $5,000 need approval

The browser-side counterpart, apex.lang, is covered in the guide to formatting dates, numbers, and translated text with apex.date and apex.locale.

CREATE_MESSAGE, UPDATE_MESSAGE, and DELETE_MESSAGE

Maintain text messages. CREATE_MESSAGE adds one for a language, and p_used_in_javascript true also makes it available to apex.lang.getMessage in the browser. UPDATE_MESSAGE and DELETE_MESSAGE change or remove one by its ID, the TRANSLATION_ENTRY_ID in APEX_APPLICATION_TRANSLATIONS. A second UPDATE_MESSAGE overload changes the name, language, and other attributes too.

Syntax:

apex_lang.create_message(p_application_id in number, p_name in varchar2, p_language in varchar2,
    p_message_text in varchar2, p_used_in_javascript in boolean default false, p_comment in varchar2 default null,
    p_metadata in clob default null)
apex_lang.update_message(p_id in number, p_message_text in varchar2)
apex_lang.delete_message(p_id in number)

This example needs a session of application 200, page 1. It rolls back at the end.

Example:

declare
    l_id   number;
    l_text varchar2(4000);
begin
    apex_lang.create_message(p_application_id     => 200,
                             p_name               => 'ORBIT_WELCOME',
                             p_language           => 'en',
                             p_message_text       => 'Welcome back, %name! You have %count open orders.',
                             p_used_in_javascript => true,       -- also for apex.lang.getMessage
                             p_comment            => 'Home page greeting');
    apex_lang.create_message(p_application_id => 200, p_name => 'ORBIT_WELCOME', p_language => 'de',
                             p_message_text   => 'Willkommen zurück, %name! Sie haben %count offene Bestellungen.');
    dbms_output.put_line(apex_lang.get_message(p_name => 'ORBIT_WELCOME', p_params => apex_t_varchar2('name', 'Kim', 'count', '3')));
    dbms_output.put_line(apex_lang.get_message(p_name => 'ORBIT_WELCOME', p_params => apex_t_varchar2('name', 'Kim', 'count', '3'), p_lang => 'de'));

    select translation_entry_id into l_id from apex_application_translations
     where application_id = 200 and translatable_message = 'ORBIT_WELCOME' and language_code = 'en';
    apex_lang.update_message(p_id => l_id, p_message_text => 'Good to see you, %name.');
    select message_text into l_text from apex_application_translations where translation_entry_id = l_id;
    dbms_output.put_line('updated:     ' || l_text);
    -- get_message caches the messages it has read: the change shows in the next request
    dbms_output.put_line('get_message: ' || apex_lang.get_message(p_name => 'ORBIT_WELCOME',
                                                                  p_params => apex_t_varchar2('name', 'Kim', 'count', '3')));
    apex_lang.delete_message(p_id => l_id);
    rollback;   -- keep the lab as it was
end;
/

Output:

Welcome back, Kim! You have 3 open orders.
Willkommen zurück, Kim! Sie haben 3 offene Bestellungen.
updated:     Good to see you, %name.
get_message: Welcome back, Kim! You have 3 open orders.

GET_MESSAGE caches the messages it has read for the request, which is why the last line still shows the old text; the change appears in the next request.

EXPORT_TEXT_MESSAGES and IMPORT_TEXT_MESSAGES

New in 26.1. Export the text messages of one language as XLIFF or CSV (apex_lang.c_export_format_xliff, c_export_format_csv), or of all languages as a ZIP file, for translators, then import the translated file, which updates existing messages and adds missing ones.

Syntax:

apex_lang.export_text_messages(p_application_id in number, p_lang_code in varchar2,
    p_format in t_export_format default c_export_format_xliff) return clob
apex_lang.export_text_messages(p_application_id in number, p_format in t_export_format default ...) return blob
apex_lang.import_text_messages(p_application_id in number, p_file in clob, p_format in t_export_format default ...)
apex_lang.import_text_messages(p_application_id in number, p_zip_file in blob)

This example needs a session of application 200, page 1. It rolls back at the end.

Example:

declare
    l_file clob;
    l_zip  blob;
    l_dir  apex_zip.t_dir_entries;
    l_name varchar2(4000);
begin
    -- one language as XLIFF (or CSV), for a translator
    l_file := apex_lang.export_text_messages(p_application_id => 200, p_lang_code => 'de',
                                             p_format => apex_lang.c_export_format_csv);
    dbms_output.put_line(dbms_lob.getlength(l_file) || ' characters of CSV, starting:');
    for r in (select column_value as line from table(apex_string.split(substr(l_file, 1, 400), chr(10)))
               fetch first 3 rows only) loop
        dbms_output.put_line('  ' || r.line);
    end loop;

    -- all languages: a ZIP file with a file per language
    l_zip := apex_lang.export_text_messages(p_application_id => 200, p_format => apex_lang.c_export_format_xliff);
    l_dir  := apex_zip.get_dir_entries(l_zip);
    l_name := l_dir.first;
    while l_name is not null loop
        dbms_output.put_line('in the ZIP: ' || l_name);
        l_name := l_dir.next(l_name);
    end loop;

    -- and back: the translated file updates (and adds) the messages
    apex_lang.import_text_messages(p_application_id => 200, p_file => l_file,
                                   p_format => apex_lang.c_export_format_csv);
    dbms_output.put_line('imported');
    rollback;
end;
/

Output:

41234 characters of CSV, starting:
  name,source,target,source language,target language,comment
  TOP_CUSTOMERS,Top Customers,Top-Kunden,en,de,
  TOP_USERS,Top Users,Top Users,en,de,
in the ZIP: f200/f200_en_de.xlf
imported

Translated Applications

SubprogramPurpose
CREATE_LANGUAGE_MAPPING(p_application_id, p_language, p_translation_application_id, p_direction_right_to_left, p_image_directory)Maps a language to the ID of its translated application.
UPDATE_LANGUAGE_MAPPING(p_application_id, p_language, p_new_trans_application_id), DELETE_LANGUAGE_MAPPING(p_application_id, p_language)Change or remove a mapping; removing it also deletes the language's strings in the translation repository.
SEED_TRANSLATIONS(p_application_id, p_language)Copies the application's translatable strings into the repository, and its text messages into the language if missing.
GET_XLIFF_DOCUMENT(p_application_id, p_page_id, p_language, p_only_modified_elements)The repository's strings for the application or a page, as XLIFF.
APPLY_XLIFF_DOCUMENT(p_application_id, p_language, p_document)Loads a translated XLIFF document into the repository.
UPDATE_TRANSLATED_STRING(p_id, p_language, p_string)Sets one translated string, by its ID in APEX_APPLICATION_TRANS_REPOS.
PUBLISH_APPLICATION(p_application_id, p_language, p_new_trans_application_id)Generates the translated application from the repository.
GET_LANGUAGE_SELECTOR_LIST, EMIT_LANGUAGE_SELECTOR_LISTThe HTML of a list of links to the published languages, returned or written to the page.

This example runs outside an APEX session after setting the workspace. Before it ran, any French mapping and French text messages left from an earlier run were removed. It maps, seeds, translates, publishes, remaps, and deletes a French version of the application.

Example:

declare
    l_xliff clob;
    l_id    number;
    procedure show(p_label varchar2) is
    begin
        for m in (select translated_application_id, requires_synchronization from apex_application_trans_map
                   where primary_application_id = 200) loop
            dbms_output.put_line(rpad(p_label, 12) || 'fr -> ' || m.translated_application_id
                                 || ', needs publishing: ' || m.requires_synchronization);
        end loop;
    end;
begin
    apex_util.set_workspace('APEXBOOK');

    -- 1. a French version of the lab, published as application 200001
    apex_lang.create_language_mapping(p_application_id => 200, p_language => 'fr',
                                      p_translation_application_id => 200001);
    show('mapped');

    -- 2. copy the translatable strings into the translation repository
    apex_lang.seed_translations(p_application_id => 200, p_language => 'fr');

    -- 3. translate: one string directly ...
    select id into l_id from apex_application_trans_repos
     where application_id = 200 and language_code = 'fr' and application_page_id = 1 and dbms_lob.compare(from_string, to_clob('Home')) = 0
     fetch first 1 row only;
    apex_lang.update_translated_string(p_id => l_id, p_language => 'fr', p_string => 'Accueil');
    -- ... or a whole page as XLIFF, the format translation tools use
    l_xliff := apex_lang.get_xliff_document(p_application_id => 200, p_page_id => 1, p_language => 'fr');
    dbms_output.put_line(regexp_substr(l_xliff, '<trans-unit[^>]*>\s*<source>Home</source>\s*<target>[^<]*</target>'));
    apex_lang.apply_xliff_document(p_application_id => 200, p_language => 'fr',
        p_document => replace(l_xliff, '<target>Orders</target>', '<target>Commandes</target>'));

    -- 4. generate the translated application
    apex_lang.publish_application(p_application_id => 200, p_language => 'fr');
    show('published');

    apex_lang.update_language_mapping(p_application_id => 200, p_language => 'fr',
                                      p_new_trans_application_id => 200002);
    show('remapped');

    apex_lang.delete_language_mapping(p_application_id => 200, p_language => 'fr');   -- and the repository
    show('deleted');

    -- seed_translations also copied the text messages to French; they stay
    for m in (select translation_entry_id from apex_application_translations
               where application_id = 200 and language_code = 'fr') loop
        apex_lang.delete_message(p_id => m.translation_entry_id);
    end loop;
    dbms_output.put_line('French text messages deleted');
end;
/

Output:

mapped      fr -> 200001, needs publishing: Yes
<trans-unit id="S-5-1-200">
<source>Home</source>
<target>Accueil</target>
published   fr -> 200001, needs publishing: No
remapped    fr -> 200002, needs publishing: Yes
French text messages deleted

After publishing, the mapping no longer needs synchronization, and any change to the application or the mapping sets the flag again, which is why the remapped version needs publishing. DELETE_LANGUAGE_MAPPING leaves behind the text messages SEED_TRANSLATIONS copied, so the example deletes them too. Scripting these steps makes it easy to republish every language as part of a deployment.

Conclusion

APEX_ERROR adds errors that appear inline or in the notification area without stopping processing, and gives an error handling function the helpers it needs: the result record, the violated constraint's name, the first ORA error text, and the item the error belongs to. APEX_DEBUG writes leveled messages to the debug log, copies them to and from DBMS_OUTPUT for scripts and tests, and cleans up afterwards; ERROR messages are always logged. APEX_LANG reads text messages with named placeholders, maintains, exports, and imports them, and scripts the full translation of an application from mapping and seeding to XLIFF and publishing.

Vinish Kapoor
Vinish Kapoor

An Oracle ACE and software veteran with 25+ years of experience, passionate about AI and IT innovation.

guest

0 Comments
Oldest
Newest Most Voted
00