項目中有一個國際化的需求:用戶登陸系統時選擇語言,登陸後所有文本信息包括頁面都轉換爲相應的語言,每個頁面不能單獨切換語言,只能登錄時選一次。項目基於shiro spring mvc搭建,下面描述一下實現思路。
首先,spring國際化的過程:
STEP 1.
在beans.xml中配置messageSource bean:
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource"> <!-- <property name="basename" value="classpath:i18n/messages" /> --> <!-- <property name="defaultEncoding" value="UTF-8"></property> --> <property name="basenames"> <list> <value>classpath:i18n/messages</value> </list> </property> </bean>
i18n/下應該有messages_en_US.properties和messages_zh_CN.properties之類的國際化文件。這樣,XmlWebApplicationContext就有了基本的國際化能力了,調用getMessage()方法即可獲取默認語言的文本了,要想獲取其他語言,還需要提供Locale參數。
STEP 2.
配置spring-mvc.xml中localeResolver bean:
<bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver"> <property name="defaultLocale" value="en_US" /> </bean>
這樣,DispatcherServlet就裝配上了localeResolver。SessionLocaleResolver的功能是在session中查找一個特殊的屬性值,這個值是用戶選擇的Locale,如果發現了,就以這個Locale作爲本次請求的Locale。實現中,可以在登陸時向session中設置用戶所選擇的Locale,配上SessionLocaleResolver就可以讓DispatcherServlet在這個用戶每次請求時都使用這個Locale了,這樣,getMessage()的Locale參數就有了。
STEP 3.
前兩步不算難,難的是如何跟shiro集成起來。
我使用了
<bean id="localeFilter" class="mycustom.LocaleFilter"/> <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> <property name="securityManager" ref="securityManager" /> <property name="loginUrl" value="/login" /> <property name="successUrl" value="/" /> <property name="unauthorizedUrl" value="/unauthorized" /> <property name="filterChainDefinitions"> <value> /login = localeFilter, authc /** = user </value> </property> </bean>
ShiroFilterFactoryBean有個功能,可以自動發現Filter,並將其加入Filter鏈中。這裏有個感念,shiro中認證是用javax.servlet.Filter接口實現的,因此web中普通的Filter可以跟shiro的認證鏈集成的很好,至少接口很熟悉。上面自定義了一個localeFilter bean,並配置爲/login = localeFilter, authc,這就是說先執行我們的localeFilter,再執行shiro默認的authc,這樣,就找到了一個向session中存用戶選擇的語言的地方----localeFilter。
代碼很簡單:
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { if (request.getParameter("locale") != null){ HttpServletRequest req = (HttpServletRequest) request; HttpSession session = req.getSession(); Locale locale = null; switch (request.getParameter("locale")){ case "zh_CN": locale = Locale.CHINA; break; case "en_US": locale = Locale.US; break; } session.setAttribute(SessionLocaleResolver.LOCALE_SESSION_ATTRIBUTE_NAME, locale); } chain.doFilter(request, response); }
代碼的功能是向session中設置用戶選擇的語言。注意,localeFilter不能放在authc後面,因爲默認的authc實現當登陸成功後,後面的Filter鏈不會執行,只能放在前面。這樣,當用戶登陸時,就可以選擇語言了。
爲什麼要這麼麻煩呢,直接從request獲取locale參數不就好了嗎?這是不行的,因爲當shiro登陸成功後,會重定向請求,定向到登陸成功頁url,重定向之後request就獲取不到在登陸時傳的locale參數了。