2020年6月15日月曜日

Oracle APEXアプリケーションのグローバル化(6) - タイムゾーンの扱いと自動タイムゾーン

Oracle APEXのアプリケーションをグローバル化する方法をこれまで紹介してきました。日本国内に居住している外国籍の方を対象にしたアプリケーションであれば、各国語に翻訳を行えば対応としては十分でしょう。

そうではなく、企業などで海外の拠点からアプリケーションのアクセスがあるような場合、日時データの取り扱いも考慮する必要があります。Oracle APEXにはアプリケーションのグローバリゼーション属性に自動タイムゾーンという属性があり、これをオンにするとブラウザから取得したタイムゾーン・オフセットをOracle APEXのアプリケーションに反映させることができます。


自動タイムゾーンをオンに切り替えると、どのようなアプリケーションでもタイムゾーンを考慮する、というものではありません。その仕組みを理解して、アプリケーション自体をタイムゾーンを考慮して作る必要があります。

これから、そのタイムゾーンを考慮したアプリケーションの作成方法について紹介します。

自動タイムゾーンがOFFの場合


Oracle Databaseが持っている日時データ型には4つの種類があります。
  1. DATE
  2. TIMESTAMP
  3. TIMESTAMP WITH LOCAL TIME ZONE
  4. TIMESTAMP WITH TIME ZONE
この中で、タイムゾーンを認識するのは後ろの2つ、TIMESTAMP WITH LOCAL TIME ZONE型(以降TSLTZ型と記述します)とTIMESTAMP WITH TIME ZONE型(同TSTZ型)です。DATE型とTIMESTAMP型はタイムゾーンを認識しないため、タイムゾーンが異なる地域からの利用が想定されるアプリケーションでの使用はお勧めできません。

どのようなことが起こるか、実際にアプリケーションを使って確認してみます。

以下の表を作成します。
create table test_datetimes (
    id             number generated by default on null as identity  
                   constraint test_datetimes_id_pk primary key,
    "場所"          varchar2(80),
    "DATE型"        date,
    "TIMESTAMP型"   timestamp,
    "TSLTZ型"       timestamp with local time zone,
    "TSTZ型"        timestamp with time zone
);
表を作成したら、SQLワークショップのオブジェクト・ブラウザから作成した表TEST_DATETIMESを選択し、アプリケーションの作成を実行します。


ウィザードによって作成されるアプリケーションは特に変更せず、アプリケーションの作成を行います。ただし、確認に使用するのはDatetimesレポートとして作成される対話モード・レポートとフォームだけです。


これで確認に使用するアプリケーションが出来上がりました。

グローバリゼーションの設定を確認します。いくつかの書式のデフォルトがDSになっています。この書式では時刻が表示されません。


時刻が表示されるようにアプリケーションの日付書式とアプリケーションのタイムスタンプ書式をYYYY/MM/DD HH24:MIへ変更します。アプリケーションのタイムスタンプ・タイムゾーン書式はタイムゾーンが表示されるようにTZRを追加してYYYY/MM/DD HH24:MI TZRに変更します。今のところは自動タイムゾーンは変更せずOFFにしておきます。


アプリケーションを実行して、Datetimesレポートを開きます。作成をクリックします。


先ほど作成した表TEST_DATETIMESにデータを登録するフォームが開きます。すべての日時型に同じデータ"2020/06/21 00:00"を入力し、作成をクリックします。


レポートに戻るので登録内容を確認します。登録したデータと同じ日時が表示されます。


TSTZ型についてはタイムゾーンの情報が+00:00と表示されています。UTCと見做せますが、アプリケーションをタイムゾーンに対応させていないので、タイムゾーン・オフセットが0になっているだけです。

上記は東京からアクセスしています。同じアプリケーションにニューヨークからアクセスしてみます。ニューヨークに出張するわけにもいかないので、コンピュータの設定を変更します。macOSの場合、システム環境設定の日付と時刻で時間帯を切り替えることができます。同様の設定はWindowsにもLinuxにもあるでしょう。


タイムゾーンをニューヨークに切り替えたのち、再度レポートを表示させます。


同じ情報が表示されています。アプリケーションの利用者は、表示されている時刻2020/06/21 00:00は東京での時刻であると理解している必要があります。

ニューヨークからデータを入力します。東京と同様に2020/06/21 00:00を設定します。


登録されたデータがレポートに表示されます。東京で登録したデータとニューヨークで登録したデータは、レポートから見て、全く同一になります。


アプリケーションが保持する日時データはすべて東京のタイムゾーンである、と決めている場合はどの日時型を使用しても、あまり問題はありません。しかし、このままでは東京の利用者が東京の現地時刻でアプリケーションを使用し、ニューヨークの利用者はニューヨークの現地時刻でアプリケーションを使用する、といった状況に対応できません。ニューヨークの利用者は時差計算を自分で行う必要があり、アプリケーションとしての使い勝手は低くなります。

自動タイムゾーンがONの場合


自動タイムゾーンをONヘ変更します。



また、アプリケーションに少し変更を加えます。Datetimesレポートに認識されているタイムゾーン・オフセットを表示するページ・アイテムを追加します。

名前をP4_TZOFFSET、ラベルをタイムゾーン・オフセット、ソースのタイプをSQL問合わせ(単一の値を返す)として、以下のSQL文をSQL問合わせに指定します。
select sessiontimezone from dual
使用はセッション・ステートの既存の値を常に置換、セッション・ステートの保持はリクエストごと(メモリーのみ)としておきます。


アプリケーションにページ・アイテムを追加した後、レポートを表示します。


タイムゾーン・オフセットとして +09:00 が設定されていることが確認できます。レポートを見ると、DATE型、TIMEZONE型、TSTZ型はタイムゾーン・オフセットの影響を受けていないことがわかります。TSLTZ型つまりTIMESTAMP WITH LOCAL TIME ZONE型は、保存されているデータにタイムゾーン・オフセットが適用され、2020/06/21 09:00として時刻が表示されています。

自動タイムゾーンがONの状態で、再度、同じ時刻を登録します。


レポートに表示される結果を確認します。TSTZ型に+09:00というタイムゾーンが加わっています。またTSLTZ型は登録した2020/06/21 00:00が表示されされています。データの登録時に東京での時間で2020/06/21 00:00と認識されているので、同じ表示になります。DATE型、TIMESTAMP型はタイムゾーンの影響を受けないため、自動タイムゾーンがOFFのときに登録したデータと変わりがありません。


次にニューヨークから自動タイムゾーンをONにしたアプリケーションにアクセスします。


タイムゾーン・オフセットとして -04:00 が設定されていることが確認できます。TSLTZ型には、設定されたタイムゾーン・オフセットが適用されています。つまり、ここで表示されている時刻はニューヨークの現地時間です。13時間(4+9)進めると日本時間になります。

ニューヨークからも同様に2020/06/21 00:00の時刻を指定してデータを登録します。


レポートを確認すると、TSTZ型で -04:00 が追加されていますが、それ以外は登録したままの情報になっています。


再度、東京からレポートを確認します。


TSLTZ型のみ、タイムゾーン・オフセットが反映された時刻が表示されています。先ほどニューヨークで入力した2020/06/21 00:00はニューヨークの現地時間であり、東京からみたレポートでは13時間進んだ(4+9=13)、2020/06/21 13:00として表示されています。

以上のように、グローバリゼーション属性の自動タイムゾーンをONにすることで、TSLTZ型(TIMESTAMP WITH LOCAL TIME ZONE型)は常にブラウザが認識した(タイムゾーン・オフセットを適用した)現地時刻で表示されるようになります。TSTZ型(TIMESTAMP WITH TIME ZONE型)も同様にタイムゾーンを認識しますが、データそのものにタイムゾーンが含まれているため、現地時刻を表示しません。現地時刻として表示するには、AT LOCAL演算子を適用します。

先ほどのDatetimesレポートのTSTZ型を現地時刻で表示させるには、レポートのソースを以下のSQLに変更します。
select ID,
       "場所",
       "DATE型",
       "TIMESTAMP型",
       "TSLTZ型",
       "TSTZ型" AT LOCAL as "TSTZ型"
from TEST_DATETIMES

この変更の結果、TSTZ型の表示は現行のタイムゾーン・オフセットでの時刻表示に変わります。TSLTZ型はタイムゾーン・オフセットが適用された時刻ですので、TSTZ型にAT LOCAL演算子を適用すると、時刻表示は同じになります。


それ以外に、TSLTZ、 TSTZ型はともにAT TIME ZONE 'オフセットまたはタイムゾーン名'演算子を適用することにより、指定したタイムゾーンの時刻に変換することができます。例えば、タイムゾーン・オフセットが東京(+09:00)と認識されている状態でニューヨークの時間で表示するにはAT TIME ZONE '-04:00'を指定します。
select ID,
       "場所",
       "DATE型",
       "TIMESTAMP型",
       "TSLTZ型",
       "TSTZ型" AT TIME ZONE '-04:00' as "TSTZ型"
from TEST_DATETIMES
このようにレポートのソースを変更した結果が以下になります。


認識されているタイムゾーン・オフセットにかかわらず、ニューヨークの時間で表示されます。

AT TIME ZONEでの指定ではタイムゾーン・オフセットという数値だけではなく、名前による指定も可能です。利用可能な名前は以下のSQLにて確認できます。
SELECT TZNAME, TZABBREV 
FROM V$TIMEZONE_NAMES
ORDER BY TZNAME, TZABBREV;
先ほどの指定はAT TIME ZONE 'US/Eastern'と指定することもできます。変更した結果は以下になります。


-04:00とUS/Easternの両方で同じ時刻の表示になっています。タイムゾーン・オフセットの数値と名前の指定の違いは夏時間の扱いです。名前による指定であれば、夏時間を考慮しますが、数値でタイムゾーン・オフセットを指定する場合はその数値がそのまま適用されます。ですので、夏時間を考慮した時刻表示を行う場合は、AT TIME ZONE演算子はタイムゾーン名による指定が必要になります。

自動タイムゾーンでは、夏時間のときはブラウザがタイムゾーン・オフセットとして-04:00を返し、そうでない場合は-05:00を返してくると想定しています。夏時間のない日本では問題になりませんが、夏時刻がある地域ではタイムゾーン・オフセットの扱いに問題が発生します。

利用者がニューヨークにいて夏時間であれば、タイムゾーン・オフセットは-04:00です。その状況で先ほどのアプリケーションにて、2020/12/21 00:00の時刻を設定します。


レポートは以下のように表示されます。


TSLTZ型、TSTZ型ともに保存時はブラウザで認識されているタイムゾーン・オフセット-04:00が適用されます。TSLTZ型は表示時も認識されているタイムゾーン・オフセットが適用され、結果として時刻の表示は変わらず2020/12/21 00:00ですが、TSTZ型はAT TIME ZONE 'US/Eastern'として夏時間を考慮させているため、表示ではタイムゾーン・オフセットとして-05:00が適用され、2020/12/20 23:00となります。

夏時間の対応が必要な場合は、Oracle APEXの自動タイムゾーンは少々力不足です。

夏時間まで考慮するとタイムゾーンは名前で設定する必要があります。これは自動タイムゾーンではできません。Oracle APEXが提供するAPEX_UTILパッケージに

SET_SESSION_TIME_ZONEプロシージャ
GET_SESSION_TIME_ZONEファンクション

に含まれており、こちらを利用することでセッション単位で名前による指定を含む任意のタイムゾーンを設定することが可能です。記事が長くなりましたので、これらの解説はまた項を改めて行います。

2020年6月11日木曜日

Oracle APEXアプリケーションのグローバル化(5) - 言語の切り替え

Oracle APEXアプリケーションをグローバル化する手順の説明です。こちらの記事からの続きになります。

言語の切り替え


多言語に対応したアプリケーションの言語を切り替える方法は、グローバリゼーション属性のアプリケーション言語の導出元として設定します。今まではセッションとしていました。


アプリケーション言語の導出元として設定できる選択肢は6つあります。
  1. NLSなし(アプリケーションは翻訳されない)
  2. アプリケーションのプライマリ言語
  3. ブラウザ(ブラウザの言語プリファレンスを使用)
  4. アプリケーション・プリファレンス(FSP_LANGUAGE_PREFERENCEを使用)
  5. アイテム・プリファレンスを使用(プリファレンスを含むアイテムを使用)
  6. セッション
これらのそれぞれについて説明していきます。

NLSなし(アプリケーションは翻訳されない)


アプリケーション言語の導出元のデフォルトはアプリケーションのプライマリ言語となっています。アプリケーションの翻訳をしていない場合は、デフォルトの設定とNLSなしの設定で、ほとんど動作に違いはありません。

Oracle APEXのアプリケーションを翻訳する際に、ベース・アプリケーションのアプリケーションのプライマリ言語の設定にかかわらず、同じ言語の翻訳を登録することができます。

例えば、今まで作成してきたアプリケーション、竹家のメニューに言語のマッピングに日本語(ja)を追加します。


アプリケーションのプライマリ言語は日本語(ja)ですが、jaの翻訳も登録されています。


レポートのタイトルを日本語メニューから、言語依存メニューに変更します。


シードを実行したのち、翻訳リポジトリにアクセスして、言語依存メニューというリージョン名をそれぞれの言語で翻訳します。

 日本語(ja)  英語(en)  簡体中国語(zh-cn)  韓国語(ko) 
 日本語メニュー  English Menu  中文菜单 한국어 메뉴

翻訳リポジトリからみると、以下のようになります。


翻訳ができたので、すべての翻訳済みアプリケーションをパブリッシュします。

アプリケーションがこのように翻訳済みになっている状態で、アプリケーション言語の導出元がNLSなしの場合、翻訳形式として何が登録されていても、ベース・アプリケーションのラベルが表示されます。NLSなしの場合、このレポートの表示は以下になります。


翻訳済みのアプリケーションが採用されないので、タイトルや列名はベース・アプリケーションでの設定内容が表示されます。

アプリケーション側で多言語対応するために使用した機能は以下の影響を受けます。
SYS_CONTEXT('USERENV','LANG')
は常にUSを返します。これは、データベースのセッション取得時にNLS_LANGUAGEとしてAMERICAN、NLS_TERRITORYとしてAMERICAが設定された結果です。

APEX_LANG.LANGおよびMESSAGEファンクションは、アプリケーションのプライマリ言語の設定を参照しています。

これらは実機で動作を確認した結果です。条件によっては異なる結果になる可能性もあります。基本的にNLSなしの設定をする場合は、アプリケーションでこれらのファンクションは必要ありません。

NLSなしの設定は、アプリケーションがすでに翻訳されていて、その上でアプリケーションのデバッグする際に翻訳なしのベース・アプリケーションを確認する場合に使用します。本番運用するアプリケーションへの設定はお勧めしません。

ベース・アプリケーションのプライマリ言語と同じ翻訳が登録されていると、翻訳されたアプリケーションが優先されます。アプリケーションの変更はベース・アプリケーションに対して行われます。翻訳されたアプリケーションが実行されていると、変更したアプリケーションの動作確認をするには、かならずシードとパブリッシュが必要になります。アプリケーション言語の導出元をNLSなしに設定するとベース・アプリケーションが優先されますが、生産性を落とさないためには、ベース・アプリケーションに設定されている言語と同じ翻訳は登録しない方がよいでしょう。

アプリケーションのプライマリ言語


最も一般的な設定です。アプリケーションの翻訳がされている場合は、その言語で画面の表示がされます。アプリケーションのプライマリ言語として設定した言語に対応した翻訳がない場合は、ベース・アプリケーションのラベルがそのまま使用されます。


アプリケーションのプライマリ言語の設定に対応したNLS_LANGUAGE、NLS_TERRITORYがセッションに設定されるため、SYS_CONTEXTファンクションも対応した短縮形を返します。

とはいえ、アプリケーションのプライマリ言語を指定する形で言語設定を固定しますので、この設定を行うアプリケーションは、翻訳する意味がありません。この設定は翻訳をする予定のないアプリケーションに使います。翻訳する予定のないアプリケーションには、NLSなしより、こちらの設定が推奨されます。

ブラウザ(ブラウザの言語プリファレンスを使用)


ブラウザが送信してくるAccept-Languageヘッダーの先頭に設定されている言語によって、アプリケーションの言語を決定します。Chromeを例にとると、以下のような画面では日本語が選択されます。


上記の設定で、Chromeは以下のAccept-Languageヘッダーを送信していました。
Accept-Language = ja,en-US;q=0.9,en;q=0.8
設定されている言語に対応した翻訳が存在する場合は良いのですが、存在しない場合に注意が必要です。例えば以下のように台湾などで使用される繁体字の中国語が優先されているとします。


この設定で、演習で作成したアプリケーションにアクセスすると表示が以下になります。


対応した翻訳が存在しないため、ベース・アプリケーションに設定されている文字列が使用されます。翻訳はありませんが、NLS_LANGUAGE、NLS_TERRITORYはブラウザの設定が反映されるので、SYS_CONTEXT('USERENV','LANG')の返り値は'ZHT'になります。表TKY_MENUS_TLにLANGUAGE列がZHTのデータはないので、一行も表示されません。

また、ブラウザの言語が英語(イギリス)が先頭の場合は以下になります。
Accept-Language = en-GB,en;q=0.9,ja;q=0.8,en-US;q=0.7
en-GBで、これと一致する翻訳アプリケーションがない場合、拡張部分のGBを除いたenで翻訳を探します。これに一致すると、その翻訳が使用されます。画面上の表示は英語の翻訳済み画面になりますが、NLS_LANGUAGEおよびNLS_TERRITORYはそれぞれ”ENGLISH"、"UNITED KINGDOM"が設定され、SYS_CONTEXT('USERENV','LANG')の結果としてGBが返されます。

このような動作になるため、アプリケーション言語をブラウザにする場合は、翻訳がない言語でのアクセスを想定してベース・アプリケーションは英語で作成することが推奨されます。また、SYS_CONTEXTファンクションを使用する際には、翻訳がない場合の対応も考えてコーディングする必要があります。例えばビューTKY_MENUS_VLの定義は以下のようになります。
CREATE OR REPLACE FORCE VIEW "TKY_MENUS_VL" ("ID", "MENU_NAME", "VOLUME", "PRICE") AS 
  select o.id, l.local_menu_name as menu_name, apex_lang.lang(o.volume) as volume, o.price
from tky_menus o join tky_menus_tl l on o.id = l.menu_id
where l.language = 
CASE WHEN SYS_CONTEXT('USERENV','LANG') IN ('US','JA','ZHS','KO') THEN
  SYS_CONTEXT('USERENV','LANG')
ELSE
  'US'
END
/
設定が簡単そうに見えますが、ブラウザの設定を使う場合はこういった対応が要求されます。特にSYS_CONTEXTファンクションを使用する場合は要注意です。

アプリケーション自体に言語を認識させて処理をする仕組みを実装している場合は、ブラウザの設定による任意の言語を受け付けるよりは、アプリケーションが対応している言語を利用者に選択させる方が合理的でしょう。Oracle APEXが提供している仕組み(アプリケーションの翻訳やAPEX_LANGパッケージ)のみを使っている場合は、ブラウザの設定はうまく動くはずです。

アプリケーション・プリファレンス(FSP_LANGUAGE_PREFERENCEを使用)


ユーザーごとに言語設定を持たせます。以下のようにAPEX_UTIL.SET_PREFERENCEプロシージャをFSP_LANGUAGE_PREFERENCEと言語を引数として呼び出します。
begin
   apex_util.set_preference
   (
     p_preference => 'FSP_LANGUAGE_PREFERENCE',
     p_value => :P2_LANGUAGE,
     p_user => :APP_USER
   );
   commit;
end;
p_userにユーザー名を設定しているので、これはログイン・ユーザーにたいして設定が保存され、次回以降のログインでも言語設定が有効になります。

このプリファレンスを使う実装例を紹介します。

以下のような非モーダルのダイアログとしてページを作成し、そこに言語設定を行うページ・アイテムを設定します。


ページ・アイテムは選択リストとします。翻訳済みの言語を選択できるようにします。


ページ・アイテムの変更時にPL/SQLコードを実行する動的アクションを定義します。


言語プリファレンスを設定するページを作成する必要はありますが、アプリケーションが対応している言語の利用に限定することができます。

指定できる言語コードについては、Oracle Application Expressのグローバリゼーション・コードを参照してください。


アイテム・プリファレンスを使用(プリファレンスを含むアイテムを使用)


いままで紹介してきた方法は、Oracle APEXが言語を設定する仕組みを提供しています。アイテム・プリファレンスを使用する方法は、言語の選択をコーディングで解決します。例えば、先ほどのプリファレンスの設定を優先するが、その設定が無い場合はブラウザの設定を参照する、といった実装が可能になります。アイテム・プリファレンスを使用する場合はコーディングは必須ですが、この次に説明するセッションでは、Oracle APEXが提供する仕組みと、コーディングによる言語の選択の両方を行うことができます。新たに開発する場合はこちらの設定を利用するよりはセッションの方がお勧めです。

共有コンポーネントのアプリケーション・アイテムとしてFSP_LANGUAGE_PREFERENCEを登録します。


あとは、PL/SQLコードを書ける場所であれば、プロセスでも動的アクションでもアプリケーション・アイテムFSP_LANGUAGE_PREFERENCEに言語を設定すると、それがアプリケーションに反映されます。とはいえ、少なくても初期値は共有コンポーネントのアプリケーションの計算によって設定することになるでしょう。


先ほどの例であげた、プリファレンスの設定を優先して、それがない場合はブラウザの設定を使うというPL/SQLコードの例が以下になります。
declare
   l_lang varchar2(80);
   l_accept_langs varchar2(400);
   l_lang_arr apex_t_varchar2;
begin
   l_lang := apex_util.get_preference('FSP_LANGUAGE_PREFERENCE');
   if l_lang is null then
       l_accept_langs := owa_util.get_cgi_env('HTTP_ACCEPT_LANGUAGE');
       if l_accept_langs is not null then
           l_lang_arr := apex_string.split(l_accept_langs, ',');
           if l_lang_arr.count > 0 then
               l_lang := l_lang_arr(1);
               if instr(l_lang, ';') > 0 then
                  l_lang := substr(l_lang,1,instr(l_lang, ';')-1);
               end if;
           end if;
       end if;
   end if;
   if l_lang is null then
     l_lang := 'en';
   end if;
   apex_debug.info('Language is set to ' || l_lang);
   return l_lang;
end;
プリファレンスはユーザーに紐づいて設定されますので、計算ポイントをユーザー認証後に設定し、計算タイプはPL/SQLファンクション本体、計算のコードとして上記のコードを設定します。


このようなコーディングを行うことで、言語を色々な方法で選択することが可能になります。

また、言語を選択するドロップダウン・メニューも、この設定を活用して実装できます。


アプリケーション・アイテムFSP_LANGUAGE_PREFERENCEをブラウザから設定変更できるように設定をします。セキュリティのセッション・ステート保護は、デフォルトでは制限付き - ブラウザから設定不可なので、チェックサムが必要 - セッション・レベルに変更します。


共有コンポーネントのナビゲーション・バー・リストを開き、デスクトップ・ナビゲーション・バーの編集を行います。


デスクトップ・ナビゲーション・バーに5つのエントリを追加します。言語(&FSP_LANGUAGE_PREFERENE.)のエントリが他の4つのエントリの親になります。また、このエントリにはターゲットの設定がありません。


言語設定を行う他の4つのエントリは以下の設定を行います。


言語設定はページ処理の一番最初に行われます。リスト・エントリに指定されているFSP_LANGUAGE_PREFERENCEの設定はその後に行われます。ですので、ターゲット・ページを&APP_PAGE_ID.としてFSP_LANGUAGE_PREFERENCEを設定しても、変更が画面に反映されるのは、再度ページをリロードした後になります。この現象を回避するために、空白のページredirectを作成し、そこから呼び出し元のページにリダイレクトさせることで、言語の設定が行われた後にページがロードされるようにします。

ページの別名をredirectとして作成し、ヘッダーの前にブランチを登録します。ブランチでは無条件でページ・アイテムP5_TARGET_PAGEに渡されたページ、つまりドロップダウン・リストを使って言語を切り替えた時に表示されていたページに再度リダイレクトします。ページ・アイテムP5_TARGET_PAGEを登録するため、静的コンテンツのリージョンをContent Bodyに登録します。ただし、ページのレンダリングが行われる前に登録したブランチによってリダイレクトしますので、このページが画面に表示されることはありません。


アイテム・プリファレンスを使用はこの例のように使用します。

セッション


今回の設定で主に使ってきた設定です。サインインの画面に表示される言語セレクタから言語を選択します。


言語セレクタから言語を選択したのち、URLを確認するとパラメータにp_lang=言語が追加されていることを確認できると思います。アプリケーション言語の導出元として使用されるのは、このp_langパラメータです。そのため、言語セレクタを使用せずに直接URLにp_lang=言語を追加して、アプリケーションの言語を切り替えることも可能です。

外部サイトに埋め込んだアプリケーションの直リンクに言語設定も含める場合などは、このセッションの設定を使うことができます。また、言語セレクタは自動的に表示されるようになっているので、追加のコーディグが不要です。作成したアプリケーションがOracle APEXが提供しているログイン・ページをそのまま使用しているのであれば、このセッションの設定で事足りるケースが多いでしょう。


海外のブログに言語セレクタの見かけを変更する方法が紹介されていました。


ページにJavaScriptとCSSの記述を追加します。

JavaScriptはページ・ロード時に実行するコードとして以下を指定します。
$(".a-LinksList-item").addClass("t-Button").addClass("t-Button--pill");
CSSはインラインで以下を指定します。
.a-LinksList--lang {text-align: center;}
a.a-LinksList-link {position: relative; z-index: 2;}
以上の設定で、切り替えがリンクではなくボタンに変わります。

言語の設定、または切り替えはAPEX_UTIL.SET_SESSION_LANGプロシージャにて実施することも可能です。このプロシージャを使い、アイテム・プリファレンスを使用のセクションで行ったログイン時に言語を設定するコーディングと、ナビゲーション・バーでの言語切り替えを実装してみます。

セッションの場合はアプリケーション・プロセスによって、ユーザー認証後の言語設定を行います。

PL/SQLコードとしては以下になります。
declare
   l_lang varchar2(80);
   l_accept_langs varchar2(400);
   l_lang_arr apex_t_varchar2;
begin
   l_lang := apex_util.get_preference('FSP_LANGUAGE_PREFERENCE');
   if l_lang is null then
       l_accept_langs := owa_util.get_cgi_env('HTTP_ACCEPT_LANGUAGE');
       if l_accept_langs is not null then
           l_lang_arr := apex_string.split(l_accept_langs, ',');
           if l_lang_arr.count > 0 then
               l_lang := l_lang_arr(1);
               if instr(l_lang, ';') > 0 then
                  l_lang := substr(l_lang,1,instr(l_lang, ';')-1);
               end if;
           end if;
       end if;
   end if;
   if l_lang is null then
     l_lang := 'en';
   end if;
   apex_debug.info('Language is set to ' || l_lang);
   apex_util.set_session_lang(l_lang);
end;
違いは一行だけで、設定する言語をreturnで戻す代わりに、APEX_UTIL.SET_SESSION_LANGプロシージャを呼び出して、言語を設定しています。


メニュー自体の設定では、パラメータp_langを設定し、表示中のページを再表示します。p_langはシステムによって認識されるため、アイテム・プリファレンスのように追加のリダイレクトを行う必要はありません。


アイテム・プリファレンスを使用する場合と比較すると、API呼び出しによる言語設定や、p_langパラメータがシステムによって解釈される、といったことより、コーディングはより容易になっています。