springboot MessageSource语言国际化

本文详细介绍了使用SpringBoot方式配置国际化资源文件(messages.properties和application.yml)以及手写IO流方式处理语言文件,包括语言文件结构、配置方法和使用MessageSource接口实现国际化功能。
摘要由CSDN通过智能技术生成

本文两种方式,一种是springboot方式,另一种是自己写的IO流读取文件形式,话不多说直接入题


《springboot方式》 


1:resources下放各个语言文件,直接放resources下都行,我是新建了一个message文件夹,放里面了,然后IDEA会自动识别成语言文件,会多一个虚拟文件夹Resource Bundle 'messages',这个不用自己建。语言文件命名规则是:messages开头,后面是具体语言类型,我现在有中文简体(zh_CN),中文繁体(zh_TW),还有英文(es_US)

messages.properties文件是默认语言文件,找不到对应语言文件或者指定语言文件里面找不到对应语言配置的时候,会来这个文件取,这个文件一定要有,注意,这个是必须的


2:application.yml文件配置


 3:在各个语言文件里面加对应语言配置,中文为避免出现乱码,所以统一转成unicode(万国码)进行配置


  4:使用

先引入MessageSource,这个是spring的接口


  MessageSource接口有三个方法,参数不一样,我们选第一个

code: 语言文件里面配置的key

args: 用来替换查询结果里面的值

defaultMessage: 找不到的时候默认值

locale: 语言地区

 第一个和第二个区别在于默认值,选择第二个方法,在语言文件里面找不到对应key的时候会抛出异常,而第一个方法则会返回你给出的默认值。

为了方便使用,最好对MessageSource进行进一步封装

还有人有疑问,语言文件命名messages开头,后面是指定规则的zh_CN,en_US这种吗,我尝试了一下,可以自己随便起名的,但一定要messages开头,比如我改语言文件名messages_zh_TW.properties 改成 messages_ab.properties,只要后面new Locale的时候传ab就可以了,具体可以看messgaeSource.getMessage的源码









《自己手写IO流方式》 但也是基于spring实现


 项目结构如下,语言文件不是必须放在resources下,放磁盘也行,文件命名也不需要messages开头,随便命令都行,叫 aaa.txt 都行,反正是IO流读取,没有这么多限制


MessageLanguageEnum类

public enum MessageLanguageEnum {
    // 中文简体
    SIMPLE_ZH("zh_CN"),
    // 中文繁体
    COMPLEX_ZH("zh_TW"),
    // 英文
    ENGLISH_US("en_US"),
    ;

    private String code;

    MessageLanguageEnum(String code) {
        this.code = code;
    }

    public String code() {
        return this.code;
    }
}

 MessageSourceConfig类

import com.example.demo.demos.constant.MessageLanguageEnum;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.concurrent.*;

@Configuration
public class MessageSourceConfig {

    private final static Logger logger = LoggerFactory.getLogger(MessageSourceConfig.class);

    // 语言文件路径
    private final static Map<String, String> messageFilePathMap = new HashMap<String, String>(){{
        put(MessageLanguageEnum.SIMPLE_ZH.code(), "message/messages_zh_CN.properties");
        put(MessageLanguageEnum.COMPLEX_ZH.code(), "message/messages_zh_TW.properties");
        put(MessageLanguageEnum.ENGLISH_US.code(), "message/messages_en_US.properties");
    }};

    @Bean
    public Map<String, Map<String, String>> messageSourceMap() {
        if (Objects.nonNull(messageFilePathMap) && messageFilePathMap.keySet().size() > 0) {
            // 开启线程池进行多线程读取
            ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(3, 5,
                    3000, TimeUnit.MILLISECONDS,
                    new LinkedBlockingQueue<>(10),
                    (r) -> new Thread(r),
                    new ThreadPoolExecutor.CallerRunsPolicy());
            Map<String, Map<String, String>> messageSourceMap = new HashMap<>();
            try {
                Map<String, FutureTask<Map<String, String>>> futureTaskMap = new HashMap<>();
                for (String languageKey : messageFilePathMap.keySet()) {
                    FutureTask<Map<String, String>> futureTask = this.createFutureTask(messageFilePathMap.get(languageKey));
                    threadPoolExecutor.submit(futureTask);
                    futureTaskMap.put(languageKey, futureTask);
                }
                for (String languageKey : futureTaskMap.keySet()) {
                    messageSourceMap.put(languageKey, futureTaskMap.get(languageKey).get());
                }
                return messageSourceMap;
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            } catch (ExecutionException e) {
                throw new RuntimeException(e);
            } finally {
                threadPoolExecutor.shutdown();
            }
        }
        return Collections.EMPTY_MAP;
    }

    /**
     * 创建多线程任务
     * @param filePath 语言文件路径
     * @return
     */
    private FutureTask<Map<String, String>> createFutureTask(String filePath) {
        return new FutureTask(() -> this.readFileToMap(filePath));
    }

    /**
     * IO流读取文件
     * @param filePath 语言文件路径
     * @return
     */
    private Map<String, String> readFileToMap(String filePath) {
        if (StringUtils.isBlank(filePath)) {
            return Collections.EMPTY_MAP;
        }
        BufferedInputStream bufferedInputStream = null;
        // ClassLoader.getSystemResourceAsStream 是读取resources文件夹下的文件,如果不在resources下,直接new InputStream 参数传路径
        try (InputStream inputStream = ClassLoader.getSystemResourceAsStream(filePath)) {
            if (Objects.isNull(inputStream)) {
                return Collections.EMPTY_MAP;
            }
            bufferedInputStream = new BufferedInputStream(inputStream);
            Properties properties = new Properties();
            properties.load(bufferedInputStream);
            Map<String, String> messageSourceMap = new HashMap<>();
            for (String key : properties.stringPropertyNames()) {
                Object value = properties.get(key);
                if (Objects.nonNull(value)) {
                    messageSourceMap.put(key, value.toString());
                }
            }
            return messageSourceMap;
        } catch (IOException e) {
            logger.error(new StringBuffer("读取文件【").append(filePath).append("】IO异常").toString(), e);
            throw new RuntimeException(e);
        } finally {
            if (Objects.nonNull(bufferedInputStream)) {
                try {
                    bufferedInputStream.close();
                } catch (IOException e) {
                    logger.error(new StringBuffer("读取文件【").append(filePath).append("】IO异常").toString(), e);
                    throw new RuntimeException(e);
                }
            }
        }
    }
}

 MessageSourceUtil类

import com.example.demo.demos.constant.MessageLanguageEnum;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Stream;

@Component
public class MessageSourceUtil {

    private Map<String, Map<String, String>> messageSourceMap;

    /**
     * 获取国际化语言
     * @param msgCode 对应语言key
     * @param locale 语言类型 zh_CN/zh_TW/en_US....
     * @return
     */
    public String getMessageSource(String msgCode, String locale) {
        if (StringUtils.isBlank(msgCode) || this.messageSourceMap.keySet().size() < 1) {
            return null;
        }
        String localeVerify = null;
        // 语言类型无法识别时,默认英语
        if (StringUtils.isBlank(locale)) {
            localeVerify = MessageLanguageEnum.ENGLISH_US.code();
        } else {
            // 不区分大小写查找语言枚举类是否存在对应配置语言
            Optional<MessageLanguageEnum> enumQueryOptional = Stream.of(MessageLanguageEnum.values()).filter(m -> StringUtils.equalsIgnoreCase(m.code(), locale)).findFirst();
            localeVerify = enumQueryOptional.isPresent() ? enumQueryOptional.get().code() : MessageLanguageEnum.ENGLISH_US.code();
        }
        Map<String, String> messageLocaleMap = this.messageSourceMap.get(localeVerify);
        return messageLocaleMap.get(msgCode);
    }

    /**
     * 获取国际化语言
     * @param msgCode 对应语言key
     * @param defaultValue 找不到对应语言配置时默认值
     * @param locale 语言类型 zh_CN/zh_TW/en_US....
     * @return
     */
    public String getMessageSource(String msgCode, String defaultValue, String locale) {
        return Optional.ofNullable(this.getMessageSource(msgCode, locale)).orElse(defaultValue);
    }

    @Resource(name = "messageSourceMap")
    public void setMessageSourceMap(Map<String, Map<String, String>> messageSourceMap) {
        this.messageSourceMap = messageSourceMap;
    }
}

使用方式 

先@Autowired 引入 MessageSourceUtil类


码字不易,于你有利,勿忘点赞 

  • 9
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值