| 代碼類型 | 範例 | 說明 |
| 語言代碼 | en, zh, ja, fr | ISO 639 |
| 地區代碼 | US, CN, JP, FR | ISO 3166 |
| Locale 代碼 | en_US, zh_CN, ja_JP | 語言_地區,對應 properties 文件後綴 |
// 使用方式
Locale locale = new Locale("zh", "CN");
ResourceBundle bundle = ResourceBundle.getBundle("res", locale);
// 自動載入 res_zh_CN.properties
String usd = bundle.getString("USD"); // 取得 "美元"
核心實現:無界有狀態 Multiton - LingualResource
package com.javapatterns.multilingual;
import java.util.*;
public class LingualResource {
// 單例緩存:Locale代碼 -> LingualResource實例
private static final Map instances = new HashMap<>(19);
private static final String BUNDLE_BASE = "res";
// 實例狀態
private final Locale locale;
private final ResourceBundle bundle;
// 私有構造:載入對應 ResourceBundle
private LingualResource(String language, String country) {
this.locale = new Locale(language, country);
this.bundle = ResourceBundle.getBundle(BUNDLE_BASE, locale);
}
// 靜態工廠方法:獲取或創建實例
public static synchronized LingualResource getInstance(String language, String country) {
String key = language + "_" + country;
if (instances.containsKey(key)) {
return instances.get(key);
}
LingualResource lr = new LingualResource(language, country);
instances.put(key, lr);
return lr;
}
// 業務方法:按鍵取得本地化字串
public String getLocaleString(String key) {
return bundle.getString(key);
}
}
資源文件範例
# res_en_US.properties
USD=US Dollar
JPY=Japanese Yen
EUR=Euro
# res_zh_CN.properties
USD=美元
JPY=日元
EUR=歐元
客戶端使用:JSP 頁面動態切換語言
<%-- 用戶選擇語言後,存入 Session --%>
<%
String lang = request.getParameter("lang"); // "en" or "zh"
String country = request.getParameter("country"); // "US" or "CN"
session.setAttribute("locale", new Locale(lang, country));
%>
<%-- 頁面顯示幣別下拉選單 --%>
<%
Locale userLocale = (Locale) session.getAttribute("locale");
LingualResource lr = LingualResource.getInstance(
userLocale.getLanguage(), userLocale.getCountry());
%>
<select name="currency">
<option value="USD"><%= lr.getLocaleString("USD") %></option>
<option value="JPY"><%= lr.getLocaleString("JPY") %></option>
<option value="EUR"><%= lr.getLocaleString("EUR") %></option>
</select>
進階話題:Java 內建格式化工具類
除了字串翻譯,數字、貨幣、百分比格式同樣需本地化:
數字格式化
NumberFormat nf = NumberFormat.getNumberInstance(Locale.GERMANY);
nf.format(1234567.89); // "1.234.567,89" (德文格式)
貨幣格式化
NumberFormat cf = NumberFormat.getCurrencyInstance(Locale.FRANCE);
cf.format(1234567.89); // "1 234 567,89 €" (法文格式)
百分比格式化
NumberFormat pf = NumberFormat.getPercentInstance(Locale.US);
pf.format(0.456789); // "45.6789%" (美式)

模式變體與選型指南
| 變體 | 適用場景 | 實現關鍵 |
| 有界 Multiton | 固定少量實例(如骰子雙例、連接池固定大小) | 靜態陣列/字段直接持有實例 |
| 無界 Multiton | 實例鍵動態增長(如 Locale、租戶配置) | Map 緩存 + 懶加載 |
| 不可變 Multiton | 實例狀態創建後不變(如 ResourceBundle) | final 字段,線程安全 |
| 可變 Multiton | 實例狀態運行時可變更 | 需同步控制,分佈式環境慎用 |
分佈式環境警示
在 EJB/RMI/微服務集群中,JVM 級 Multiton 會導致每節點各自一份實例,狀態不一致風險極高。解決方案:
- 改用分佈式緩存 存儲共享狀態
- 或將 Multiton 降級為無狀態工具類,狀態外部化
總結
Multiton 模式以「受控實例集合」優雅解決了「按鍵區分、狀態隔離、全局訪問」的三重需求。結合 ResourceBundle 機制,我們以 50 行核心代碼構建了企業級多語言支撐框架——這正是設計模式的價值:用經典結構消解業務複雜度。
- 上一篇文章: JavaMail 發送郵件完整實戰:從簡單文本到 HTML 與附件的封裝工具類
- 下一篇文章: Java 平台構建 CRM 系統:漸進式實施策略與技術選型優勢深度解析
Copyright ©
程式碼之家 All Rights Reserved