Spring国际化教程

188 阅读7分钟

1.创建Resource Bundle

      注意:这里添加Locales的时候,命令一定要和Locale内置的国家方言定义一致,如en,zh_CN,en_US等等,zh、en前面代表方言,CN、US后面代表国家。

      language_en_US.properties:

name=test
args=hello,{0}

      language_zh_CN.properties:

name=测试
args=你好,{0}

2.绑定Resource Bundle

      在Spring中主要提供了两个实现类来进行Resouce Bundle的绑定,如下:       org.springframework.context.support.ResouceBundleMessageSource,该类是基于JDK的标准实现,和JDK一样,只能从类路径中读取资源,不能指定外部文件。       org.springframework.context.support.ReloadableResouceBundleMessageSource比ResouceBundleMessageSource更加灵活,它允许从任何Spring路径读取资源(不仅仅是类路径),并支持热重载bundle属性文件。       代码如下:

 <!--绑定Resource Bundle,该类只能从类路径读取,不能指定外部资源文件-->
    <bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
        <property name="defaultEncoding" value="UTF-8"/>
        <property name="basename" value="classpath:/i18n/language"/>
    </bean>

    <!--绑定Resource Bundle,可以从类路径读取,也可以指定外部资源文件,一般建议使用该类-->
    <bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
        <property name="basename" value="classpath:/i18n/language"/>
        <property name="defaultEncoding" value="UTF-8"/>
    </bean>

      因为我是放在了类路径下面,所以两个类都可以用。注意:\color{#00ffff}{bean的id必须为messageSource}

3.自动解析Locale和TimeZone

      Spring提供了org.springframework.web.servlet.LocaleResolver解析器来解析Locale,看下它的类图:

      我们在使用的时候主要是用到如下四个解析器:           1.org.springframework.web.servlet.i18n.CookieLocaleResolver           2.org.springframework.web.servlet.i18n.SessionLocaleResolver           3.org.springframework.web.servlet.i18n.FixedLocaleResolver           4.org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver               1.此解析器检查客户端发送的请求中的accept-language头,通常这个字段包含了用户操作系统的语言环境,而且不能设置Locale,会抛出异常。

              2.此解析器在请求的时候,如果DispatcherServlet找不到名称为localeReslover的bean,将会初始化该类作为默认解析器。               3.请注意,\color{#00ffff}{此解析器不支持TimeZone}

       1.CookieLocaleResolver

                由名字我们也可以看出来该解析器是通过Cookie来解析Locale和TimeZone的,当一个请求过来的时候,该解析器会从HttpServletRequest中拿到我们设置的key对应的Cookie,然后通过解析Cookie的值,来判断当前是哪个Locale。

<!--此解析器检查客户端可能存在Cookie,以查看是否指定了区域设置或时区-->
    <bean id="localeResolver" class="org.springframework.web.servlet.i18n.CookieLocaleResolver">
        <property name="defaultLocale" value="zh_CN"/>
        <property name="cookieName" value="clientlanguage"/>
        <!--单位:s,如果设置为-1,并不会持久化,当浏览器关闭的时候删除-->
        <property name="cookieMaxAge" value="100000"/>
    </bean>

                源码如下:

       2.SessionLocaleResolver

                该解析器与CookieLocaleResolver相比,把Locale和TimeZone存储在Servlet容器的HttpSession中。因此,这些设置对于每个会话来说都是临时的,在每个会话终结的时候都会丢失。

<!--此解析器允许我们从可能与用户请求关联的Session检索Locale和TimeZone
        与CookieLocaleResolver相比,此策略将本地选择的语言环境设置在Servlet容器的HttpSession中
        因此,这些设置对于每个会话来说都是临时的,因此在每个会话终止时都会丢失
        该解析器将仅根据当前的HttpServletRequest评估并修改相应的HttpSession属性-->
    <bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver">
        <property name="defaultLocale" value="zh_CN"/>
        <property name="localeAttributeName" value="clientlanguage"/>
        <property name="timeZoneAttributeName" value="clienttimezone"/>
    </bean>  

                源码如下:

       3.FixedLocaleResolver

                该解析器一旦设置了默认的Locale和TimeZone,将不能更改,更改会抛出异常。

 <!--固定解析器,一旦设置,不允许修改,修改会抛出异常-->
    <bean id="localeResolver" class="org.springframework.web.servlet.i18n.FixedLocaleResolver">
        <property name="defaultLocale" value=""/>
        <property name="defaultTimeZone" value=""/>
    </bean>

                源码如下:

      注意:\color{#00ffff}{当我们使用上述三种LocaleResolver的时候,必须将bean的名称申明为localeResolver}。原因是当我们查看org.springframework.web.servlet.DispatcherServlet#initLocaleResolver源码时,可以看到如下:

4.设置Locale

      我们该如何根据请求动态的设置我们的Locale?在上面,我们看到了Locale的解析是由org.springframework.web.servlet.LocaleResolver这个顶层接口来做的,这个顶层接口中有两个方法,如下:

      这也就意味着,如果我们想要更改Locale,就必须要拿到LocaleResolver,通过调用setLocale方法来更改我们的Locale。       在Spring中提供了一个类:org.springframework.web.servlet.i18n.LocaleChangeInterceptor,看这个名字我们就知道这个是一个拦截器。下面看下他的具体实现:

      正如我们前面说的,这个拦截器通过我们指定的某个请求是否有我们指定的参数名称,来进行请求拦截,然后根据我们参数的值来更改Locale。下面看下具体使用:

<mvc:interceptors>
        <!--使用自带的拦截器-->
        <bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor">
            <property name="paramName" value="language"/>
        </bean>
    </mvc:interceptors>

      我们也可以自定义拦截器,实现和Spring提供的差不多,代码如下:

public class InternationalInterceptor extends HandlerInterceptorAdapter {

    private static final String DEFAULT_PARAM_NAME = "language";

    private String paramName = DEFAULT_PARAM_NAME;

    public String getParamName() {
        return paramName;
    }

    public void setParamName(String paramName) {
        this.paramName = paramName;
    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String localeString = request.getParameter(getParamName());
        LocaleResolver localeResolver = RequestContextUtils.getLocaleResolver(request);
        if (localeResolver == null) {
            throw new IllegalArgumentException("No LocaleResolver found: not in a DispatcherServlet request?");
        }
        localeResolver.setLocale(request, response, StringUtils.parseLocaleString(localeString));
        return true;
    }

}

#5.完整配置及测试       完整配置:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <!--自动解析Locale的几种方式-->
    <!--此解析器允许我们从可能与用户请求关联的Session检索Locale和TimeZone
        与CookieLocaleResolver相比,此策略将本地选择的语言环境设置在Servlet容器的HttpSession中
        因此,这些设置对于每个会话来说都是临时的,因此在每个会话终止时都会丢失
        该解析器将仅根据当前的HttpServletRequest评估并修改相应的HttpSession属性-->
    <bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver">
        <property name="defaultLocale" value="zh_CN"/>
    </bean>

    <!--绑定Resource Bundle,可以从类路径读取,也可以指定外部资源文件,一般建议使用该类-->
    <bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
        <property name="basename" value="classpath:/i18n/language"/>
        <property name="defaultEncoding" value="UTF-8"/>
    </bean>


    <mvc:interceptors>
        <!--自定义拦截器,拦截参数为language的HTTP请求,重新设置区域化信息-->
        <!--        <bean class="com.ly.spring.interceptor.InternationalInterceptor">-->
        <!--            <property name="paramName" value="language"/>-->
        <!--        </bean>-->
        <!--使用自带的拦截器-->
        <bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor">
            <property name="paramName" value="language"/>
        </bean>
    </mvc:interceptors>

</beans>
@Controller
public class InternationalController {
    /**
     * 通过拦截器,根据Locale,改变返回的信息
     *
     * @param language
     * @return
     */
    @GetMapping("/international")
    public String international(String language) {
        System.out.println(language);
        return "international";
    }

}
<%@ page contentType="text/html;charset=UTF-8" language="java"  pageEncoding="UTF-8" %>
<%@taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>Title</title>
</head>
<body>
选择语言:
<a href="http://localhost:8080/spring_i18n/international?language=zh_CN">中文</a>
<a href="http://localhost:8080/spring_i18n/international?language=en_US">English</a>
<br>
<spring:message code="name"/>
</body>
</html>

      测试结果: