2025年1月29日水曜日

APEX 24.2の新しいテキスト・メッセージの扱いについて

Oracle APEX 24.2では、今までよりテキスト・メッセージを柔軟に扱うことができるようになりました。とはいえ、そもそもテキスト・メッセージ自体があまり使われていないように感じるので、新機能も含めてテキスト・メッセージについて紹介します。

今回の記事では以下のAPEXアプリケーションを使って、テキスト・メッセージの使い方について説明します。

説明に使用するAPEXアプリケーションのエクスポートを以下に置きました。
https://github.com/ujnak/apexapps/blob/master/exports/sample-text-message.zip

テキスト・メッセージはひとつの言語(例えば日本語)だけでも良いのですが、今回は、英語、日本語、簡体中国語、韓国語の4種類のメッセージを作成しています。


テキスト・メッセージの作成は、共有コンポーネントテキスト・メッセージより行います。


今回は2つのテキスト・メッセージにそれぞれ、4つの言語テキストを作成しています。

テキスト・メッセージの作成には、名前言語JavaScriptで使用テキストおよびコメントを指定します。アプリケーションがテキスト・メッセージを取り出す際には、セッション言語に一致するテキスト・メッセージが取り出されます。

テキスト・メッセージをJavaScriptから参照する場合は、JavaScriptで使用オンにします。

テキスト・メッセージサブスクリプションをサポートしています。アプリケーションに設定したテキスト・メッセージを別のアプリケーションで使用する場合は、最初にマスターとなるアプリケーションを作成して、そのアプリケーションにテキスト・メッセージを作成し、マスターとなるアプリケーションからテキスト・メッセージをサブスクライブするとよいでしょう。


テキスト・メッセージT_GREETING0として、APEX 24.2以前で扱えるテキスト・メッセージを作成します。24.2以前では置換文字列として、位置パラメータ%0、%1、... %9を使用できます。

T_GREETING0の言語ごとに、以下のテキストを設定します。

英語: I hope this message finds you well. <b>This is %1 from %0 Corporation.</b>
日本語: いつも大変お世話になっております。<b>%0株式会社の %1 です。</b>
簡体中国語: 非常感谢您长期以来的支持与信任。<b>我是 %0公司的 %1。</b>
韓国語: 평소 베풀어 주신 성원에 깊이 감사드립니다. <b>%0회사 %1 입니다.</b>

T_GREETING1として、APEX 24.2で扱えるテキスト・メッセージを作成します。24.2より置換文字列に名前を使えます。そのため、置換文字列の位置を気にする必要がありません。

英語: I hope this message finds you well. <b>This is %sender from %company Corporation.</b>
日本語: いつも大変お世話になっております。<b>%company株式会社の %sender です。</b>
簡体中国語: 非常感谢您长期以来的支持与信任。<b>我是 %company公司的 %sender。</b>
韓国語: 평소 베풀어 주신 성원에 깊이 감사드립니다. <b>%company회사 %sender 입니다.</b>

APEXアプリケーションにサポート言語を追加します。言語ごとのテキスト・メッセージが不要な場合は、翻訳の追加は不要です。その際は作成するテキスト・メッセージの言語として、アプリケーションのプライマリ言語(通常は日本語)を指定します。

共有コンポーネントアプリケーション翻訳を開きます。


アプリケーション言語の定義を開きます。


翻訳アプリケーションとして、APEXの(ワークスペースではなく)インスタンス全体で使用されていないアプリケーションIDを指定します。以下のSELECT文で、未使用のアプリケーションIDを見つけることができます。
with all_application_ids as (
    -- 最小のアプリケーションIDから最大までの連番
    select (select min(application_id) from apex_applications) + level - 1 as n
    from dual
    connect by level <=
        (
            select max(application_id) - min(application_id) + 1
            from apex_applications
        )
)
select n as unused_application_id
from all_application_ids
where n not in (select application_id from apex_applications) -- 使用中のIDを除く
order by n
実際には適当な数値を入力して、エラーが発生しない値を見つける、といった指定の仕方になるかと思います。

アプリケーション言語マッピングとして、英語(en)中国語(中国)(zh-cn)韓国語(ko)を作成します。


今回はセッション言語の切り替えが行えれば良いので、アプリケーションの翻訳までは行いません。アプリケーションを翻訳する場合は、テキストのシードやテキストの翻訳、アプリケーションのパブリッシュといった作業を実施します。


テキスト・メッセージには、会社名担当者名に置き換えるパラメータを含めています。それらを置き換える値を、アプリケーション定義置換に設定します。

会社名として使用する置換文字列としてG_COMPANY置換値としてOracle Breadsを設定します。担当者名はG_SENDER、置換値としてMugiを設定します。


実際にAPEXアプリケーションにテキスト・メッセージを表示してみます。

APEX 24.2以前は、以下の置換文字列にてテキスト・メッセージに置き換えます。静的メッセージで位置パラメータを置き換える手段はありません。

&APP_TEXT$T_GREETING0.

静的コンテンツのリージョンにテキスト・メッセージT_GREETING0を表示します。


画面の表示は以下になります。会社名、担当者名が抜けているため、このままでは使用できません。24.2以前でテキスト・メッセージに含まれる位置パラメータを置き換えるには、PL/SQLによる動的コンテンツかJavaScriptによるコーディングが必要です。

テキスト・メッセージに含まれるHTMLタグはデフォルトでエスケープされます。HTMLをそのまま解釈させたい場合は、置換文字列名の末尾に!RAWを加えます。


&APP_TEXT$T_GREETING0!RAW.

セキュリティの脆弱性につながるため、テキスト・メッセージが勝手に書き換えられることが無いように注意します。


テキスト・メッセージのエスケープを抑止すると、HTMLのタグが解釈され、メッセージの一部が太字に変わります。


APEX 24.2では、テキスト・メッセージに位置パラメータの代わりに置換文字列を設定できます。テキスト・メッセージT_GREETING1は、以下のように置換文字列%companyおよび%userを含んでいます。

いつも大変お世話になっております。<b>%company株式会社の %sender です。</b>

APEX 24.2では、以下の指定でテキスト・メッセージの置き換えができます。

&{T_GREETING1 sender=&G_SENDER. company=&G_COMPANY. }!RAW.


画面の表示は以下になります。会社名%company、担当者名%senderが、置換文字列G_COMPANYおよびG_SENDERの値に置き換えられています。


英語での表示を確認します。開発者ツールバーセッションよりセッション・オーバーライドを開きます。

セッション・オーバーライドの有効化オンにし、アプリケーション言語オン言語英語
(en)を選択して、設定を保存します。


英語(en)での表示は以下になります。


中国語(中国)(zh-cn)での表示は以下になります。


韓国語(ko)での表示は以下になります。


アプリケーション言語の切り替えは、アプリケーション定義グローバリゼーションで定義します。このアプリケーションでは、アプリケーションのプライマリ言語日本語(ja)に固定しているため、必ず日本語のテキスト・メッセージが表示されます。

ユーザーのセッションごとにアプリケーションの表示言語を切り替える場合は、アプリケーション言語の導出元を変更します。言語の切り替え方法については、以前の記事「Oracle APEXアプリケーションのグローバル化(5) - 言語の切り替え」が参考になります。


PL/SQLの動的コンテンツでテキスト・メッセージを表示します。

APEX 24.2以前ではAPEX_LANG.MESSAGEを呼び出して、テキスト・メッセージを表示する文字列に置き換えます。24.2以前では会社名や担当者名は位置パラメータ%0および%1にて指定されているため、引数:G_COMPANYおよび:G_SENDERの順番は重要です。
declare
    l_clob clob;
begin
    l_clob := apex_lang.message('T_GREETING0', :G_COMPANY, :G_SENDER);
    return l_clob;
end;

上記の設定を行った動的コンテンツのリージョンは、以下のように表示されます。HTMLタグをエスケープするには、開発者によるコーディングが必要です。


APEX 24.2では、APEX_LANG.GET_MESSAGEを呼び出します。テキスト・メッセージに含まれる置換文字列%companyおよび%senderの置換値を、JSONドキュメントとして与えます。
declare
    l_clob clob;
begin
    l_clob := apex_lang.get_message('T_GREETING1',
        apex_t_varchar2(
            'sender', :G_SENDER,
            'company', :G_COMPANY
        )
    );
    return l_clob;
end;

動的コンテンツの表示は同じです。


続いて、JavaScriptによるテキスト・メッセージの参照方法を紹介します。

テキスト・メッセージのJavaScriptで使用オンにすると、ページがロードされるときにJavaScriptからテキスト・メッセージが参照できるように、テキスト・メッセージが設定されます。


こうするとテキスト・メッセージをロードするためのコードは不要になりますが、テキスト・メッセージを参照しないページにも、JavaScriptで使用オンになっているテキスト・メッセージがロードされます。JavaScript APIとしてapex.lang.loadMessagesおよびapex.lang.loadMessagesIfNeededが提供されているので、テキスト・メッセージが参照される頻度を鑑みて、JavaScriptで使用を設定します。

置換文字列G_COMPANYG_SENDERの値をJavaScriptから参照できるように、ページ・プロパティのJavaScriptのファンクションおよびグローバル変数の宣言に以下を記述します。
const COMPANY = '&G_COMPANY.';
const SENDER  = '&G_SENDER.';

JavaScriptによる表示は、静的コンテンツのリージョンに行うことにします。ソースHTMLコードに以下を記述します。

<div id="greeting-pre242"></div>


APEX 24.2以前では、apex.lang.formatMessageを以下のように呼び出します。静的コンテンツのリージョンに、テキスト・メッセージT_GREETING0の置換値が表示されます。
document.getElementById("greeting-pre242").innerHTML = apex.lang.formatMessage( "T_GREETING0", COMPANY, SENDER );

テキスト・メッセージのJavaScriptで使用オフのときは、以下のコードを実行します。
const promise = apex.lang.loadMessages( ["T_GREETING0"] );
promise.done( () => {
    document.getElementById("greeting-pre242").innerHTML = apex.lang.formatMessage( "T_GREETING0", COMPANY, SENDER );
}).fail( () => {
    apex.debug.error("Could not get messages.");
});
または、以下のコードを実行します。
apex.lang.loadMessagesIfNeeded( ["T_GREETING0"], () => {
    document.getElementById("greeting-pre242").innerHTML = apex.lang.formatMessage( "T_GREETING0", COMPANY, SENDER );
});
どのコードでも、静的コンテンツへの表示は同じです。


APEX 24.2では、apex.lang.formatMessageに、テキスト・メッセージに含まれる置換文字列の置換値を、JSONドキュメントとして与えます。
document.getElementById("greeting-242").innerHTML = apex.lang.formatMessage( "T_GREETING1", { sender: SENDER, company: COMPANY } );
テキスト・メッセージのJavaScriptで使用オフのときは、以下のコードを実行します。
const promise242 = apex.lang.loadMessages( ["T_GREETING1"] );
promise242.done( () => {
    document.getElementById("greeting-242").innerHTML = apex.lang.formatMessage( "T_GREETING1", { sender: SENDER, company: COMPANY } );
}).fail( () => {
    apex.debug.error("Could not get messages.");
});
または、以下のコードを実行します。
apex.lang.loadMessagesIfNeeded( ["T_GREETING1"], () => {
    document.getElementById("greeting-242").innerHTML = apex.lang.formatMessage("T_GREETING1",  { sender: SENDER, company: COMPANY });
});
APEX 24.2でも、静的コンテンツへの表示は同じです。


apex.lang.formatMessageに与えら得れた置換値は必ずエスケープされます。

例えば、担当者名を青色(u-hot-text)に変更します。アプリケーション定義のG_SENDERを以下に変更します。

<span class="u-hot-text">Mugi</span>


JavaScriptによる表示はHTMLタグがそのまま表示され、担当者名の色は変わりません。


JavaScriptでHTMLタグをエスケープせずそのまま出力するには、apex.lang.formatMessageの代わりにapex.lang.formatMessageNoEscapeを呼び出します。

例えば、コードを以下のように変更します。
apex.lang.loadMessagesIfNeeded( ["T_GREETING0"], () => {
    document.getElementById("greeting-pre242").innerHTML = apex.lang.formatMessageNoEscape( "T_GREETING0", COMPANY, SENDER );
});
HTMLタグのエスケープを抑止すると、JavaScriptによる表示でも担当者名は青くなります。


動的にHTMLコンテンツを生成する際に、生成するHTMLの安全性については開発者が担保する必要があります。HTMLタグが含まれていなければ、innerHTMLではなくinnerTextに書き込むといった対応も考えられます。

最後にAPEX 24.2のページ・デザイナに、テキスト・メッセージ・ピッカーが追加されています。有効にするには、ページ・デザイナのユーティリティ表示より、テキスト・メッセージ・ピッカーを有効にします。


プロパティ・エディタで文字列を入力する箇所に、地球のアイコンのボタンが表示されます。


アイコンをクリックすると、作成済みのテキスト・メッセージとテキストが一覧されます。


選択したテキスト・メッセージが置換文字列として挿入されます。


Oracle APEXのテキスト・メッセージとAPEX 24.2で新規に追加された機能の紹介は以上になります。

今回作成したアプリケーションのように、メインのアプリケーションと追加された翻訳が同期していないと、アプリケーションをエクスポートするときに警告が表示されるようになりました。これも、APEX 24.2の新機能です。


翻訳されたアプリケーションが同期していないと、アプリケーションのエクスポート時に翻訳が失われることがあります。警告を表示することにより、翻訳アプリケーションを安全にエクスポートできます。

今回の記事は以上になります。

Oracle APEXのアプリケーション作成の参考になれば幸いです。