Spring国际化的应用及原理详解

1. 简介

Spring国际化(Spring Internationalization,简称i18n)是Spring框架提供的一种机制,用于支持多语言的应用程序。它使得开发者能够轻松地在应用程序中实现不同语言的支持,从而满足全球化的需求。通过Spring国际化,开发者可以将应用程序的文本、标签、消息等资源抽取出来,并使用合适的语言文件进行翻译,使得应用程序能够根据用户的语言偏好自动切换语言。这种机制不仅简化了多语言支持的实现,还使得应用程序更加易于维护和扩展。在Spring国际化的实现中,主要涉及到了MessageSource、LocaleResolver等核心组件,它们共同协作,实现了语言切换的功能。通过使用Spring国际化的API,开发者可以方便地定义语言区域、加载资源文件、处理消息等操作,从而快速构建多语言的应用程序。

2. API介绍

ApplicationContext 接口扩展了一个名为 MessageSource 的接口,因此提供了国际化("i18n")功能。Spring 还提供了 HierarchicalMessageSource 接口,该接口可以分层解析消息。这些接口共同构成了 Spring 实现消息解析的基础。这些接口定义的方法包括:

  • String getMessage(String code, Object[] args, String default, Locale loc)

用于从 MessageSource 获取消息的基本方法。如果在指定的本地没有找到消息,则使用默认消息。通过标准库提供的 MessageFormat 功能,传入的任何参数都会成为替换值。

  • String getMessage(String code, Object[] args, Locale loc)

与前一种方法基本相同,但有一点不同:不能指定默认信息。如果找不到信息,就会抛出 NoSuchMessageException 异常。

  • String getMessage(MessageSourceResolvable resolvable, Locale locale)

前面方法中使用的所有属性也都封装在一个名为 MessageSourceResolvable 的类中,你可以使用该方法。

3. 国际化初始化

Spring容器ApplicationContext初始化过程中,会从容器中查找MessageSource类型的Bean。并且该Bean的名称必须是 messageSource。如果找到了这样一个 Bean,对前面方法的所有调用都会委托给消息源。如果没有找到消息源,ApplicationContext 会尝试查找包含同名Bean的父类。如果找到了,它就会使用该 bean 作为消息源。如果 ApplicationContext 无法找到任何消息源,则会实例化一个空的 DelegatingMessageSource,以便能够接受对上述方法的调用。


public abstract class AbstractApplicationContext {public void refresh() {// 初始化消息源initMessageSource();}/*** 初始化消息源。* 如果当前上下文中没有定义消息源,则使用父级消息源。*/
protected void initMessageSource() {ConfigurableListableBeanFactory beanFactory = getBeanFactory();if (beanFactory.containsLocalBean(MESSAGE_SOURCE_BEAN_NAME)) {this.messageSource = beanFactory.getBean(MESSAGE_SOURCE_BEAN_NAME, MessageSource.class);// 使消息源知道父级消息源。if (this.parent != null && this.messageSource instanceof HierarchicalMessageSource hms &&hms.getParentMessageSource() == null) {// 只有当父级消息源尚未注册时,才将父上下文设置为父级消息源。hms.setParentMessageSource(getInternalParentMessageSource());}if (logger.isTraceEnabled()) {logger.trace("使用的消息源为 [" + this.messageSource + "]");}}else {// 使用空消息源以能够接受getMessage调用。DelegatingMessageSource dms = new DelegatingMessageSource();dms.setParentMessageSource(getInternalParentMessageSource());this.messageSource = dms;beanFactory.registerSingleton(MESSAGE_SOURCE_BEAN_NAME, this.messageSource);if (logger.isTraceEnabled()) {logger.trace("没有'" + MESSAGE_SOURCE_BEAN_NAME + "' bean,使用 [" + this.messageSource + "]");}}
}
}


4. 国际化配置

基于Spring环境


@Bean(AbstractApplicationContext.MESSAGE_SOURCE_BEAN_NAME)
public MessageSource messageSource() {ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource() ;// 这里设置的是basename,message是文件的前缀(不是包)messageSource.addBasenames("classpath:com/pack/main/databinder/message") ;return messageSource ;
}

在包com/pack/main/databinder下建2个文件分别:message_zh_CN.properties和message_en_US.properties。文件内容如下:

message_zh_CN.properties

#姓名必须填写
user.name.empty=\u59D3\u540D\u5FC5\u987B\u586B\u5199

message_en_US.properties

user.name.empty=name is required

调用


try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class)) {// Locale.CHINA或者Locale.USSystem.out.println(context.getMessage("user.name.empty", null, Locale.CHINA)) ;
}

基于SpringBoot环境

spring:messages:basename: message

注意:你需要提供一个默认的message.properties文件


@RestController
@RequestMapping("/i18n")
public class I18NController {@Resourceprivate ApplicationContext context ;@GetMapping("/index")public String index() {return context.getMessage("user.name.empty", null, "默认消息", LocaleContextHolder.getLocale()) ;}}

Locale从当前线程上下文中获取。该Locale是在DispatcherServlet中初始化的。

在接口调用时,我们只需要指定Access-Language header

5. 其它配置

Spring为我们提供了一个便捷的类,可以更方便的访问消息源,项目中只需要注册如下bean:

@Bean
public MessageSourceAccessor messageSourceAccessor(MessageSource messageSource) {MessageSourceAccessor accessor = new MessageSourceAccessor(messageSource) ;return accessor ;
}

访问

@Resource
private MessageSourceAccessor accessor ;
@GetMapping("/index")
public String index() {return accessor.getMessage("user.name.empty") ;
}

带占位符的消息访问

在消息文件中定义如下:


#年龄的取值范围从{0}~{1}
user.age.range=\u5E74\u9F84\u7684\u53D6\u503C\u8303\u56F4\u4ECE{0}~{1}

访问

@GetMapping("/index")
public String index() {return accessor.getMessage("user.age.range", new Object[] {1, 100}) ;
}

注:Spring 还提供了一个ReloadableResourceBundleMessageSource 类。该变体支持相同的捆绑文件格式,但比基于 JDK 的标准 ResourceBundleMessageSource 实现更灵活。特别是,它允许从任何 Spring 资源位置(而不仅仅是从类路径)读取文件,并支持捆绑属性文件的热重载(同时在两者之间有效地缓存它们)。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

面试必问究极重点之HashMap的底层原理

1.底层数据结构 JDK版本不同的数据结构 1.7 数组 链表 1.8 数组 (链表 | 红黑树) 2.添加数据put 在添加一个值的时候,首先会计算他的hash码,然后进行二次hash,在对当前长度取模得到在底层数组中的索引位置当取模完…

electron——查看electron的版本(代码片段)

electron——查看electron的版本(代码片段)1.使用命令行: npm ls electron 操作如下: 2.在软件内使用代码,如下: console.log(process) console.log(process.versions.electron) process 里包含很多信息: process详…

【Linux】——基本指令(二)

💗个人主页💗 ⭐个人专栏——数据结构学习⭐ 💫点击关注🤩一起学习C语言💯💫 目录 导读:1. vim 指令2. head指令3. tail指令4. tree指令5. 输出重定向6. echo指令7. wc指令8. | 字符9. date指令…

PCIe 6.0生态业内进展分析总结

上一篇,我们针对PCIe 6.0的功能更新与实现挑战做了简单的分析与总结。更多详细内容可以参考: 扩展阅读:浅析PCIe 6.0功能更新与实现的挑战 那么,PCIe 6.0已经发布了一段时间了,业内硬件支持PCIe 6.0目前有哪些进展呢…

面试算法93:最长斐波那契数列

题目 输入一个没有重复数字的单调递增的数组,数组中至少有3个数字,请问数组中最长的斐波那契数列的长度是多少?例如,如果输入的数组是[1,2,3,4,5,6,7&#x…

外包干了5个月,技术明显退步了...

先说一下自己的情况,本科生,19年通过校招进入湖南某软件公司,干了接近4年的功能测试,今年12月份,感觉自己不能够在这样下去了,长时间呆在一个舒适的环境会让一个人堕落!而我已经在一个企业干了四年的功能测…

周鸿祎分享大模型十大趋势:2024将出现杀手级应用

1月5日,“2023年风马牛年终秀”上,三六零(601360.SH,下称“360”)集团创始人周鸿祎分享了对2024年大模型发展趋势的十大预测,呼吁企业树立AI信仰,All in AI。他认为,创新才能破局&am…

ctfshow——信息搜集

文章目录 web 1web 2web 3web 4web 5web 6web 7web 8web 9web 10web 11web 12web 13web 14web 15web 16web 17web 18web 19web 20 web 1 题目提示开发注释未及时删除。 直接右键查看源代码。 web 2 在这关我们会发现:1)无法使用右键查看源代码&…

Linux服务器的几种类型

Linux是一个开源操作系统内核,用作各种Linux发行版(也称为“distros”)的核心组件。由Linus Torvalds于1991年开发,Linux基于Unix操作系统。它以其稳定性、安全性和多功能性而闻名。 Linux的关键特点: 开源性质&#…

OpenCASCADE MFC例子

OpenCASCADE MFC例子 说明 一直对OpenCASCADE一直都比较感兴趣,这个例子是我参考这位大神C幼儿园中班小朋友的专栏做出来的OpenCASCADE_C幼儿园中班小朋友的博客-CSDN博客 不过我用的是vcpkg的方式安装OpenCASCADE,这个需要注意一下,可能需…

[蓝桥杯学习] 树链剖分

定义 将树分割成若干条链,以维护树上的信息,若无特殊需求,一般是重链剖分。 重链剖分 如何重链剖分 两个dfs 第一个dfs是预处理各个结点的基本信息,第二个dfs是利用信息进行剖分(dfs序) 操作步骤 第一…

git在本地创建dev分支并和远程的dev分支关联起来

文章目录 git在本地创建dev分支并和远程的dev分支关联起来1. 使用git命令2. 使用idea2.1 先删除上面建的本地分支dev2.2 通过idea建dev分支并和远程dev分支关联 3. 查看本地分支和远程分支的关系 git在本地创建dev分支并和远程的dev分支关联起来 1. 使用git命令 git checkout…

构建高效秒杀系统的设计原理及注意事项

😄 19年之后由于某些原因断更了三年,23年重新扬帆起航,推出更多优质博文,希望大家多多支持~ 🌷 古之立大事者,不惟有超世之才,亦必有坚忍不拔之志 🎐 个人CSND主页——Mi…

设计模式——迭代器模式(Iterator Pattern)

概述 迭代器模式(Iterator Pattern):提供一种方法来访问聚合对象,而不用暴露这个对象的内部表示,其别名为游标(Cursor)。迭代器模式是一种对象行为型模式。 在软件开发中,我们经常需要使用聚合对象来存储一系列数据。聚合对象拥有…

UG装配-沿线运动

如果希望图中圆柱销沿着槽运动,直接约束面是困难的,我们可以画出圆弧的中心线和圆柱销的中心点,约束点在线上,进行移动 需要注意的是,我们在零件中画点和线的时候,在装配体默认加载模型引用集的时候是无法显…

最新ChatGPT网站源码,支持Midjourney绘画,GPT语音对话+GPT-4识图理解能力+ChatFile文档对话总结+DALL-E3文生图

一、前言 SparkAi创作系统是基于ChatGPT进行开发的Ai智能问答系统和Midjourney绘画系统,支持OpenAI-GPT全模型国内AI全模型。本期针对源码系统整体测试下来非常完美,可以说SparkAi是目前国内一款的ChatGPT对接OpenAI软件系统。那么如何搭建部署AI创作Ch…

基于 ESP32-C3 开启 Flash 加密和安全启动并进行 OTA 测试

软件: esp-idf v5.1.2 硬件: ESP32-C3 board 1. 首先,准备一个明文固件 hello-world.bin 基于 esp-idf-v5.1.2\examples\get-started\hello_world 例程,使用如下指令,直接编译,获取明文固件 hello-worl…

IDEA中自动导包及快捷键

导包设置及快捷键 设置:Setting->Editor->General->Auto import快捷键 设置:Setting->Editor->General->Auto import java区域有两个关键选项 Add unambiguous imports on the fly 快速添加明确的导包 IDEA将在我们书写代码的时候…

wpsjs学习——获取单元格批注

1.获取第一个单元格的值&#xff1a; wps.Application.ActiveSheet.Range(A1).Value2; 2.1.获取第一个单元格的批注&#xff1a; wps.Application.ActiveSheet.Range(A1).Comment.Text(); <div class"global">获取表格信息<div class"divItem">…

厚积薄发11年,鸿蒙究竟有多可怕

​12月20日中国工程院等权威单位发布《2023年全球十大工程成就》。本次发布的2023全球十大工程成就包括“鸿蒙操作系统”在内。入围的“全球十大工程成就”&#xff0c;主要指过去五年由世界各国工程科技工作者合作或单独完成且实践验证有效的&#xff0c;并且已经产生全球影响…