Java 原生 Locale 建構子僅支援固定參數組合,實務上常需從字串(如 zh_CN、en-US、zh_TW_STROKE)動態解析。本文提供一個健壯的 LocaleUtil,相容大小寫、底線/連字號分隔符、三段式 variant,並修正 Apache LocaleUtils 不支援小寫開頭的缺陷。
language、language_country、language_country_variant 三種格式。_ (Java 標準) 與 - (IETF BCP 47)。Locale.ENGLISH,不拋出異常。import java.util.Locale;
public class LocaleUtil {
private static final Locale DEFAULT_LOCALE = Locale.ENGLISH;
public static final String IETF_SEPARATOR = "-";
public static final String SEPARATOR = "_";
private static final String EMPTY = "";
public static Locale toLocale(String language) {
if (isBlank(language)) return DEFAULT_LOCALE;
return langToLocale(language, SEPARATOR);
}
public static Locale langToLocale(String lang, String separator) {
if (isBlank(lang)) return DEFAULT_LOCALE;
String language = EMPTY, country = EMPTY, variant = EMPTY;
int i1 = lang.indexOf(separator);
if (i1 < 0) {
language = lang;
} else {
language = lang.substring(0, i1);
++i1;
int i2 = lang.indexOf(separator, i1);
if (i2 < 0) {
country = lang.substring(i1);
} else {
country = lang.substring(i1, i2);
variant = lang.substring(i2 + 1);
}
}
// 正規化
if (language.length() == 2) language = language.toLowerCase();
else language = EMPTY;
if (country.length() == 2) country = country.toUpperCase();
else country = EMPTY;
if (variant.length() > 0 && (language.length() == 2 || country.length() == 2)) {
variant = variant.toUpperCase();
} else {
variant = EMPTY;
}
return new Locale(language, country, variant);
}
private static boolean isBlank(String s) {
return s == null || s.isEmpty();
}
}
| 輸入字串 | 解析結果 (toString) | 說明 |
|---|---|---|
zh_CN | zh_CN | 標準底線分隔 |
zh-cn | zh_CN | 連字號 + 小寫 → 自動正規化 |
en_US_POSIX | en_US_POSIX | 三段式 variant 保留 |
JAVA | en | 非標準語言碼 → 回預設 |
null | en | 空值安全 |
LocaleUtil.toLocale(request.getHeader("Accept-Language")) 取得首選語系。@Value("${app.locale:zh_TW}") 搭配 LocaleUtil.toLocale() 注入 Bean。toString() 格式(zh_TW),讀取時再轉 Locale 物件。Locale.forLanguageTag(String)(支援 BCP 47),但仍需自行處理預設值與空值邏輯。POSIX、STROKE),勿用於核心邏輯分支。u-ca-japanese),請改用 Locale.Builder 或 ICU4J。將 LocaleUtil 納入專案共用模組,即可一勞永逸解決語系標籤解析的跨平台相容問題。