什么是I8n

国际化(
I18n
)指的是设计和开发产品的过程,使得它们能够适应多种语言和文化环境,而不需要进行大量的代码更改。这通常涉及到创建一个基础版本的产品,然后通过配置和资源文件来添加对不同语言和地区的支持。

这样,当产品需要在新的地理区域或语言环境中使用时,只需要添加或更新相应的资源文件,而不需要修改产品本身的代码。

"
I18n
" 是 "
Internationalization
" 的缩写形式,之所以是 "
I18n
" 而不是 "
Int
" 或者其他缩写,是因为 "
Internationalization
" 这个词的第一个字母是 "
I
",最后一个字母是 "
n
",而在这两个字母之间有 18 个字符。因此,"
I18n
" 成为了一个流行的缩写方式,类似的还有 "
K8s
" 。


Spring Boot I18n

I8n
体现在
Java
后端比较常见的就是错误提示,而
SpringBoot
本身就提供了对I8n的支持。使用也非常简单,可以参考一下
官方文档



添加国际化资源文件

SpringBoot
默认会读取
classpath/resource
下的
messages
目录里的国际化资源文件,可以通过下面这个配置更改:

spring.messages.basename=messages

咱们先通过
Idea
创建国际化资源文件,方法如下:

Untitled
Untitled

创建完成后在
reources
目录下能看到这些国际化资源文件,图中为了演示,定义了咱们开发中比较常见的登录错误提示信息。

Untitled



创建工具类

创建工具类的目的是方便咱们获取国际化信息

public class I18nUtil {
 public static String getI18nMessage(String code) {
         try {
           // SpringBoot提供有一个MessageSource的默认实现
             MessageSource messageSource = SpringUtils.getBean("messageSource");
             // 从SpringBoot中获取请求上下文 语言
             Locale locale = LocaleContextHolder.getLocale();
             // 因为我项目只有中文和英文,所以判断一下除了指定中文,否则默认使用英文  
             // 这里对简体中文的定义,Locale[] SIMPLIFIED_CHINESE = {Locale.CHINESE, Locale.CHINA, Locale.SIMPLIFIED_CHINESE, Locale.PRC};
             if (Arrays.stream(LocaleUtils.SIMPLIFIED_CHINESE).toList().contains(locale)) {
                 locale = Locale.SIMPLIFIED_CHINESE;
             } else {
                locale = Locale.US;
             }
            return messageSource.getMessage(code, null, locale);
         } catch (Exception e) {
             log.error("获取国际化内容异常", e);
             // 如果获取国际化信息失败就返回原内容
             return code;
         }
     }
}



定义接口统一返回类

@Data
public class RestResult<Timplements Serializable {

    private static final long serialVersionUID = 1L;

    /** 状态标识码 */
    private String code;

    /** 描述信息 */
    private String message;

    /** 数据 */
    private T data;
    
    public String getMessage(){
     if (StrUtil.isNotEmpty(this.code)) {
       // 获取I8n
      return I18nUtil.getI18nMessage(this.code)
     }
     return this.message;
    }
    
    // 省略其他方法。。。
}



请求头添加语言编码

curl -H "Accept-Language: zh-CN" http://example.com


Spring Cloud 配置I8n

上面是一个简单的是
SpringBoot
项目配置I8n的流程,但是在
Spring Cloud
下通常会有多个服务(
SpringBoot
项目)。按照我们的习惯一般有一个公共项目(
common
),用于封装
POJO
和一些通用的工具类、常量类之类的功能,然后把这个项目打成
jar
包,其他业务服务都会依赖这个项目,达到功能复用的目的。通用我们也会将一些通用的错误信息(比如:用户Id不能为空)放到
common
项目里面,那么怎么把这些通用错误信息配置成
I8n
呢?

按照上面所说,我们把
common
项目打成
jar
包供其他业务服务使用,那么当其他服务引入
common
依赖以后,
common
就和业务服务同处于一个
Spring
容器,这时候上面的
I8n
配置就无法读取到
common
里面的
I8n
配置了(上面讲过了
SpringBoot
默认读取的是项目
classpath/resource
下的配置文件)。因此我们需要自定义一个
MessageSource
然后读取
common
下的
I8n
配置就可以了。

  1. 首先,我们在 common resource 下创建 I8n 配置文件,方法和上面一样,因为和业务服务同处一个 Spring 容器,所以我们需要将资源名称区分开,我们暂且就叫 common_messages , 如下图所示:
Untitled
Untitled
  1. 然后,我们配置一个自定义 MessageSource Bean ,并且读取我们设置的 common_messages
@Bean(name = "commonMessageSource")
public ResourceBundleMessageSource resourceBundleMessageSource() throws IOException {
    ResourceBundleMessageSource source = new ResourceBundleMessageSource();
    source.setDefaultEncoding(StandardCharsets.UTF_8.displayName());
    source.setBasename("common_messages");
    return source;
}
  1. 最后,调整 I8nUtil 逻辑,先读取业务服务自己的 I8n ,如果没有再从 common I8n 获取,如果还没有则返回原 code
public class I18nUtil {
 public static String getI18nMessage(String code) {
         try {
           // SpringBoot提供有一个MessageSource的默认实现
             MessageSource messageSource = SpringUtils.getBean("messageSource");
             // 从SpringBoot中获取请求上下文 语言
             Locale locale = LocaleContextHolder.getLocale();
             // 因为我项目只有中文和英文,所以判断一下除了指定中文,否则默认使用英文  
             // 这里对简体中文的定义,Locale[] SIMPLIFIED_CHINESE = {Locale.CHINESE, Locale.CHINA, Locale.SIMPLIFIED_CHINESE, Locale.PRC};
             if (Arrays.stream(LocaleUtils.SIMPLIFIED_CHINESE).toList().contains(locale)) {
                 locale = Locale.SIMPLIFIED_CHINESE;
             } else {
                locale = Locale.US;
             }
            try{
              // 先读取业务服务自己配置的I8n
             return messageSource.getMessage(code, null, locale);
            }catch (NoSuchMessageException e) {
                // 如果没有获取到,再从common里面读取I18n
              ResourceBundleMessageSource commonMessageSource = SpringUtils.getBean("commonMessageSource");
                return commonMessageSource.getMessage(code, null, locale);
            }
         } catch (Exception e) {
             log.error("获取国际化内容异常", e);
             // 如果获取国际化信息失败就返回原内容
             return code;
         }
     }
}

本文使用
markdown.com.cn
排版

标签: none

添加新评论