先看看 Java SE 中,使用 ResourceBundle
時如何根據基礎名稱取得對應的訊息檔案:
- 使用指定的
Locale
物件取得訊息檔案 - 使用
Locale.getDefault()
取得的物件取得訊息檔案 - 使用基礎名稱取得訊息檔案
在 JSTL 略有不同,簡單地說,JSTL 的 i18n 相容性標籤,會嘗試從屬性範圍中取得 javax.servlet.jsp.jstl.fmt.LocalizationContext
物件,藉以決定資源包與地區資訊,具體來說,決定訊息檔案的順序如下:
- 使用指定的
Locale
物件取得訊息檔案 - 根據瀏覽器
Accept-Language
標頭指定的偏好地區(Prefered locale)順序,這可以使用HttpServletRequest
的getLocales()
來取得 - 根據後備地區(fallback locale)資訊取得訊息檔案
- 使用基礎名稱取得訊息檔案
例如,〈訊息標籤〉的範例並沒有指定 Locale
,而瀏覽器指定的偏好地區為 "zh_TW"
,所以會嘗試尋找 messages3_zh_TW.properties 檔案,結果沒有找到,而範例並沒有設置偏好地區,所以才尋找 messages.properties 檔案。
<fmt:message>
標籤有個 bundle
屬性,可用以指定 LocalizationContext
物件,可以在建立 LocalizationContext
物件時指定 ResourceBundle
與 Locale
物件,例如下面的程式碼會嘗試從四個不同的訊息檔案中取得訊息並顯示出來:
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="java.util.*, javax.servlet.jsp.jstl.fmt.*"%>
<%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<!DOCTYPE html>
<%
// 假設這邊的Java程式碼是在另一個控制器中完成的
ResourceBundle zh_TW = ResourceBundle.getBundle("hello",
new Locale("zh", "TW"));
ResourceBundle zh_CN = ResourceBundle.getBundle("hello",
new Locale("zh", "CN"));
ResourceBundle ja_JP = ResourceBundle.getBundle("hello",
new Locale("ja", "JP"));
ResourceBundle en_US = ResourceBundle.getBundle("hello",
new Locale("en", "US"));
pageContext.setAttribute("zh_TW", new LocalizationContext(zh_TW));
pageContext.setAttribute("zh_CN", new LocalizationContext(zh_CN));
pageContext.setAttribute("ja_JP", new LocalizationContext(ja_JP));
pageContext.setAttribute("en_US", new LocalizationContext(en_US));
%>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<fmt:message bundle="${zh_TW}" key="cc.openhome.hello"/><br>
<fmt:message bundle="${zh_CN}" key="cc.openhome.hello"/><br>
<fmt:message bundle="${ja_JP}" key="cc.openhome.hello"/><br>
<fmt:message bundle="${en_US}" key="cc.openhome.hello"/>
</body>
</html>
在這個 JSP 中,分別使用四個不同的 ResourceBundle
建立了四個 LocalizationContext
,並指定為 page
屬性範圍,而在使用 <fmt:message>
時,指定 bundle
屬性為不同的 LocalizationContext
, 範例還準備了四個不同的 .properties,分別代表繁體中文的 hello_zh_TW.properties、簡體中文的 hello_zh_CN.properties、日文的 hello_ja_JP.properties與美式英文的 hello_en_US.properties,結果如下所示:
如果要共用 Locale
資訊,可以使用 <fmt:setLocale>
標籤,在 value
屬性上指定地區資訊,這是最簡單的方式。例如:
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<fmt:setLocale value="zh_TW"/>
<fmt:setBundle basename="hello"/>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<fmt:message key="cc.openhome.hello"/>
</body>
</html>
這個 JSP 會使用 hello_zh_TW.properties 網頁,結果就是顯示「哈囉」的文字。
<fmt:setLocale>
會呼叫 HttpServletResponse
的 setLocale()
設定回應編碼,事實上,<fmt:bundle>
、<fmt: setBundle>
或 <fmt:message>
也會呼叫 HttpServletResponse
的 setLocale()
設定回應編碼,不過要注意的是,在 Servlet 規範中,如果使用了 setCharacterEncoding()
或 setContentType()
時指定了 charset
,setLocale()
就會被忽略。
<fmt:requestEncoding>
用來設定請求物件的編碼處理,它會呼叫 HttpServletRequest
的 setCharacterEncoding()
,必須在取得任何請求參數之前使用。
<fmt:: message>
等標籤會使用 LocalizationContext
取得地區與資源包資訊,<fmt:setLocale>
其實就會在屬性範圍中設定 LocalizationContext
,如果想使用程式碼設定 LocalizationContext
物件,可以透過 javax.servlet.jsp.jstl.core.Config
的 set()
方法來設定。例如:
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="java.util.*,javax.servlet.jsp.jstl.core.*"%>
<%@ page import="javax.servlet.jsp.jstl.fmt.*"%>
<%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<%
Locale locale = new Locale("ja", "JP");
ResourceBundle res = ResourceBundle.getBundle("hello", locale);
Config.set(pageContext, Config.FMT_LOCALIZATION_CONTEXT,
new LocalizationContext(res), PageContext.PAGE_SCOPE);
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<fmt:message key="cc.openhome.hello"/>
</body>
</html>
在這個 JSP 中, 並沒有使用 <fmt:setLocale>
也沒有指定 <fmt:message>
的 bundle
屬性,所以會使用預設的 LocalizationContext
,在設定 LocalizationContext
時可以指定屬性範圍,<fmt: message>
會自動在四個屬性範圍中依次搜尋 LocalizationContext
,找到的話就使用,如果後續有使用使用 <fmt: setLocale>
或指定 <fmt:message>
的 bundle
屬性,則以後續指定為主。
另一個指定預設 LocalizationContext
的方式,就是直接指定屬性名稱,例如也許在 ServletContextListener
中如下指定:
...
public void contextInitialized(ServletContextEvent sce) {
Locale locale = new Locale("ja", "JP");
ResourceBundle res = ResourceBundle.getBundle("hello", locale);
ServletContext context = sce.getServletContext();
context.setAttribute(
"javax.servlet.jsp.jstl.fmt.LocalizationContext.application",
new LocalizationContext(res));
}
...
屬性名稱開頭是 "javax.servlet.jsp.jstl.fmt.localizationContext"
並加上一個範圍後綴字,四個範圍的後綴字是 ".page"
、".request"
、".session"
與 ".application"
。若使用 <fmt: setBundle>
時,就會設置這個屬性,範圍可由 scope
屬性來決定,預設值是 "page"
。
<fmt:setLocale>
可以設置地區資訊,如果想使用程式碼來設置地區資訊,可以使用 Config
的 set()
如下設定:
<%
...
Config.set(pageContext, Config.FMT_LOCALE,
new Locale("ja", "JP"), PageContext.PAGE_SCOPE);
%>
或者是直接指定屬性名稱,例如也許在 ServletContextListener
中如下指定:
...
public void contextInitialized(ServletContextEvent sce) {
ServletContext context = sce.getServletContext();
context.setAttribute(
" javax.servlet.jsp.jstl.fmt.locale.application",
new Locale("ja", "JP"));
}
...
屬性名稱開頭是 "javax.servlet.jsp.jstl.fmt.locale"
並加上一個範圍後綴字,四個範圍的後綴字是 ".page"
、".request"
、".session"
與 ".application"
。若使用 <fmt:setLocale>
時,就會設置這個屬性,範圍可由 scope
屬性來決定,預設值是 "page"
。
如果想要設置後備地區資訊,則可以使用 Config
的 set()
如下設定:
<%
...
Config.set(pageContext, Config.FMT_FALLBACK_LOCALE,
new Locale("ja", "JP"), PageContext.PAGE_SCOPE);
%>
或者是直接指定屬性名稱,例如也許在 ServletContextListener
中如下指定:
...
public void contextInitialized(ServletContextEvent sce) {
ServletContext context = sce.getServletContext();
context.setAttribute(
" javax.servlet.jsp.jstl.fmt.fallbackLocale.application",
new LocalizationContext(new Locale("ja", "JP")));
}
...
屬性名稱開頭是 "javax.servlet.jsp.jstl.fmt.fallbackLocale"
並加上一個範圍後綴字,四個範圍的後綴字是 ".page"
、".request"
、".session"
與 ".application"
。
Locale
、LocalizationContext
或後備地區資訊會分別被哪個標籤所使用或設置,在 JSTL 的規格書 JSR52 表格 8.11 作了不錯的整理,建議參考。