第一节:SpringMVC 国际化

一、简单国际化

  1、操作步骤

    (1)编写国际化资源文件;

    (2)让 SpringMVC 的 ResourceBundleMessageSource 管理国际化资源文件;

    (3)在页面中通过标签取值;

  2、代码示例

    (1)国际化资源文件

      中文:login_zh_CN.properties

welcomeInfo=欢迎来到登录页面
username=用户名
password=密码
loginBtn=登录

      英文:login_en_US.properties

welcomeInfo=welcom to login
username=USERNAME
password=PASSWORD
loginBtn=LOGIN

    (2)配置文件

    <!--  让SpringMVC管理国际化  id 必须是 messageSource-->
    <bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
        <property name="basename" value="login"/>
    </bean>

    (3)控制器跳转

    @RequestMapping(value = "/toLoginPage")
    public String toLoginPage() {
        return "login";
    }

    (4)页面取值

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<html>
<head>
    <title>登录</title>
</head>
<body>
<h1>
    <fmt:message key="welcomeInfo" />
</h1>
<form action="" method="post">
    <fmt:message key="username" /><input /><br>
    <fmt:message key="password" /><input /><br>
    <input type="submit" value='<fmt:message key="loginBtn" />'/>
</form>

</body>
</html>

二、区域信息解析器

   国际化的区域信息是决定国际化显示的因素。

   SpringMVC 中区域信息是又是区域信息解析器得到的:

   DispatcherServlet 中:

/** LocaleResolver used by this servlet */
private LocaleResolver localeResolver;

  现象:区域是按照浏览器带来语言信息决定

Locale locale = request.getLocale(); //获取到浏览器的区域信息

  

  来看 LocaleResolver 的装配:

    /**
     * Initialize the LocaleResolver used by this class.
     * <p>If no bean is defined with the given name in the BeanFactory for this namespace,
     * we default to AcceptHeaderLocaleResolver.
     */
    private void initLocaleResolver(ApplicationContext context) {
        try {
            this.localeResolver = context.getBean(LOCALE_RESOLVER_BEAN_NAME, LocaleResolver.class);
            if (logger.isDebugEnabled()) {
                logger.debug("Using LocaleResolver [" + this.localeResolver + "]");
            }
        }
        catch (NoSuchBeanDefinitionException ex) {
            // We need to use the default.
            this.localeResolver = getDefaultStrategy(context, LocaleResolver.class);//加载默认的策略
            if (logger.isDebugEnabled()) {
                logger.debug("Unable to locate LocaleResolver with name '" + LOCALE_RESOLVER_BEAN_NAME +
                        "': using default [" + this.localeResolver + "]");
            }
        }
    }

  如果配置文件中没有配置区域解析器,会加载 DispatcherServlet.properties 中的区域解析器:

org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver

  所有用到区域信息的地方,默认都会用AcceptHeaderLocaleResolver获取的:

public class AcceptHeaderLocaleResolver implements LocaleResolver {

    @Override
    public Locale resolveLocale(HttpServletRequest request) {
        return request.getLocale();
    }

    @Override
    public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {
        throw new UnsupportedOperationException(
                "Cannot change HTTP accept header - use a different locale resolution strategy");
    }

}

  等到其他地方再用到区域解析器的时候,直接获取:

Locale locale = this.localeResolver.resolveLocale(request);

  

三、程序中获取国际化信息

  SpringMVC 使用 MessageSource 帮我们管理了国际化配置,可以通过注入 messageSource 来获取国际化信息

@Controller
public class I18nTestController {

    @Autowired
    private ResourceBundleMessageSource messageSource;
    
    
    @RequestMapping(value = "/toLoginPage")
    public String toLoginPage(Locale locale) {
        System.out.println("locale = " + locale);
        String key = "welcomeInfo";
        //第一个参数:获取的key;第二个参数:国际化文件中动态参数;第三个参数:Locale 区域信息
        String message = messageSource.getMessage(key, null, locale);
        System.out.println("message = " + message);
        return "login";
    }
}

四、动态切换国际化——自定义区域解析器

  我们可以通过自定义区域解析器来替换默认的 AcceptHeaderLocaleResolver 区域解析器:

  (1)页面发送请求及请求参数

<a href="${ctp}/toLoginPage?locale=zh_CN">中文</a>|<a href="${ctp}/toLoginPage?locale=en_US">English</a>

  (2)自定义区域解析器

public class MyLocaleResolver implements LocaleResolver {

    /**
     * 解析返回 Locale,如果带了请求参数 locale,就获取参数的信息,否则就获取请求的区域信息
     * @param request
     * @return
     */
    @Override
    public Locale resolveLocale(HttpServletRequest request) {
        Locale locale = null;

        String localeStr = request.getParameter("locale");
        System.out.println("localeStr = " + localeStr);
        if (localeStr != null && !"".equals(localeStr)) {
            locale = new Locale(localeStr.split("_")[0], localeStr.split("_")[1]);
        } else {
            locale = request.getLocale();
        }
        return locale;
    }

    /**
     * 修改 Locale
     * @param request
     * @param response
     * @param locale
     */
    @Override
    public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {
        throw new UnsupportedOperationException(
                "Cannot change HTTP accept header - use a different locale resolution strategy");
    }
}

  (3)配置区域解析器

    <!--  自定义区域信息解析器  -->
    <bean id="localeResolver" class="com.njf.component.MyLocalResolver"></bean>

  (4)控制器

@Controller
public class I18nTestController {

    @Autowired
    private ResourceBundleMessageSource messageSource;
    
    
    @RequestMapping(value = "/toLoginPage")
    public String toLoginPage(Locale locale) {
        System.out.println("locale = " + locale);
        String key = "welcomeInfo";
        String message = messageSource.getMessage(key, null, locale);
        System.out.println("message = " + message);
        return "login";
    }
}

  (5)页面取值

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<html>
<head>
    <title>登录</title>
</head>
<body>
<h1>
    <fmt:message key="welcomeInfo" />
</h1>
<form action="" method="post">
    <fmt:message key="username" /><input /><br>
    <fmt:message key="password" /><input /><br>
    <input type="submit" value='<fmt:message key="loginBtn" />'/>
</form>

</body>
</html>

五、各种区域解析器

  区域解析器继承树:

   

   (1)AcceptHeaderLocaleResolver:使用请求头的区域信息

public class AcceptHeaderLocaleResolver implements LocaleResolver {

    @Override
    public Locale resolveLocale(HttpServletRequest request) {
        return request.getLocale();
    }

    @Override
    public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {
        throw new UnsupportedOperationException(
                "Cannot change HTTP accept header - use a different locale resolution strategy");
    }

}

   (2)MyLocaleResolver:自定义区域解析器

   (3)SessionLocalResolver:区域信息是从 session 中获取,可以根据请求参数创建一个 locale 对象,放在 session 中;

public class SessionLocaleResolver extends AbstractLocaleContextResolver {
    @Override
    public Locale resolveLocale(HttpServletRequest request) {
        Locale locale = (Locale) WebUtils.getSessionAttribute(request, LOCALE_SESSION_ATTRIBUTE_NAME);
        if (locale == null) {
            locale = determineDefaultLocale(request);
        }
        return locale;
    }

    @Override
    public void setLocaleContext(HttpServletRequest request, HttpServletResponse response, LocaleContext localeContext) {
        Locale locale = null;
        TimeZone timeZone = null;
        if (localeContext != null) {
            locale = localeContext.getLocale();
            if (localeContext instanceof TimeZoneAwareLocaleContext) {
                timeZone = ((TimeZoneAwareLocaleContext) localeContext).getTimeZone();
            }
        }
        WebUtils.setSessionAttribute(request, LOCALE_SESSION_ATTRIBUTE_NAME, locale);
        WebUtils.setSessionAttribute(request, TIME_ZONE_SESSION_ATTRIBUTE_NAME, timeZone);
    }

}

   (4)FixedLocalResolver:获取本地固定的区域信息,使用系统默认的区域信息

public class FixedLocaleResolver extends AbstractLocaleContextResolver {
    @Override
    public Locale resolveLocale(HttpServletRequest request) {
        Locale locale = getDefaultLocale();
        if (locale == null) {
            locale = Locale.getDefault();
        }
        return locale;
    }

    @Override
    public LocaleContext resolveLocaleContext(HttpServletRequest request) {
        return new TimeZoneAwareLocaleContext() {
            @Override
            public Locale getLocale() {
                return getDefaultLocale();
            }
            @Override
            public TimeZone getTimeZone() {
                return getDefaultTimeZone();
            }
        };
    }

    @Override
    public void setLocaleContext(HttpServletRequest request, HttpServletResponse response, LocaleContext localeContext) {
        throw new UnsupportedOperationException("Cannot change fixed locale - use a different locale resolution strategy");
    }

}

   (5)CookieLocaleResolver:区域信息是从 cookie中获取

public class CookieLocaleResolver extends CookieGenerator implements LocaleContextResolver {

    @Override
    public Locale resolveLocale(HttpServletRequest request) {
        parseLocaleCookieIfNecessary(request);
        return (Locale) request.getAttribute(LOCALE_REQUEST_ATTRIBUTE_NAME);
    }

    @Override
    public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {
        setLocaleContext(request, response, (locale != null ? new SimpleLocaleContext(locale) : null));
    }
}

六、动态切换国际化——使用 SessionLocaleResolver

  1、配置 SessionLocaleResolver

    <!--  区域信息从 session 中获取  -->
    <bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver"/>

  2、控制器方法

  从请求参数中获取 locale 参数,然后把区域信息手动放到 session 域中:

    @RequestMapping(value = "/toLoginPage")
    public String toLoginPage(@RequestParam(value = "locale", defaultValue = "zh_CN") String localeStr, Locale locale, HttpSession session) {
        System.out.println("locale = " + locale);
        String key = "welcomeInfo";
        String message = messageSource.getMessage(key, null, locale);
        System.out.println("message = " + message);

        Locale localeInfo = null;
        System.out.println("localeStr = " + localeStr);
        if (localeStr != null && !"".equals(localeStr)) {
            localeInfo = new Locale(localeStr.split("_")[0], localeStr.split("_")[1]);
        } else {
            localeInfo = locale;
        }

        //把区域信息存在session中
        session.setAttribute(SessionLocaleResolver.class.getName() + ".LOCALE", localeInfo);

        return "login";
    }

七、动态切换国际化——使用SessionLocaleResolver 与LocaleChangeInterceptor 拦截器

  1、配置拦截器(需要配置 SessionLocaleResolver 和 LocaleChangeInterceptor )

    <!--  区域信息从 session 中获取  -->
    <bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver"/>

    <!--  配置国际化拦截器  -->
    <mvc:interceptors>
        <bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor"/>
    </mvc:interceptors>

  2、控制器方法

    @RequestMapping(value = "/toLoginPage")
    public String toLoginPage(@RequestParam(value = "locale", defaultValue = "zh_CN") String localeStr, Locale locale, HttpSession session) {
        System.out.println("locale = " + locale);
        String key = "welcomeInfo";
        String message = messageSource.getMessage(key, null, locale);
        System.out.println("message = " + message);

        return "login";
    }

  3、LocaleChangeInterceptor 源码

public class LocaleChangeInterceptor extends HandlerInterceptorAdapter {

    /**
     * Default name of the locale specification parameter: "locale".
     */
    public static final String DEFAULT_PARAM_NAME = "locale";

    private String paramName = DEFAULT_PARAM_NAME;


    /**
     * Set the name of the parameter that contains a locale specification
     * in a locale change request. Default is "locale".
     */
    public void setParamName(String paramName) {
        this.paramName = paramName;
    }

    /**
     * Return the name of the parameter that contains a locale specification
     * in a locale change request.
     */
    public String getParamName() {
        return this.paramName;
    }


    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws ServletException {

        String newLocale = request.getParameter(this.paramName);
        if (newLocale != null) {
            LocaleResolver localeResolver = RequestContextUtils.getLocaleResolver(request);
            if (localeResolver == null) {
                throw new IllegalStateException("No LocaleResolver found: not in a DispatcherServlet request?");
            }
            localeResolver.setLocale(request, response, StringUtils.parseLocaleString(newLocale));
        }
        // Proceed in any case.
        return true;
    }

}

   拦截器会从请求中获取 locale 参数,然后把区域化信息放在 session 域中。

  注意:使用拦截器切换语言的参数名称必须:locale

八、总结

  1、默认情况下,SpringMVC  根据 Accept-Language 参数判断客户端的本地化类型。

  2、当接受到请求时,SpringMVC 会在上下文中查找一个本地化解析器(LocalResolver),找到后使用它获取请求所对应的本地化类型信息。

  3、SpringMVC 还允许装配一个动态更改本地化类型的拦截器,这样通过指定一个请求参数就可以控制单个请求的本地化类型。

  4、SessionLocaleResolver & LocaleChangeInterceptor 工作原理 

  

  5、本地化解析器和本地拦截器

    AcceptHeaderLocaleResolver根据 HTTP 请求头的 Accept-Language 参数确定本地化类型,如果没有显式定义本地化解析器, SpringMVC 使用该解析器。
    CookieLocaleResolver:根据指定的 Cookie 值确定本地化类型
    SessionLocaleResolver:根据 Session 中特定的属性确定本地化类型
    LocaleChangeInterceptor:从请求参数中获取本次请求对应的本地化类型。

    

原文地址:https://www.cnblogs.com/niujifei/p/15650764.html