Help us understand the problem. What is going on with this article?

org.springframework.context.MessageSource.getMessage()でデフォルトメッセージの思わぬ挙動

More than 1 year has passed since last update.

やりたかったこと

 spring-bootで多言語化対応をする際に、意図しない挙動に遭遇したのでここに記します(恐らくspringも同じ)。
やりたかったこととしては、外部から言語情報を取得し、その言語のメッセージを表示するが、もし未対応の言語であれば何かしらのデフォルトメッセージを表示するということ。しかし、このデフォルトメッセージが思った通りに表示されない場合があります。

 以下のように、英語と日本語の言語プロパティファイルがあるとします。

messages_en.properties
key1=hoge
key2=fuga
messages_ja.properties
key1=ほげ
key2=ふが

 この時、以下のdisplay()メソッドで、中国語を指定してメッセージを取得しようとするとどうなるでしょうか?

    @Autowired
    private MessageSource messageSource;

    public void display() {
        System.out.println(getMessage1(Locale.CHINESE));
        System.out.println(getMessage2(Locale.CHINESE));
    }

    /**
     * 多言語化メッセージを取得します。
     * @param locale ロケール
     * @return 多言語化メッセージ
     */
    private String getMessage1(Locale locale) {
        return messageSource.getMessage("key1", null, locale);
    }

    /**
     * 多言語化メッセージを取得します。
     * @param locale ロケール
     * @return 多言語化メッセージ
     */
    private String getMessage1(Locale locale) {
        return messageSource.getMessage("key1", null, "デフォルトメッセージ", locale);
    }

 こちらのJavadocを観る限り、getMessage1ではNoSuchMessageExceptionが投げられ、getMessage2では"デフォルトメッセージ"と表示される、、、ように思われるのですが、私の環境ではそうなりませんでした。両者とも"ほげ"と出てくるのです。

何がいけなかったか

 上記のコードは悪くありません。原因はMessageSourceのプロパティでした。
spring-bootではこちらにあるとおり、共通アプリケーションプロパティが定義されているのですが、そのなかにspring.messages.fallback-to-system-localeというプロパティがあります。
そのコメントには、

Whether to fall back to the system Locale if no files for a specific Locale have been found.

とあります。訳は、

指定したロケールのファイルが見つからない場合、システムロケールにフォールバックするかどうか。

です。これがデフォルト(要は未定義の場合)ではtrueとなっています。つまり、このプロパティがtrueになっている以上、getMessage1でNoSuchMessageExceptionが投げられることはなく、getMessage2でデフォルトメッセージが返されることはありません。アプリケーションがデプロイされている環境の言語が表示されるため、今回の私の環境の場合では、該当しない言語が渡された場合は常に日本語のメッセージが出力されてしまっていたというわけです。

どうすればよいか

 修正は簡単です。application.yml(またはapplication.properties)に以下のプロパティを設定すればよいです。

application.yml
spring.messages.fallback-to-system-locale: false

 もし、MessageSource をJava側で定義していた場合は、以下のメソッドで設定できます。

messageSource.setFallbackToSystemLocale(false);

終わりに

 なかなか原因を見つけにくく大変でした。そして、環境依存で発覚する挙動の変化なので、自国でのテストは問題なくても、外国サーバにデプロイしたら発覚するという厄介な不具合になりかねないので注意が必要です。

nisioka
ここでの発言は個人の見解であり、所属する組織の公式見解ではありません。 書き込む内容としてはwebアプリ系が多いと思います。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away