[INDEX]
2001-08-19

Servlet/JSPの表示言語切替えの方法

はじめに

ブラウザからWWWサーバに対して送られるヘッダ情報のひとつに
        Accept-Language
がある。これは、ブラウザが扱うことができる言語情報をサーバに対して知らせるために定義されている。この値は具体的には
        Accept-Language: ja
        Accept-Language: en
        Accept-Language: ja,en; q=0.5
などのように言語情報とそれらの優先順位を含んでいる。Netscape CommunicatorやInternet Explorerはもともと多言語対応がされており、優先的に使用したい言語を指定することができる。既定値は日本語版のブラウザでは
        Accept-Language: ja
であり、英語版のブラウザ
        Accept-Language: en
となっている。従って、サーバ側ではこのヘッダ情報によって表示するコンテンツの内容を切り替えることができる。(この仕組みはコンテンツ・ネゴシエーションと呼ばれる。)

言語判定の具体的な方法

Accept-Languageの値は、複数指定可能だが、優先度の高い言語が前の方に現れるので、始めの方だけを見て判断すれば良い。言語指定のパラメータはISOの2桁コードなので、最初の2文字だけを見れば良い。Accept-Languageヘッダがない場合や、その他の言語が指定されているときの既定値を日本語にするとして、次のようなロジックとなる:
        1. 言語モードを日本語にする
        2. IF Accept-Charsetの値が存在する
                AND
              Accept-Charsetの値が'en'で始まる THEN
                2.1 言語モードを英語にする
           ENDIF

Servletでは

Servletではサーバからブラウザに送信する言語情報はContent-Typeヘッダを用いる。Content-Typeは本来はデータの種別を表わすものであるが、副属性としてcharset情報を付加することができる。charsetは本来は文字コードセットを示すもので、言語とは意味あいが異なる。これは欧米では1バイトコードしか用いられないので符号化方法を意識しなくてよく、
        文字コードセット=言語
とみなすことで事足りているからである。ところが日本では、文字コードセットこそJIS X 0201+ JIS X 0208という組み合わせでほぼ網羅されているが、マルチバイト符号化をする必要があり、符号化も合わせて意識しなければならない。具体的には
        ISO-2022-JP
        EUC-JP
        Shift_JIS
という3種類の符号化方式が使われている。そこで、上記のcharset属性にはこれらの「符号化方式」を指定することになっている。

つまり、「Shift_JIS符号化方式のHTMLファイルを送信する」場合 には

        Content-Type: text/html; charset=Shift_JIS
というヘッダ情報を送れば良い。ServletではServletResponseクラスのAPIとしてsetContentType()が用意されているので、
        response.setContentType("text/html; charset=Shift_JIS");
のようにAPIを呼び出しておく。

Servletの実装によっては、setContentType()でセットされた言語情報をその後の文字処理に影響を及ぼすことがある。たとえば、OracleのJDBCはデータベースとのデータのやりとりに、ここで設定された言語情報が使われるので、ページ中のできるだけ早いタイミングでContent-Typeを設定しておくことが望ましい。

JSPでは

JSPでの言語処理もServletと同じであるが、ページ(.jsp)ファイルの符号化方式に注意が必要である。一旦コンパイルされてしまうとJavaの内部コードであるUnicodeで文字列を扱うようになるが、それまで
  1. .jspファイル => .javaファイルの変換
  2. .javaファイル => .classファイルの変換(コンパイル)
というステップを踏むことになるが、2.のステップでは.javaファイルの符号化方式を正しくコンパイラが解釈しないと、文字化けした結果しか出力されなくなってしまう。JSPエンジンの内部的で使われるJavaコンパイラは、.javaソースファイルの符号化方式を
  1. encodingパラメータで指定した値
  2. 実行時の環境変数
  3. JavaVMのプロパティの既定値
の順で判断するらしく、明示的に指定されかった場合には多くの場合ISO-8859-1が使われてしまう。Oracle JSPではこの処理を
	<%@ page contentType="text/html; charset=Shift_JIS" %>
のように記述するページディレクティブで判定するようである。つまり、Shift_JIS符号化方式で書かれている.jspファイルでは上記のようにページディレクティブを指定することによって、正しく符号化方式を判定して文字化けを防いでいる。
Apache+JservやTomcatの実装ではここがうまくハンドリングされていないようで、設定などで工夫する必要があるようである。

コンテント・ネゴシエーションの方法

ここまでの知識を前提として、実際にコンテント・ネゴシエーションをどのように実装するかを以下に記述する。ポイントは
  1. ブラウザからのAccept-Languageの取得・言語判定
  2. ContentTypeの設定
  3. 言語モードによる出力の制御
である。

日本語(Shift_JIS)を既定値とする場合のJSPソースの例:

<%@ page contentType="text/html; charset=Shift_JIS" %>
<HTML>
<HEAD><TITLE>Content negotiation sample</TITLE></HEAD>
<BODY>
<%
    int LANG_JA = 0;
    int LANG_EN = 1;
    int lang = LANG_JA;
    String langStr = request.getHeader("accept-language");

    if (langStr != null && langStr.startsWith("en")) {
	lang = LANG_EN;
	response.setContentType("text/html; charset=iso-8859-1");
    }

    if (lang == LANG_EN) {
%>
<DIV ALIGN="CENTER"><H1>English</H1></DIV>
<%
    } else {
%>
<DIV ALIGN="CENTER"><H1>日本語</H1></DIV>
<%
    }
%>
</BODY>
</HTML>
このページにブラウザからアクセスしてみると、ブラウザで設定してある言語指定によって表示が切り替わることが確認できるはずである。

ただ、ページ中で言語を意識すべき箇所が現れる度に上記のif (lang == ...) のような記述を埋め込むのは、ソースの見通しが悪いし、あまり格好が良くない。ここは以下のようにJSPタグで制御したいものである。

<lang:en>
    <DIV ALIGN="CENTER"><H1>English</H1></DIV>
</lang:en>
<lang:ja>
    <DIV ALIGN="CENTER"><H1>日本語</H1></DIV>
</lang:ja>
実は、JSP1.1以上ではtaglibというJSPタグを拡張できる仕組みがあり、これを利用することで上記のような表現が可能である。しかし、不幸にしてOracle iAS 1.0のJSPは1.0であり、タグの拡張がサポートされていない。替りにJMLが用意されていて、<jml:...>という形の拡張タグを使うことができる。このJMLタグの中に
	<jml:if>
というものがあるので、これを利用すると
<%@ page contentType="text/html; charset=Shift_JIS" %>
<%@ taglib uri="oracle.jsp.parse.OpenJspRegisterLib" prefix="jml" %>
<HTML>
<HEAD><TITLE>Content negotiation sample</TITLE></HEAD>
<BODY>
<%
    int LANG_JA = 0;
    int LANG_EN = 1;
    int lang = LANG_JA;
%>
<jml:if condition="lang == LANG_EN">
  <DIV ALIGN="CENTER"><H1>English</H1></DIV>
</jml:if>
<jml:if condition="lang == LANG_JA">
  <DIV ALIGN="CENTER"><H1>日本語</H1></DIV>
</jml:if>
</BODY>
</HTML>
のように記述することができる。しかし、これでは最初の例のようにJava Scriptletで書くのとほとんど変わりない割に、Oracle JSPだけでしか動かないプログラムとなってしまうため、移植性が低い。Oracle iASでしか使わないプログラムだからといって移植性を犠牲にすることは目先のことしか考えていない。移植性の低いプログラムはプラットホームのバージョンの違いにすら敏感だからである。特に、JSP1.0と1.1との差は非常に大きく、開発の効率が格段によくなるにも関わらず、JSP1.0しか使えないiAS 1.0を使いつづけなければならないのが、こうした移植性を考慮せずに開発したプログラムが足枷となるとしたら大変つまらないことである。

以上
kazm-s@shorindo.com