springboot中实现国际化的步骤及实现原理

2,765 阅读6分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第11天,点击查看活动详情

一、springboot的国际化

1-1、通过在application.properties配置spring.message.basename指定国际化资源文件

上篇文章主要介绍了springboot中,MessageSourceAutoConfiguration自动配置类的加载原理及使用方式。得知,如果我们要使用国际化需要在将配置文件放到resource中,并且文件名需要命名为messages。但是在实际项目中,我们一般不会这么做,一般都是在resource中创建i18n文件夹,然后里面放我们的国际化资源文件。

通过上篇文章对源码的解读,有个核心方法getMatchOutcome,如下

image.png 这个方法定义了从application.properties中获取配置的key为:spring.messages.basename。因此我们在资源文件中配置这个key指向我们的i18n,这样就可以找到国际化资源文件,只要找到了,那MessageSourceAutoConfiguration就可以加载了。

1-1-1、在resource中添加i18n文件夹,并且添加国际化配置文件

resource中三个配置文件的配置信息,如下 image.png

把resource下的资源文件删除,然后通过在application.properties中配置 spring.messages.basename=i18n.message就可以告诉MessageSourceAutoConfiguration中的@Conditional(ResourceBundleCondition.class)去哪里加载国际化配置文件了。

image.png

这样我们就完成了自定义指定国际化配置文件的处理。

1-1-2、小结

一旦匹配成功,自动配置类就会生效,就会帮我们配置一个meesageSource的Bean

image.png 这样就有两种方式都可以让国际化配置文件生效:

  • 1、将以messages命名的国际化资源文件放到resource根目录下(文件可以写为messages_zn_CN.properties/messages_en_US.properties)。
  • 2、在application.properties中通过配置spring.messages.bashname=i18n.message 告诉自动配置类的生效条件去resource/i18n下找message命名的文件(文件可以写为message_zn_CN.properties/message_en_US.properties)

两者需要注意的是,第一种命名必须为messages,第二种是可以随便自定义的,只要和spring.message.bashname配置一致就可以

实际项目中建议选择第二种,这样代码可读性比较强。

1-2、如何获得accept-language

一般情况下:
1、我们可以去去解析请求头中的accept-language
2、解析url参数中比如local=zn

而springboot中其实WebMvcAutoConfiguration 类也帮我配置了一个解析请求头中的accept-language 的localResolver

1-2-1、WebMvcAutoConfiguration中LocaleResolver的解析

LocaleResolver这个Bean就是帮助我们实现国际化的,首先可以看到这个Bean上加了一个@ConditionalOnMissingBean(name = DispatcherServlet.LOCALE_RESOLVER_BEAN_NAME)注解,而LOCALE_RESOLVER_BEAN_NAME=localeResolver因此只要我们自己创建了localeResolver的Bean,springboot中的localeResolver就不生效了。 image.png

1-2-1-1、判断走配置文件还是请求头获取Local

首先进入getLocaleResolver这个方法 image.png

这样就进入了WebProperties类,可以看到这个类的配置信息为spring.web。同时 getLocaleResolver这个方法中的localeResolver最终被LocaleResolver.ACCEPT_HEADER;赋值。 image.png

再次进入ACCEPT_HEADER,可以看到LocaleResolver是一个枚举类型,里面有两个值,分别为:
FIXED:使用配置当中的Local配置
ACCEPT_HEADER:使用请求头中的配置

image.png

如果走配置文件,那么就会进入if里面

if (this.webProperties.getLocaleResolver() == WebProperties.LocaleResolver.FIXED) {
   return new FixedLocaleResolver(this.webProperties.getLocale());
}

再次进入getLocale()这个方法,因此我们要通过配置文件来设置的话,就需要在application.properties中配置spring.web.locale=zn_CH,如下: image.png

通过以上的方式就直接吧国际化语言设死了,因此我们要是没有设置,那么就会默认走请求头当中的ACCEPT_HEADER来获取国际化语言的类型了。

1-2-1-2、最终localeResolver方法含义如下

@Override
@Bean
@ConditionalOnMissingBean(name = DispatcherServlet.LOCALE_RESOLVER_BEAN_NAME)
public LocaleResolver localeResolver() {
   // 当配置spring.mvc.locale-resolver=fiexd
   if (this.webProperties.getLocaleResolver() == WebProperties.LocaleResolver.FIXED) {
     // 就会使用配置文件中的本地化语言:spring.mvc.locale=en_US 就可以设死本地化语言
      return new FixedLocaleResolver(this.webProperties.getLocale());
   }
   // 默认就是使用AcceptHeaderLocaleResolver 作为本地化解析器
   AcceptHeaderLocaleResolver localeResolver = new AcceptHeaderLocaleResolver();
   //spring.mvc.locale=en_US 作为默认的本地化语言
   localeResolver.setDefaultLocale(this.webProperties.getLocale());
   return localeResolver;
}

通过上面代码分析,可以得知要在本地设死语言就需要在application.properties中设置

spring.web.locale-resolver=fixed
spring.web.locale=zh_CN

这样就设置本地语言为zh_CN了。

1-2-1-2-1、AcceptHeaderLocaleResolver

image.png 其含义为:

public Locale resolveLocale(HttpServletRequest request) {
   // 当Accept-Languag为null 才会使用使用配置文件中设置的locale:spring.mvc.locale
    Locale defaultLocale = this.getDefaultLocale();
    if (defaultLocale != null && request.getHeader("Accept-Language") == null) {
        return defaultLocale;
    } else {
        // 就是使用request.getLocale();
        Locale requestLocale = request.getLocale();
        List<Locale> supportedLocales = this.getSupportedLocales();
        if (!supportedLocales.isEmpty() && !supportedLocales.contains(requestLocale)) {
            Locale supportedLocale = this.findSupportedLocale(request, supportedLocales);
            if (supportedLocale != null) {
                return supportedLocale;
            } else {
                return defaultLocale != null ? defaultLocale : requestLocale;
            }
        } else {
            return requestLocale;
        }
    }
}

1-3、使用messageSource.getMessage获取国际化资源文件

首先通过@Autowrite将MessageSource注入到Handler中,然通过messageSource.getMessage获得国际化里面的值。 image.png

1-3-1、messageSource.getMessage()方法详解

可以看到getMessage()有三个方法,一般情况下,使用第二种。 image.png

//code代表资源文件里面的key,args如果资源文件中的值可传参,这个地方可以传参,locale就是当前语言
String getMessage(String code, @Nullable Object[] args, Locale locale) throws NoSuchMessageException;

1-3-2、传入参数的测试

因为要使用ACCEPT_HEADER获得语言,因此这两行配置一定要注释掉。 image.png

1-3-2-1、在配置文件中配置传入的参数位

image.png

1-3-2-1、在getMessage()中传入参数

image.png

1-3-2-2、访问接口测试

可以看到参数已经成功传入 image.png

1-3-2-3、将浏览器切换为英文

image.png

1-3-3、使用工具类获取国际化参数值

一般情况下,我们一个项目会创建一个公共帮助类,用来获取国际化的配置,要不还得每个Controller进行单独处理,太过繁琐。

package com.jony.utils;

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


@Component
public class i18nMessageUtils {

    private static MessageSource messageSource;

    public i18nMessageUtils(MessageSource messageSource) {
        i18nMessageUtils.messageSource = messageSource;
    }

    /**
     * 获取单个国际化翻译值
     */
    public static String get(String msgKey,String[] strArr) {
        try {
            return messageSource.getMessage(msgKey, strArr, LocaleContextHolder.getLocale());
        } catch (Exception e) {
            return msgKey;
        }
    }
}

这样在使用的时候就可以直接进行获取,如下

image.png

1-4、随意切换本地语言,进行缓存

刚刚我们通过切换浏览器语言来实现国际化的处理,但是这样太麻烦了,还需要重启浏览器,而且一些普通用户想切换语言,对于浏览器的设置,可能还不太了解,因此一般网站系统,会有一个切换语言的功能,来实现国际化的操作。

然而自动配置类中的localeResovler它只会从accept-language中解析,因此我们需要覆盖原有localeResolver

1-4-1、使用cookie设置本地语言

首先在我们自己写的自动配置类中,添加localeResolver方法,这样springboot的localeResolver方法就不起作用了。

同时使用拦截器,对LocaleChangeInterceptor进行拦截,这样就可以将传入的国际化标签存入cookie中。

image.png

1-4-1-1、LocaleChangeInterceptor拦截器的处理

这个方法就会获取url传入的参数locale获得国际化标记,然后放入到cookie中。 image.png

1-4-2、测试

首次访问链接加上国际,就会将国际化locale的值存入cookie中,如下:

image.png

这样后面即使我们在URL未添加locale,也会自动从cookie中获取,得到国际化的值

image.png