JSP 國際化 (i18n) 完整實作指南
掌握 JSP 國際化核心機制:Locale 偵測與解析、語言編碼設定、日期時間格式化、貨幣與百分比區域化處理,建構全球化 Web 應用程式。
核心概念釐清
| 術語 | 縮寫 | 定義 |
| 國際化 | i18n | 應用程式支援多語言/地區,無需修改程式碼 |
| 本地化 | l10n | 為特定地區添加資源(翻譯、格式、文化慣例) |
| 區域 | Locale | 語言代碼 + 國家代碼,如 zh_TW、en_US、ja_JP |
Locale 物件取得與偵測
取得客戶端 Locale
java.util.Locale locale = request.getLocale();
Locale 常用方法
| 方法 | 回傳值 | 範例 (en_US) |
| getCountry() | ISO 3166 國碼 (大寫) | US |
| getDisplayCountry() | 當地語系國家名稱 | United States |
| getLanguage() | ISO 639 語言代碼 (小寫) | en |
| getDisplayLanguage() | 當地語系語言名稱 | English |
| getISO3Country() | 三字母國碼 | USA |
| getISO3Language() | 三字母語言代碼 | eng |
偵測 Locale 完整範例
<%@ page import="java.io.*,java.util.Locale" %>
<%@ page import="javax.servlet.*,javax.servlet.http.*" %>
<%
Locale locale = request.getLocale();
String language = locale.getLanguage();
String country = locale.getCountry();
%>
<html>
<head><title>Locale 偵測</title></head>
<body>
<center><h1>Locale 偵測結果</h1></center>
<p align="center">
語言代碼:<%= language %><br/>
國家代碼:<%= country %><br/>
顯示語言:<%= locale.getDisplayLanguage() %><br/>
顯示國家:<%= locale.getDisplayCountry() %>
</p>
</body>
</html>
語言編碼與特殊字元處理
設定 Content-Language 標頭
response.setContentType("text/html; charset=UTF-8");
response.setHeader("Content-Language", "zh-TW"); // 或 en, ja, ko 等
HTML 字元實體參考
| 字元 | 實體代碼 | 說明 |
| ñ | ñ / ñ | 西語 n 含波浪號 |
| ¡ | ¡ / ¡ | 倒置驚嘆號 |
| ¿ | ¿ / ¿ | 倒置問號 |
| € | € / € | 歐元符號 |
| 中文 | 直接使用 UTF-8 編碼 | 無需實體編碼 |
多語言頁面範例 (西班牙語)
<%@ page import="java.io.*,java.util.Locale" %>
<%@ page import="javax.servlet.*,javax.servlet.http.*" %>
<%
response.setContentType("text/html; charset=UTF-8");
response.setHeader("Content-Language", "es");
String title = "En Español";
%>
<html>
<head><title><%= title %></title></head>
<body>
<center><h1><%= title %></h1></center>
<div align="center">
<p>En Español</p>
<p>¡Hola Mundo!</p>
</div>
</body>
</html>
區域特定格式化
日期時間格式化 - DateFormat
<%@ page import="java.text.DateFormat,java.util.Date,java.util.Locale" %>
<%
Locale locale = request.getLocale();
// FULL: 完整格式 (含星期、月份名稱) | SHORT: 短格式 (數字)
String date = DateFormat.getDateTimeInstance(
DateFormat.FULL,
DateFormat.SHORT,
locale
).format(new Date());
%>
<html><body>
<p>本地化日期時間:<%= date %></p>
</body></html>
格式化風格對照
| 風格常數 | en_US 範例 | zh_TW 範例 |
| FULL | Monday, January 15, 2024 2:30 PM | 2024年1月15日 星期一 下午2:30 |
| LONG | January 15, 2024 2:30:45 PM EST | 2024年1月15日 下午2:30:45 |
| MEDIUM | Jan 15, 2024 2:30:45 PM | 2024/1/15 下午2:30:45 |
| SHORT | 1/15/24 2:30 PM | 2024/1/15 下午2:30 |
貨幣格式化 - NumberFormat.getCurrencyInstance()
<%@ page import="java.text.NumberFormat,java.util.Locale" %>
<%
Locale locale = request.getLocale();
NumberFormat currencyFormat = NumberFormat.getCurrencyInstance(locale);
String formatted = currencyFormat.format(1000000); // 1,000,000
%>
<p>本地化貨幣:<%= formatted %></p>
貨幣格式範例
| Locale | 1,000,000 格式化結果 |
| en_US | $1,000,000.00 |
| zh_TW | NT$1,000,000 |
| zh_CN | ¥1,000,000.00 |
| ja_JP | ¥1,000,000 |
| de_DE | 1.000.000,00 € |
| fr_FR | 1 000 000,00 € |
百分比格式化 - NumberFormat.getPercentInstance()
<%@ page import="java.text.NumberFormat,java.util.Locale" %>
<%
Locale locale = request.getLocale();
NumberFormat percentFormat = NumberFormat.getPercentInstance(locale);
String formatted = percentFormat.format(0.51); // 51%
%>
<p>本地化百分比:<%= formatted %></p>
百分比格式範例
| Locale | 0.51 格式化結果 |
| en_US | 51% |
| de_DE | 51 % |
| fr_FR | 51 % |
| ja_JP | 51% |
資源包機制 - 進階國際化
ResourceBundle 檔案結構
src/
└── resources/
├── messages.properties # 預設 (en)
├── messages_zh.properties # 中文
├── messages_zh_TW.properties # 繁體中文
├── messages_ja.properties # 日文
└── messages_en_US.properties # 美式英文
messages.properties 範例
# 預設英文
welcome.message=Welcome to our website!
login.button=Login
error.required=This field is required.
messages_zh_TW.properties 範例
# 繁體中文
welcome.message=歡迎來到我們的網站!
login.button=登入
error.required=此欄位為必填。
JSP 中使用 ResourceBundle
<%@ page import="java.util.ResourceBundle,java.util.Locale" %>
<%
Locale locale = request.getLocale();
ResourceBundle bundle = ResourceBundle.getBundle("resources.messages", locale);
%>
<html><body>
<h1><%= bundle.getString("welcome.message") %></h1>
<button><%= bundle.getString("login.button") %></button>
</body></html>
JSTL 格式化標籤庫 - 簡化國際化
引入標籤庫
<%@ taglib prefix="fmt" uri="https://java.sun.com/jsp/jstl/fmt" %>
常用標籤
| 標籤 | 用途 | 範例 |
| <fmt:setLocale> | 設定頁面 Locale | <fmt:setLocale value="zh_TW"/> |
| <fmt:setBundle> | 載入資源包 | <fmt:setBundle basename="messages"/> |
| <fmt:message> | 輸出本地化訊息 | <fmt:message key="welcome.message"/> |
| <fmt:formatNumber> | 格式化數字/貨幣/百分比 | <fmt:formatNumber value="${price}" type="currency"/> |
| <fmt:formatDate> | 格式化日期時間 | <fmt:formatDate value="${now}" type="both" dateStyle="full"/> |
| <fmt:parseNumber> | 解析本地化數字 | <fmt:parseNumber value="${input}" type="currency"/> |
| <fmt:parseDate> | 解析本地化日期 | <fmt:parseDate value="${input}" pattern="yyyy-MM-dd"/> |
JSTL 完整範例
<%@ taglib prefix="fmt" uri="https://java.sun.com/jsp/jstl/fmt" %>
<%@ taglib prefix="c" uri="https://java.sun.com/jsp/jstl/core" %>
<fmt:setLocale value="${pageContext.request.locale}" />
<fmt:setBundle basename="resources.messages" />
<html><body>
<h1><fmt:message key="welcome.message" /></h1>
<p>價格:<fmt:formatNumber value="${123456.78}" type="currency" /></p>
<p>日期:<fmt:formatDate value="${now}" type="both" dateStyle="long" timeStyle="short" /></p>
<p>進度:<fmt:formatNumber value="${0.75}" type="percent" /></p>
</body></html>
最佳實務建議
- UTF-8 全端到端:資料庫、伺服器、JSP、瀏覽器統一 UTF-8
- 外部化字串:所有使用者可見文字放入 ResourceBundle
- 避免硬編碼格式:使用 DateFormat、NumberFormat、JSTL fmt 標籤
- 測試多語言:瀏覽器語言偏好設定切換驗證
- 考慮 RTL 語言:阿拉伯文、希伯來文需 CSS direction: rtl
- 時區處理:日期時間儲存用 UTC,顯示時轉換使用者時區