项目中有一个国际化的需求:用户登陆系统时选择语言,登陆后所有文本信息包括页面都转换为相应的语言,每个页面不能单独切换语言,只能登录时选一次。项目基于shiro spring mvc搭建,下面描述一下实现思路。

    首先,spring国际化的过程:

STEP 1.

在beans.xml中配置messageSource bean:

	
		
		
		
			
				classpath:i18n/messages
			
		
	

i18n/下应该有messages_en_US.properties和messages_zh_CN.properties之类的国际化文件。这样,XmlWebApplicationContext就有了基本的国际化能力了,调用getMessage()方法即可获取默认语言的文本了,要想获取其他语言,还需要提供Locale参数。

STEP 2.

配置spring-mvc.xml中localeResolver bean:

	
	    
	

这样,DispatcherServlet就装配上了localeResolver。SessionLocaleResolver的功能是在session中查找一个特殊的属性值,这个值是用户选择的Locale,如果发现了,就以这个Locale作为本次请求的Locale。实现中,可以在登陆时向session中设置用户所选择的Locale,配上SessionLocaleResolver就可以让DispatcherServlet在这个用户每次请求时都使用这个Locale了,这样,getMessage()的Locale参数就有了。

STEP 3.

前两步不算难,难的是如何跟shiro集成起来。

我使用了

	
	
		
		
		
		
		
			
			    /login = localeFilter, authc
			    /** = user
			
		
	

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参数了。