Spring MessageSource国际化原理

spring framework提供MessasgeSource来实现国际化。

MessageSource用法

准备properties文件,放在resources文件夹下面。这是默认语言和韩语的文件。

  • i18n/message.properties
  • i18n/message_ko.properties

文件里面的内容是key-value格式,使用{0}、{1}作为变量占位符:

argument.required=The {0} argument is required.

注册MessageSource Bean

spring提供了MessageSource的实现类ResourceBundleMessageSource。它从resources下查找多语言配置,并且会缓存结果。这是spring boot里MessageSource的初始化方法。

@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;
}

这里配置了messageSource的几个属性,简单介绍下对应的效果。

  • setBasenames:多语言文件的名字,多个名字用都好隔开。例如要使用上面两个文件,basenames要等于i18n/message。最终是基于basename+local+".properties"找到对应的多语言资源文件。
  • setDefaultEncoding:设置多语言文件里编码格式。默认是ISO-8859-1,要改成UTF-8。
  • setFallbackToSystemLocale:要获取默认Locale时,是不是要返回系统的Locale,也就是Locale.getDefault()。
  • setCacheMillis:控制资源文件文件本地缓存的过期时间,默认-1表示不过期。
  • setAlwaysUseMessageFormat:是否总是使用MessageFormat来解析多语言文本内容。
  • setUseCodeAsDefaultMessage:code不存在时,是否直接返回code值。

使用MessageSource

MessageSource接口提供了三个API:

// 设置参数和默认值
String getMessage(String code, @Nullable Object[] args, @Nullable String defaultMessage, Locale locale);

// 设置参数
String getMessage(String code, @Nullable Object[] args, Locale locale) throws NoSuchMessageException;

// 从多个code中返回第一个找到的值
String getMessage(MessageSourceResolvable resolvable, Locale locale) throws NoSuchMessageException;

这里都是获取单个code的方法,没有获取数组的API。我们可以参考application.properties里数组的配置方式,增强MessageSource功能。 数组的定义格式:

argument.required[0]=The {0} argument is required. argument.required[1]=The {0} argument is required.

public List<String> getMessageList(String code, Object[] args, Locale locale) {
    List<String> result = new ArrayList<>();
    int i = 0;
    while(true) {
        val listCode = String.format("%s[%d]", code, i++);
        String message = getMessage(listCode, args, locale);
        if (message == null) {
            break;
        }
        result.add(message);
    }
    return result;
}

原理介绍

查找code的过程。

  1. 遍历basenames找到对应的资源文件
// 没有变量的情况
protected String resolveCodeWithoutArguments(String code, Locale locale) {
	Set<String> basenames = getBasenameSet();
	for (String basename : basenames) {
		ResourceBundle bundle = getResourceBundle(basename, locale);
		if (bundle != null) {
			String result = getStringOrNull(bundle, code);
			if (result != null) {
				return result;
			}
		}
	}
	return null;
}

// 有变量的情况,使用MessageFormat对变量进行替换
protected MessageFormat resolveCode(String code, Locale locale) {
	Set<String> basenames = getBasenameSet();
	for (String basename : basenames) {
		ResourceBundle bundle = getResourceBundle(basename, locale);
		if (bundle != null) {
			MessageFormat messageFormat = getMessageFormat(bundle, code, locale);
			if (messageFormat != null) {
				return messageFormat;
			}
		}
	}
	return null;
}
  1. 获得ResourceBundle
protected ResourceBundle doGetBundle(String basename, Locale locale) throws MissingResourceException {
	ClassLoader classLoader = getBundleClassLoader();
	Assert.state(classLoader != null, "No bundle ClassLoader set");

	MessageSourceControl control = this.control;
	if (control != null) {
		try {
			return ResourceBundle.getBundle(basename, locale, classLoader, control);
		}
		catch (UnsupportedOperationException ex) {
			// ...
		}
	}

	// Fallback: plain getBundle lookup without Control handle
	return ResourceBundle.getBundle(basename, locale, classLoader);
}

  1. 回调MessageSourceControl.newBundle()加载资源文件 ResourceBundle.getBundle(basename, locale, classLoader, control)方法最终回调control.newBundle()方法来查找资源文件。 MessageSourceControl重写了newBundle(),替换了java.properties的资源查找类型。
public ResourceBundle newBundle(String baseName, Locale locale, String format, ClassLoader loader, boolean reload)
		throws IllegalAccessException, InstantiationException, IOException {

	// Special handling of default encoding
	if (format.equals("java.properties")) {
		String bundleName = toBundleName(baseName, locale);
		final String resourceName = toResourceName(bundleName, "properties");
		final ClassLoader classLoader = loader;
		final boolean reloadFlag = reload;
		InputStream inputStream = null;
		if (reloadFlag) {
			URL url = classLoader.getResource(resourceName);
			if (url != null) {
				URLConnection connection = url.openConnection();
				if (connection != null) {
					connection.setUseCaches(false);
					inputStream = connection.getInputStream();
				}
			}
		}
		else {
			inputStream = classLoader.getResourceAsStream(resourceName);
		}
		if (inputStream != null) {
			String encoding = getDefaultEncoding();
			if (encoding != null) {
				try (InputStreamReader bundleReader = new InputStreamReader(inputStream, encoding)) {
					return loadBundle(bundleReader);
				}
			}
			else {
				try (InputStream bundleStream = inputStream) {
					return loadBundle(bundleStream);
				}
			}
		}
		else {
			return null;
		}
	}
	else {
		// 将 “java.class” 格式的处理委托给标准 Control
		return super.newBundle(baseName, locale, format, loader, reload);
	}
}

这里resourceName的格式是{baseName}_{locale}.properties。并且通过reload参数控制,是否重新加载资源文件。 最终用loadBundle(bundleReader)返回PropertyResourceBundle对象。 PropertyResourceBundle会在初始化的时候读取reader里的内容,存到一个Map<String, Object> lookup,后面就拿多语言code去Map里找。找的顺序是先从当前lookup里查,查不到再去parent.lookup里查。

public final Object getObject(String key) {
    Object obj = handleGetObject(key);
    if (obj == null) {
        if (parent != null) {
            obj = parent.getObject(key);
        }
        if (obj == null) {
            throw new MissingResourceException("Can't find resource for bundle "
                                               +this.getClass().getName()
                                               +", key "+key,
                                               this.getClass().getName(),
                                               key);
        }
    }
    return obj;
}

spring文档:Additional Capabilities of the ApplicationContext :: Spring Framework

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/895799.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Next.js 项目实战系列】05-删除 Issue

原文链接 CSDN 的排版/样式可能有问题&#xff0c;去我的博客查看原文系列吧&#xff0c;觉得有用的话&#xff0c;给我的库点个star&#xff0c;关注一下吧 上一篇【Next.js 项目实战系列】04-修改 Issue 删除 Issue 添加删除 Button​ 本节代码链接 这里我们主要关注布局…

Win10 IDEA远程连接HBase

Win10 IDEA远程连接HBase Win10 IDEA连接虚拟机中的Hadoop&#xff08;HDFS&#xff09; 关闭Hadoop和Hbase 如果已经关闭不需要走这一步 cd /usr/local/hbase bin/stop-hbase.sh cd /usr/local/hadoop ./sbin/stop-dfs.sh获取虚拟机的ip 虚拟机终端输入 ip a关闭虚拟机…

VS Code开发qt项目

没整明白&#xff0c;尴尬 安装扩展 设置cmake路径 前提是已经安装了QT 报错 用msvc选windows启动&#xff0c;用mingw则选gdb启动

Vue3 新特性、Pinia

一、新特性 - defineOptions 背景说明 因为我们用了<script setup>语法&#xff0c;没办法给setup去提供一些平级的属性&#xff0c;官方就提供了一个叫做 defineOptions 的语法 所以在 Vue3.3 中引入了 defineOptions 宏&#xff0c;用来定义 Options API 的选项。可以…

[OpenCV] 数字图像处理 C++ 学习——17模板匹配详细讲解+附完整代码

文章目录 前言1.理论基础1.1模板匹配介绍1.2匹配算法介绍 2.代码实现2.1模块匹配(matchTemplate)2.2最佳匹配函数(minMaxLoc()) 3.完整代码 前言 模板匹配是图像处理和计算机视觉领域中的一种经典技术&#xff0c;它通过在大图像中搜索与小图像&#xff08;模板&#xff09;匹…

gewechat免费开源微信机器人开发

​聊天机器人&#xff0c;是一种通过自然语言模拟人类进行对话的程序。通常运行在特定的软件平台上&#xff0c;如PC平台或者移动终端设备平台。聊天机器人系统的主要功能是同用户进行基本沟通并自动回复用户有关产品或服务的问题&#xff0c;以实现降低企业客服运营成本、提升…

Tkinter 的布局pack() and grid()笔记

#encodingutf-8 import tkinter import re import tkinter.messagebox import tkinter.simpledialog import sys import os def get_resources_path(relative_path):if getattr(sys,frozen, False):base_pathsys._MEIPASS#获取临时文件else:base_pathos.path.dirname(".&q…

鸿蒙应用开发----西西购物商城(一)

目录 前言 一、项目介绍 二、项目结构 三、开发工具 四、样式展示 前言 harmonyos是华为推出的一款新一代操作系统&#xff0c;致力于打破设备间的边界&#xff0c;构建统一的智能生态。西西购物商城作为一款基于harmonyos开发的应用&#xff0c;能够利用鸿蒙的分布式技术…

R语言绘制Venn图(文氏图、温氏图、维恩图、范氏图、韦恩图)

Venn图&#xff0c;又称文氏图&#xff0c;标题中其他名字也是它的别称&#xff0c;由封闭圆形组成&#xff0c;代表不同集合。圆形重叠部分表示集合交集&#xff0c;非重叠处为独有元素。在生物学、统计学等领域广泛应用&#xff0c;可展示不同数据集相似性与差异&#xff0c;…

大数据新视界 --大数据大厂之大数据在智慧城市建设中的应用:打造智能生活的基石

&#x1f496;&#x1f496;&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎你们来到 青云交的博客&#xff01;能与你们在此邂逅&#xff0c;我满心欢喜&#xff0c;深感无比荣幸。在这个瞬息万变的时代&#xff0c;我们每个人都在苦苦追寻一处能让心灵安然栖息的港湾。而 我的…

【MR开发】在Pico设备上接入MRTK3(一)——在Unity工程中导入MRTK3依赖

写在前面的话 在Pico上接入MRTK3&#xff0c;目前已有大佬开源。 https://github.com/Phantomxm2021/PicoMRTK3 也有值得推荐的文章。 MRTK3在PICO4上的使用小结 但由于在MacOS上使用MRTK3&#xff0c;无法通过Mixed Reality Feature Tool工具管理MRTK3安装包。 故记录一下…

集合collection和泛型

collection可以直接打印内容&#xff0c;而不是地址&#xff0c;内部已经重写了。 List家族&#xff1a; package com.itheima.d6_collection_update_delete;import java.util.ArrayList; import java.util.Iterator; import java.util.List;/**目标&#xff1a;研究集合遍历并…

Fooocus模型部署指南

一、介绍 Fooocus是一款开源的AI绘画工具&#xff0c;由斯坦福大学博士生张吕敏&#xff08;Github上用户名lllyasviel&#xff09;开发。是一款结合StableDiffusion和Midjourney理念的图像生成工具&#xff0c;提供离线、开源且易于使用的界面。它简化安装步骤&#xff0c;用…

【软件测试】JUnit

Junit 是一个用于 Java 编程语言的单元测试框架&#xff0c;Selenium是自动化测试框架&#xff0c;专门用于Web测试 本篇博客介绍 Junit5 文章目录 Junit 使用语法注解参数执行顺序断言测试套件 Junit 使用 本篇博客使用 Idea集成开发环境 首先&#xff0c;创建新项目&#…

【深度学习】评价指标

https://zhuanlan.zhihu.com/p/479060683 https://blog.csdn.net/fyfugoyfa/article/details/136414958 Confusion Matrix&#xff0c;是一个用来衡量模型好坏的统计量&#xff0c;其中Accuracy&#xff0c;Precision&#xff0c;F1 score&#xff0c;Recall&#xff0c;ROC曲线…

[SAP ABAP] SE11定义数据类型(结构与表类型)

1.定义结构 使用事务码SE11创建数据类型(结构)&#xff0c;输入自定义的数据类型名称&#xff0c;点击创建按钮 勾选结构并点击确定按钮 填写简短描述&#xff0c;并在"组件"页签上添加相关字段信息&#xff0c;点击激活按钮即可生效该结构ZSPO_HEADER_437 2.定义表…

企业网站设计之网站结构设计

创意主题网站的结构设计更是需要更为精心的策划和执行&#xff0c;以确保吸引并保持用户的注意力。以下是创意主题网站结构设计的关键要素&#xff1a; 1. 目标明确的导航栏 导航栏是用户进入网站并浏览内容的入口。在创意主题网站中&#xff0c;导航栏的设计要突显网站的主…

【大数据算法】一文掌握大数据算法之:大数据算法分析技术。

大数据算法分析技术 1、引言2、 大数据分析技术2.1 时间/空间复杂度2.2 I/O 复杂度2.3 结果质量2.4 通信复杂度 3、总结 1、引言 小屌丝&#xff1a;鱼哥&#xff0c;最近更文有些不频繁了哈。 小鱼&#xff1a;这一个月不见&#xff0c;你这说话方式也变了。 小屌丝&#xff…

Electron-(一)创建桌面应用

一、概述 本文通过核心步骤介绍&#xff0c;形成使用Electron进行桌面应用创建的概述性内容。 在当今的软件开发领域&#xff0c;Electron 作为一款强大的工具&#xff0c;为开发者提供了一种便捷的方式来创建跨平台的桌面应用。本文将通过详细介绍核心步骤&#xff0c;带您领…

PPT自动化:掌握 python-pptx 的基础元素

文章目录 📖 介绍 📖🏡 演示环境 🏡📒 PPT 📒📝 什么是 Slide?📝 了解 Shape📝 深入 Paragraph📝 探索 Run⚓️ 相关链接 ⚓️📖 介绍 📖 初学python-pptx,掌握 python-pptx 与 PPT 元素的对应关系是至关重要的一步。今天,我们一起来了解一下 PPT 中…