Spring Boot の国際化(i18n)機能は、Spring Framework の MessageSource インターフェースと LocaleResolver を基盤としており、ユーザーのロケールに応じて動的にテキストを切り替えることが可能です。以下では、その動作原理と具体的な実装手順を解説します。
1. 動作原理
主要コンポーネント
| コンポーネント | 役割 |
|---|---|
MessageSource |
ロケールに対応したメッセージを提供するインターフェース。デフォルト実装は ResourceBundleMessageSource で、.properties ファイルからメッセージを読み込む。 |
LocaleResolver |
現在のリクエストにおけるロケール(例: ja_JP, en_US)を決定する。デフォルトでは HTTP ヘッダーの Accept-Language を使用。 |
LocaleChangeInterceptor(オプション) |
URL パラメータ(例: ?lang=ja_JP)による動的な言語切り替えを可能にするインターセプター。 |
処理フロー
- アプリ起動時:Spring Boot が自動的に
classpath:i18n/messages*.propertiesをスキャンし、MessageSourceを設定。 - リクエスト受信時:
LocaleResolverがヘッダーやパラメーターから現在のロケールを取得。- コントローラーが
messageSource.getMessage(key, args, locale)を呼び出し。 MessageSourceがmessages_{locale}.propertiesから該当メッセージを返却。
- マッチしない場合:デフォルトファイル(
messages.properties)またはシステムロケールにフォールバック(設定次第)。
2. 実装手順
ステップ1:リソースファイルの作成
src/main/resources/i18n/ 配下に以下のファイルを配置:
resources/
└── i18n/
├── messages.properties # デフォルト
├── messages_ja_JP.properties # 日本語
├── messages_zh_CN.properties # 簡体字中国語
└── messages_en_US.properties # 英語(米国)
messages_ja_JP.properties の例:
greeting=こんにちは、{0}さん!
user.login=ログイン
⚠️
.propertiesファイルは **UTF-8** で保存し、IDE(例:IntelliJ IDEA)で「Transparent native-to-ascii conversion」を有効にしてください。これにより日本語などの非ASCII文字が正しく扱われます。
ステップ2:application.yml の設定
spring:
messages:
basename: classpath:i18n/messages
encoding: UTF-8
fallback-to-system-locale: false
default-locale: ja_JP
cache-duration: 3600s
ステップ3:URLパラメーターによる言語切り替えの有効化
デフォルトではヘッダーのみ対応のため、?lang=ja_JP 形式で切り替えられるようにカスタマイズします。
// config/LocaleConfig.java
@Configuration
public class LocaleConfig implements WebMvcConfigurer {
@Bean
public LocaleResolver localeResolver() {
SessionLocaleResolver resolver = new SessionLocaleResolver();
resolver.setDefaultLocale(Locale.JAPAN);
return resolver;
}
@Bean
public LocaleChangeInterceptor localeChangeInterceptor() {
LocaleChangeInterceptor interceptor = new LocaleChangeInterceptor();
interceptor.setParamName("lang");
return interceptor;
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(localeChangeInterceptor());
}
}
ステップ4:コントローラーでの利用
@RestController
public class GreetingController {
private final MessageSource msgSrc;
public GreetingController(MessageSource msgSrc) {
this.msgSrc = msgSrc;
}
@GetMapping("/greet")
public String greet(@RequestParam String name) {
Locale current = LocaleContextHolder.getLocale();
return msgSrc.getMessage("greeting", new Object[]{name}, current);
}
@GetMapping("/login-label")
public String loginLabel() {
return msgSrc.getMessage("user.login", null, LocaleContextHolder.getLocale());
}
}
ステップ5:動作確認
GET /greet?name=太郎→ 「こんにちは、太郎さん!」GET /greet?name=Alice&lang=en_US→ 「Hello, Alice!」GET /login-label?lang=zh_CN→ 「登录」
3. 応用パターン
データベース駆動の動的国際化
MessageSource を独自実装し、DBからメッセージを取得。CaffeineやRedisでキャッシュすることでパフォーマンスを確保。
バリデーションメッセージの国際化
@NotBlank(message = "{field.name.required}")
private String name;
対応する ValidationMessages_ja_JP.properties にメッセージを定義。
フロントエンド連携(SPA対応)
API経由でJSON形式の言語リソースを提供し、Vue.jsやReact側で動的に読み込み。
4. よくある問題と対処
| 問題 | 解決策 |
|---|---|
| 日本語が文字化けする | プロパティファイルをUTF-8保存 + IDEのネイティブ変換設定を有効化 |
| 言語が切り替わらない | LocaleChangeInterceptor が登録され、パラメーター名(例: lang)が一致しているか確認 |
| リソースファイルが見つからない | basename のパスが classpath:i18n/messages で、実際のファイルが resources/i18n/ 配下にあるか確認 |
| 本番環境で更新が反映されない | spring.messages.cache-duration を適切に設定し、キャッシュを無効化または短縮 |