Spring国际化实现

news2024/11/16 11:50:36

Java国际化

Java使用Unicode来处理所有字符。

Locales

国际化主要涉及的是数字、日期、金额等。

有若干个专门负责格式处理的类。为了对格式进行控制,可以使用Locale类。它描述了:

  • 一种语言
  • 一个位置(通常包含)
  • 一段脚本(可选,自Java SE7开始支持)
  • 一个变体(可选)
  • 指定诸如方言或拼写规则之类的杂项。

Locale对象示例

language=English,location=United States
language=German,location=Germany   #货币表示为欧元
#瑞士有四种官方语言,一个说德语的瑞士人使用:货币会被表示成瑞士法郎。
language=German,location=Switzerland

如果只设定了语言,则不能处理和国家相关的问题。

language=German

为了以一种简练而标准的方式来表达语言和位置,Java语言使用ISO所定义的编码。本地语言由小写的2个字母来代替,它遵循ISO-639-1,国家代码由大写的2个字母的代码来表示,它遵循ISO-3166-1,

只要提供了语言代码,或者语言与国家代码,就可以构造Locale对象了。

Locale german=new Locale("de")
Locale germanGermany=new Locale("de","DE")
locale germanSwitzerland = new Locale("de","CH")

JAVA SE定义了大量Locale对象语言Locale,但是没有设定位置。

//获取默认Locale
Locale locale = Locale.getDefault()

对于所有依赖Locale的类,可以返回一个他们所支持的Locale列表。

Locale[] l = DataFormat.getAvailableLocales();

参考

数字格式

数字和货币是高度依赖locale的,Java提供了一个格式器(formatter)对象的集合,可以对java.txt包中的数字值进行格式化和解析。

相关类:

  • NumberFormat

参考

货币标识(ISO 4217):https://www.iban.hk/currency-codes

日期和时间

当格式化日期和时间时,需要考虑4个与Locale相关的问题:

  • 月份和星期
  • 年月日的顺序
  • 公历可能不是首选的日期表示法
  • 时区

相关类:

  • DataFormat

排序

Java语言中,String类的CompareTo()方式是用Unicode字符来决定顺序的。

如果需要根据Locale排序,则先获取一个Locale对象,然后获取**Collator**对象,再比较。

Locale loc = new Locale("de","DE");
Collator col = Collator.getInstance(loc);
if (col.Compare(a,b) < 0)
{
	... ...
}

排序强度

可以设置排序器的强度来选择不同的排序行为。字符间的差别可以分为:首要的(Primary),其次的(secondary),和再次的(tertiary)。

**Collator**定义了对应的枚举值

分解

偶尔一个字符或字符序列在描述成Unicode时,可以有多种方式。Unicode标准对字符串定义了四种范化形式:D,KD,C,KC。参考:https://www.unicode.org/reports/tr15/tr15-23.html

消息格式化

MessageFormat类,用来格式化带变量的文本,

"On {2},a {0} destroyed {1} houses and cuased {3} of damage"

占位符索引后可以跟一个类型和一个风格,他们之间用逗号隔开。

类型可以是:

  • number
  • time
  • date
  • choice

如果类型是number,则风格可以是:

  • integer
  • currency
  • precent

参考DecimalFormat类,SimpleDateFormat类。

静态的MessageFormat.format()方法使用当前Locale对值进行格式化,如果要指定Locale,则:

MessageFormat mf= new MessageFormat(pattern,loc);
String msg= mf.format(new Object[]{values});

选择格式

选择格式可以用来根据不同语言,使用特殊的语法,例如a,an等

choice格式化选项就是为了这个目的。一个格式化选项是由一个序列对构成的,每一个对包括:

  • 一个下限(lower limit)
  • 一个格式字符串(format string)

下限和格式字符串由一个#符号分隔,对与对之间由符号|分隔。

{1,choice,0#no houses|1#one house|2#{1} houses}
{1}结果
0“no houses”
1“one house”
3“3 houses”
-1“no houses”

也可以用<,小于等于(\u2264)实现#相同的结果。

文本文件和字符集

源文件的字符编码

在程序编译和运行时,有3种字符编码参与其中:

  • 源文件:本地编码
  • 类文件:modified UTF-8
  • 虚拟机:UTF-16

为了使源文件到处使用,则必须使用普通的ASCII吗,也就是说必须把所有非ASCII字符转换成等价的UNICODE字符。例如,spring源码中的 message资源文件。JDK自带工具,native2ascii,可以将本地字符编码转换成普通的ASCII编码。

资源包

定位资源包

当本地化一个应用时,会制造很多资源包(resource bundle)。每个包,都要为想要支持的locale提供相应的版本,格式:

包名_语言_国家
包名_语言
ResourceBundle resource=ResourceBundle.getBundle(bundleName,currentLocale);

getBundle方法试图加载匹配当前Locale定义的语言和国家的包,如果失败,则依次通过放弃国家和语言来进行查找,然后同样的查找被应用于默认的Locale,如果最后还是不行,则去查看默认的包文件,如果失败,则抛出异常。

  1. 包名_当前locale的语言_当前locale的国家_当前locale的变量
  2. 包名_当前locale的语言_当前locale的国家
  3. 包名_当前locale的语言
  4. 包名_默认locale的语言_默认locale的国家_默认locale的变量
  5. 包名_默认locale的语言_默认locale的国家
  6. 包名_默认locale的语言
  7. 包名

可以使用JDK命令对属性文件中非Unicode字符进行转码

native2ascii -encoding GBK messages_zh_CN.properties m.zh

属性文件

ResourceBundle resource=ResourceBundle.getBundle(bundleName,currentLocale);
String s = resource.getString("name")

包类

为了提供字符串类型以外的资源,需要定义类,它必须扩展自ResourceBundle类,使用标准的命名规则来命名类。

MyResource.java
Myresource_en.java
Myresource_en_UN.java
  //获取资源
 ResourceBundle resource=ResourceBundle.getBundle(bundleName,currentLocale);
(Color) resource.getObject("name");
(Map) resource.getObject("name");

Spring中的国际化实现

pring中国际化是通过MessageSource这个接口来支持的

public interface MessageSource {
 /**
     * 获取国际化信息
     * @param code 表示国际化资源中的属性名;
     * @param args 用于传递格式化串占位符所用的运行参数;
     * @param defaultMessage 当在资源找不到对应属性名时,返回defaultMessage参数所指定的默认信息;
     * @param locale 表示本地化对象
     */
	@Nullable
	String getMessage(String code, @Nullable Object[] args, @Nullable String defaultMessage, Locale locale);
 
	String getMessage(String code, @Nullable Object[] args, Locale locale) throws NoSuchMessageException;
 
	String getMessage(MessageSourceResolvable resolvable, Locale locale) throws NoSuchMessageException;

}

常见3个实现类

ResourceBundleMessageSource:这个是基于Java的ResourceBundle基础类实现,允许仅通过资源名加载国际化资源

ReloadableResourceBundleMessageSource:这个功能和第一个类的功能类似,多了定时刷新功能,允许在不重启系统的情况下,更新资源的信息

StaticMessageSource:它允许通过编程的方式提供国际化信息,一会我们可以通过这个来实现db中存储国际化信息的功能。

ResourceBundleMessageSource

类继承图:

image-20230219141426868

  • HierarchicalMessageSource

    支持层级资源查找

public interface HierarchicalMessageSource extends MessageSource {
	void setParentMessageSource(@Nullable MessageSource parent);
	@Nullable
	MessageSource getParentMessageSource();
}
  • MessageSourceSupport

    提供 参数占位 格式化支持。

AbstractMessageSource

工厂方法模式。

public abstract class AbstractMessageSource extends MessageSourceSupport implements HierarchicalMessageSource {

	@Nullable
	private MessageSource parentMessageSource;

	@Nullable
	private Properties commonMessages;

	private boolean useCodeAsDefaultMessage = false;
	
	//获取消息 
	@Override
	public final String getMessage(String code, @Nullable Object[] args, @Nullable String defaultMessage, Locale locale) {
		String msg = getMessageInternal(code, args, locale);
		if (msg != null) {
			return msg;
		}
		if (defaultMessage == null) {
			return getDefaultMessage(code);
		}
		return renderDefaultMessage(defaultMessage, args, locale);
	}

	@Override
	public final String getMessage(String code, @Nullable Object[] args, Locale locale) throws NoSuchMessageException {
		String msg = getMessageInternal(code, args, locale);
		if (msg != null) {
			return msg;
		}
		String fallback = getDefaultMessage(code);
		if (fallback != null) {
			return fallback;
		}
		throw new NoSuchMessageException(code, locale);
	}

	@Override
	public final String getMessage(MessageSourceResolvable resolvable, Locale locale) throws NoSuchMessageException {
		String[] codes = resolvable.getCodes();
		if (codes != null) {
			for (String code : codes) {
				String message = getMessageInternal(code, resolvable.getArguments(), locale);
				if (message != null) {
					return message;
				}
			}
		}
		String defaultMessage = getDefaultMessage(resolvable, locale);
		if (defaultMessage != null) {
			return defaultMessage;
		}
		throw new NoSuchMessageException(!ObjectUtils.isEmpty(codes) ? codes[codes.length - 1] : "", locale);
	}


	/**
	 * 内部 实现,用于 子类扩展
	 */
	@Nullable
	protected String getMessageInternal(@Nullable String code, @Nullable Object[] args, @Nullable Locale locale) {
		if (code == null) {
			return null;
		}
		if (locale == null) {
			locale = Locale.getDefault();
		}
		Object[] argsToUse = args;

		if (!isAlwaysUseMessageFormat() && ObjectUtils.isEmpty(args)) {
			String message = resolveCodeWithoutArguments(code, locale);
			if (message != null) {
				return message;
			}
		}

		else {
 
			argsToUse = resolveArguments(args, locale);

			MessageFormat messageFormat = resolveCode(code, locale);
			if (messageFormat != null) {
				synchronized (messageFormat) {
					return messageFormat.format(argsToUse);
				}
			}
		}

		// Check locale-independent common messages for the given message code.
		Properties commonMessages = getCommonMessages();
		if (commonMessages != null) {
			String commonMessage = commonMessages.getProperty(code);
			if (commonMessage != null) {
				return formatMessage(commonMessage, args, locale);
			}
		}

		// Not found -> check parent, if any.
		return getMessageFromParent(code, argsToUse, locale);
	}

    	/**
	 * 内部 实现,用于 子类扩展
	 */
    	@Nullable
	protected abstract MessageFormat resolveCode(String code, Locale locale);

    
}

AbstractResourceBasedMessageSource

提供设置 basename的能力。

public abstract class AbstractResourceBasedMessageSource extends AbstractMessageSource {
    //保存basename。
	private final Set<String> basenameSet = new LinkedHashSet<>(4);
    //默认编码
	@Nullable
	private String defaultEncoding;

	private boolean fallbackToSystemLocale = true;
	//默认locale。
	@Nullable
	private Locale defaultLocale;
}	

ResourceBundleMessageSource实现

	protected MessageFormat resolveCode(String code, Locale locale) {
		Set<String> basenames = getBasenameSet();
        //循环所有basename
		for (String basename : basenames) {
			ResourceBundle bundle = getResourceBundle(basename, locale);
			if (bundle != null) {
                //获取指定的MessageFormat
				MessageFormat messageFormat = getMessageFormat(bundle, code, locale);
				if (messageFormat != null) {
					return messageFormat;
				}
			}
		}
		return null;
	}

bean名称必须是messageSource

Spring MVC 国际化使用

创建消息属性文件

通过basename指定消息的名称,则对不同locale创建不同的属性文件。具体见前面内容。

示例:

ValidationMessages.properties
ValidationMessages_ar.properties
ValidationMessages_cs.properties
ValidationMessages_de.properties
ValidationMessages_en.properties
ValidationMessages_es.properties
ValidationMessages_fa.properties
ValidationMessages_fr.properties

Spring boot 的默认 basename 为 :messages,通过 spring.messages.basename 设置。属性文件配置在resources下。同一个basename会以 Resource Bundle 目录显示。

在每个属性文件中,为每个属性设置属性值。

示例:

javax.validation.constraints.NotNull.message         = must not be null
javax.validation.constraints.Null.message            = must be null
javax.validation.constraints.Past.message            = must be a past date

设置LocaleResolver

Spring Mvc 默认有4种 LocaleResolver。

public interface LocaleResolver {
 
	Locale resolveLocale(HttpServletRequest request);
 
	void setLocale(HttpServletRequest request, @Nullable HttpServletResponse response, @Nullable Locale locale);

}

AcceptHeaderLocaleResolver

接受 Accept-Language header 来设置locale。

SessionLocaleResolver

SessionLocaleResolver 将客户端的 Locale 保存到 HttpSession 对象中,并且可以进行修改(这意味着当前环境信息,前端给浏览器发送一次即可记住,只要 session 有效,浏览器就不必再次告诉服务端当前的环境信息)

public static final String LOCALE_SESSION_ATTRIBUTE_NAME = SessionLocaleResolver.class.getName() + ".LOCALE";

CookieLocaleResolver

Locale会保存到cookie中,cookieName:org.springframework.web.servlet.i18n.CookieLocaleResolver.LOCALE

public static final String LOCALE_REQUEST_ATTRIBUTE_NAME = CookieLocaleResolver.class.getName() + ".LOCALE";

接受的request参数,默认参数名为locale,通过LocaleChangeInterceptor 设置,可以在构造CookieLocaleResolver时设置。

FixedLocaleResolver

固定Locale。

spring.mvc.locale=zh_CN
#或者
spring:
  web:
    locale: zh_CN
    locale-resolver: fixed

spring.web.locale-resolver 优先级比 spring.mvc.locale-resolver 高一些。

spring.web.localespring.mvc.locale 这两个配置属性,假如存在,就会成为AcceptHeaderLocaleResolver 的默认的Locale 区域对象。 并在请求响应的请求头中没有Accept-Language这个属性时,成为AcceptHeaderLocaleResolver返回的Locale 区域对象。

LocaleChangeInterceptor

LocaleChangeInterceptor 则主要是负责参数解析的,在配置拦截器的时候,设置了参数名为 locale(默认),也就是说可以通过 locale 参数来传递当前的环境信息


	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws ServletException {
        //通过设置的参数,获取locale。
		String newLocale = request.getParameter(getParamName());
		if (newLocale != null) {
			if (checkHttpMethod(request.getMethod())) {
                //获取 LocaleResolver
				LocaleResolver localeResolver = RequestContextUtils.getLocaleResolver(request);
				if (localeResolver == null) {
					throw new IllegalStateException(
							"No LocaleResolver found: not in a DispatcherServlet request?");
				}
				try {
                    //设置 locale。
					localeResolver.setLocale(request, response, parseLocaleValue(newLocale));
				}
				catch (IllegalArgumentException ex) {
					if (isIgnoreInvalidLocale()) {
						if (logger.isDebugEnabled()) {
							logger.debug("Ignoring invalid locale value [" + newLocale + "]: " + ex.getMessage());
						}
					}
					else {
						throw ex;
					}
				}
			}
		}
		// Proceed in any case.
		return true;
	}

配置


@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(localeChangeInterceptor());
    }

    @Bean
    public LocaleResolver localeResolver() {
        return new CookieLocaleResolver();
    }

    /**
     * 切换语言按钮URL?language=zh_CN,切换后将语言信息存入cookie;
     *
     * @return
     */
    @Bean
    public LocaleChangeInterceptor localeChangeInterceptor() {
        LocaleChangeInterceptor lci = new LocaleChangeInterceptor();
        //承载 locale的参数。
        lci.setParamName("language");
        return lci;
    }
}

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

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

相关文章

CMMI之需求开发流程

需求开发&#xff08;Requirement Development, RD&#xff09;的目的是通过调查与分析&#xff0c;获取用户需求并定义产品需求。需求开发过程域是SPP模型的重要组成部分。本规范阐述了需求开发过程域的两个主要规程&#xff1a; 需求调查 [SPP-PROC-RM-SURVEY] 需求定义 [SPP…

消失的数字【C语言】

题目&#xff1a; 数组nums包含从0到n的所有整数&#xff0c;但其中缺了一个。请编写代码找出那个缺失的整数。你有办法在O(n)时间内完成吗&#xff1f; 解法&#xff1a; int missingNumber(int* nums, int numsSize){int val0;for(int i0;i<numsSize;i){val^nums[i];}fo…

Vue3之条件渲染

1.何为条件渲染 条件渲染就是在指定的条件下&#xff0c;渲染出指定的UI。比如当我们显示主页的时候&#xff0c;应该隐藏掉登录等一系列不相干的UI元素。即UI元素只在特定条件下进行显示。而在VUE3中&#xff0c;这种UI元素的显示和隐藏可以通过两个关键字&#xff0c;v-if 和…

Qt动画框架详解

目录1.前言2.原理3.属性动画4.并行执行的动画5.顺序执行的动画6.扩展属性动画支持的数据类型1.前言 为软件适当的添加一些动画&#xff0c;能够提高软件的用户体验。在使用Qt框架开发软件时&#xff0c;我们可以用Qt提供的动画框架来为QWidget等UI元素添加动画效果。本文从动画…

程序员和他的女朋友一起创建了价值 150,000,000 美元的网站

本篇文章讲述了Otis和Elizabeth Chandler创办Goodreads.com的故事。他们从小就爱读书&#xff0c;创办网站前他们的困惑是没有很多人在线分享书评。Otis和Elizabeth觉得如果有一个地方把所有人的评论和评价收集起来&#xff0c;那将会很有价值。奥蒂斯和伊丽莎白从小就喜欢读书…

MMKV与mmap:全方位解析

概述 MMKV 是基于 mmap 内存映射的移动端通用 key-value 组件&#xff0c;底层序列化/反序列化使用 protobuf 实现&#xff0c;性能高&#xff0c;稳定性强。从 2015 年中至今&#xff0c;在 iOS 微信上使用已有近 3 年&#xff0c;其性能和稳定性经过了时间的验证。近期已移植…

海量数据相似数据查询方法

1、海量文本常见 海量文本场景&#xff0c;如何寻找一个doc的topn相似doc&#xff0c;一般存在2个问题&#xff0c; 1)、两两对比时间o(n^2) 2)、高维向量比较比较耗时。 文本集可以看成(doc,word)稀疏矩阵&#xff0c;一般常见的方法是构建到排索引&#xff0c;然后进行归并…

论文投稿指南——中文核心期刊推荐(社会学)

【前言】 &#x1f680; 想发论文怎么办&#xff1f;手把手教你论文如何投稿&#xff01;那么&#xff0c;首先要搞懂投稿目标——论文期刊 &#x1f384; 在期刊论文的分布中&#xff0c;存在一种普遍现象&#xff1a;即对于某一特定的学科或专业来说&#xff0c;少数期刊所含…

【论文精读】DeepWalk: Online Learning of Social Representations

DeepWalk: Online Learning of Social Representations 本文是我参加Datawhale的CS224W图机器学习时的笔记&#xff0c;第一次学习图机器学习&#xff0c;对DeepWalk这篇开山之作的理解。 论文的三位作者均来自纽约州立大学石溪分校&#xff0c;杨振宁和丘成桐也曾在此教学。 …

JavaScript内存泄露和垃圾回收机制

1、是什么&#xff1f;内存泄露&#xff08;Memory leak&#xff09;是在计算机科学中&#xff0c;由于疏忽或错误造成程序未能释放已经不再使用的内存。并非指内存在物理上的消失&#xff0c;而是应用程序分配某段内存后&#xff0c;由于设计错误&#xff0c;导致在释放该段内…

Spring Boot 项目介绍

Spring Boot 项目介绍 作为学习过 Java 的软件开发者&#xff0c;相信都知道 Spring 这一伟大的框架&#xff0c;它所拥有的强大功能之一就是可以集成各种开源软件。但随着互联网的高速发展&#xff0c;各种框架层出不穷&#xff0c;这就对系统架构的灵活性、扩展性、可伸缩性…

【项目精选】基于JSP物流信息网(论文+源码+视频)

点击下载源码 近年来&#xff0c;随着时代的进步&#xff0c;社会随之不断发展&#xff0c;经济也快速发展起来了&#xff0c;人民的消费水平在不断地提高&#xff0c;平常的实体店消费已经不能满足人们的需求&#xff1b;在者&#xff0c;互联网技术的不断发展也为电子商务的兴…

CSS Houdini

前言 最近看了几篇文章&#xff0c;是关于 CSS Houdini 的。作为一个前端搬砖的还真不知道这玩意&#xff0c;虽然不知道的东西挺多的&#xff0c;但是这玩意有点高大上啊。 Houdini 是一组底层 API&#xff0c;它们公开了 CSS 引擎的各个部分&#xff0c;从而使开发人员能够通…

Matlab搭建AlexNet实现手写数字识别

Matlab搭建AlexNet实现手写数字识别 个人博客地址 文章目录Matlab搭建AlexNet实现手写数字识别环境内容步骤准备MNIST数据集数据预处理定义网络模型定义训练超参数网络训练和预测代码下载环境 Matlab 2020aWindows10 内容 使用Matlab对MNIST数据集进行预处理&#xff0c;搭建…

基于Spring Boot框架的人事管理系统的设计与实现(程序+详细文档)

大家好✌&#xff01;我是CZ淡陌。这里为大家分享优质的实战项目&#xff0c;本人在Java毕业设计领域有多年的经验&#xff0c;陆续会更新更多优质的Java实战项目&#xff0c;希望你能有所收获&#xff0c;少走一些弯路&#xff01; &#x1f345;更多优质项目&#x1f447;&am…

面向六部十层电梯群控算法的研究

面向六部十层电梯群控算法的研究 赵大权&#xff0c;张翔宇 &#xff08;晋中学院 机械学院&#xff0c;山西 晋中 030619&#xff09; 摘 要&#xff1a; 在商业大楼和高层写字楼里&#xff0c;基于可编程逻辑控制器&#xff08;PLC&#xff09;对电梯运行进行控制是当前主流…

【LeetCode】剑指 Offer(4)

目录 写在前面&#xff1a; 题目&#xff1a;剑指 Offer 10- I. 斐波那契数列 - 力扣&#xff08;Leetcode&#xff09; 题目的接口&#xff1a; 解题思路&#xff1a; 代码&#xff1a; 过啦&#xff01;&#xff01;&#xff01; 题目&#xff1a;剑指 Offer 10- II. …

河南工程学院2.17蓝桥杯培训

乘法口诀数列&#xff1a;https://www.acwing.com/problem/content/3466/ 剪绳子&#xff1a;https://www.acwing.com/problem/content/68Sin SinSine之舞&#xff1a;http://lx.lanqiao.cn/problem.page?gpidD5272 数列&#xff1a;https://www.acwing.com/problem/content/…

【郭东白架构课 模块一:生存法则】13|法则六:如何鉴别文化环境是否有利于架构师的生存?

你好&#xff0c;我是郭东白。 架构师通常并不管理团队&#xff0c;而是管理架构活动。更准确地说&#xff0c;是定义和引导架构活动。因为每个参与架构活动的个体&#xff0c;都有各自工作的优先级和汇报关系。因此在没有管理、考核和激励等手段的保障下&#xff0c;唯一能够…

Chrome 又不支持 HTTP/2 网站的原因

导读昨晚偶尔清理 Chrome 插件时发现我的 “HTTP/2 and SPDY indicator”插件好像好久没亮了。这个插件在你访问到一个支持 HTTP/2 &#xff08;或之前的 SPDY 协议&#xff09;的网站时会点亮&#xff0c;而我明明记得之前专门让 https://linux.cn/ 支持了 HTTP/2 。 我的第一…