Solon 採取不同的做法。整個 i18n 模組圍繞三種解析器、一個註解和一個工具類別建構而成——無需 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 properties:

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

Enter fullscreen mode Exit fullscreen mode

就是這樣。無需 bean 宣告、無需位置配置。Solon 會自動發現它們。

三種語系解析器,一行切換

Solon 內建三種語系解析器。預設為標頭式,但您只需一行程式碼即可切換。

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. Cookie 解析器

預設從名為 SOLON.LOCALE 的 cookie 讀取語系:

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

Enter fullscreen mode Exit fullscreen mode

自訂 cookie 名稱:

resolver.setCookieName("lang");

Enter fullscreen mode Exit fullscreen mode

3. Session 解析器

從 session 屬性讀取語系:

@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. 自訂解析器

若三種解析器皆不適用,可直接實作 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

三種讀取訊息的方式

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 — 命名 Bundle 存取

當您需要從特定 bundle 讀取訊息時:

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 — 程式化 Bundle

進階用法,取得原始 bundle:

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

若未指定 bundle 名稱,預設為 i18n.messages

模板語法範例

Freemarkerlogin.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 對每個名稱使用扁平化 bundle)
  • 無開發模式自動重新載入(需重啟以取得變更,或實作支援熱重載的自訂 I18nBundleFactory
  • 無內建語言切換端點(故意留給解析器處理——透過查詢參數切換需自訂 LocaleResolver

您可獲得:

  • 不到 60 秒即可運作的 i18n
  • 三種可立即上線的解析器策略
  • 跨 5 種引擎的一流模板整合
  • 適用於分散式配置的可延伸工廠模式
  • 零 XML、零攔截器、零繁瑣設定

摘要

Solon 的 i18n 模型遵循框架理念:慣例優於配置,但需要時可切換。一個依賴、一個模板註解、一個程式化存取工具,以及三個即插即用的解析器,涵蓋絕大多數真實情境。

若您曾為 MessageSource 配置苦惱,不妨試試看——體驗將令人驚艷。