• spring boot + thymeleaf 3 国际化


    ** 原创文章,请勿转载 **

    在给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=u8FD9u662Fu4E00u4E2Au6D4Bu5B57u7B26u4E32u3002

    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;
    }

    最终呈现:

  • 相关阅读:
    Go组件学习——Web框架Gin
    一次线上接口超时的排查过程
    Go组件学习——手写连接池并没有那么简单
    终于跑通分布式事务框架tcc-transaction的示例项目
    Go语言学习——如何实现一个过滤器
    Go组件学习——database/sql数据库连接池你用对了吗
    我们这一年 | 诗人呀
    NodeMan架构
    NodeMan介绍
    微信小程序录音实现
  • 原文地址:https://www.cnblogs.com/bear129/p/7803457.html
Copyright © 2020-2023  润新知