Sun Java Solaris Communities My SDN Account
 
Article

JavaServer Pages 技術による多言語 Web アプリケーションの開発

 

Norbert Lindenberg
2003 年 12 月

English: Developing Multilingual Web Applications Using JavaServer Pages Technology
中文: 使用 JavaServer Pages 技术开发多语言 Web 应用程序

今では、JavaServer Pages (JSP) 技術は Web アプリケーション開発者のお気に入りのツールになっています。JSP ページを使用すれば、開発者は他のプログラミング知識がなくても動的な Web ページを設計できるだけでなく、拡張可能なタグメカニズムを使用することによって、JSP ページの下で動作するソフトウェアコンポーネントの機能を利用できるからです。

Java Community Process を通じて開発された新しい拡張機能は、多言語アプリケーション開発のための幅広いサポートを提供します。この JavaServer Pages Standard Tag Library には、地域対応の機能とロケール対応の書式化をサポートする一連のタグなど、さまざまな機能が定義されています。

この解説では、最初に JavaServer Pages 技術について簡単に説明します。これによって、国際化の諸問題に対してこの技術をどのように使用すればよいかがわかります。次に、多言語 Web アプリケーションの開発に特有の主な問題をいくつか取り上げ、JavaServer Pages 技術 (ロケールの判別と地域対応、文字のエンコーディング、書式化と解析など) を使ってそれらの問題をどのように解決するかを説明します。

JavaServer Pages 技術

JavaServer Pages (と、関連するいくつかの技術) は、Web アプリケーションのプレゼンテーションレイヤに位置しています。開発者は、JSP ページを使用することによって、ネットワーク上のビジネスロジックやデータベースといったさまざまなサービスと対話する動的な Web ページを開発できます。

JavaServer Pages

JSP 技術に基づくページでは、HTML や XML などの静的なコンテンツと XML 風のタグが結合されます。そして、これらのタグと下位で動作するソフトウェアライブラリとが接続されます。これらのライブラリは、通常、Java プログラミング言語で作成されています。この場合に特に重要な Java 技術は、JSP と Java クラス間の汎用インタフェースとしての JavaBeans コンポーネントアーキテクチャと、SQL データベースのアクセスに必要な Java Database Connectivity (JDBC) API、それに、XML 処理に必要なさまざまなライブラリです。

JSP ページ自体はサーブレット形式の Java コードにコンパイルされ、実行されます。サーブレットとは、コンパイルされ、サーバーにリンクされる Web サーバーの拡張です。これによって、実行は、スクリプト言語の場合よりも速くなります。Java プログラミング言語で作成されたサーブレットと JSP ページは、いっしょに使用されることがよくあります。この場合には、サーブレットがコントローラになり、JSP ページがアプリケーションのビューになります。

JavaServer Pages とその下で動作するサーブレット技術は、HTTP 要求/応答情報の処理や、クッキーや URL 再作成に基づくセッション維持を強力にサポートします。

JSP 技術を使用する重要な理由の 1 つは、ページオーサーの仕事とアプリケーション開発者の仕事を分離できる点にあります。Java ステートメントを JSP ページに直接組み込むことは「可能」ではありますが、開発者は、この方法を避けるべきだと考えるようになり、今ではカスタムタグを使用する傾向にありま す。

JavaServer Pages Standard Tag Library

JavaServer Pages Standard Tag Library (JSTL) には、JSP ページでよく使われる機能のいくつかをまとめたカスタムアクションが含まれています。JSTL は、その構築に貢献してくれた人々がそれぞれのライブラリを通して得た経験に基づいて作られました。このライブラリは、アプリケーションが実際に動作する サーバーから独立した標準的なインタフェースとなります。

JSTL では、カスタムタグの他に、式言語が新たに導入されます。そのため、JSP ページでスクリプト言語式を使用する必要性はさらに薄れます。さらに、JSTL では、JSP ページでのスクリプトライブラリやタグライブラリの使用に制約を課すタグライブラリ検証機能が導入されます。式言語の拡張版と、スクリプト機能を抑制する 機能は、その後、JSP 2.0 仕様に組み込まれています。したがって、JSTL のこのような機能は JSP 1.2 を使用する場合にのみ必要です。

カスタムアクションの対象となる主な分野は次のとおりです。

  • 変数操作: 「コア」ライブラリには、さまざまなスコープ (page, request, session, application) で変数を定義し、削除し、あるいは、それらの変数を生成ページとして描画できるアクションがあります
  • コントロールフロー: 「コア」ライブラリには、タグに基づくコントロールフロー構造 (条件文や反復文) を可能にするいくつかのアクションがあります。その結果、スクリプト言語のコードを組み込む必要性がなくなります。
  • URL 関連のアクション: 「コア」ライブラリには、URL で定義されたコンテンツを JSP ページにインポートしたり、URL を内部形式から外部形式に変換したり (セッション管理に必要な情報の収集など)、あるいは、ページを別の Web ページにリダイレクトしたりするアクションがあります。
  • XML 処理: 「xml」ライブラリには、XML 文書を解析するアクションの他に、XPath 式を使ったコンテンツや、XPath 式に基づくコントロールフロー、XSLT スタイルシートを使った変換などを抽出するアクションがあります。
  • リレーショナルデータベースアクセス: 「sql」ライブラリには、Web アプリケーションで簡単な SQL クエリーやアップデートを行うことができるアクションがあります。
  • 国際化と書式化 (この解説の中心): 「fmt」ライブラリには、ロケールの判別や、地域対応、文字エンコーディングの判別、ロケールに依存する書式化や解析などをサポートするアクションがあ ります。

ロケールの判別と地域対応

多言語 Web アプリケーションの設計では、まず、ユーザーの言語とロケール設定をどのように判別し、これらの設定を、アプリケーションや Java 実行時環境がサポートするロケール群とどのように対応付けるかを決める必要があります。このセクションでは、最初に、Web アプリケーションで対処しなければならない外部環境とその要件について述べます。次に、その下で動作する Java 2 Standard Edition (J2SE) プラットフォームの機能について概観し、最後に、JavaServer Pages Standard Tag Library のタグによって環境と J2SE がどのように接続されるのかを説明します。

ユーザー設定の判別

ユーザーの言語設定を Web アプリケーションから判別する方法は 2 つあります。最初の方法は、HTTP 要求ヘッダーフィールド Accept-Language でブラウザからサーバーに伝送される言語とロケールの設定を使用するものです。標準では多様な言語タグがサポートされていますが、通常は、ISO 639 言語コード (たとえば、日本語なら ja) と ISO 3166 国コード (イタリアなら IT) の組み合わせが使用されます。ユーザーは、通常、ブラウザの設定の中で言語リストを作成できます。しかし、残念ながら、この方法は信頼性が高いとはいえま せん。ユーザーが必ずリストを作成するとは限りませんし、アプリケーションがサポートするロケールがリストに必ず含まれているとは限らないからです。この ような問題があるため、多言語アプリケーションでは、通常、もう 1 つの方法を併用します。この方法では、サポートする言語の中からどれかをユーザーに選択してもらい、それをユーザーのプロファイルに格納したり、セッショ ンの間だけ保存したりします。好ましい方法としては、最初に Accept-Language 情報を使用し、それでも情報が得られないときは、アプリケーションの開始ページで言語を明示的に選択できるようにすべきです。

Accept-Language ロケールは、主に、言語の設定や文化的な設定のために用意されているものです。したがって、たとえば、ロケールがユーザーの居住国を表していると解釈すべ きではありません。さらに、ブラウザのロケールに言語コードしか定義されていないことがよくありますが、ロケールに依存する機能は、国によって異なる場合 があります (日付形式など)。国の指定がない場合は、通常、その言語を使用する主要国の慣習に従うと 仮定することが妥当であるといえます。たとえば、日本語だけが指定されている場合は、日本で一般的な日付形式を使用します。しかし、通貨な ど重要な機能が国によって異なる場合は、この仮定を訂正する機会をユーザーに提供する必要があります。

多くの場合、Web アプリケーションはいくつかのコンポーネントから構成されています。しかし、すべてのコンポーネントの地域対応が、同じ言語セットに対するものであるとは 限りません。とりわけ興味深いコンポーネントの 1 つに Java 実行時環境があります。このコンポーネントは、日付書式など、ロケールに依存するある種の機能分野で、40 を超える言語の 100 以上のロケールをサポートします。これは、一般的な Web アプリケーションがサポートするロケールの数をはるかに超えています。したがって、アプリケーションの開発者は、地域対応の機能をアプリケーション全体で サポートされている言語に限定するか、個々のコンポーネントの機能をそのまま利用できるようにするかを決める必要があります。前者の方法ではページに常に 同じ言語が表示されますが、後者の方法では同じページに異なる言語が表示されることがあります。たとえば、ほとんどのテキストにはある言語が使用される が、書式化された日付には別の言語が使用されるといった具合です。

Java 2 Standard Edition プラットフォームでの地域対応

あるアプリケーションがどのロケールをサポートするかを JSTL がどのように判別するかを理解するために、Java 2 Standard Edition プラットフォームでの地域対応がどのようになされているかを見てみましょう。コア部分には、java.util パッケージの 2 つのクラス LocaleResourceBundle があります。

Locale オブジェクトはロケールを単に識別するためのものです。これらのオブジェクトでは、ISO 639 言語コード (たとえば、日本語なら ja) と ISO 3166 国コード (イタリアなら IT)、 さらに、場合によっては (標準化されていない) バリアント文字列とが組み合わされます。HTTP ではロケール識別子に同じ ISO 標準が使用されているため、比較は一般に容易です。

ResourceBundle オブジェクトは、一連のキー/値の組み合わせで表された地域対応可能なオブジェクトのコンテナです。基本リソースバンドルでは、基本バンドル名、一連の キー、デフォルト値 (一般には英語の値になりますが、必須というわけではありません) を定義します。たとえば、簡単な Messages リソースバンドルとして、キーに greeting-day を、デフォルト値に Hello をそれぞれ指定できます。さらに、基本バンドルの他に、言語/国固有のバンドルを定義できます。その場合には、名前として基本バンドル名と、言語/国コー ドやそのバリアントを示す接尾辞を、値として、地域対応化された値をそれぞれ指定します。たとえば、ド イツの Messages_de リソースバンドルには、キー greeting-day の値として Guten Tag を指定できます。あるいは、オーストリアの Messages_de_AT バンドルで Servus と指定すれば、ドイツ語の値を変更できます。リソースバンドルは、Java クラスとして実装することも、単純な「プロパティ」テキストファイルとして実装することもできます。

JavaServer Pages アプリケーションの地域対応アプローチ

JavaServer Pages に基づくアプリケーションの地域対応には、通常、2 つの方法が使用されます。最初の方法では、国際化ページを使って、ロケールに依存するコンテンツをカスタムタグから (しばしばリソースバンドルから) 取得します。この方法は、通常、ページの構造が複雑で、かつその構造の整合性をすべてのロケールの間で維持する必要があるあるときに有効です。もう 1 つの方法では、ロケール固有の個々のページと、サーブレットを使用します。このサーブレットは、ユーザーが好むロケールに基づいて適切なページを呼び出し ます。この方法は、ページの主な内容がテキストである場合や、ページの構造がロケールによって著しく異なる恐れがある場合に有効です。

JSTL におけるロケールの判別と地域対応

JSTL は J2SE の機能の上に構築されたもので、ロケールの判別と地域対応を行います。ロケールの判別機能は、どちらの JSP 地域対応方法 (前出の説明を参照) でも使用できますが、地域対応化機能は国際化ページをサポートするためのものです。

JSTL は、ユーザーのロケール設定を判別する前述の方法を両方ともサポートします。アプリケーションでは、JSTL の <fmt:setLocale> アクションを使って特定のロケールを使用することができます。特定のロケールとは、通常、サポートされる言語の中からユーザーが明示的に選択したもので す。<fmt:setLocale> アクションを使用しないと、Accept-Language ヘッダーに示される優先ロケールの中から、サポートされる最初のロケールが、ロケールに依存する操作によって選択されます。

次の例は、Web アプリケーションの最初のページで使用できるいくつかのコード部分です。これらのコード部分を同時に使用すれば、ユーザーが自身のロケールを極めて容易に 選択できます。このコードは、ページ locale-choice.jsp の一部であるものとします。

<%-- Interpret user's locale choice --%>
<c:if test="${param['locale'] != null}">
<fmt:setLocale value="${param['locale']}" scope="session" />
</c:if>
 
<%-- Offer locale choice to user --%>
<a href="locale-choice.jsp?locale=en-US">USA</a> -
<a href="locale-choice.jsp?locale=de-DE">Deutschland</a> -
<a href="locale-choice.jsp?locale=ja-JP">&#26085;&#26412;</a>
 
<%-- Use URL rewriting to ensure proper session tracking --%>
<form method="get" action="<c:url value='/locale-choice.jsp' />">
<input type=submit value="Stay in session">
</form>

最初のセクション (このセクションは生成された HTML ページのどのコンテンツよりも前に来る必要がある) では、ユーザーがどのロケールを選択したかを解析します。その結果、要求パラメータから JSP ページを知ることができます。locale パラメータが定義されている場合は、それに基づいてセッションのロケールが設定されます。

2 つめのセクション (このセクションは生成された HTML ページのコンテンツの一部です) は、同じページに戻るユーザーリンクです。ただし、locale パラメータは、選択された国に従って設定されます。国名はその地域の言語で示されるため、ページの残りの部分が他国の言語で表示されていても、ユーザーは 国名を容易に判読できます。たとえば、「&#26085;&#26412;」は「日本」という文字 (「Japan」に対応する日本語の文字) の数値指定文字参照で す。日本語のフォントがあれば、最近のブラウザはこのテキストを正しく表示します。古いブラウザにも対応したい場合は、イメージを使用する方がいいかもしれません。

最後のセクションは、<c:url> タグを使って、セッション ID を含む URL をどのように生成するかを示しています。ただし、これは、このセッションを管理する必要がある場合の処理です。(クッキーが有効になっていれば、URL 再作成の代わりにクッキーが使用されます。) これによって、ここで行われたロケールの選択が、Web アプリケーションのすべてのページで有効になります。

ロケールがアプリケーション自身のユーザーインタフェースから選択され、<fmt:setLocale> を使って設定されている場合には、このロケールをアプリケーションが実際にサポートするものとみなすことができます。一方、<fmt:setLocale> が使用されていないために、JSTL が Accept-Language ヘッダーのロケールリストから、サポートされるロケールを見つける必要がある場合には、これほど簡単ではありません。

JSTL は、どのロケールがサポートされているかを判別するために、アプリケーションが使用しているリソースバンドルを参照します。リソースバンドルにアクセスす るアクションには、<fmt:bundle><fmt:setBundle> があります。基本的な機能はどちらも同じです。これらのアクションはリソースバンドルを参照し、「地域対応コンテキスト」を作成します。これには、バンド ルに関する参照情報と、このバンドルを要求するときに使用されたロケールに関する参照情報が含まれています。

<fmt:bundle><fmt:setBundle> アクションによるリソースバンドルの参照では、複数の要求ロケールをサポートできます (したがって、Accept-Language ヘッダーに示されたリストを処理できます)。さらに、この参照では、Web アプリケーションによって定義されたフォールバックロケールを使用できます。ロケールが <fmt:setLocale> アクションによって設定されている場合には、<fmt:bundle><fmt:setBundle> アクションは、そのロケールのバンドルを要求します。あるいは、これが失敗した場合には、フォールバックロケールのバンドルを要求します。<fmt:setLocale> が使用されていない場合には、これらのアクションは、要求が成功するまで、Accept-Language ヘッダーに示されたロケールのバンドル (または、フォールバックロケール) を順に要求します。どちらの場合でも、基本的な参照 (バンドルの基本名と 1 つの要求ロケール) では、要求ロケール自体のリソースバンドルを探します。そして、この要求ロケールからまずバリアント部分を落とし、次に国 部分を落とすことによって単純になったロケールを連続的に探します。すべての要求ロケールと フォールバックロケールに対する参照が失敗した場合には、基本バンドルが使用されます。

この例をいくつか示します。あるアプリケーションに対して enzh_CNzh_TWjako のバンドルが指定されているものとします。フォルバックロケールは en で、<fmt:setLocale> タグは使用されていません。次の表は、要求ロケールリストから得られる地域対応コンテキストのバンドルとロケールを示しています。

要求されたロケール

得られるバンドル

得られるロケール

zh_SG, zh_CN

zh_CN

zh_CN

zh, ja

ja

ja

es_MX

en

en

en_US

en

en_US

ja, zh_CN

ja

ja

ResourceBundle クラスを注意深く見ると、JSTL アクションによる参照方法は、ResourceBundle によるものと異なることがわかります。つまり、ResourceBundle による方法で得られる要求ロケールは 1 つだけです。これでは、Accept-Language ヘッダーに示されるロケールリストを処理できません。さらに、この方法では、フォ-ルバックとして Java 実行時のデフォルトロケールを使用します。これは Web アプリケーションやそのユーザーにとって不都合であると同時に、可搬性に欠ける結果を招きかねません。

ところで、リソースバンドルを参照するアクションがなぜ 2 つあるのでしょうか。その違いは使い方にあります。<fmt:bundle> タグはネストしたタグのコンテキストとなり、<fmt:setBundle> タグは、得られた地域対応コンテキストをある変数に格納します。この変数には、同じページでそれ以後に使用されるアクションからアクセスできるだけでな く、変数の有効範囲によっては他のページのアクションからもアクセスできます。

地域対応コンテキストを使用する JSTL タグに <fmt:message> アクションがあります。このアクションは、その最も単純な形式において、指定されたキーのメッセージを地域対応コンテキストのリソースバンドルから取得 し、それを、生成されたページに挿入します。次の例は、場合によって異なるこのタグの使い方を示しています。

<fmt:setBundle basename="Errors" var="errorBundle" />
<fmt:bundle basename="Messages">
<%-- Localization context established by <fmt:bundle> tag --%>
<fmt:message key="greeting" />
<p>
<%-- Localization context established by <fmt:setBundle> tag --%>
<fmt:message key="emptyField" bundle="${errorBundle}" />
</fmt:bundle>

次に、地域対応コンテキストと関連付けられた要求ロケールがなぜあるのでしょうか。JSTL は、このロケールを通して、書式化タグを、アプリケーションがサポートする言語に限定します。これによって、表示されるページでは常に同じ言語が使用され ます。<fmt:bundle> タグ内にネストされている書式化アクションは、そのタグの地域対応コンテキストを使って、使用すべきロケールを決めます。たとえば、次のページ部分がある とします。

<jsp:useBean id="now" class="java.util.Date" />
<fmt:formatDate value="${now}" timeStyle="long" dateStyle="long" />
<p>
<fmt:bundle basename="Messages">
<fmt:formatDate value="${now}" timeStyle="long" dateStyle="long" />
</fmt:bundle>

HTTP の Accept-Language ロケールが fren で、かつ、動作する Java 実行時が両言語の日付書式化をサポートする場合には (ただし、Web アプリケーションの Messages バンドルは en だけに存在する)、最初の日付はフランス語で書式化されますが、2 つ目は英語で書式化されます。したがって、ページ設計者は、共通の言語を一貫して使用するか、適切なタグネストを使って、存在するすべての地域対応を利用 するかを決めることができます。

最後に、地域対応コンテキストは、なぜ要求ロケールを使用するのでしょうか (見つけたリソースバンドルのロケールではなく)。それは、書式化タグによっては必要になるかもしれない重要な情報が失われるのを防ぐためです。多くのア プリケーションは同じ言語のさまざまな異形を区別せずに、たとえば、英語のリソースバンドルだけを提供します。テキストが英国でもオーストラリアでもシン ガポールでも同じように理解されることを期待するわけです。しかし、日付を書式化する場合には、国が重要になります。英国人にとって「2/6/02」は 「2 June 2002」ですが、米国式に慣れた人には「February 6, 2002」を意味します。したがって、多くの場合、要求ロケール (リソースバンドルロケールではなく) が使用される場合には、国情報が保存されます。

文字のエンコーディング

テキストをコンピュータに格納したり、ネットワーク経由で伝送したりする場合には、現在、テキストを表す方法として 2 つのモデルが使用されています。1 つは、少数の言語や国、オペレーティングシステムに特有の古い文字エンコーディングモデルです (ISO 8859 シリーズや Windows コードページ、EUC エンコーディングなど)。もう 1 つは、あらゆる言語を表すことができ (少なくとも理論的には)、どこにでも使用できる Unicode ベースの新しいエンコーディングモデルです。

旧モデルには重大な欠点があります。

  • 旧文字エンコーディングは、通常、少数の言語セットだけしかサポートしません。たとえば、Shift-JIS は日本語と英語をサポートしますが、その他のアジアやヨーロッパの言語はサポートしません。ISO 8859-1 は、西ヨーロッパの言語をいくつもサポートしますが、東ヨーロッパの言語はサポートしません。
  • 文字を変換すると、予期せぬ情報の損失を招くことがあります。開発者は、しばしば、ドイツ語やフランス語など、西ヨーロッパ諸国の言語のエン コーディングに ISO 8859-1 を使用しますが、「€」文字 (ドイツやフランスなど、多くの西ヨーロッパ諸国で共通に使用される通貨記号) がこのエンコーディングではサポートされないことを発見して驚くことがあります。このような情報の損失を避けるためには、ブラウザがどのオペレーティング システムで動作しているかによって、Windows-1252 や ISO 8859-15 などのエンコーディングを使用する必要があります。

Web コンテンツの作成や配布、解釈に使用される主要なソフトウェアシステムの最新バージョンでは、新しいモデルが主流になっています。このようなバージョン は、通常、内部処理に Unicode を使用しています。あるいは、少なくとも、UTF-8 (Web で使用される Unicode ベースのエンコーディング) をどのように処理すべきかを承知しています。Unicode ベースのエンコーディングには明確な利点があります。このエンコーディングでは、多言語のページが可能で、ロケール処理の問題が文字エンコーディングと明 確に分離されます。さらに、エンコーディングの変換によって情報が失われるおそれもほとんどありません。Unicode ベースのエンコーディングは、現代のサーバーシステムやクライアントシステムにうまく当てはまります。

それにもかかわらず、多くの Web 開発者は依然として UTF-8 の使用を躊躇しています。その理由には、このエンコーディングがうまく動作しない旧バージョンのブラウザをサポートする必要性や、このエンコーディングを サポートするツールの欠如などが考えられます。

JavaServer Pages 技術は両方のモデルをサポートします。次の各セクションでは、文字のエンコーディングが問題となるさまざまな分野を概観し、JSP 技術や JSTL がこのような問題をどのように処理するのかを説明します。

ソースページエンコーディングの処理

JSP ソースファイルのエンコーディングは、それを編集するツールがあるかどうかで決まります。そのため、一般には、国やオペレーティングシステム固有のエン コーディングが使用されます。文字エンコーディングを JSP 実行時環境 (「コンテナ」) に伝える方法はいくつかあります。そして、そのメカニズムとルールはこれまでにある程度の進化を見てきました。さらに、JSP ファイルの構文として標準構文と XML ベースの新構文があることも好都合です。

JSP 2.0 仕様は、文字エンコーディングを検出するときに 2 つの構文を区別できます。XML 構文のファイルであれば、エンコーディングは XML 仕様に従って検出されます。つまり、デフォルトは UTF-8 か UTF-16 であり、それ以外のエンコーディングは、ファイル冒頭の XML 宣言で宣言されていなければなりません。標準構文のファイルの場合は、コンテナが 2 つの主要な情報源を参照します。コンテナは、まず、アプリケーションの配備記述子の中から、jsp-property-group の URL パターンがファイルと一致する jsp-property-group の要素 page-encoding を探します。次に、コンテナは、ページ自体にある属性 pageEncoding を探します。どちらも見つからないと、コンテナは contentType 属性の charset をさらに探します (次のセクションの「Web ページエンコーディングの処理」を参照)。あるいは、最終的なフォールバックとして ISO 8859-1 を使用します。

ここでは、JSP 2.0 に基づくアプリケーションに対する簡単な推奨事項を紹介します。まず、XML 構文のファイルの場合、UTF-8 か UTF-16 で適切にエンコーディングされていないファイルは文字エンコーディングを明示すべきです。標準構文のファイルの場合は、すべてのソールファイルに UTF-8 を使用するのであれば、配備記述子にある単一要素 page-encoding を使ってこのことを述べるべきです。ロケール固有のエンコーディングを使用する場合は、ファイルをこのロケールに従って編成または命名し、page-encoding 要素を使ってこの関係を記述します。たとえば、韓国語のすべてのファイルが EUC-KR でエンコードされ、Web アプリケーションの /ko/KR サブディレクトリに格納されているなら、次のように指定します。

<jsp-property-group>
<url-pattern>/ko/KR/*</url-pattern>
<page-encoding>EUC-KR</page-encoding>
</jsp-property-group>

アプリケーションのソースファイルをこのように編成できない場合は、個々のソースファイルに pageEncoding 属性を追加します。ただし、この属性はファイルの冒頭に指定する必要がある点と、この属性は、ASCII の拡張である文字エンコーディングを指定するためにしか使用できない点に注意してください。つまり、2 つめの制約は、UTF-8 や多くの旧文字エンコーディングでは問題ありませんが、UTF-16 や EBCDIC ベースのエンコーディングでは問題があります。contentType 属性の charset 値を使用してソースページのエンコーディングを指定する方法はお勧めできません。この値は、Web ページのエンコーディングを指定する場合にだけ使用すべきです (次のセクションを参照)。

JSP 1.2 仕様は、ソースファイルの文字エンコーディングに関して、標準構文のファイルと XML 構文のファイルを明確に区別していません。さらに、この仕様では、配備記述子に文字エンコーディングを指定できません。したがって、JSP 1.2 コンテナ用のアプリケーションでは、文字エンコーディングを正しく検出するために、pageEncoding 属性を使って各ソースファイルの文字エンコーディングを指定すべきです。

JSTL には <c:import> アクションが定義されています。このアクションを使えば、URL で指定された外部データを JSP が生成したページに組み込むことができます。さらに、このアクションでは、文字エンコーディングを指定できます。外部データにそれ自体のエンコーディング が指定されていない場合は、この文字エンコーディングが使用されます。

Web ページエンコーディングの処理

Web アプリケーションは、生成される Web ページに使用される文字エンコーディング (応答文字エンコーディング) を選択する必要があります。ただし、その際には、ターゲットとなるブラウザの機能や、ページコンテンツの書き込みシステムや言語の機能、さらに場合によっ てはブラウザのホストオペレーティングシステムの機能を考慮する必要があります。HTTP 仕様では、文字エンコーディングは Content-Type エンティティヘッダーの charset パラメータに指定されます。

ターゲットとするすべてのブラウザが UTF-8 をサポートする場合には、通常、このエンコーディングを使用すべきです。それによって、多言語文書がサポートされるだけでなく、文字変換による情報の損失 を避けることができます。

UTF-8 を使用できない場合は、文字エンコーディングと使用する言語 (特殊文字を含む) の対応に注意を払う必要があります。間違いが起るのを避けるためには、ページ全体で強制的に同じ言語を使用する必要があるかもしれません。これについて は、この解説の始めの方にある「ロケールの判別と地域対応」セクションを参照してください。さらに、「€」文字の使用を避ける必要があるかもしれません。

ページの文字エンコーディングは、Web アプリケーションで明示的に指定することもできますし、ロケール情報を使って間接的に JSP 技術に選択させることもできます。

  • 明示的に指定する場合には、ページの contentType 属性を使う方法が最も簡単です。この場合には、文字エンコーディングだけでなく、生成されるページのコンテンツタイプをアプリケーションで指定できます。 要求の処理中にアプリケーションで文字エンコーディングを設定したい場合には、カスタムアクションかある種の Java コードを使って javax.servlet.ServletResponse.setContentType メソッドか、新しい (Servlet 2.4 の) javax.servlet.ServletResponse.setCharacterEncoding メソッドを呼び出す必要があります。
  • さらに、文字エンコーディングは、<fmt:message> などの JSTL 形式化アクションや、<fmt:bundle><fmt:setBundle><fmt:setLocale> アクションによって無条件かつ暗黙的に判別されます (JSTL 書式化アクションの場合は、それらのアクションが地域対応コンテキストを作成するときに判別されます)。これらのアクションは、ServletResponse.setLocale メソッドを通して、地域対応コンテキストのロケールまたは指定されたロケールを文字エンコーディングに対応付け、それをページのコンテンツタイプ上に設定 します。Servlet 2.4 仕様では、配備記述子の locale-encoding-mapping-list 要素を通してアプリケーションからこの対応付けを制御できます。アプリケーションにこの要素が備わっていない場合や、以前の Servlet 仕様に基づくコンテナが使用されている場合には、ロケールと文字エンコーディングの対応付けがどうなるかはそのコンテナに依存します (一般的な実装では、古い文字エンコーディングが使用されています)。

古い文字エンコーディングがサポートされているのであれば、文字エンコーディングの暗黙的な判別であってもかまいません (ただし、その場合でも、ページの中で同じ言語が常に使用され、かつ、一般的に使用されている文字エンコーディングでサポートされない恐れがある特殊文字 が使用されないことが前提です)。しかし、UTF-8 を使用するためには明示的な指定が必要です。Servlet 2.4 仕様では、明示的な指定が暗黙的な指定よりも優先するため、contentType 属性の一部として文字エンコーディングを指定するだけで十分です。その後で JSTL 書式化アクションを使用しても、文字エンコーディングには影響を及ぼしません。しかし、以前のバージョンの Servlet 仕様では、明示的な指定が、ロケール情報に基づく暗黙的な指定を置き換えるとは限りませんでした。以前のバージョンに基づくコンテナとの互換性を必要とす る場合は、明示的な文字エンコーディングを指定してからカスタムアクションを最初に使用するまでの間に ServletResponse.flushBuffer を呼び出すことによって、文字エンコーディングをフリーズする必要があります。これは、カスタムアクションが文字エンコーディングを暗黙的に判別する恐れ があるからです。

要求パラメータエンコーディングの処理

JSP ページは Web ページを生成できるだけでなく、HTTP 要求に付随するパラメータを受信し、解釈することができます。この要求は、通常、前に生成された Web ページの一部であるフォームからの入力です。このようなパラメータの文字エンコーディングはどこにも指定されていません。しかし、ブラウザは、事実上の標 準として、このフォームを含むページと同じエンコーディングを使用します。

このことは、以前に生成されたページのエンコーディングを Web アプリケーションが常に知っていなければならないことを意味します。一般的に使用されるメカニズムでは、エンコーディングの名前がフォーム自体の隠された フィールドに格納され、次回の要求でそれが最初のパラメータとして抽出され、他のパラメータの復号化に使用されます。しかし、JSP ページでは、セッション管理を通して要求と要求の間で情報を共有することができます。

アプリケーションでは、JSTL カスタムアクション <fmt:requestEncoding> を使って、パラメータに使 用される エンコーディングを指定できます。アプリケーションが常に UTF-8 のページだけを送信するのであれば、このアプリケーションでは、要求エンコーディングとしてこのエンコーディングを指定するだけですみます。しかし、生成 されたページのエンコーディングをアプリケーションで明示的に指定する場合には、アプリケーションでセッション情報の一部としてエンコーディングを管理 し、それを <fmt:requestEncoding>アクションに明示的に渡すべきです。文字エンコーディ ングの判別を暗黙的な処理にまかせる場合には、アプリケーションでは、単に、文字エンコーディングを指定せずに <fmt:requestEncoding> アクションを使用します。生成されたページのエンコーディングを暗黙的に判別するアクションもまたセッションの中で情報を格納しますので、<fmt:requestEncoding> からそれを読み取り、使用できます。

書式化と解析

数値や日付などのデータを地域対応書式で表示する機能は、ユーザーの入力を解釈する機能と同じように、どのようなアプリケーションでも必要になりま す。個々の言語や文化の書式はさまざまであるため、そのためのライブラリが存在しなければ、この仕事は、開発者にとって簡単な仕事とはいえません。

幸いなことに、そのようなライブラリがあります。Java 2 Standard Edition (J2SE) プラットフォームの java.text パッケージには、一般的に使用されるデータタイプの書式化や解析を行うクラス群が含まれています。しかも、 Sun の実装では、これらのクラスは 100 以上のロケール向けに地域対応がされています。

JavaServer Pages Standard Tag Library には、この機能を JSP ページで直接使用できるようにするカスタムアクションが含まれています。

書式化アクションや解析アクションのロケールの判別

数値や日付のための書式アクションや解析アクションを使用する場合には、事前定義された地域対応コンテキストがあっても (たとえば、<fmt:bundle> タグの中にタグがネストしている場合)、あるいは、なくてもかまいません。事前定義された地域対応コンテキストがあれば、アクションは地域対応コンテキス トのロケールを使用します。ない場合には、使用するロケールを、<fmt:bundle><fmt:setBundle> アクション向けの改定されたリソースバンドル参照方法 (既出の説明を参照) に従って判別します。このアルゴリズムの主な相違点は、リソースバンドルを参照する代わりに、java.text.NumberFormat.getAvailableLocales メソッド (数値の書式化/解析アクションの場合) や java.text.DateFormat.getAvailableLocales メソッド (日付や時刻の書式化/解析アクションの場合) を使って、サポートされるロケールを判別する点です。

数値の書式化と解析

数値の形式化や解析を行う JSTL カスタムアクション <fmt:formatNumber><fmt:parseNumber> は、J2SE のクラス java.text.NumberFormat に基づいて構築されています。これらのアクションは、単純な数値だけでなくパーセントや通貨の数値も処理します。

これらのアクションによる通貨の書式化サポートは、とりわけ興味深いものがあります。これまで、多くの書式化ライブラリでは、通貨記号はロケールか ら導き出せるものとされていました。たとえば、ロケールが China なら、通貨は RMB であるといった具合です。しかし、国境を越えた取引が行われる環境では、このことはあまり意味がありません。たとえば、ある英国の企業がポンドで計算した 価格を Web アプリケーションが RMB で表示するとします。これには 2 つの問題があります。RMB の価値はポンドよりもずっと低く、RMB をポンドに戻すことは困難であるかもしれません。通貨の選択は実際にはビジネスの問題であるため、通貨は、書式の一部としてではなく、値の一部として取り 扱う必要があります。

<fmt:formatNumber> アクションを使えば、アプリケーションで ISO 4217 通貨コードか通貨記号を指定できます。これは、地域対応化された数値書式化機能がデフォルトで使用する通貨を置き換えます。たとえば、値プロパティと通貨 プロパティをもつ価格ビーンを使用するアプリケーションがあるとします。次のページ部分を使えば、この価格を書式化できます。

<fmt:formatNumber type="currency" value="${price.value}"
currencyCode="${price.currency}" />

JSP ページで通貨コードを指定すると、その下で動作する NumberFormat オブジェクトは、使用されている地域対応コンテキストのロケール向けに地域対応化された通貨記号をその通貨に対して使用しようとします。たとえば、米国ド ル向けに通貨コード USD を指定すると、ロケールが en_US なら、通常、「$」が使用されます。あるいは、その他のロケールで US$ が通貨記号として有効であれば、通常、US$ が使用されます。あるいは、地域対応化された記号が不明なら、通常、フォールバックとして通貨コード USD が使用されます。

日付と時刻の書式化と解析

日付や時刻の書式化や解析を行う JSTL カスタムアクション <fmt:formatDate><fmt:parseDate> は J2SE クラス java.text.DateFormat に基づいて構築されています。これらのアクションは、さまざまな日付や時刻の表示をサポートします。

興味深い点は、表示される日付や時刻がロケール固有の書式に依存するだけでなく、時間帯の認識にも依存していることです。ユーザーにとって、通常、 サーバーの時間帯は重要ではありません。しかし、一方で、ユーザーがどの時間帯にいるのかを簡単に知る方法はありません。アプリケーションでは、クライア ント側の JavaScript コードを使用することによって、GMT とユーザーの時間帯とのオフセットを知ることができます。あるいは、現在の時間帯をユーザープロファイルの一部としてユーザーに指定させることもできま す。JSTL アクションではこの問題を解決できませんが、JSTL には、時間帯の日付や時刻の書式化と解析を知るためのカスタムアクションが 2 つあります。<fmt:timeZone><fmt:setTimeZone> です。<fmt:bundle><fmt:setBundle> の場合と同じように、<fmt:timeZone> タグはネストされたタグの時間帯を定義し、<fmt:setTimeZone> は時間帯を変数に格納します。この変数はそれ以後のアクションで使用されます。

メッセージの書式化

前に述べた <fmt:message> アクションでは、リソースバンドルから文字列を取得し、それを、生成されたページに挿入できるだけではありません。このアクションでは、さらに、パラメー タによる置き換えを行ない、パラメータを必要に応じて書式化することができます。このアクションは java.text.MessageFormat クラスに基づいているため、リソースバンドルから取得した文字列は、実際には、MessageFormat パターン文字列である場合があります。必要な引数は、<fmt:param> アクションで指定します。

たとえば、JSP ページに次のテキストが指定されているとします。

<jsp:useBean id="now" class="java.util.Date" />
<fmt:bundle basename="Messages">
<fmt:message key="greeting">
<fmt:param value="${now}" />
</fmt:message>
</fmt:bundle>

見つかったリソースバンドルがドイツ語で、キー greeting の値が「Willkommen! Heute ist der {0,date,long}.」であるなら、2002 年 6 月 21 日に出力されるページ内容は、「Willkommen! Heute ist der 21. Juni 2002.」となります。

まとめ

これまでの説明でおわかりのように、JavaServer Pages 技術 (とりわけ、JavaServer Pages Standard Tag Library) は、多言語アプリケーションの構築に使用できる強固な基盤であるといえます。しかし、設計に当たっては、いくつかの選択肢を慎重に検討する必要がありま す。たとえば、ユーザーの言語とロケールの設定をどのように判別するかや、地域対応化のために JSP ページの構造をどのようにするか、すべてのページを単一の言語に限定するか既存のロケールサポートを全面的に使用するか、どの文字エンコーディングモデル を使用するか、といったことです。JSP 技術を使えばどの選択肢にも対応できますので、世界中のユーザーに最も適切な方法で情報を提供できます。とりわけ重要な点は、ユーザー独自の言語で提供で きることです。

参照

R. Fielding 他: 『Hypertext Transfer Protocol -- HTTP/1.1. RFC 2616』The Internet Society、1999 年

Dave Raggett 他 (ed.): 『HTML 4.01 Specification』World Wide Web Consortium、1999 年

Tim Bray 他 (ed.): 『Extensible Markup Language (XML) 1.0 (Second Edition)』World Wide Web Consortium、 2000 年

JavaTM 2 Platform, Standard Edition, v 1.4.2 API Specification』 Sun Microsystems、2002 年

Danny Coward (ed.): 『JavaTM Servlet Specification. Version 2.3』Sun Microsystems、 2001 年

Danny Coward, Yutaka Yoshida (ed.): 『JavaTM Servlet Specification. Version 2.4』Sun Microsystems、 2003 年

Eduardo Pelegrí-Llopart (ed.): 『JavaServer PagesTM Specification. Version 1.2』 Sun Microsystems、2001 年

Mark Roth, Eduardo Pelegrí-Llopart (ed.): 『JavaServer PagesTM Specification. Version 2.0』 Sun Microsystems、2003 年

Pierre Delisle (ed.): 『JavaServer PagesTM Standard Tag Library. Version 1.0』Sun Microsystems、2002 年

Pierre Delisle (ed.): 『JavaServer PagesTM Standard Tag Library. Version 1.1』Sun Microsystems、2003 年

著者について

Norbert Lindenberg 氏は Sun Microsystems の Java Web Services グループに属し、Java の国際化で指導的な役割を担っています。Sun に入社する前は、General Magic や Apple Computer でさまざまな国際化プロジェクトに係わってきました。ドイツの Universität Karlsruhe からコンピュータサイエンスの修士号を取得しています。

Oracle is reviewing the Sun product roadmap and will provide guidance to customers in accordance with Oracle's standard product communication policies. Any resulting features and timing of release of such features as determined by Oracle's review of roadmaps, are at the sole discretion of Oracle. All product roadmap information, whether communicated by Sun Microsystems or by Oracle, does not represent a commitment to deliver any material, code, or functionality, and should not be relied upon in making purchasing decisions. It is intended for information purposes only, and may not be incorporated into any contract.