Solon は異なるアプローチを取ります。i18n モジュール全体は 3 つのリゾルバー、1 つのアノテーション、そしてユーティリティクラスを中心に構築されています — XML 不要、インターセプター登録不要、ボイラープレート設定不要。

どのように動作するのか見てみましょう。

リソースの規約

メッセージファイルを resources/i18n/ に配置します:

resources/i18n/messages.properties           # デフォルト (例: 中国語)
resources/i18n/messages_en_US.properties     # 米語
resources/i18n/messages_ja_JP.properties     # 日本語

Enter fullscreen mode Exit fullscreen mode

ファイル形式は標準的な Java プロパティです:

# messages_en_US.properties
login.title=Sign In
login.welcome=Welcome, {0}!
app.name=Solon Application

Enter fullscreen mode Exit fullscreen mode

これだけです。Bean 宣言も、場所の設定も必要ありません。Solon が自動的に検出します。

3 つのロケールリゾルバー、1 つのスイッチ

Solon には 3 つの組み込みロケールリゾルバーが用意されています。デフォルトは ヘッダーベース ですが、1 行で切り替え可能です。

1. ヘッダーリゾルバー (デフォルト)

Content-Language ヘッダーを読み取り、Accept-Language にフォールバックします:

# app.yml — 設定は不要で、すぐに動作します

Enter fullscreen mode Exit fullscreen mode

ヘッダー名をカスタマイズする場合:

@Configuration
public class I18nConfig {
    @Bean
    public LocaleResolver localeInit() {
        LocaleResolverHeader resolver = new LocaleResolverHeader();
        resolver.setHeaderName("lang");  // "lang" ヘッダーを読み取るようになります
        return resolver;
    }
}

Enter fullscreen mode Exit fullscreen mode

2. クッキーリゾルバー

デフォルトで SOLON.LOCALE という名前のクッキーからロケールを読み取ります:

@Configuration
public class I18nConfig {
    @Bean
    public LocaleResolver localeInit() {
        return new LocaleResolverCookie();
    }
}

Enter fullscreen mode Exit fullscreen mode

クッキー名をカスタマイズする場合:

resolver.setCookieName("lang");

Enter fullscreen mode Exit fullscreen mode

3. セッションリゾルバー

セッション属性からロケールを読み取ります:

@Configuration
public class I18nConfig {
    @Bean
    public LocaleResolver localeInit() {
        return new LocaleResolverSession();
    }
}

Enter fullscreen mode Exit fullscreen mode

属性名をカスタマイズする場合:

resolver.setAttrName("lang");

Enter fullscreen mode Exit fullscreen mode

4. カスタムリゾルバー

3 つのいずれもユースケースに合わない場合は、LocaleResolver を直接実装します:

@Component
public class QueryParamResolver implements LocaleResolver {
    @Override
    public Locale getLocale(Context ctx) {
        String lang = ctx.param("lang");
        if (lang != null) {
            return LocaleUtil.toLocale(lang);
        }
        return Locale.getDefault();
    }

    @Override
    public void setLocale(Context ctx, Locale locale) {
        ctx.setLocale(locale);
    }
}

Enter fullscreen mode Exit fullscreen mode

メッセージの読み取り方法は 3 通り

1. I18nUtil — クイックアクセス

どこでも使える静的メソッドでワンオフのルックアップ:

// 現在のリクエストコンテキストから (ロケールは自動的に解決されます)
String title = I18nUtil.getMessage("login.title");

// 特定の Locale を指定
String welcome = I18nUtil.getMessage(locale, "login.welcome", "Alice");

// Context オブジェクトを指定
I18nUtil.getMessage(ctx, "app.name");

Enter fullscreen mode Exit fullscreen mode

2. I18nService — 名前付きバンドルアクセス

特定のバンドルからメッセージが必要な場合:

I18nService loginI18n = new I18nService("i18n.login");

@Mapping("/greet")
public String greet(Locale locale) {
    return loginI18n.get(locale, "login.welcome", "Alice");
}

Enter fullscreen mode Exit fullscreen mode

3. I18nBundle — プログラムによるバンドル

高度な用途では生のバンドルを取得します:

I18nBundle bundle = I18nUtil.getBundle("i18n.messages", locale);
String value = bundle.get("login.title");
String formatted = bundle.getAndFormat("login.welcome", "Alice");

Enter fullscreen mode Exit fullscreen mode

@I18n — テンプレート統合

コントローラーに @I18n を追加すると、すべてのテンプレートで i18n 変数が利用可能になります:

@I18n("i18n.login")
@Controller
public class LoginController {
    @Mapping("/login")
    public ModelAndView login() {
        return new ModelAndView("login.ftl");
    }
}

Enter fullscreen mode Exit fullscreen mode

バンドル名を指定しない場合、デフォルトで i18n.messages になります。

テンプレート構文の例

Freemarker (login.ftl):

<h1>${i18n["login.title"]}</h1>
<p>${i18n.getAndFormat("login.welcome", session.user.name)}</p>

Enter fullscreen mode Exit fullscreen mode

Thymeleaf:

<h1 th:text="${i18n.get('login.title')}">Sign In<h1>
<p th:text="${i18n.getAndFormat('login.welcome', 'Alice')}">Welcome!<p>

Enter fullscreen mode Exit fullscreen mode

Beetl:

<h1>${i18n["login.title"]}<h1>
<p>${@i18n.getAndFormat("login.welcome", "Alice")}<p>

Enter fullscreen mode Exit fullscreen mode

Enjoy:

<h1>#(i18n.get("login.title"))<h1>
<p>#(i18n.getAndFormat("login.welcome", "Alice"))<p>

Enter fullscreen mode Exit fullscreen mode

分散環境向けの拡張

翻訳を構成センターや CMS で管理するチーム向けに、I18nBundleFactory を実装します:

@Component
public class RemoteBundleFactory implements I18nBundleFactory {
    @Override
    public I18nBundle create(String bundleName, Locale locale) {
        // リモート構成センターから取得
        Props props = getRemoteProps(bundleName, locale);
        return new I18nBundleImpl(props, locale);
    }
}

Enter fullscreen mode Exit fullscreen mode

登録します:

I18nUtil.setBundleFactory(new RemoteBundleFactory());

Enter fullscreen mode Exit fullscreen mode

Solon の組み込み I18nBundleFactoryLocalresources/i18n/ から読み取ります。ファクトリーアプローチにより、アプリケーションコードを変更せずに Redis、Nacos など任意のソースに切り替えることができます。

最小限の依存関係

solon-i18n モジュールは solon-core 以外の推移的依存関係がありません。solon-web を使用している場合、すでに含まれています。

<dependency>
    <groupId>org.noear</groupId>
    <artifactId>solon-i18n</artifactId>
</dependency>

Enter fullscreen mode Exit fullscreen mode

トレードオフ

得られないもの:

  • MessageSource スタイルの階層はありません (Solon は名前ごとにフラットなバンドルを使用)
  • 開発モードでの自動リロードはありません (変更を反映するには再起動するか、ホットリロード付きのカスタム I18nBundleFactory を実装してください)
  • 組み込みの言語切り替えエンドポイントはありません (意図的にリゾルバーに委ねられています — クエリパラメータによる切り替えにはカスタム LocaleResolver が必要です)

得られるもの:

  • 60 秒以内で動作する i18n
  • 3 つの本番対応リゾルバー戦略
  • 5 つのエンジンにわたるファーストクラスのテンプレート統合
  • 分散構成向けの拡張可能なファクトリーパターン
  • XML ゼロ、インターセプターゼロ、手続きゼロ

まとめ

Solon の i18n モデルはフレームワークの哲学に従っています: 設定より規約、ただし必要に応じて切り替え可能。1 つの依存関係、テンプレート用に 1 つのアノテーション、プログラムによるアクセス用に 1 つのユーティリティ、そして 3 つのドロップインリゾルバーで、ほとんどの実世界のシナリオをカバーします。

MessageSource の設定で苦労している方は、ぜひ試してみてください — 意外と爽快です。