相信很多朋友在做J2EE國際化的時候,很可能遇到這個問題。因為request中的Locale是非常重要的,它代表著使用者瀏覽器的設定,同時,很多應用都是在這裡讀取使用者Locale然後進行國際化的。
但是問題就在於,如果我們的應用中很多地方都適應request.getLocale()來擷取Locale,但是我們希望使用自己的Locale,比如說是User資訊中包含了一個叫Locale的欄位。那麼我們怎樣使用比較方便呢?有人說把Locale存在Session中比較合適。其實這個要視情況而定,我們應用中很多使用request.getLocale(),如果我們放在Session中,那麼要去改很多getLocale的方法,很不合算。
如果不需要瀏覽器的Locale或者說,需要讓request的getLocale返回一個Locale是我們設定的User.getLcoale()欄位的話,怎麼解決這一問題,request沒有setLocale()這一方法。
- 在web.xml中使用filter,給request封裝,重寫getLocale()方法
1.使用HttpServletRequestWrapper封裝使用者的request,在這裡可以構造自己想要的方法:
public class LocaleRequestWrapper extends HttpServletRequestWrapper{ public LocaleRequestWrapper(HttpServletRequest request) { super(request); } public Enumeration getLocales() { Vector v = new Vector(1); v.add(getLocale()); return v.elements(); } public Locale getLocale() { String localeStr = ((HttpServletRequest) getRequest()).getSession().getAttribute("locale").toString(); Locale locale = new Locale(localeStr); return locale; }}
這裡,locale變數被設定在了Session中,在request中提取,並封裝了改Locale並向下傳遞。
getLocales()返回的是browser中使用者所選的所有languages的一個list。而預設情況下,getLocale()返回的是第一個,對該方法進行重寫之後,返回的就是我們放在Session中的locale了。
2.在I18NFilter 中提取使用者的request並封裝為LocaleRequestWrapper,向下傳遞:
public class I18NFilter implements Filter { transient static protected MessageLogger LOG = MessageLogger.getLogger(I18NFilter.class); @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { if (servletRequest instanceof HttpServletRequest && ((HttpServletRequest) servletRequest).getSession().getAttribute("locale") != null) { LOG.debug("I18N filter processing..."); HttpServletRequest req = (HttpServletRequest)servletRequest; LocaleRequestWrapper wrapper = new LocaleRequestWrapper(req); filterChain.doFilter(wrapper, servletResponse); } else { filterChain.doFilter(servletRequest, servletResponse); } } @Override public void destroy() { }}
3.在web.xml中配置該filter
<filter> <filter-name>I18NFilter</filter-name> <filter-class>com.rsi.uif.filter.I18NFilter</filter-class> </filter><filter>
別忘了配置filter-mapping:
<filter-mapping> <filter-name>I18NFilter</filter-name> <url-pattern>/route/*</url-pattern></filter-mapping>
這種方案是可行的,但是它有一個明顯的問題:當使用者第一層登陸進來的時候,由於初次訪問server,session中的locale還沒有設定(及可能是filter攔截的locale的時候,要訪問的頁面還沒有還是執行,session中沒有locale),這時候會出現整個架構使用了瀏覽器的lcoale。
根據我自己的具體應用,做了如下修改:
- 拋開filter,在核心的servlet中的適當位置,對request進行wrap:
在我們的Framework中,其實使用request.getLocale()來擷取locale進行國際化操作的地方主要集中於view上,有一個view的Utility類,用於將request傳遞給view(JSP\JSF, etc).那麼我們找到了合適的位置,在這裡,我們不需要去讀session中的lcoale,由於在核心servlet中,context是必須的,而且通過context肯定可以獲得user的相關資訊。比如context.getUser().getLocale().
好了,再按照剛才的第一個方案中使用的wrapper,對locale封裝。這樣user Locale 就進入我們的系統啦,融合默契。
public class LocaleRequestWrapper extends HttpServletRequestWrapper{ private Locale locale; public LocaleRequestWrapper(HttpServletRequest request,Locale locale) { super(request); this.locale = locale; } public Enumeration getLocales() { Vector v = new Vector(1); v.add(getLocale()); return v.elements(); } public Locale getLocale() { return this.locale; }}
這裡可以看到,我們新添加了一個欄位,叫做locale,在wrap使用者的Lcoale的時候要進行初始化:
LocaleRequestWrapper lrw = new LocaleRequestWrapper((HttpServletRequest)request,context.getUserContext().getUser().getLocale());//封裝dispatcher.forward(lrw, response);//轉寄給view
後記--值得改進一下的地方:
其實不應該把request的getLocales()給簡單的重寫掉,保留browser的locale在某些情況下是非常有用的。