本篇内容介绍了“如何配置SpringMVC国际化”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!
还是先来说说用法,再来说源码,这样大家不容易犯迷糊。我们先说在 SSM 中如何处理国际化问题。
首先国际化我们可能有两种需求:
在页面渲染时实现国际化(这个借助于 Spring 标签实现)
在接口中获取国际化匹配后的消息
大致上就是上面这两种场景。接下来松哥通过一个简单的用法来和大家演示下具体玩法。
首先我们在项目的 resources 目录下新建语言文件,language_en_US.properties 和 language_zh-CN.properties,如下图:
内容分别如下:
language_en_US.properties:
login.username=Username login.password=Password
language_zh-CN.properties:
login.username=用户名 login.password=用户密码
这两个分别对应英中文环境。配置文件写好之后,还需要在 SpringMVC 容器中提供一个 ResourceBundleMessageSource 实例去加载这两个实例,如下:
<bean class="org.springframework.context.support.ResourceBundleMessageSource" id="messageSource"> <property name="basename" value="language"/> <property name="defaultEncoding" value="UTF-8"/> </bean>
这里配置了文件名 language 和默认的编码格式。
接下来我们新建一个 login.jsp 文件,如下:
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> <spring:message code="login.username"/> <input type="text"> <br> <spring:message code="login.password"/> <input type="text"> <br> </body> </html>
在这个文件中,我们通过 spring:message 标签来引用变量,该标签会根据当前的实际情况,选择合适的语言文件。
接下来我们为 login.jsp 提供一个控制器:
@Controller public class LoginController { @Autowired MessageSource messageSource; @GetMapping("/login") public String login() { String username = messageSource.getMessage("login.username", null, LocaleContextHolder.getLocale()); String password = messageSource.getMessage("login.password", null, LocaleContextHolder.getLocale()); System.out.println("username = " + username); System.out.println("password = " + password); return "login"; } }
控制器中直接返回 login 视图即可。
另外我这还注入了 MessageSource 对象,主要是为了向大家展示如何在处理器中获取国际化后的语言文字。
配置完成后,启动项目进行测试。
默认情况下,系统是根据请求头的中 Accept-Language 字段来判断当前的语言环境的,该这个字段由浏览器自动发送,我们这里为了测试方便,可以使用 POSTMAN 进行测试,然后手动设置 Accept_Language 字段。
首先测试中文环境:
然后测试英文环境:
都没问题,完美!同时观察 IDEA 控制台,也能正确打印出语言文字。
上面这个是基于 AcceptHeaderLocaleResolver 来解析出当前的区域和语言的。
有的时候,我们希望语言环境直接通过请求参数来传递,而不是通过请求头来传递,这个需求我们通过 SessionLocaleResolver 或者 CookieLocaleResolver 都可以实现。
先来看 SessionLocaleResolver。
首先在 SpringMVC 配置文件中提供 SessionLocaleResolver 的实例,同时配置一个拦截器,如下:
<mvc:interceptors> <mvc:interceptor> <mvc:mapping path="/**"/> <bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor"> <property name="paramName" value="locale"/> </bean> </mvc:interceptor> </mvc:interceptors> <bean class="org.springframework.web.servlet.i18n.SessionLocaleResolver" id="localeResolver"> </bean>
SessionLocaleResolver 是负责区域解析的,这个没啥好说的。拦截器 LocaleChangeInterceptor 则主要是负责参数解析的,我们在配置拦截器的时候,设置了参数名为 locale(默认即此),也就是说我们将来可以通过 locale 参数来传递当前的环境信息。
配置完成后,我们还是来访问刚才的 login 控制器,如下:
此时我们可以直接通过 locale 参数来控制当前的语言环境,这个 locale 参数就是在前面所配置的 LocaleChangeInterceptor 拦截器中被自动解析的。
如果你不想配置 LocaleChangeInterceptor 拦截器也是可以的,直接自己手动解析 locale 参数然后设置 locale 也行,像下面这样:
@Controller public class LoginController { @Autowired MessageSource messageSource; @GetMapping("/login") public String login(String locale,HttpSession session) { if ("zh-CN".equals(locale)) { session.setAttribute(SessionLocaleResolver.LOCALE_SESSION_ATTRIBUTE_NAME, new Locale("zh", "CN")); } else if ("en-US".equals(locale)) { session.setAttribute(SessionLocaleResolver.LOCALE_SESSION_ATTRIBUTE_NAME, new Locale("en", "US")); } String username = messageSource.getMessage("login.username", null, LocaleContextHolder.getLocale()); String password = messageSource.getMessage("login.password", null, LocaleContextHolder.getLocale()); System.out.println("username = " + username); System.out.println("password = " + password); return "login"; } }
SessionLocaleResolver 所实现的功能也可以通过 CookieLocaleResolver 来实现,不同的是前者将解析出来的区域信息保存在 session 中,而后者则保存在 Cookie 中。保存在 session 中,只要 session 没有发生变化,后续就不用再次传递区域语言参数了,保存在 Cookie 中,只要 Cookie 没变,后续也不用再次传递区域语言参数了。
使用 CookieLocaleResolver 的方式很简单,直接在 SpringMVC 中提供 CookieLocaleResolver 的实例即可,如下:
<bean class="org.springframework.web.servlet.i18n.CookieLocaleResolver" id="localeResolver"/>
注意这里也需要使用到 LocaleChangeInterceptor 拦截器,如果不使用该拦截器,则需要自己手动解析并配置语言环境,手动解析并配置的方式如下:
@GetMapping("/login3") public String login3(String locale, HttpServletRequest req, HttpServletResponse resp) { CookieLocaleResolver resolver = new CookieLocaleResolver(); if ("zh-CN".equals(locale)) { resolver.setLocale(req, resp, new Locale("zh", "CN")); } else if ("en-US".equals(locale)) { resolver.setLocale(req, resp, new Locale("en", "US")); } String username = messageSource.getMessage("login.username", null, LocaleContextHolder.getLocale()); String password = messageSource.getMessage("login.password", null, LocaleContextHolder.getLocale()); System.out.println("username = " + username); System.out.println("password = " + password); return "login"; }
配置完成后,启动项目进行测试,这次测试的方式跟 SessionLocaleResolver 的测试方式一致,松哥就不再多说了。
除了前面介绍的这几种 LocaleResolver 之外,还有一个 FixedLocaleResolver,因为比较少见,松哥这里就不做过多介绍了。
Spring Boot 和 Spring 一脉相承,对于国际化的支持,默认是通过 AcceptHeaderLocaleResolver 解析器来完成的,这个解析器,默认是通过请求头的 Accept-Language 字段来判断当前请求所属的环境的,进而给出合适的响应。
所以在 Spring Boot 中做国际化,这一块我们可以不用配置,直接就开搞。
首先创建一个普通的 Spring Boot 项目,添加 web 依赖即可。项目创建成功后,默认的国际化配置文件放在 resources 目录下,所以我们直接在该目录下创建四个测试文件,如下:
我们的 message 文件是直接创建在 resources 目录下的,IDEA 在展示的时候,会多出一个 Resource Bundle,这个大家不用管,千万别手动去创建这个目录。
messages.properties 这个是默认的配置,其他的则是不同语言环境下的配置,en_US 是英语(美国),zh_CN 是中文简体,zh_TW 是中文繁体(文末附录里边有一个完整的语言简称表格)。
四个文件创建好之后,第一个默认的我们可以先空着,另外三个分别填入以下内容:
messages_zh_CN.properties
user.name=江南一点雨
messages_zh_TW.properties
user.name=江南壹點雨
messages_en_US.properties
user.name=javaboy
配置完成后,我们就可以直接开始使用了。在需要使用值的地方,直接注入 MessageSource 实例即可。
在 Spring 中需要配置的 MessageSource 现在不用配置了,Spring Boot 会通过 org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration 自动帮我们配置一个 MessageSource 实例。
创建一个 HelloController ,内容如下:
@RestController public class HelloController { @Autowired MessageSource messageSource; @GetMapping("/hello") public String hello() { return messageSource.getMessage("user.name", null, LocaleContextHolder.getLocale()); } }
在 HelloController 中我们可以直接注入 MessageSource 实例,然后调用该实例中的 getMessage 方法去获取变量的值,第一个参数是要获取变量的 key,第二个参数是如果 value 中有占位符,可以从这里传递参数进去,第三个参数传递一个 Locale 实例即可,这相当于当前的语言环境。
接下来我们就可以直接去调用这个接口了。
默认情况下,在接口调用时,通过请求头的 Accept-Language 来配置当前的环境,我这里通过 POSTMAN 来进行测试,结果如下:
小伙伴们看到,我在请求头中设置了 Accept-Language 为 zh-CN,所以拿到的就是简体中文;如果我设置了 zh-TW,就会拿到繁体中文:
是不是很 Easy?
有的小伙伴觉得切换参数放在请求头里边好像不太方便,那么也可以自定义解析方式。例如参数可以当成普通参数放在地址栏上,通过如下配置可以实现我们的需求。
@Configuration public class WebConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { LocaleChangeInterceptor interceptor = new LocaleChangeInterceptor(); interceptor.setParamName("lang"); registry.addInterceptor(interceptor); } @Bean LocaleResolver localeResolver() { SessionLocaleResolver localeResolver = new SessionLocaleResolver(); localeResolver.setDefaultLocale(Locale.SIMPLIFIED_CHINESE); return localeResolver; } }
在这段配置中,我们首先提供了一个 SessionLocaleResolver 实例,这个实例会替换掉默认的 AcceptHeaderLocaleResolver,不同于 AcceptHeaderLocaleResolver 通过请求头来判断当前的环境信息,SessionLocaleResolver 将客户端的 Locale 保存到 HttpSession 对象中,并且可以进行修改(这意味着当前环境信息,前端给浏览器发送一次即可记住,只要 session 有效,浏览器就不必再次告诉服务端当前的环境信息)。
另外我们还配置了一个拦截器,这个拦截器会拦截请求中 key 为 lang 的参数(不配置的话是 locale),这个参数则指定了当前的环境信息。
好了,配置完成后,启动项目,访问方式如下:
我们通过在请求中添加 lang 来指定当前环境信息。这个指定只需要一次即可,也就是说,在 session 不变的情况下,下次请求可以不必带上 lang 参数,服务端已经知道当前的环境信息了。
CookieLocaleResolver 也是类似用法,不再赘述。
默认情况下,我们的配置文件放在 resources 目录下,如果大家想自定义,也是可以的,例如定义在 resources/i18n 目录下:
但是这种定义方式系统就不知道去哪里加载配置文件了,此时还需要 application.properties 中进行额外配置(注意这是一个相对路径):
spring.messages.basename=i18n/messages
另外还有一些编码格式的配置等,内容如下:
spring.messages.cache-duration=3600 spring.messages.encoding=UTF-8 spring.messages.fallback-to-system-locale=true
spring.messages.cache-duration 表示 messages 文件的缓存失效时间,如果不配置则缓存一直有效。
spring.messages.fallback-to-system-locale 属性则略显神奇,网上竟然看不到一个明确的答案,后来翻了一会源码才看出端倪。
这个属性的作用在 org.springframework.context.support.AbstractResourceBasedMessageSource#getDefaultLocale 方法中生效:
protected Locale getDefaultLocale() { if (this.defaultLocale != null) { return this.defaultLocale; } if (this.fallbackToSystemLocale) { return Locale.getDefault(); } return null; }
从这段代码可以看出,在找不到当前系统对应的资源文件时,如果该属性为 true,则会默认查找当前系统对应的资源文件,否则就返回 null,返回 null 之后,最终又会调用到系统默认的 messages.properties 文件。
国际化这块主要涉及到的组件是 LocaleResolver,这是一个开放的接口,官方默认提供了四个实现。当前该使用什么环境,主要是通过 LocaleResolver 来进行解析的。
LocaleResolver
public interface LocaleResolver { Locale resolveLocale(HttpServletRequest request); void setLocale(HttpServletRequest request, @Nullable HttpServletResponse response, @Nullable Locale locale); }
这里两个方法:
鸿蒙官方战略合作共建——HarmonyOS技术社区
resolveLocale:根据当前请求解析器出 Locale 对象。
设置 Locale 对象。
我们来看看 LocaleResolver 的继承关系:
虽然中间有几个抽象类,不过最终负责实现的其实就四个:
AcceptHeaderLocaleResolver:根据请求头中的 Accept-Language 字段来确定当前的区域语言等。
SessionLocaleResolver:根据请求参数来确定区域语言等,确定后会保存在 Session 中,只要 Session 不变,Locale 对象就一直有效。
CookieLocaleResolver:根据请求参数来确定区域语言等,确定后会保存在 Cookie 中,只要 Session 不变,Locale 对象就一直有效。
FixedLocaleResolver:配置时直接提供一个 Locale 对象,以后不能修改。
接下来我们就对这几个类逐一进行分析。
AcceptHeaderLocaleResolver 直接实现了 LocaleResolver 接口,我们来看它的 resolveLocale 方法:
@Override public Locale resolveLocale(HttpServletRequest request) { Locale defaultLocale = getDefaultLocale(); if (defaultLocale != null && request.getHeader("Accept-Language") == null) { return defaultLocale; } Locale requestLocale = request.getLocale(); List<Locale> supportedLocales = getSupportedLocales(); if (supportedLocales.isEmpty() || supportedLocales.contains(requestLocale)) { return requestLocale; } Locale supportedLocale = findSupportedLocale(request, supportedLocales); if (supportedLocale != null) { return supportedLocale; } return (defaultLocale != null ? defaultLocale : requestLocale); }
鸿蒙官方战略合作共建——HarmonyOS技术社区
首先去获取默认的 Locale 对象。
如果存在默认的 Locale 对象,并且请求头中没有设置 Accept-Language 字段,则直接返回默认的 Locale。
从 request 中取出当前的 Locale 对象,然后查询出支持的 supportedLocales,如果 supportedLocales 或者 supportedLocales 中包含 requestLocale,则直接返回 requestLocale。
如果前面还是没有匹配成功的,则从 request 中取出 locales 集合,然后再去和支持的 locale 进行比对,选择匹配成功的 locale 返回。
如果前面都没能返回,则判断 defaultLocale 是否为空,如果不为空,就返回 defaultLocale,否则返回 defaultLocale。
再来看看它的 setLocale 方法,直接抛出异常,意味着通过请求头处理 Locale 是不允许修改的。
@Override public void setLocale(HttpServletRequest request, @Nullable HttpServletResponse response, @Nullable Locale locale) { throw new UnsupportedOperationException( "Cannot change HTTP accept header - use a different locale resolution strategy"); }
SessionLocaleResolver 的实现多了一个抽象类 AbstractLocaleContextResolver,AbstractLocaleContextResolver 中增加了对 TimeZone 的支持,我们先来看下 AbstractLocaleContextResolver:
public abstract class AbstractLocaleContextResolver extends AbstractLocaleResolver implements LocaleContextResolver { @Nullable private TimeZone defaultTimeZone; public void setDefaultTimeZone(@Nullable TimeZone defaultTimeZone) { this.defaultTimeZone = defaultTimeZone; } @Nullable public TimeZone getDefaultTimeZone() { return this.defaultTimeZone; } @Override public Locale resolveLocale(HttpServletRequest request) { Locale locale = resolveLocaleContext(request).getLocale(); return (locale != null ? locale : request.getLocale()); } @Override public void setLocale(HttpServletRequest request, @Nullable HttpServletResponse response, @Nullable Locale locale) { setLocaleContext(request, response, (locale != null ? new SimpleLocaleContext(locale) : null)); } }
可以看到,多了一个 TimeZone 属性。从请求中解析出 Locale 还是调用了 resolveLocaleContext 方法,该方法在子类中被实现,另外调用 setLocaleContext 方法设置 Locale,该方法的实现也在子类中。
我们来看下它的子类 SessionLocaleResolver:
@Override public Locale resolveLocale(HttpServletRequest request) { Locale locale = (Locale) WebUtils.getSessionAttribute(request, this.localeAttributeName); if (locale == null) { locale = determineDefaultLocale(request); } return locale; }
直接从 Session 中获取 Locale,默认的属性名是 SessionLocaleResolver.class.getName() + ".LOCALE",如果 session 中不存在 Locale 信息,则调用 determineDefaultLocale 方法去加载 Locale,该方法会首先找到 defaultLocale,如果 defaultLocale 不为 null 就直接返回,否则就从 request 中获取 Locale 返回。
再来看 setLocaleContext 方法,就是将解析出来的 Locale 保存起来。
@Override public void setLocaleContext(HttpServletRequest request, @Nullable HttpServletResponse response, @Nullable 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, this.localeAttributeName, locale); WebUtils.setSessionAttribute(request, this.timeZoneAttributeName, timeZone); }
保存到 Session 中即可。大家可以看到,这种保存方式其实和我们前面演示的自己保存代码基本一致,殊途同归。
FixedLocaleResolver 有三个构造方法,无论调用哪一个,都会配置默认的 Locale:
public FixedLocaleResolver() { setDefaultLocale(Locale.getDefault()); } public FixedLocaleResolver(Locale locale) { setDefaultLocale(locale); } public FixedLocaleResolver(Locale locale, TimeZone timeZone) { setDefaultLocale(locale); setDefaultTimeZone(timeZone); }
要么自己传 Locale 进来,要么调用 Locale.getDefault() 方法获取默认的 Locale。
再来看 resolveLocale 方法:
@Override public Locale resolveLocale(HttpServletRequest request) { Locale locale = getDefaultLocale(); if (locale == null) { locale = Locale.getDefault(); } return locale; }
这个应该就不用解释了吧。
需要注意的是它的 setLocaleContext 方法,直接抛异常出来,也就意味着 Locale 在后期不能被修改。
@Override public void setLocaleContext( HttpServletRequest request, @Nullable HttpServletResponse response, @Nullable LocaleContext localeContext) { throw new UnsupportedOperationException("Cannot change fixed locale - use a different locale resolution strategy"); }
CookieLocaleResolver 和 SessionLocaleResolver 比较类似,只不过存储介质变成了 Cookie,其他都差不多,松哥就不再重复介绍了。
搜刮了一个语言简称表,分享给各位小伙伴:
语言 | 简称 |
---|---|
简体中文(中国) | zh_CN |
繁体中文(中国台湾) | zh_TW |
繁体中文(中国香港) | zh_HK |
英语(中国香港) | en_HK |
英语(美国) | en_US |
英语(英国) | en_GB |
英语(全球) | en_WW |
英语(加拿大) | en_CA |
英语(澳大利亚) | en_AU |
英语(爱尔兰) | en_IE |
英语(芬兰) | en_FI |
芬兰语(芬兰) | fi_FI |
英语(丹麦) | en_DK |
丹麦语(丹麦) | da_DK |
英语(以色列) | en_IL |
希伯来语(以色列) | he_IL |
英语(南非) | en_ZA |
英语(印度) | en_IN |
英语(挪威) | en_NO |
英语(新加坡) | en_SG |
英语(新西兰) | en_NZ |
英语(印度尼西亚) | en_ID |
英语(菲律宾) | en_PH |
英语(泰国) | en_TH |
英语(马来西亚) | en_MY |
英语(阿拉伯) | en_XA |
韩文(韩国) | ko_KR |
日语(日本) | ja_JP |
荷兰语(荷兰) | nl_NL |
荷兰语(比利时) | nl_BE |
葡萄牙语(葡萄牙) | pt_PT |
葡萄牙语(巴西) | pt_BR |
法语(法国) | fr_FR |
法语(卢森堡) | fr_LU |
法语(瑞士) | fr_CH |
法语(比利时) | fr_BE |
法语(加拿大) | fr_CA |
西班牙语(拉丁美洲) | es_LA |
西班牙语(西班牙) | es_ES |
西班牙语(阿根廷) | es_AR |
西班牙语(美国) | es_US |
西班牙语(墨西哥) | es_MX |
西班牙语(哥伦比亚) | es_CO |
西班牙语(波多黎各) | es_PR |
德语(德国) | de_DE |
德语(奥地利) | de_AT |
德语(瑞士) | de_CH |
俄语(俄罗斯) | ru_RU |
意大利语(意大利) | it_IT |
希腊语(希腊) | el_GR |
挪威语(挪威) | no_NO |
匈牙利语(匈牙利) | hu_HU |
土耳其语(土耳其) | tr_TR |
捷克语(捷克共和国) | cs_CZ |
斯洛文尼亚语 | sl_SL |
波兰语(波兰) | pl_PL |
瑞典语(瑞典) | sv_SE |
西班牙语(智利) | es_CL |
“如何配置SpringMVC国际化”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注亿速云网站,小编将为大家输出更多高质量的实用文章!
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。