- はじめに
- 多言語対応とは
- さっそく環境準備から
- message.propertiesを作成する
- Configを設定する
- ResourceBundleMessageSourceを継承した独自クラスを作成する
- Controllerから作成したMultiMessageSourceを呼び出す
- 実行してみる
- まとめ
はじめに
はじめまして、バックエンドエンジニアのryrkssです。
今回、担当する開発業務にて、Javaのフレームワークの中でも有名なSpring FrameworkにあるSpring Bootを使用して、多言語対応しましたのでそのお話をさせていただきたく思います。
多言語対応で調べたときの記事で動的ではない言語対応方法(ユーザの言語設定に左右されない)の記事が多い印象でした。
たしかにサンプルとしてはいいかもしれないんですが、実際のアプリケーションでは動的に言語を切り替えたいはずです。
それをSpring Boot + Javaで実現する1つの方法を記載したいと思います。
今回のゴールはブラウザ設定に応じた言語でJsonレスポンスを返却するというところにおきます。
Spring Bootについてはこちらのブログでも紹介されておりますので参考までに。
・【入門】Spring Bootとは~実践まで - RAKUS Developers Blog | ラクス エンジニアブログ
多言語対応とは
その名前の通り、様々な言語に対応することです。
※念のため補足ですが、ここでいう言語は話し言葉の言語であり、プログラミング言語ではありません。
さっそく環境準備から
こちらは本記事の本題ではないので、さくっと説明します。
以下に手順を書いていきますが、皆さんのやりやすい方法で実行環境を作成してください。
- Spring Initializrを使ってプロジェクトを作成
- 依存関係に
org.springframework.boot:spring-boot-starter-web
を追加
※lombok
はお好みで。本記事では使用しています。 - Controllerとレスポンスオブジェクトを作成
- アプリケーションを実行する
- Controller
@RestController public class MultilingualController { @GetMapping("/message") public ResponseData message() { return new ResponseData("多言語対応"); } }
- レスポンスオブジェクト
@Value public class ResponseData { String message; }
これでPostmanなどからAPIを叩けば、以下のようなJson形式でボディレスポンスが返ってくると思います。
- レスポンス
{ "message": "多言語対応" }
ただ見ての通り、固定値の多言語対応
の文字列を返しているだけなので、
当たり前ですが今のままでは他の言語で返してはくれません。
次からいよいよ多言語対応の実装に入っていきましょう!
本題に入るので順序立てて説明していきます。
message.propertiesを作成する
実際にJsonへ返すメッセージをpropertiesファイルで定義します。
※yamlでも可能ですのでこちらもお好みで。
本記事はpropertiesの例のみ記載しますので、yamlの場合は適宜変換してお読みください。
Springでメッセージ管理を行いたい場合、基本的にorg.springframework.context.MessageSource
を使用します。
このクラスをDIし、使用することでpropertiesファイルから指定されたキーに紐づくメッセージが取得できるようになります。
まずはメッセージの出力元となるpropertiesファイルから作成します。
src/main/resources/
に以下2ファイルを配置し、それぞれ key=valueの形でメッセージを記載します。
messages_ja.properties
(日本語)
application.success=成功 application.error=失敗
messages_en.properties
(英語)
application.success=succeeded application.error=failed
デフォルトはmessages.propertiesが使用されますが、messages_{ロケール}.properties
のように末に対応したい言語のロケールをファイル名に使用することでリクエストにより使用するmessagesファイルを動的に振り分けることができます。
※propertiesで用意していないロケールがリクエストで飛んできた場合にmessages.propertiesファイルが使用されます。ここでは分かりやすいようにja
, en
のみの用意で話を進めます。
ここでのポイントはmessagesプロパティ間でのキーは必ず同一にすることです。
MessageSource
を使用するとき、propertiesファイルにあるキー名を指定してそれに紐づくValueを取得する動きとなります。
propertiesファイルの書き方は色々あるので気になる方は調べてください。
これで、出力するメッセージの元は作成できました。
続いてMessageSource
の設定をConfigファイルに定義していきたいと思います。
Configを設定する
xmlベースで設定もできますが、今回はJavaベースで設定を記述していきます。
※個人的にはJavaConfigの方が分かりやすくて好きです。
ここではMessageSource
のベース設定をしていきます。
@Configuration public class AppConfig { @Bean public MultiMessageSource messageSource() { MultiMessageSource messageSource = new MultiMessageSource(); // ① messageSource.setBasenames("messages"); // ② messageSource.setDefaultEncoding(StandardCharsets.UTF_8.name()); // ③ return messageSource; } }
① MessageSource
の実装クラスResourceBundleMessageSource
を継承してカスタマイズする独自クラス
簡単に言うとここでリクエストヘッダーからAccept-Language
を読み取り、それに応じたmessages_xx.propertiesの取得結果を返します。
※詳細は後述します。
② どのpropertiesファイルを取得対象とするかresources
からの相対パスかつprefix(_
まで)で定義
※例: resources/sample/messages_ja.properties
→sample/messages
また、messageSource.setBasenames("messages", "sample/paths")
のように複数定義することも可能となっており、
自由に定義できるのでアプリケーションの設計により柔軟に対応できると思います。
③ messages.propertiesを解釈する文字コードをセット
ResourceBundleMessageSourceを継承した独自クラスを作成する
ここでは実際にリクエストの情報を元に動的にmessages_xx.properties
からメッセージを取得する部分を作っていきます。
といってもこちらで実装する部分は少なく、Springに用意されているAcceptHeaderLocaleResolver
というクラスがロケールの判定を行い、returnしているgetMessage
メソッドで振り分けを担ってくれるので、その設定を定義してあげるくらいの実装になります。
public class MultiMessageSource extends ResourceBundleMessageSource { @Autowired HttpServletRequest request; // ① public String getMessage(String key, Object... params) { // ② AcceptHeaderLocaleResolver localeResolver = new AcceptHeaderLocaleResolver(); // ③ localeResolver.setDefaultLocale(Locale.JAPANESE); // ④ localeResolver.setSupportedLocales(List.of(Locale.JAPANESE, Locale.ENGLISH)); // ⑤ return super.getMessage(key, params, localeResolver.resolveLocale(request)); // ⑥ } }
① HttpServletRequestをDIする
動的にmessages_xx.propertiesを振り分けるための情報のメインがこの中にあります。
このリクエストヘッダー内のAccept-Language
に設定されている値を⑥で出てくるlocaleResolver.resolveLocale(request)
で使用します。
このヘッダーにはブラウザの言語設定の情報が入ってくるので、言語に応じた情報を返却するメインの情報源となります。
② 呼び出すメソッドを定義
第一引数:messages_xx.propertiesに設定しているキー
第二引数~:messages_xx.propertiesにパラメータを設定している場合に使用
戻り値:Stringで返ります(messages.propertiesのValue値)
③ 変換のメインクラスとなるAcceptHeaderLocaleResolver
のインスタンス化
④ デフォルトロケールを設定
ここで名前の通りデフォルトで使用する言語情報をセットします。
デフォルトロケールがmessagesの振り分けに使用されるパターンは以下です。
- リクエストヘッダー内の
Accept-Language
がNULLの場合 - 後述する
SupportedLocales
に値がセットされていない場合 - リクエストヘッダーの言語設定と
SupportedLocales
の値がマッチしない場合
⑤ サポートロケールを設定
ここでは作成するアプリケーションがサポートしている言語は何かを明示的に指定します。
※List型で渡すことに注意してください。
サポートロケールがmessagesの振り分けに使用されるパターンは以下です。
⑥ 親クラスResourceBundleMessageSource
の親の親....AbstractMessageSource
のgetMessageメソッドの結果を返却
第一引数:messages_xx.propertiesに設定しているキー
第二引数~:messages_xx.propertiesにパラメータを設定している場合に使用
第三引数:Locale{ロケール}を指定 ※こちらを使用してどのmessages_xx.propertiesからValueを引いてくるかを判定します
今回の多言語対応のメインである、第三引数で行っているlocaleResolver.resolveLocale(request)
について説明します。
AcceptHeaderLocaleResolver
クラスのresolveLocale
メソッドでリクエストヘッダーに応じたロケールを取得してくれます。
そのため、resolveLocale
メソッドには引数としてHttpServletRequest
を渡してあげるだけでいわゆる動的な多言語対応が実現します。
resolveLocale
メソッドでやっていることは前述した④、⑤で設定した内容とリクエストを使用してロケール情報を返しています。
...ただ正直この中身の作りはいまいちな気がしています。。
個人的には想定外のAccept-Language
が来たときは一律デフォルトロケールを設定してくれればいいと思っているのですが、確実に想定外が発生しないような動きにするにはデフォルトロケールとサポートロケールを設定する必要があります。
気になる方はそんなに難しいことはしていないのでresolveLocale
の中身を見てみてください。
また、私が調べてみた限りの情報なので、もしかしたらもっとうまい方法があるかもしれません。
これで多言語対応の内部ロジックは終わりです。
あとはControllerを少し修正して結果を確認してみましょう!
Controllerから作成したMultiMessageSource
を呼び出す
冒頭で作成したレスポンスをべた書きしているMultilingualController
を修正します。
@AllArgsConstructor @RestController public class MultilingualController { private final MultiMessageSource messageSource; // ① @GetMapping("/message") public ResponseData message() { String message = messageSource.getMessage("application.success"); // ② return new ResponseData(message); } }
① カスタマイズしたメッセージクラスMultiMessageSource
をDIする
② getMessage
メソッドをコールし、引数にmessages_xx.propertiesに定義されているキーを渡す
※ここではキーだけ渡してますが、パラメータを設定している場合はそれも渡すことが可能です。
実行してみる
実際に想定通りの言語で返ってくるかAPIを実行してみましょう。
- Accept-Language=ja(日本語)
指定した通り日本語でメッセージが返ります。
{ "message": "成功" }
- Accept-Language=en(英語)
指定した通り英語でメッセージが返ります。
{ "message": "succeeded" }
{ "message": "成功" }
- Accept-Languageヘッダーなし
デフォルトロケールで指定した言語でメッセージが返ります。
{ "message": "成功" }
まとめ
長々と書いてきましたが、実際にコーディングするのはごくわずかです。
ほとんどはSpringの標準パッケージの中でよしなにやってくれるので非常に簡単に実装できました。
もちろん他にも作りこめば色々できると思います。
また、今回はブラウザの言語設定によってメッセージを切り替える例をご紹介しましたが、
他にもSpringで多言語対応をするやり方は調べれば出てきます。
例えばUI上で言語を選択して切り替えることもSpringの機能を使用して比較的簡単にできるようです。
気になる方は調べてみてください!
エンジニア中途採用サイト
ラクスでは、エンジニア・デザイナーの中途採用を積極的に行っております!
ご興味ありましたら是非ご確認をお願いします。
https://career-recruit.rakus.co.jp/career_engineer/カジュアル面談お申込みフォーム
どの職種に応募すれば良いかわからないという方は、カジュアル面談も随時行っております。
以下フォームよりお申込みください。
rakus.hubspotpagebuilder.comラクスDevelopers登録フォーム
https://career-recruit.rakus.co.jp/career_engineer/form_rakusdev/イベント情報
会社の雰囲気を知りたい方は、毎週開催しているイベントにご参加ください!
◆TECH PLAY
techplay.jp
◆connpass
rakus.connpass.com