読者です 読者をやめる 読者になる 読者になる

メンチカツには醤油でしょ!!

ITエンジニア徒然 (AWS/Java/JavaScript/Google Spreadsheets/Jenkins/Mac/外部コミュニティ・勉強会レポ)

Struts2で表示言語を動的に切り替える。しかもアプリ内で

環境

Struts 2.3.24.1
Struts Convention Plugin 2.3.24.1
struts.xml での struts.enable.DynamicMethodInvocation は true

前置き

表示言語といっても色々あるのですが、
リソース(ApplicationResources.properties)のStruts2で表示言語の切り替えは
struts.xml にて
<constant name="struts.custom.i18n.resources" value="messages" />
と指定して、
クラスパス上にmessages.propertiesを各言語で配置して、
JSP には
<s:text name="label.user.id" />
としておけば切り替わるのは基本的で、調べればどこにでも出てきますが
ログインユーザーによって言語を切り替えたい場合の実装についてです。

実現させること

・画面表示を動的に切り替えたい (messages_jaやen.properties を配置して対応)
・ログイン時にユーザーによって表示言語を切り替えたい。
 (OSなどの環境によって切り替えるのではない。) ←

try1

最初に試したのは、LoginAction.java にて

if (日本語) {
  // 日本語セット
} else if (英語) {

  // http://stackoverflow.com/questions/18509523/set-locale-via-code-in-struts2
  sessionMap.put(I18nInterceptor.DEFAULT_SESSION_ATTRIBUTE, new Locale("en", "US")); // "WW_TRANS_I18N_LOCALE"

  // こんなことも試したけど...
  sessionMap.put(I18nInterceptor.DEFAULT_PARAMETER, new Locale("en", "US")); // "request_locale"
  sessionMap.put(I18nInterceptor.DEFAULT_REQUESTONLY_PARAMETER, new Locale("en", "US")); // "request_only_locale"

  // http://stackoverflow.com/questions/11223446/struts2-change-locale
  ActionContext.getContext().setLocale(new Locale("en", "US"));
  ServletActionContext.getContext().setLocale(new Locale("en","US"));
}

のような対応です。情報ソースは行コメントに記載してあります。
上記2つのどれかで出来るとハッピーですね。

ちなみにJSPでは
<footer> Locale="${locale}" </footer>
を書いて今どのロケールがセットされてるのか表示しておくとデバッグとしてわかりやすいと思います。

・・・ここまでで実現できた方はハッピーですね。
私はできませんでした。

try2

リクエストパラメーターに
http://~~/login.action?request_locale=en_US
なんてやり方もチラホラでしたが、なんだか気に入らないのです。
試してもできなかったし...

try3

色々試しているうちに気づいたことがあって、実は
・messages_ja.properties ←日本語
・messages.properties ←英語
というように配置していたのですが、
enをセットしても日本語で表示されてしまっているということに気づきました。

試しに、
・messages_en.properties ←英語
・messages_ja.properties ←日本語
・messages.properties ←英語(_enと同じものをコピー...)
という構成にしたら表示されました。
日本語のWindowsを使っているからなのかjaの方を読みにいってしまってましたのでしょうか・・・ナットクイカン。

d.hatena.ne.jp


> 日本語だけ定義しておいて、それ以外は全部英語としたい場合に、
> 「Messages.propertiesに、英語のメッセージを設定しておけば、日本以外のロケールの場合は英語になる」
> というような、動作をさせることができない。

OMG...

ResourceBundle.Control control = ResourceBundle.Control.getNoFallbackControl(ResourceBundle.Control.FORMAT_DEFAULT);
ResourceBundle jprb = ResourceBundle.getBundle("Messages", new Locale("ja", "JP"), control);
ResourceBundle enrb = ResourceBundle.getBundle("Messages", new Locale("en", "US"), control);
System.out.println(jprb.getString("key"));
System.out.println(enrb.getString("key"));

とすればいけるようですが、
getText()を呼ばずにこちらを呼ぶことにかなりかなり抵抗があります。

結局どうしたのか

LoginAction.java にて

// http://stackoverflow.com/questions/18509523/set-locale-via-code-in-struts2
sessionMap.put(I18nInterceptor.DEFAULT_SESSION_ATTRIBUTE, userLocale);

そして CustomI18nInterceptor.java にて

ActionContext.getContext().setLocale(userLocale);

として遷移ごとにセットし続けることにより実現させました。

もちろんstruts.xmlには

<interceptor name="i18n" class="jp.co.your.package.CustomI18nInterceptor" />

と設定しました。

Interceptorの設定に関しては以前書いた下記を参照ください。

ryoichi0102.hatenablog.com

ryoichi0102.hatenablog.com