Springboot国际化信息(i18n)解析

国际化信息理解

国际化信息也称为本地化信息 。 Java 通过 java.util.Locale 类来表示本地化对象,它通过 “语言类型” 和 “国家/地区” 来创建一个确定的本地化对象 。举个例子吧,比如在发送一个具体的请求的时候,在header中设置一个键值对:"Accept-Language":"zh",通过Accept-Language对应值,服务器就可以决定使用哪一个区域的语言,找到相应的资源文件,格式化处理,然后返回给客户端。

MessageSource

Spring 定义了 MessageSource 接口,用于访问国际化信息。

  • getMessage(String code, Object[] args, String defaultMessage, Locale locale)
  • getMessage(String code, Object[] args, Locale locale)
  • getMessage(MessageSourceResolvable resolvable, Locale locale)

 

 MessageSourceAutoConfiguration

 springboot提供了国际化信息自动配置类,配置类中注册了ResourceBundleMessageSource实现类。

 1 @Configuration
 2 @ConditionalOnMissingBean(value = MessageSource.class, search = SearchStrategy.CURRENT)
 3 @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
 4 @Conditional(ResourceBundleCondition.class)
 5 @EnableConfigurationProperties
 6 public class MessageSourceAutoConfiguration {
 7 
 8     private static final Resource[] NO_RESOURCES = {};
 9 
10     @Bean
11     @ConfigurationProperties(prefix = "spring.messages")
12     public MessageSourceProperties messageSourceProperties() {
13         return new MessageSourceProperties();
14     }
15 
16     @Bean
17     public MessageSource messageSource(MessageSourceProperties properties) {
18         ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
19         if (StringUtils.hasText(properties.getBasename())) {
20             messageSource.setBasenames(StringUtils.commaDelimitedListToStringArray(
21                     StringUtils.trimAllWhitespace(properties.getBasename())));
22         }
23         if (properties.getEncoding() != null) {
24             messageSource.setDefaultEncoding(properties.getEncoding().name());
25         }
26         messageSource.setFallbackToSystemLocale(properties.isFallbackToSystemLocale());
27         Duration cacheDuration = properties.getCacheDuration();
28         if (cacheDuration != null) {
29             messageSource.setCacheMillis(cacheDuration.toMillis());
30         }
31         messageSource.setAlwaysUseMessageFormat(properties.isAlwaysUseMessageFormat());
32         messageSource.setUseCodeAsDefaultMessage(properties.isUseCodeAsDefaultMessage());
33         return messageSource;
34     }
35 
36     protected static class ResourceBundleCondition extends SpringBootCondition {
37 
38         private static ConcurrentReferenceHashMap<String, ConditionOutcome> cache = new ConcurrentReferenceHashMap<>();
39 
40         @Override
41         public ConditionOutcome getMatchOutcome(ConditionContext context,
42                 AnnotatedTypeMetadata metadata) {
43             String basename = context.getEnvironment()
44                     .getProperty("spring.messages.basename", "messages");
45             ConditionOutcome outcome = cache.get(basename);
46             if (outcome == null) {
47                 outcome = getMatchOutcomeForBasename(context, basename);
48                 cache.put(basename, outcome);
49             }
50             return outcome;
51         }
52 
53         private ConditionOutcome getMatchOutcomeForBasename(ConditionContext context,
54                 String basename) {
55             ConditionMessage.Builder message = ConditionMessage
56                     .forCondition("ResourceBundle");
57             for (String name : StringUtils.commaDelimitedListToStringArray(
58                     StringUtils.trimAllWhitespace(basename))) {
59                 for (Resource resource : getResources(context.getClassLoader(), name)) {
60                     if (resource.exists()) {
61                         return ConditionOutcome
62                                 .match(message.found("bundle").items(resource));
63                     }
64                 }
65             }
66             return ConditionOutcome.noMatch(
67                     message.didNotFind("bundle with basename " + basename).atAll());
68         }
69 
70         private Resource[] getResources(ClassLoader classLoader, String name) {
71             String target = name.replace('.', '/');
72             try {
73                 return new PathMatchingResourcePatternResolver(classLoader)
74                         .getResources("classpath*:" + target + ".properties");
75             }
76             catch (Exception ex) {
77                 return NO_RESOURCES;
78             }
79         }
80 
81     }
82 
83 }
View Code

首先MessageSource配置生效依靠一个ResourceBundleCondition条件,从环境变量中读取spring.messages.basename对应的值,默认值是messages,这个值就是MessageSource对应的资源文件名称,资源文件扩展名是.properties,然后通过PathMatchingResourcePatternResolver从“classpath*:”目录下读取对应的资源文件,如果能正常读取到资源文件,则加载配置类。

 

 springmvc自动装配配置类,注册了一个RequestContextFilter过滤器。

 每一次请求,LocaleContextHolder都会保存当前请求的本地化信息。

 通过MessageSourceAccessor根据code获取具体信息时,如果默认配置的本地化对象为空,则通过LocaleContextHolder获取。

 上图的messageSource是应用程序上下文对象(本文创建的是GenericWebApplicationContext实例),该messageSource对象会调用ResourceBundleMessageSource实例获取具体信息。

ValidationAutoConfiguration

参数校验hibernate-validator是通过这个自动装配加载进来的。

 1 @Configuration
 2 @ConditionalOnClass(ExecutableValidator.class)
 3 @ConditionalOnResource(resources = "classpath:META-INF/services/javax.validation.spi.ValidationProvider")
 4 @Import(PrimaryDefaultValidatorPostProcessor.class)
 5 public class ValidationAutoConfiguration {
 6 
 7     @Bean
 8     @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
 9     @ConditionalOnMissingBean(Validator.class)
10     public static LocalValidatorFactoryBean defaultValidator() {
11         LocalValidatorFactoryBean factoryBean = new LocalValidatorFactoryBean();
12         MessageInterpolatorFactory interpolatorFactory = new MessageInterpolatorFactory();
13         factoryBean.setMessageInterpolator(interpolatorFactory.getObject());
14         return factoryBean;
15     }
16 
17     @Bean
18     @ConditionalOnMissingBean
19     public static MethodValidationPostProcessor methodValidationPostProcessor(
20             Environment environment, @Lazy Validator validator) {
21         MethodValidationPostProcessor processor = new MethodValidationPostProcessor();
22         boolean proxyTargetClass = environment
23                 .getProperty("spring.aop.proxy-target-class", Boolean.class, true);
24         processor.setProxyTargetClass(proxyTargetClass);
25         processor.setValidator(validator);
26         return processor;
27     }
28 
29 }
View Code

MethodValidationPostProcessor这个后置处理处理方法里单个参数校验的注解(JSR和Hibernate validator的校验只能对Object的属性(也就是Bean的域)进行校验,不能对单个的参数进行校验。)。

LocalValidatorFactoryBean实现了javax.validation.ValidatorFactory和javax.validation.Validator这两个接口,以及Spring的org.springframework.validation.Validator接口,你可以将这些接口当中的任意一个注入到需要调用验证逻辑的Bean里。

 默认情况下,LocalValidatorFactoryBean创建的validator使用PlatformResourceBundleLocator获取资源的绑定关系,获取的资源名称是:ValidationMessages

 用户自定义的校验信息放在项目classpath目录下。

另外hibernate-validator还会加载默认的校验资源文件,名称是:org.hibernate.validator.ValidationMessages。可以看到,默认的校验资源捆绑文件包含了不同区域的信息的配置。

通过LocalValidatorFactoryBean获取的validator是如何根据不同的地区加载不同校验资源文件呢?hibernate-validator暴露了一个消息插补器(MessageInterpolator),spring正是重新代理这个消息插补器。

 通过LocaleContextMessageInterpolator源码,可以看到最终还是通过LocaleContextHolder获取当前时区信息。

 

是否可以自定义国际化校验的资源信息呢?当然是肯定的,我们只需要重写LocalValidatorFactoryBean类型bean的创建过程,通过setValidationMessageSource方法指定自定义的资源信息。

MessageSource测试

基础测试

建立Resouce bundle messages

编写message source测试方法,从request中获取当前Locale值

 编写测试类,指定当前请求的Locale值或者设置请求头的header值:Accept-Language:zh

根据测试类中请求的Locale值不同,获取到的文本也不同。

格式化测试

建立Resouce bundle messages

编写message source测试方法,从request中获取当前Locale值

 编写测试类,指定当前请求的Locale值或者设置请求头的header值:Accept-Language:zh

 

根据测试类中请求的Locale值不同,获取到的格式化的文本也不同。

静态message source测试

动态注册message(可区分Locale),可用于自定义message source。

编写测试的方法,通过MessageSourceAccessor访问。

 编写测试类,获取自定义message source中的信息。

根据测试类中请求的Locale值不同,获取到的文本也不同。

 

转载于:https://www.cnblogs.com/hujunzheng/p/11037577.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Spring Boot 国际化i18n)是指在应用程序中支持多种语言和文化的能力。通过使用 Spring Boot 的 i18n 功能,可以轻松地将应用程序本地化为不同的语言和文化,以便更好地满足不同用户的需求。在 Spring Boot 中,可以使用 MessageSource 接口和 ResourceBundle 类来实现国际化。通过在应用程序中使用这些类,可以轻松地将应用程序的文本和消息本地化为不同的语言和文化。 ### 回答2: Spring Boot 是一种基于 Spring 框架的快速应用程序开发工具,可以快速创建基本的 Spring 应用程序并自动引入所需的依赖项。在 Spring Boot 应用程序中,国际化i18n)是非常重要的功能之一,因为它可以帮助应用程序更好地支持不同国家和地区的用户。 国际化是指将应用程序的用户界面以及其他文字内容翻译成不同语言,以便支持不同地区的用户。在 Spring Boot 中,国际化可以通过使用 Spring 的 MessageSource 接口来实现。MessageSource 是一个用于加载和解析国际化资源文件的接口,并用于将这些资源文件中的消息文本转换成应用程序可以使用的文本。国际化资源文件通常以属性文件的形式存在,其中包含被翻译的消息文本。 在 Spring Boot 中,可以使用 @EnableAutoConfiguration 注解自动配置 MessageSource,并且Spring Boot 的默认消息源加载器将查找以下资源文件:messages.properties、messages_en.properties、messages_zh.properties等。这些文件必须在 classpath 路径下的某个位置可以找到。 在应用程序中使用国际化消息时,需要在 bean 中注入 MessageSource,并使用它来读取需要翻译的消息文本。例如,可以使用以下代码获取“hello”的国际化文本: @Autowired private MessageSource messageSource; String helloText = messageSource.getMessage("hello", null, LocaleContextHolder.getLocale()); 这个代码片段将使用 MessageSource 来查找可以匹配“hello”关键字的消息文本,并返回一个形如“你好”的字符串。LocaleContextHolder.getLocale() 方法将返回当前用户的语言环境,以便实现语言的不同切换。 总之,Spring Boot 的国际化(i18n)功能非常强大,并且很容易实现。通过使用 MessageSource 接口和属性文件,应用程序可以支持多种语言环境,为不同国家和地区的用户提供更好的体验。 ### 回答3: Spring Boot 是一款使用 Spring 框架的 Java 开发框架,它在优化 Spring 框架的同时,也非常注重国际化支持。在 Spring Boot 中,国际化i18n)是一项非常重要的功能,它允许开发人员在应用程序中使用多种语言,以满足不同地区用户的需求。 在 Spring Boot 中实现国际化功能,可以通过配置文件(Properties)或 Yaml 文件来完成。在配置文件中,可以定义键值对的形式来保存不同语言版本的文本信息,如下所示: ```properties hello.world = Hello World! ``` 在应用程序中,可以通过 Spring Boot 的 MessageSource 解析器来获取配置文件中的文本信息,如下所示: ```java @Autowired private MessageSource messageSource; public String getHelloWorldMessage(){ return messageSource.getMessage("hello.world", null, Locale.getDefault()); } ``` 在上面的代码中,messageSource.getMessage 方法中的三个参数分别代表需要获取的文本信息的键、参数和语言环境。Locale.getDefault() 方法会根据当前用户的语言环境自动选择合适的语言版本。 除了使用配置文件之外,还可以使用数据库或注解来进行国际化。Spring Boot 支持使用 Thymeleaf 模板引擎进行注解国际化。可以在模板中使用 @{...} 语法将文本信息注入到 HTML 标签中,如下所示: ```html <h1 th:text="#{welcome.message}">Welcome to our site!</h1> ``` 在上面的代码中,#{welcome.message} 表示要注入的文本信息的键。 总之,Spring Boot 的国际化支持非常丰富,开发人员可以根据具体需求选择不同的方式来实现国际化功能。这将大大提升应用程序的跨地区使用能力,也为开发人员提供了更加便捷和灵活的开发体验。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值