2011年8月8日 星期一

Spring internationalization (i18n) 學習筆記

Spring 提供了下列幾種 MessageSource 給 i18n 用.
StaticMessageSource
ResourceBundleMessageSource
ReloadableResourceBundleMessageSource

使用及設定上3者都差不多, ReloadableResourceBundleMessageSource 的設定檔不需要放在 classpath 內, 且可以指定 cache, 故我選擇它實作.


實作環境Spring 3.1.0M1


在 spring 設定檔內設定如下:
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basename">
<value>/WEB-INF/i18n/messages</value>
</property>
<property name="defaultEncoding" value="UTF-8"/>
<property name="cacheSeconds"><value>0</value></property>
</bean>


basename 指定訊息檔要放的位置及檔名.
本範例在 /WEB-INF/i18n/  下會有 messages.properties, messages_en.properties, messages_zh_TW.properties ... 等各國語言的檔案.


messages.properties 內容為 Key=Value 型式的文字檔.

舉例來說:
messages_en_US.properties 的內容:
hello=Hello


messages_zh_TW.properties 的內容:

hello=哈囉


MessageSource會依 Http header 內的 Accept-Language 來判斷應選用那種語系.

如果該語系對應的檔案不存在, 預設會使用 messages.properties (注意, messages 為我在spring內的設定, 並非真的 spring 預設檔名). 

如果 messages.properties 不存在, 則會拋出 exception.


在 jsp 內可使用 spring tag <s:message> 來取值 (記得要先宣告 <%@ taglib prefix="s" uri="http://www.springframework.org/tags"%>), 例

<s:message code="hello" text="hello" />


或是用 jstl tag <fmt:message> 來取值 (記得要先宣告 <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> ), 例

<fmt:message key="hello" />


我個人比較喜歡用 spring tag, 因為有預設值可防止打錯字之類的低級錯誤.


spring:message 中帶參數的做法如下:

<s:message code="hello" text="hello {0}. {1}" argumentSeparator="|" arguments="Mr|Liu" />


詳請可參閱Spring文件message tag的說明.


如果在 jsp 中不用 spring tag 而想直接用 messageSource 的方式如下:
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"
import="org.springframework.context.ApplicationContext"
import="org.springframework.web.context.support.WebApplicationContextUtils"
import="org.springframework.web.servlet.support.RequestContextUtils"
.
.
.
<%
    ApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext( application.getContext(request.getContextPath())) ;
    Locale locale = RequestContextUtils.getLocale(request) ;
    out.println(ctx.getMessage("MESSAGE", null, "Message", locale)) ;
%>



messages.properties 規定需為 iso-8859-1 編碼, 故若非英文語系的檔案, 需做特殊處理.



我的作法是, 編寫一個 messages_zh_TW.txt 的檔案, UTF-8 編碼 (不要有 BOM, 不然轉碼時第一行會出錯.)




然後以 jdk 附的 native2ascii 進行轉檔, 指令如下:

native2ascii -encoding utf8 messages_zh_TW.txt messages_zh_TW.properties

經此步驟轉出來的檔案才能派上用場.


如果要讓使用者可以自行選擇語系呢 ?
Spring 提供了 LocaleResolver 搭配 LocaleChangeInterceptor 進行這件事.

先在 spring 設定檔內設定如下:

<bean id="localeResolver" class="org.springframework.web.servlet.i18n.CookieLocaleResolver" />
<mvc:interceptors>
<bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor" />
</mvc:interceptors>

註: 除了 CookieLocaleResolver 還有 SessionLocaleResolver 可以用.

接著只要在網址後面加上參數 locale=zh_TW 或 locale=en_US ... 就可以變換不同語系了.



註: LocaleChangeInterceptor 有個參數 paramName 可用來指定參數名稱, 預設是 locale, 您也可以指定為 lang 或其它您喜歡的名稱.







最後, 想知道瀏覽器送出的 accept-language 為何, 可在 jsp 內以下列方式取得.

request.getHeader("Accept-Language") ;


在 Java 可以下列方式取得目前的 locale:
RequestContextUtils.getLocale(request)


最後的最後, locale 不論大寫或小寫, i18n 都可以正確判斷並發揮作用.


補充, 如果要指定 locale 的作法如下:
LocaleEditor localeEditor = new LocaleEditor();
localeEditor.setAsText("en_US"); // your locale string
LocaleResolver localeResolver = RequestContextUtils.getLocaleResolver(request);
localeResolver.setLocale(request, response, (Locale) localeEditor.getValue());

沒有留言: