SpringBoot项目国际化

目录

1. 创建国际化文件Resource Bundle

项目结构图:

国际化文件结构图:

在IntelliJ IDEA中创建国际化文件:

2. 国际化配置类InternationalConfig

代码:

@Configuration
public class InternationalConfig {

    @Value(value = "${spring.messages.basename}")
    private String basename;

    @Bean(name = "messageSource")
    public ResourceBundleMessageSource getMessageResource() {
        ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
        messageSource.setBasename(basename);
        return messageSource;
    }
}
复制代码

3. application.yml配置文件中配置国际化文件路径

spring:
  profiles:
    active: dev
  # 配置国际化文件路径
  messages:
    basename: i18n/messages
    
---

spring:
  profiles: dev
  
---

spring:
  profiles: test
  
---

spring:
  profiles: prod
  
复制代码

4. 国际化处理类MessageSourceHandler

代码:

@Component
public class MessageSourceHandler {

    @Autowired
    private HttpServletRequest request;

    @Autowired
    private MessageSource messageSource;

    public String getMessage(String messageKey) {
        String message = messageSource.getMessage(messageKey, null, RequestContextUtils.getLocale(request));
        return message;
    }
}
复制代码

注意:

  • 如果是根据Request请求的语言来决定国际化:
@Autowired
private HttpServletRequest request;

public String getMessage(String messageKey) {
    String message = messageSource.getMessage(messageKey, null, RequestContextUtils.getLocale(request));
    return message;
}
复制代码
  • 如果是根据应用部署的服务器系统来决定国际化:
public String getMessage(String messageKey) {
    String message = messageSource.getMessage(messageKey, null, LocaleContextHolder.getLocale());
    return message;
}
复制代码

5. 国际化使用

引入MessageSourceHandler类的对象messageSourceHandler,调用其messageSourceHandler.getMessage()方法即可。

@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

    // 引入国际化处理类
    @Autowired
    private MessageSourceHandler messageSourceHandler;

    private String handleException(Exception e, String code) {
        return handleException(e, code, null);
    }
    
    // 具体异常处理类 
    private String handleException(Exception e, String code, Object body) {
        String msgKey = e.getMessage();
        String msg = msgKey;
        try {
            msg = messageSourceHandler.getMessage(msgKey);
        } catch (Exception ex) {
            log.error(ex.getMessage(), ex);
        }
        if (StringUtils.isEmpty(msg)) {
            if (StringUtils.isEmpty(msgKey)) {
                msg = messageSourceHandler.getMessage(ErrorTypeEnum.INTERNAL_SERVER_ERROR.getMessage());
            } else {
                msg = msgKey;
            }
        }
        log.error("Return Error Message : " + msg);
        return msg;
    }

    // 请求错误异常处理 
    @ExceptionHandler(BadRequestException.class)
    public String handleBadRequest(BadRequestException e) {
        return handleException(e, e.getCode());
    }
    
    // 服务器内部异常处理
    @ExceptionHandler(InternalServerException.class)
    public String handleInternalServerError(InternalServerException e) {
        return handleException(e, e.getCode());
    }
    
    // 调用其他服务异常处理
    @ExceptionHandler(InvokeOtherServerException.class)
    public String handleInvokeOtherServerError(InvokeOtherServerException e) {
        return handleException(e, e.getCode(), e.getBody());
    }

    // DAO异常处理
    @ExceptionHandler(DaoException.class)
    public String handleDaoError(DaoException e) {
        return handleException(e, e.getCode());
    }
}
复制代码

FAQ:

1. 中文国际化出现乱码或\uXXXX的问题

如图:

乱码问题根源:

<1> 创建国际化文件,IDEA默认工程的初始默认编码是GBK,如下图:

<2> 当我添加了一些国际化内容时,此时意识到编码不是UTF-8,我修改了一下默认的工程编码和系统properties编码,改为UTF-8,如下图所示。

由于我是在GBK编码下加的中文国际化内容,后又把工程编码和properties编码改为了UTF-8,两边编码不一致,导致出现乱码。

\uXXXX问题根源:

\uXXXX是Unicode的转义字符,和\n,\r同属于转义字符,看一下IntelliJ官网对此说明,如下:

IntelliJ官网的文档地址:www.jetbrains.com/help/idea/2…

## 在properties文件中格式为\uXXXX的所有转义字符,在资源编译器中被显示为未转义的Unicode字符
All escaped characters in the *.properties files in the format \uXXXX, are displayed in the resource bundle editor as un-escaped unicode literals.

## 反之亦然,如果在资源编译器中输入非ASCII字符,则它将反映在底层的properties文件中作为相应的格式为\uXXXX的转义字符
Vice versa, if a non-ASCII character is entered in the resource bundle editor, it is reflected in the underlying *.properties file as a corresponding escaped character in the format \uXXXX.

##下面是举了个例子
For example, if the *.properties file contains a property value
Was ich nicht wei\u00df, macht mich nicht hei\u00df

then the resource bundle editor will show
Was ich nicht weiß, macht mich nicht heiß

## 资源编译器本身不做任何转换。若要在属性文件中正确解析转义序列,请在“设置/首选项”对话框的“文件编码页”中选择“透明本机到ascii转换”复选框。
Resource bundle editor itself does not perform any conversion. To have escape sequences properly resolved in properties files, select the check box Transparent native-to-ascii conversion in the File Encoding page of the Settings/Preferences dialog.

## 可以使用大写和小写十六进制符号(例如'\u00E3'与'\u00e3')对非ascii符号进行编码。大写默认使用。要使用小写,请将bin/idea.properties文件(安装IntelliJ的文件夹)中的'idea.native2ascii.lowercase'属性设置为true。
It is possible to encode non-ascii symbols using both upper- and lower-case hex symbols (e.g. '\u00E3' vs '\u00e3'). Upper case is used by default. To use lower case, set 'idea.native2ascii.lowercase' property in the bin/idea.properties file to true.

Refer to the section Tuning IntelliJ IDEA for details.
复制代码

继续跳转Tuning IntelliJ IDEA for details,见下截图:

总结:输入汉字(非ASCII码),在IntelliJ资源编译器中显示转义的Unicode码(\uXXXX(X大写)),勾上"Transparent native-to-ascii conversion",则在资源编译器中转换显示为汉字,其实际存储为转义的Unicode码。

解决方法:

有关编码的文章,可参考:www.ruanyifeng.com/blog/2007/1…

2. SpringBoot有没有自带的国际化资源配置类?

有,当然有。

SpringBoot提供了自动配置类MessageSourceAutoConfiguration,

@Configuration
@ConditionalOnMissingBean(value = MessageSource.class, search = SearchStrategy.CURRENT)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Conditional(ResourceBundleCondition.class)
@EnableConfigurationProperties
public class MessageSourceAutoConfiguration {

	private static final Resource[] NO_RESOURCES = {};

	@Bean
	@ConfigurationProperties(prefix = "spring.messages")
	public MessageSourceProperties messageSourceProperties() {
		return new MessageSourceProperties();
	}

    // 配置了MessageSource的Bean,并装配了上面MessageSourceProperties的Bean
	@Bean
	public MessageSource messageSource(MessageSourceProperties properties) {
		ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
		if (StringUtils.hasText(properties.getBasename())) {
			messageSource.setBasenames(StringUtils.commaDelimitedListToStringArray(
					StringUtils.trimAllWhitespace(properties.getBasename())));
		}
		if (properties.getEncoding() != null) {
			messageSource.setDefaultEncoding(properties.getEncoding().name());
		}
		messageSource.setFallbackToSystemLocale(properties.isFallbackToSystemLocale());
		Duration cacheDuration = properties.getCacheDuration();
		if (cacheDuration != null) {
			messageSource.setCacheMillis(cacheDuration.toMillis());
		}
		messageSource.setAlwaysUseMessageFormat(properties.isAlwaysUseMessageFormat());
		messageSource.setUseCodeAsDefaultMessage(properties.isUseCodeAsDefaultMessage());
		return messageSource;
	}
	
	...
}

// 国际化资源文件配置类Properties
public class MessageSourceProperties {

	/**
	 * Comma-separated list of basenames (essentially a fully-qualified classpath
	 * location), each following the ResourceBundle convention with relaxed support for
	 * slash based locations. If it doesn't contain a package qualifier (such as
	 * "org.mypackage"), it will be resolved from the classpath root.
	 */
	// 默认国际化资源文件名为messages,默认放在类路径下,在application.yml中不需要做任何国际化路径配置
	private String basename = "messages";

	/**
	 * Message bundles encoding.
	 */
	private Charset encoding = StandardCharsets.UTF_8;

	/**
	 * Loaded resource bundle files cache duration. When not set, bundles are cached
	 * forever. If a duration suffix is not specified, seconds will be used.
	 */
	@DurationUnit(ChronoUnit.SECONDS)
	private Duration cacheDuration;

	/**
	 * Whether to fall back to the system Locale if no files for a specific Locale have
	 * been found. if this is turned off, the only fallback will be the default file (e.g.
	 * "messages.properties" for basename "messages").
	 */
	private boolean fallbackToSystemLocale = true;

	/**
	 * Whether to always apply the MessageFormat rules, parsing even messages without
	 * arguments.
	 */
	private boolean alwaysUseMessageFormat = false;

	/**
	 * Whether to use the message code as the default message instead of throwing a
	 * "NoSuchMessageException". Recommended during development only.
	 */
	private boolean useCodeAsDefaultMessage = false;
	
	...
}
复制代码

如果创建自定义的国际化资源(Resource Bundle)文件,例如:i18n/messages,则需要在application.yml中配置该自定义国际化文件的路径。

如果在resources文件夹路径下直接创建messages国际化资源文件(名字必须为messages),则不需要在applicaiton.yml中配置国际化文件路径。

国际化处理类见上面 4. 国际化处理类MessageSourceHandler,国际化使用是一样的。

扩展学习

  1. 简书上一篇“SpringBoot - Web开发国际化”的文章:www.jianshu.com/p/01e0c7251…

  2. 阮老师一篇关于编码的文章:www.ruanyifeng.com/blog/2007/1…

回到目录

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值