** 原创文章,请勿转载 **
在给spring boot 1.5.6 + thymeleaf 3进行国际化时,踩了一个坑(其实不止一个)。 现象:
看到了吧, 就是取值的key, 后面被加了_en_US 或 _zh_CN, 以及前后的问号。
先看下代码,首先两个资源文件:
messages_en_US.properties
page.content=this is a test string.
message_zh_CN.properties, 在eclipse里找开的,内容是: 这是一个测试字符串
page.content=\u8FD9\u662F\u4E00\u4E2A\u6D4B\u5B57\u7B26\u4E32\u3002
i18n.html:
<!DOCTYPE html SYSTEM "http://www.thymeleaf.org/dtd/xhtml1-strict-thymeleaf-4.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> </head> <body> <h1>spring boot, thymeleaf 3 国际化</h1> <hr> <a href="/i18n?lang=en_US">English</a> | <a href="/i18n?lang=zh_CN">中文</a> <br> <br> <br> <br> from thymeleaf engine: <span th:text="#{page.content}"></span> <br> <br> from controller model: <span th:text="${content}"></span> </body> </html>
其中, #{page.content} 来源于thymeleaf 3, ${content} 则由controller的Model返回,代码是hardcode, 返回的都是中文
controller:
@Controller public class I18nController { @Autowired private MessageSource messageSource; @GetMapping("/i18n") public String i18n(Model model) { String message = messageSource.getMessage("page.content", null, Locale.SIMPLIFIED_CHINESE); System.out.println("message=" + message); model.addAttribute("content", message); return "/i18n"; } }
从现象看, ${content}这个是没有问题的, 也就是controller里messageSource.getMessage() 是正常的,再也就是说,资源文件是成功加载的,可见,问题出在thymeleaf 3.
再看下thymeleaf 3的配置:
@Configuration public class ThymeleafConfig implements ApplicationContextAware { private static final String UTF8 = "UTF-8"; private ApplicationContext applicationContext; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } private SpringResourceTemplateResolver htmlTemplateResolver() { SpringResourceTemplateResolver templateResolver = new SpringResourceTemplateResolver(); templateResolver.setApplicationContext(this.applicationContext); templateResolver.setPrefix("classpath:/templates/"); templateResolver.setSuffix(".html"); templateResolver.setTemplateMode(TemplateMode.HTML); templateResolver.setCacheable(false); templateResolver.setCharacterEncoding("UTF-8"); return templateResolver; } @Autowired private MessageSource messageSource; // 如果显示 ??x_zh_CN??, 缺少spring-context-support private SpringTemplateEngine templateEngine(SpringResourceTemplateResolver templateResolver) { SpringTemplateEngine templateEngine = new SpringTemplateEngine(); templateEngine.setTemplateResolver(templateResolver); templateEngine.setEnableSpringELCompiler(false); return templateEngine; } @Bean public ViewResolver htmlViewResolver(SpringTemplateEngine templateEngine) { ThymeleafViewResolver resolver = new ThymeleafViewResolver(); resolver.setApplicationContext(applicationContext); resolver.setTemplateEngine(templateEngine(htmlTemplateResolver())); resolver.setCharacterEncoding("UTF-8"); return resolver; } }
** 临时补一句,国际化要spring-context-support包的支持。
web的配置:
@Configuration public class MvcConfig extends WebMvcConfigurerAdapter { @Bean public LocaleResolver localeResolver() { SessionLocaleResolver slr = new SessionLocaleResolver(); // 默认语言 slr.setDefaultLocale(Locale.SIMPLIFIED_CHINESE); return slr; } @Bean public LocaleChangeInterceptor localeChangeInterceptor() { LocaleChangeInterceptor lci = new LocaleChangeInterceptor(); // 参数名 lci.setParamName("lang"); return lci; } @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(localeChangeInterceptor()); } // 这个MessageSource可有可无,spring boot默认是有一个的。 // 如果没有自定义messageSource, 要有一个messages.properties文件。// 如果有这个定义, 就不需要messages.properites // @Bean // public ResourceBundleMessageSource messageSource() { // ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource(); // messageSource.setBasename("messages"); // return messageSource; // } }
查找原因, 在thymeleaf.org上看到这样一段:(在线文档3.1: Using th:text and externalizing text)
thymeleaf叫外部文本,无论是在外部的文件里,或者是在数据库,当然国际化使用的是外部文件。
从这段信息看, thymeleaf使用IMessageResolver接口来加载外部文本的,而且有一个标准的实现:
org.thymeleaf.messageresolver.StandardMessageResolver,所以加载.properties文件应该是和这些信息相关。
但 thymeleaf-spring4 里是怎么用的呢,看下代码吧:
org.thymeleaf.spring4.messageresolver.SpringMessageResolver:
private final StandardMessageResolver standardMessageResolver; private MessageSource messageSource; public SpringMessageResolver() { super(); this.standardMessageResolver = new StandardMessageResolver(); }
它就是使用 org.thymeleaf.messageresolver.StandardMessageResolver, 这样看来,我只要在模板引擎的设置里加上这个IMessageResolver的实现即可,新的代码是这样:
private SpringTemplateEngine templateEngine(SpringResourceTemplateResolver templateResolver) { SpringTemplateEngine templateEngine = new SpringTemplateEngine(); SpringMessageResolver messageResolver = new SpringMessageResolver(); // messageResolver.setMessageSource(messageSource); // 加入这三行,即为解决方案 templateEngine.setMessageResolver(messageResolver); // templateEngine.setTemplateResolver(templateResolver); templateEngine.setEnableSpringELCompiler(false); return templateEngine; }
最终呈现: