i18n文字列取得は「画面の言葉をコードから切り離す」技術
i18n(internationalization)は「多言語対応」のことです。
i18n文字列取得は、「日本語・英語・中国語…など、ユーザーの言語に合わせた文言を取り出す仕組み」を指します。
業務システムでは、画面メッセージやエラーメッセージを
ソースコードにベタ書きしてしまうと、言語追加や文言修正のたびにビルドが必要になります。
そこで登場するのが「プロパティファイル+キー+Locale+ユーティリティ」という構成です。
文言はファイルに、ロジックはコードに分けることで、
多言語対応も文言変更も、ずっと楽になります。
基本の仕組み:ResourceBundle と properties ファイル
プロパティファイルで「キー→文言」を定義する
まずは、言語ごとのメッセージを *.properties ファイルに書きます。
例えば、クラスパス上に messages.properties(デフォルト)、messages_ja.properties(日本語)、messages_en.properties(英語)を用意します。
messages_ja.properties:
greeting=こんにちは {0} さん
error.notfound=データが見つかりませんでした
messages_en.properties:
greeting=Hello {0}
error.notfound=Data not found
ここで大事なのは、「キー(greeting, error.notfound)で文言を管理する」という発想です。
画面やコードでは「キー」だけを知っていればよく、実際の文言はファイル側で差し替えられます。
ResourceBundle で言語ごとのメッセージを取得する
Java標準の ResourceBundle を使うと、Locale に応じて適切なファイルから文言を取得できます。
import java.util.Locale;
import java.util.ResourceBundle;
public class I18nSample {
public static void main(String[] args) {
Locale ja = Locale.JAPANESE;
Locale en = Locale.ENGLISH;
ResourceBundle bundleJa = ResourceBundle.getBundle("messages", ja);
ResourceBundle bundleEn = ResourceBundle.getBundle("messages", en);
System.out.println(bundleJa.getString("error.notfound")); // データが見つかりませんでした
System.out.println(bundleEn.getString("error.notfound")); // Data not found
}
}
Javaここでの重要ポイントは、getBundle("messages", locale) とするだけで、messages_ja.properties や messages_en.properties を自動で選んでくれることです。
ファイル名のルール(ベース名+_言語コード)さえ守れば、Javaがよしなに切り替えてくれます。
プレースホルダ付きメッセージを扱う(MessageFormat)
{0} などのプレースホルダに値を埋め込む
多くのメッセージは、固定文言だけでなく「名前」「日付」「数値」などを埋め込みたいはずです。
そのときに使うのが MessageFormat です。
先ほどの greeting を使ってみます。
import java.text.MessageFormat;
import java.util.Locale;
import java.util.ResourceBundle;
public class I18nGreeting {
public static void main(String[] args) {
Locale ja = Locale.JAPANESE;
Locale en = Locale.ENGLISH;
ResourceBundle bundleJa = ResourceBundle.getBundle("messages", ja);
ResourceBundle bundleEn = ResourceBundle.getBundle("messages", en);
String templateJa = bundleJa.getString("greeting");
String templateEn = bundleEn.getString("greeting");
String msgJa = MessageFormat.format(templateJa, "山田");
String msgEn = MessageFormat.format(templateEn, "Yamada");
System.out.println(msgJa); // こんにちは 山田 さん
System.out.println(msgEn); // Hello Yamada
}
}
Javaここで深掘りしたいポイントは二つです。
一つ目は、「テンプレート側に {0} という“位置指定の穴”をあけておく」という考え方です。{0} に1番目の引数、{1} に2番目の引数…というルールで埋め込まれます。
二つ目は、「言語ごとに文言の順番が変わっても、テンプレート側で吸収できる」ことです。
例えば、英語では "Hello {0}"、別の言語では "{0} さん、こんにちは" のように順番が違っても、
コード側は MessageFormat.format(template, name) のままでOKです。
実務で使いやすくするための i18n ユーティリティクラス
ResourceBundle と MessageFormat をまとめてラップする
毎回 ResourceBundle.getBundle と MessageFormat.format を書くのは面倒なので、
よく使うパターンをユーティリティにまとめてしまいます。
import java.text.MessageFormat;
import java.util.Locale;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
public final class I18n {
private static final String BUNDLE_NAME = "messages";
private I18n() {}
public static String get(Locale locale, String key, Object... args) {
if (locale == null) {
locale = Locale.getDefault();
}
try {
ResourceBundle bundle = ResourceBundle.getBundle(BUNDLE_NAME, locale);
String template = bundle.getString(key);
if (args == null || args.length == 0) {
return template;
}
return MessageFormat.format(template, args);
} catch (MissingResourceException e) {
// キーが見つからない場合のフォールバック
return "!" + key + "!";
}
}
}
Java使い方はとてもシンプルになります。
Locale ja = Locale.JAPANESE;
Locale en = Locale.ENGLISH;
System.out.println(I18n.get(ja, "greeting", "山田"));
System.out.println(I18n.get(en, "greeting", "Yamada"));
System.out.println(I18n.get(ja, "error.notfound"));
System.out.println(I18n.get(en, "error.notfound"));
Javaここでの重要ポイントは三つです。
一つ目は、「Locale・キー・可変長引数(プレースホルダ用)」というインターフェースにしていることです。
これだけ渡せば、言語切り替えもプレースホルダ展開も一発でできます。
二つ目は、「キーが見つからない場合のフォールバック」を決めていることです。
ここでは "!key!" のように目立つ形で返しています。
これにより、設定漏れやタイプミスにすぐ気づけます。
三つ目は、「Locale が null のときは Locale.getDefault() を使う」というポリシーです。
これにより、「特に指定がなければサーバのデフォルトロケールを使う」という自然な挙動になります。
フォールバックとデフォルト言語の考え方
指定言語がないとき、どの言語を使うか
ResourceBundle は、指定した Locale に対応するファイルがない場合、
より一般的なもの(言語だけ、さらにデフォルト)へとフォールバックしていきます。
例えば、Locale("ja", "JP") で messages_ja_JP.properties がなければ、messages_ja.properties → messages.properties の順に探します。
この仕組みを前提に、
「最低限 messages.properties(デフォルト言語)だけは必ず用意しておく」
というルールにしておくと、
どの言語でも必ず何かしらのメッセージが表示されるようになります。
実務では、まず日本語だけで messages.properties を作り、
あとから messages_en.properties を追加する、という進め方もよくあります。
実務での i18n文字列取得の使いどころ
画面メッセージ・エラーメッセージをすべてキー管理にする
例えば、コントローラやサービスの中で、
こんなコードを書いていたとします。
throw new IllegalStateException("データが見つかりませんでした");
Javaこれを i18n 対応すると、こうなります。
throw new IllegalStateException(I18n.get(locale, "error.notfound"));
Java画面側でも同様に、
「日本語の文言を直接書く」のではなく「キーで取得する」スタイルに統一します。
これにより、
「文言の変更」=「propertiesファイルの編集」
「言語追加」=「新しいpropertiesファイルの追加」
という形に整理され、コード側はほとんど触らなくて済むようになります。
まとめ:i18n文字列取得ユーティリティで身につけたい感覚
i18n文字列取得は、「文言をコードから切り離し、キーとLocaleで管理する」ための仕組みです。
押さえておきたいのは次の感覚です。
文言は *.properties に「キー→文言」として定義するResourceBundle.getBundle("basename", locale) で言語ごとのファイルを自動選択できるMessageFormat で {0} などのプレースホルダに値を埋め込める
ユーティリティクラス(I18n.get(locale, key, args…))にまとめると、業務コードがすっきりする
キーが見つからないとき・Locale がないときのフォールバックルールを決めておく
もしあなたのコードのどこかに、
日本語メッセージをそのままベタ書きしている行があったら、
それを「キー+i18nユーティリティ」に置き換えてみてください。
その小さな一歩が、
「多言語対応を前提にした、長く保守しやすいシステム」を作れるエンジニアへの、確かなステップになります。
