i18n
近年、ウェブサイトの多言語化が求められております。
そういった中で、spring がどのようなサポートをしてくれるのでしょうか?
messages ファイルの設置。
メッセージリソースの配置位置を調整します。デフォルトだと src/main/resources/messages_*.properties に配置するようになっていますが、src/main/resources/ 直下にファイルがたくさん置かれるのは避けたいので、 src/main/resources/i18n/messages_*.properties に変更します。
See Appendix A. Common application properties
# メッセージリソースのファイル名をデフォルトの `src/main/resources/messages_ja.properties` から
# `src/main/resources/i18n/messages_ja.properties` に変更する。
spring.messages.basename: i18n/messagessrc/main/resources/i18n/messages.properties を設定します。 messages.properties がデフォルトの言語リソースになりますので、英語のリソースを置きます。 messages.properties が正しく配置されていないと、MessageSourceAutoConfiguration が設定されなくてハマるので注意。
intro=Welcome
greeting.message = hello, {0}src/main/resources/i18n/messages_ja.properties を設定します。
intro=いらっしゃい
greeting.message=こんにちは、{0}なお、書くプロパティには {0} のようなプレースホルダを利用できます。0 origin であることに注意してください。
MessageSource から多言語化されたリソースを取得する
以下のように、MessageSource を DI して、getMessage メソッドを呼ぶだけでOKです。 かんたんですね。
public class MessageSourceTest {
@Autowired
MessageSource messageSource;
@Test
public void contextLoads() {
System.out.println(messageSource.getMessage("greeting.message", new Object[]{"Taro"}, Locale.ENGLISH));
}
}CookieLocaleResolver を設定する。
ブラウザは、OS の設定またはブラウザの設定をもとに、Accept-Language ヘッダにより Locale が判断されます。 しかし、ユーザーが自分で言語設定を変更したいという場合もあるでしょう。
そういった場合には、Cookie に Locale 情報を保存する CookieLocaleResolver が便利です。 Session に情報を入れてもいいのですが、Session に入れるとサーバーサイドにロケール情報を保持することになり、ログインしてないユーザーの言語設定をサーバーサイドに保存する羽目になってサーバーサイドのコストが増大します。 Locale だけを保存する cookie を発行する CookieLocaleResolver を利用するのがおすすめです。
設定は以下のように、new するだけです。かんたんですね。
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.i18n.CookieLocaleResolver;
@Configuration
public class I18nConfig {
@Bean
public CookieLocaleResolver localeResolver() {
CookieLocaleResolver cookieLocaleResolver = new CookieLocaleResolver();
cookieLocaleResolver.setCookieName("lang");
return cookieLocaleResolver;
}
}cookie の名前はデフォルトのままでもいいのですが org.springframework.web.servlet.i18n.CookieLocaleResolver.LOCALE がデフォルト値なので、設定したほうが良いでしょう。
setDefaultLocale
cookieLocaleResolver.setDefaultLocale というメソッドがあるので、 cookieLocaleResolver.setDefaultLocale(Locale.ENGLISH); などと指定したくなるところですが、指定してはいけません。
ここでいう defaultLocale は、accept-language よりも優先したいデフォルトの設定、という意味だからです。 実装は以下のようになっています。defaultLocale を設定すると request.getLocale() が呼ばれません。
protected Locale determineDefaultLocale(HttpServletRequest request) {
Locale defaultLocale = getDefaultLocale();
if (defaultLocale == null) {
defaultLocale = request.getLocale();
}
return defaultLocale;
}このメソッドは、Accept-Language ヘッダを無視するという指定なのです。
ロケールを取得する
現在のリクエストコンテキストのロケール情報を取得するには以下のようにします。 RequestContextUtils.getLocaleResolver(HttpServletRequest) を利用して request から LocaleResolver を取得して そこから Locale を取得します。
@GetMapping("/get-locale")
@ResponseBody
public String getLocale(HttpServletRequest httpServletRequest) {
LocaleResolver localeResolver = RequestContextUtils.getLocaleResolver(httpServletRequest);
Locale locale = localeResolver.resolveLocale(httpServletRequest);
return locale.toString();
}ロケールを変更する
以下のようにして、locale を変更することができます。
@PostMapping("/set-locale")
public String setLocale(
@RequestParam(value = "locale") String localeName,
HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse
) {
Locale locale = Locale.forLanguageTag(localeName);
log.info("locale: {}, {}", localeName, locale);
LocaleResolver localeResolver = RequestContextUtils.getLocaleResolver(httpServletRequest);
localeResolver.setLocale(httpServletRequest, httpServletResponse, locale);
return "redirect:/i18n";
}localeResolver.setLocale により、set-cookie ヘッダが HttpServletResponse に設定されます。
Locale の変更機能は @PostMapping で設定したほうが良いでしょう。そうしないと、変なリンクを踏ませて言語設定を変える遊びが気軽にできてしまいます。
特定のリクエストでのみ言語設定を一時的に変えたい
cookie にセットせずに、一時的に言語設定を変更したい、なんてハックっぽいことをしたいときには HttpServletRequest に setAttribute すればできます。
@GetMapping("/i18n")
public String i18n(
@RequestParam(value = "l", required = false) String languageTag,
HttpServletRequest httpServletRequest
) {
if (languageTag != null) {
httpServletRequest.setAttribute(CookieLocaleResolver.LOCALE_REQUEST_ATTRIBUTE_NAME, Locale.forLanguageTag(languageTag));
}
return "i18n";
}freemarker で i18n
spring.ftl には i18n 用のヘルパー関数が含まれています。 こちらご利用ください。
message
ローカライズされた言語リソースを取得します。
<@spring.message "intro" /> のように利用します。
定義は以下のとおりです。
<#macro message code>${springMacroRequestContext.getMessage(code)}</#macro>messageText
<@spring.messageText "intro", "Welcome" /> のように利用します。 言語リソースが存在しない場合に 第2引数が利用されます。
定義は以下のとおりです。
<#macro messageText code, text>${springMacroRequestContext.getMessage(code, text)}</#macro>messageArgs
{0} のようなプレースホルダを置換しながらフォーマットします。
<@spring.messageArgs "greeting.message", ['John'] /> のように利用します。
FAQ
言語リソースが読み込まれない
logging.level.org.springframework.boot: DEBUGを設定してログをだしてください。
MessageSourceAutoConfiguration did not match
- No bundle found for spring.messages.basename: i18n/messages (MessageSourceAutoConfiguration.ResourceBundleCondition)のようなメッセージが出ていると思います。
これは、src/main/resources/i18n/messages.properties が設置されていないと MessageSourceAutoConfiguration が動作しないという意味です。 デフォルトの言語のリソースをこのパスにも設置する必要があります。