リソースバンドルと通貨記号

¥が¤になってしまう!

eコマースなどでは、海外のからも読めるように、日本語の他、英語やドイツ語などの表示に対応する必要があります。そこで、普通、各国語用のプロパティファイルを用意して、自動的に表示が切り替わるようにします。

※詳細は、わかりやすいJavaEE 10章「リソースバンドルと国際化」を参照

ところが、このようにしたとたんに、それまでなんともなかった通貨記号の表示がおかしくなります。JSFでは、通貨形式での表示は、数値のコンバータタグを使って次のようにします。

<h:outputText value="#{bb.price}">
   <f:convertNumber type="currency" />
</h:outputText>

これが、次の図のようになってしまうのです。さて、どうしたことでしょう。

ブラウザのロケール設定

これには、ブラウザのロケール設定が関連しています。そもそも、ロケール設定には次の3つの方法があります。

1.ブラウザのロケール設定を受け取ってそれに合わせて表示する(上の例の場合)

2.setLocaleメソッドを使ってプログラムで強制的に指定する

3.<f:view  locale=”~” /> を使ってJSFページごとに指定する

上の例では、1.に頼って表示していますので、少ししらべて見ることにしました。

次のコードをバッキングビーンに追加します。@PostConstruct は、CDIやEJBで使えるライフサイクルコールバックメソッドを定義するアノテーション(わかりやすいJavaEE 14章「CDIとEJB」を参照)です。@Postconstructを付けたこのメソッドは、コンストラクタを実行した直後に実行されるメソッドです。

コードの内容は、実行中のプログラムがブラウザから受け取ったロケールを取り出して表示するだけです。

@PostConstruct
public void localeCheck(){
    Locale locale = FacesContext.getCurrentInstance()
                                 .getViewRoot().getLocale();
    System.out.println("*** " + locale + " ***");
}

実行した結果、GlassFishのログには次のように表示されました。

[2015-09-14T10:11:22.113+0900] [glassfish 4.1] 
[INFO] [] [] [tid: _ThreadID=29 _ThreadName=Thread-8] 
[timeMillis: 1442193082113] [levelValue: 800] [[
 *** ja ***]]

なんと!、ja になっています。ja は「日本語」と言うロケールで、「日本国」ではありません。国コードが含まれていないので、通貨記号(国に帰属します)を特定できなかったわけです。本来、通貨記号を正しく表示するためには、ja_JPというロケール設定であることが必要です。

この時、使用したChromeブラウザでは、ロケール設定は次のようでした。

多国語対応ページで通貨記号を変えることに意味があるか?

これはFireFoxでも、IEでも同様で、ja_JPというロケールが指定できないのです。ただ、よく考えてみると、多国語対応時に、本当に通貨記号を変えてもいいのでしょうか。

\3,200 を記号だけ変えて$3,200.00 などと表示すると、これはまたおかしなことになります。そうするのであれば、金額も変更して$24.00と表示する必要があります。つまり、各国別に価格データを持っていなければならないので、現実的ではありません。

海外のサイトでは、説明などは日本になっていても、価格は$表記のままのサイトがほとんどです。そして、そうするなら、通貨記号をロケールに併せて変更してないけない、ということになります。

つまり、

多国語対応ページでは、type=currencyを使わない

というのが、現実的な解答のようです。

それでも気になる人へ

それでも気になる人がいるかもしれませんね(私もそうなんですが)。商品データを国別に用意しておけば、ローケルを判定して表示を(通貨記号も含めて)変えることができないわけではありません。以下をクリックしてみてください。サンプルプログラムを動かすことができます。起動したら、国旗のマークをクリックして、変化を確認してください。

プログラムによるローケルの設定

日本、アメリカ、ドイツに対応しています。なお、プロジェクトファイルもダウンロードできます。プログラムの説明は、ソースコード中に書いておきましたので見てください。

プロジェクトファイルのダウンロード

なお、ソースコードを以下に掲示しておきます。

<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://xmlns.jcp.org/jsf/html" xmlns:f="http://xmlns.jcp.org/jsf/core">
    <h:head>
        <title>#{res.title}</title>
        <h:outputStylesheet library="css" name="mystyle.css"/>
    </h:head>
    <h:body>
        <h2>#{res.title}</h2>
        <h:form>
            <div id="loclales">
                <h:commandLink action="#{bb.setLocale('ja_JP')}">
                    <h:graphicImage library="images" name="jp.gif" style="margin:5px" />
                </h:commandLink>
                <h:commandLink action="#{bb.setLocale('en_US')}">
                    <h:graphicImage library="images" name="us.gif" style="margin:5px" />
                </h:commandLink>
                <h:commandLink action="#{bb.setLocale('de_DE')}">
                    <h:graphicImage library="images" name="de.gif" style="margin:5px" />
                </h:commandLink>
            </div>
            <div id="content" >
                <h:panelGrid columns="2" >
                    #{res.product}<h:outputText value="#{bb.title}"/>
                    #{res.price}<h:outputText value="#{bb.price}">
                        <f:convertNumber type="currency" />
                    </h:outputText>
                </h:panelGrid>
            </div>
        </h:form>            
    </h:body>
</html>
package beans;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import javax.annotation.PostConstruct;
import javax.enterprise.context.RequestScoped;
import javax.faces.context.FacesContext;
import javax.inject.Named;

@Named
@RequestScoped
public class Bb {
    /* 日本、アメリカ、ドイツに対応するので、その国記号付きのロケール
     * setLocaleメソッドで使う
    */
    private static final Locale[] locales = {Locale.JAPAN, Locale.GERMANY, Locale.US};
    
    /* 1件だけ用意したデータです。本来はデータベースにいれておくものですが、
     * 簡単にしました。
     * static にしてあらかじめ作っておきます。
     */
    private static Book book;
    static{
        Map<String, String> titles = new HashMap();
        titles.put(Locale.JAPANESE.toString(), "わかりやすいJavaEE");
        titles.put(Locale.ENGLISH.toString(), "Descriptive JavaEE");
        titles.put(Locale.GERMAN.toString(), "Beschreibende JavaEE");

        titles.put(Locale.JAPAN.toString(), "わかりやすいJavaEE");
        titles.put(Locale.US.toString(), "Descriptive JavaEE");
        titles.put(Locale.GERMANY.toString(), "Beschreibende JavaEE");
        
        Map<String, Integer> prices = new HashMap();
        prices.put(Locale.JAPANESE.toString(), 3200);
        prices.put(Locale.ENGLISH.toString(), 24);
        prices.put(Locale.GERMAN.toString(), 27);

        prices.put(Locale.JAPAN.toString(), 3200);
        prices.put(Locale.US.toString(), 24);
        prices.put(Locale.GERMANY.toString(), 27);

        book = new Book("9784798042169", titles, prices);        
    }
    
    private String title;       // 書籍の名前
    private Integer price;      // 価格
    
    /* ウェブで国旗のアイコンをクリックすると、指定されたロケール記号を持ち
      ここへ来る。ロケールを切り替え、データもロケールに合わせて入れ替えます。
       voidなので、終了後、画面が再表示されます。
    */
    public void setLocale(String localeStr) {
        for (Locale lo : locales) {
            if (localeStr.equals(lo.toString())) {
                FacesContext.getCurrentInstance().getViewRoot().setLocale(lo);
                title = book.getTitle().get(lo.toString());
                price = book.getPrice().get(lo.toString());
                break;
            }
        }
    }
    /* コンストラクタの終了直後に実行するライフサイクル・コールバックメソッド 
     * (わかりやすいJavaEE 14章を参照)
     * ブラウザのロケ-ル設定を受け取り、初期表示データをローケルに合わせてセットする
     * また、言語だけのロケールだったら、国記号付きに変えてセットしなおす
    */
    @PostConstruct
    public void init() {
        Locale locale = FacesContext.getCurrentInstance().getViewRoot().getLocale();
        this.title = book.getTitle().get(locale.toString());
        this.price = book.getPrice().get(locale.toString());
        
        /* 言語コードだけでは、通貨記号を表示できないので、国記号付きに変更する */
        if(locale.equals(Locale.JAPANESE))  chgLocale(Locale.JAPAN);
        else if(locale.equals(Locale.GERMAN))  chgLocale(Locale.GERMANY);
        else if(locale.equals(Locale.ENGLISH))  chgLocale(Locale.US);
    }
    public void chgLocale(Locale locale){
         FacesContext.getCurrentInstance().getViewRoot().setLocale(locale);
    }
    /* セッターとゲッター */
    public String getTitle() {
        return title;
    }
    public void setTitle(String title) {
        this.title = title;
    }
    public Integer getPrice() {
        return price;
    }
    public void setPrice(Integer price) {
        this.price = price;
    }
}
package beans;
import java.util.Map;
// 本来はデータベースに入れるエンティティクラス
// 各国用のデータがあるので、titleとpriceはMapになっています。
// Mapのキーはロケールコード(ex. ja_JPなど)です
public class Book {
    private String code;                   // ISBNコード
    private Map<String,String> title;      // 書籍名      
    private Map<String, Integer> price;    // 価格 
    /*
     * Mapのキーはいづれもローケル文字列(ex. ja_JPなど)
    */
    public Book(String code, Map<String,String>title, Map<String,Integer> price){
        this.code = code;
        this.title=title;
        this.price=price;
    }
    public String getCode() {
        return code;
    }
    public void setCode(String code) {
        this.code = code;
    }
    public Map<String, String> getTitle() {
        return title;
    }
    public void setTitle(Map<String, String> title) {
        this.title = title;
    }
    public Map<String, Integer> getPrice() {
        return price;
    }
    public void setPrice(Map<String, Integer> price) {
        this.price = price;
    }
}
<?xml version='1.0' encoding='UTF-8'?>
<faces-config version="2.2" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-facesconfig_2_2.xsd">
    <application>
        <resource-bundle>
            <base-name>res/myres</base-name>
            <var>res</var>
        </resource-bundle>
        <locale-config>
            <default-locale>en</default-locale>
            <default-locale>en_US</default-locale>
            <supported-locale>ja</supported-locale>
            <supported-locale>en</supported-locale>
            <supported-locale>de</supported-locale>            
            <supported-locale>ja_JP</supported-locale>
            <supported-locale>en_US</supported-locale>
            <supported-locale>de_DE</supported-locale>            
        </locale-config>          
    </application>
</faces-config>

リソース

読者になる

コメントをどうぞ

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

%d人のブロガーが「いいね」をつけました。