SpringBoot国际化

时间:2023-02-03 14:55:00


软件的国际化

软件的国际化:软件开发时,要使它能同时应对世界不同地区和国家的访问,并针对不同地区和国家的访问,提供相应的、符合来访者阅读习惯的页面或数据。国际化internationalization,在i和n之间有 18 个字母,所以叫i18n。

MessageSource组件

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

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

getMessage(String code, Object[] args, Locale locale)

getMessage(MessageSourceResolvable resolvable, Locale locale)

属性

说明

code

国际化资源中的属性名

args

传递格式化串占位符所用的运行期参数,当在资源找不到对应属性名时,会返回defaultMessage参数所指定的默认信息

locale

表示本地化对象

resolvable

封装了属性名、参数数组以及默认信息的大类

其内部实现(MessageSource 是如何从 properties 文件中读取国际化的值的?),大致看了一下源码并进行了调试,最终了解到它是将 properties文件中的msgKey、value都load到了一个lookup的HashMap中(第一次加载时会缓存起来,后面直接走的缓存,有兴趣的同学可以在 PropertyResourceBundle.java:157 中寻找答案),得到 msgKey -> value,外层还有一个 locale 做索引,即 locale -> (msgKey -> value),最后是通过 locale + msgKey 得到相应的 value 。

最终,一个 properties 文件对应于一个 PropertyResourceBundle 对象。

Spring区域信息解析器

spring 支持获取请求头Accept-Language区域信息解析器。

SpringBoot国际化

不需重写。只需要Http请求头中带有Accept-Language就可以实现国际化语言支持。

其他文章多次提到自定义编写此部分,大家一定要避免此坑。

关于Accept-Language如下

Accept-Language

Accept-Language:表示浏览器所支持的语言。

当我们在开发国际化的网站时,后端接口的信息需要根据用户所使用的语言返回对应的内容。作为后端我需要前端在请求头的Accept-Language属性声明需要返回的语言。

格式

Accept-Language: lange-range[weight]

示例

  • Accept-Language: zh-cn,zh;q=0.5

意思: 支持的语言分别是简体中文和中文,优先支持简体中文。

zh-cn:表示简体中文,zh:表示中文(包括简体中文,繁体中文)

  • Accept-Language: zh-CN,zh;q=0.9,en;q=0.8

意思:优先支持中文,如果没有中文则支持英文。

q:表示他之前语言的权重, 0 <= q <= 1

  • Accept-Language: zh-cn;q=0.8,en-US;q=0.9

意思:优先支持英文,如果没有英文则支持中文。

  • Accept-Language: zh-cn;q=0.8,en-US;q=0.6

意思:优先支持中文,如果没有中文则支持英文。

  • **Accept-Language: ***

意思: 支持所有语言

配置国际化资源文件

在application.yml中指定资源文件配置

spring: 
  messages:
    basename: i18n/messages
    encoding: UTF-8

然后在resource下创建i18n目录,然后在其下创建文件

messages.properties不带后缀为默认语言资源

可以按照一定规则分类编写,后期会很多信息。

# 参数校验信息
validation.UserVo.NAME_NOT_EMPTY=姓名不能为空
validation.UserVo.AGE_NOT_EMPTY=年龄不能为空

# 异常信息
exception.userBizException.10000 = 用户保存异常

# 断言校验信息
assert.userService.CHECK_ITEM_KEY = 姓名[{0}]已经存在,请勿重复创建

支持占位符参数。

国际化工具类

import org.springframework.context.MessageSource;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.stereotype.Component;

/**
 * 国际化工具类
 * @Author : xiayi
 * @Date: 2023-01-31 14:59
 */
@Component
public class I18nUtil {
    public static MessageSource messageSource;

    public I18nUtil(MessageSource messageSource) {
        // spring的bean注入
        I18nUtil.messageSource = messageSource;
    }

    /**
     * 获取国际化翻译值
     */
    public static String get(String msgKey) {
        return messageSource.getMessage(msgKey, null, LocaleContextHolder.getLocale());
    }

    /**
     * 获取国际化翻译值(包括占位符)
     */
    public static String get(String msgKey, Object... args) {
        return messageSource.getMessage(msgKey, args, LocaleContextHolder.getLocale());
    }
}

使用方式:

I18nUtil.get("assert.userService.CHECK_ITEM_KEY",user.getName())

Validation国际化

Spring Boot Validation支持JSR-380(aka. Bean Validation 2.0,part of Jakarta EE and JavaSE)注解,可通过验证注解的message属性设置验证错误提示信息,且每个验证注解都有默认的message配置,例如@NotEmpty的message属性值设置如下图:

    /**
     * 名称
     */
    @NotEmpty(message = "{validation.DictSaveVO.name.NAME_NOT_EMPTY}")
    private String name;

注意:

默认的message = "{...}"的形式即指定国际化属性的名称,后续会根据语言环境替换为对应的值,而这些国际化属性的定义可参见hibernate-validator中的org.hibernate.validator.ValidationMessages.properties等一些列国际化属性定义:

SpringBoot国际化

Spring Boot Validation起初其并不直接支持读取Spring Boot自身的国际化配置(通过spring.messages进行配置),而是需要在resources/ValidationMessages.properties中进行配置的国际化属性才会生效,而后续在Spring Boot 2.6+版本才支持Validation与Spring Boot自身的国际化配置相结合。接下来结合Spring Boot 2.5和2.6版本分别介绍下Spring Boot Validation如何集成自定义国际化验证提示信息。

在Spring Boot 2.5.x版本中,Spring Boot Validation默认只支持读取resources/ValidationMessages.properties系列文件的中的国际化属性,且中文需要进行ASCII转码才可正确显示而就算Spring Boot应用亦声明了自身的国际化配置,但是Spring Boot Validation框架是读取不到的,而想要Spring Boot Validation框架和Spring Boot自身使用同样的国际化配置,则可通过如下方式进行配置:

import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.validation.Validator;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * Spring Web验证器自定义国际化文件配置<br/>
 * 注:适用于Spring Boot 2.5.*
 *
 */
@Configuration
public class WebValidationConfig implements WebMvcConfigurer {

    /**
     * 国际化消息源
     */
    private MessageSource messageSource;

    public WebValidationConfig(MessageSource messageSource) {
        //注入Spring Boot国际化消息源(需通过spring.messages明确指定)
        this.messageSource = messageSource;
    }

    /**
     * 使用自定义LocalValidatorFactoryBean,
     * 设置Spring国际化消息源
     */
    @Bean
    @Override
    public Validator getValidator() {
        LocalValidatorFactoryBean bean = new LocalValidatorFactoryBean();
        //仅兼容Spring Boot spring.messages设置的国际化文件和原hibernate-validator的国际化文件
        //不支持resource/ValidationMessages.properties系列
        bean.setValidationMessageSource(this.messageSource);
        return bean;
    }
}

Spring Boot 2.6.x版本之后已支持验证注解message属性引用Spring Boot自身国际化配置,无需上面自定义配置。

SpringBoot2.7.6已设置ApplicationContext实现MessageSource

SpringBoot国际化

避坑:

使用原始validator校验,不要自定义注解来实现拦截,否则无法国际化。

参考:

https://blog.csdn.net/u013565163/article/details/104262975

https://blog.csdn.net/GAOXINXINGgaoxinxing/article/details/92642470

https://blog.csdn.net/haihui_yang/article/details/83987839

https://blog.csdn.net/qq_42315935/article/details/125337214

https://blog.csdn.net/luo15242208310/article/details/124897389