日時の表記や通貨の単位などロケールに依存した処理はいろいろあります。

たとえば、第51回第52回で取りあげた和暦もロケールに応じて選択されることを紹介しました。

Javaではすでに様々なロケールが用意されています。頻繁に使用されるen_USやja_JPなどはjava.util.Localeクラスの定数として定義されています。

定数で定義されたロケール以外にも、アラビア語を表すarから、台湾を表すzh_TWまで、なんと152種類ものロケールがサポートされています。

しかし、世の中にはもっと様々なロケールが存在しています。

Unicodeコンソーシアムが策定しているCommon Locale Data Repository (CLDR)では400以上のロケールが定義されています。

これらのロケールには、ISO 639-2において定義されている3文字による言語コードが含まれています。ところが、Localeクラスでは3文字の言語コードを扱えないので、CLDRのすべてを使うことはできません。それらを除いたとしても、300以上のロケールを定義されています。

Javaでサポートされていないロケールを使用する場合、ロケールに依存した処理も更新しなくてはなりません。そのために、使用されるのがJava SE 6で採用されたLocale Sensitive Services SPIです。

Locale Sensitive Services SPIでは以下の9種類のクラスに対して、新たなロケールに対応させることが可能です。

  • java.util.Locale
  • java.util.TimeZone
  • java.util.Currency
  • java.text.BreakIterator
  • java.text.Collator
  • java.text.DateFormat
  • java.text.DateFormatSymbols
  • java.text.NumberFormat
  • java.text.DecimalFormatSymbols

ここでは、NumberFormatクラスを新しいロケールに対応させてみましょう。

サンプルのソース LocaleSensitiveServicesSample.java
YenNumberFormatProvider.java

NumberFormatクラスでは通貨のフォーマットもできます。日本語環境では10円は「¥ 10」とフォーマットされます。

しかし、できれば「10円」とフォーマットしてみたくないですか。もちろん、NumberFormatクラスの派生クラスであるjava.text.DecimalFormatクラスを使用すれば、「10円」とフォーマットすることも可能です。

とはいうものの、せっかくの機会ですので、ロケールに応じて「¥ 10」と「10円」を切り替えられるようにしてみましょう。

そのためには、「10 円」と書かせるためのロケールと、ロケールに応じた処理を行うサービスプロバイダを作成しなくてはなりません。

当初、Java SE 6で新たにサポートされたja_JP_JPロケールを使用しようとしたのですが、どうやらすでにサポートされているロケールに対して動作を変更することはできないようです。

しかたないので、新しいロケールを作ってしまいました。ロケールは言語コード、国コード、バリアントから成り立っています。言語はja、国はJPという部分はそのままにして、バリアントをYENとしてみました。つまり、ja_JP_YENです。

次に、このロケールの時の、通貨のフォーマットを決めなくてはなりません。

ロケールに応じて処理を変化させるために用意されているのが、java.util.spi.LocaleServiceProviderクラスです。

上述した9種類のクラスはすべてLocaleServiceProviderクラスを派生させたサービスプロバイダが用意されています。いずれのクラスもjava.text.spiパッケージ、もしくはjava.util.spiパッケージに属しています。

通貨のフォーマットを行うのはNumberFormatクラスであり、そのサービスプロバイダに相当するのがjava.text.spi.NumberFormatProviderクラスなのです。

そこで、NumberFormatProviderクラスを派生させてYenNumberFormatProviderクラスを作成しました。

public class YenNumberFormatProvider extends NumberFormatProvider {
    private final static Locale YEN = new Locale("ja", "JP", "YEN");
 
    public Locale[] getAvailableLocales() {
        // このクラスがサポートしているロケールの一覧を返す
        return new Locale[] {YEN};
    }
 
    // 通貨フォーマット
    public NumberFormat getCurrencyInstance(Locale locale) {
        if (YEN.equals(locale)) {
            // 円で表示するための DecimalFormatを生成
            return new DecimalFormat("#,###円");
        } else {
            // ja_JP_YEN以外は対応できないのでnullを返す
            return null;
        }
    }

getAvailableLocalesメソッドはLocaleServiceProviderメソッドで定義されている唯一のメソッドです。

このメソッドではサービスプロバイダでサポートしているロケールの一覧を返します。戻り値の型はLocaleクラスの配列です。

YenNumberFormatProviderクラスではja_JP_YENロケールにだけ対応しているので、青字で示したようにja_JP_YENロケールを配列にして返しています。

これ以外のメソッドはサービスプロバイダごとに定義されています。

NumberFormatProviderクラスでは次の4つのメソッドが定義されています。

  • NumberFormat getCurrencyInstance(Locale locale)
  • NumberFormat getIntegerInstance(Locale locale)
  • NumberFormat getNumberInstance(Locale locale)
  • NumberFormat getPercentInstance(Locle locale)

それぞれ、NumberFormatクラスの同名メソッドに対応しています。

たとえば、NumberFormatクラスのgetCurrencyInstanceメソッドがコールされると、引数のロケールがNumberFormatクラスでサポートしていない場合、サービスプロバイダの同名メソッドがコールされるわけです。

さて、このサンプルでは通貨フォーマットだけをおこなうので、getCurrencyInstanceメソッドを使用します。

ロケールがja_JP_YENの場合、赤字で示しているようにDecimalFormatオブジェクトを生成して返しています。フォーマットのパターンは「#,###円」としました。これは3桁ごとにカンマが表示され、数字の後ろに「円」が表示されるパターンです。

このクラスで扱えないロケールの場合、nullを返します。

この先は会員の登録が必要です。有料会員(月額プラン)は初月無料!

日経 xTECHには有料記事(有料会員向けまたは定期購読者向け)、無料記事(登録会員向け)、フリー記事(誰でも閲覧可能)があります。有料記事でも、登録会員向け配信期間は登録会員への登録が必要な場合があります。有料会員と登録会員に関するFAQはこちら