やりたかったこと
spring-bootで多言語化対応をする際に、意図しない挙動に遭遇したのでここに記します(恐らくspringも同じ)。
やりたかったこととしては、外部から言語情報を取得し、その言語のメッセージを表示するが、もし未対応の言語であれば何かしらのデフォルトメッセージを表示するということ。しかし、このデフォルトメッセージが思った通りに表示されない場合があります。
例
以下のように、英語と日本語の言語プロパティファイルがあるとします。
key1=hoge
key2=fuga
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)に以下のプロパティを設定すればよいです。
spring.messages.fallback-to-system-locale: false
もし、MessageSource をJava側で定義していた場合は、以下のメソッドで設定できます。
messageSource.setFallbackToSystemLocale(false);
終わりに
なかなか原因を見つけにくく大変でした。そして、環境依存で発覚する挙動の変化なので、自国でのテストは問題なくても、外国サーバにデプロイしたら発覚するという厄介な不具合になりかねないので注意が必要です。