业务层资源国际化处理

SpringBoot 后端 i18n 国际化实现 本文介绍前后端分析项目实现国际化的方案。并详细介绍了在前后端混合实现国际化方案中,Spring Boot 后端是如何实现异常消息的国际化。 阅读详情

用过struts2等mvc框架开发的同学都知道,使用struts2处理国际化的消息非常简单直观,但是mvc框架的定位是在展示层(jsp,action)等,在一个典型的3层结构中,处于最上层的位置,按照分层设计原则,下层组件是不可以调用上层组件的,这样就存在一个问题,我们在业务层中可能也会出现一些需要国际化处理的消息信息,这些信息如何设置呢?

     在这篇文章中,我们将借鉴struts2的国际化处理机制,但是要比struts2简单的多,因为业务层需要国际化处理的消息毕竟是少数,废话不多说,直接上干货

先举个例子-业务层未国际化处理前的代码

  @Override
    public ExecuteResult<User> login(String userName, String password) {
        ExecuteResult<User> executeResult = new ExecuteResult<User>();
        User userInfo = userDAO.getUserByName(userName);
        if(userInfo == null){
            executeResult.addErrorMessage("不存在的用户名");
            return executeResult;
        }
        if(!userInfo.getPassword().equals(password)){
            executeResult.addErrorMessage("密码错误");
            return executeResult;
        }
        executeResult.setResult(userInfo);
        return executeResult;
    }
设计实现:

1.添加国际化消息拦截器

我们的应用是基于struts2的,struts2默认提供了一个i18n拦截器,这里我们扩展了以下该拦截器的功能,将locale信息保存起来

/**
 * 扩展struts2默认的i18n拦截器,添加设置local到LocaleContextHolder中的功能
 * @author WangXuzheng
 * @see com.opensymphony.xwork2.interceptor.I18nInterceptor
 * @see org.springframework.context.i18n.LocaleContextHolder
 */
public class I18nResolverInterceptor extends I18nInterceptor {
	private static final long serialVersionUID = 5888969294461266478L;
	@Override
	protected void saveLocale(ActionInvocation invocation, Locale locale) {
		super.saveLocale(invocation, locale);
		LocaleContextHolder.setLocale(locale);
	}
}
2.在struts.xml中替换默认的i18n拦截器

<interceptor name="i18n" class="com.haier.openplatform.i18n.interceptor.I18nResolverInterceptor"/>
3.设计业务层国际化资源解析器接口

/**
 * 国际化资源处理器
 * @author WangXuzheng
 *
 */
public interface I18nResolver {
	/**
	 * 设置要进行资源处理的目标类
	 * @param clazz
	 */
	@SuppressWarnings("rawtypes")
	public void setClass(Class clazz);
	/**
	 * 解析国际化资源文件,如果找不到该code,返回默认的消息
	 * @param code i18n资源的key值
	 * @return 如果找到返回具体的消息值,如果不存在返回默认消息
	 * @see java.text.MessageFormat
	 */
	String getMessage(String code);
	
	/**
	 * 解析国际化资源文件,如果找不到该code,返回默认的消息
	 * @param code i18n资源的key值
	 * @return 如果找到返回具体的消息值,如果不存在返回默认消息
	 * @see java.text.MessageFormat
	 */
	String getMessage(String code,String arg);
	/**
	 * 解析国际化资源文件,如果找不到该code,返回默认的消息
	 * @param code i18n资源的key值
	 * @param args 资源中的变量值
	 * @param defaultMessage 默认消息
	 * @return 如果找到返回具体的消息值,如果不存在返回默认消息
	 * @see java.text.MessageFormat
	 */
	String getMessage(String code, Object[] args, String defaultMessage);
	/**
	 * 解析国际化资源文件查找指定code对应的消息,如果不存在,抛出异常
	 * @param code
	 * @param args
	 * @return
	 * @throws MessageNotFoundException
	 * @see java.text.MessageFormat
	 * @throws MessageNotFoundException
	 */
	String getMessage(String code, Object[] args);
}
默认的实现类-这里我们的资源配置文件放在和待处理的类同文件下同名的.properties文件中

例如:java类

com.haier.openplatform.showcase.security.service.impl.UserServiceImpl

 对应的资源文件为

com/haier/openplatform/showcase/security/service/impl/UserServiceImpl_zh_CN.properties

com/haier/openplatform/showcase/security/service/impl/UserServiceImpl_en_US.properties

/**
 * 默认的资源文件解析器,该类读取<code>org.springframework.context.i18n.LocaleContextHolder</code>中保存的Local信息解析资源文件
 * @author WangXuzheng
 * @see org.springframework.context.i18n.LocaleContextHolder
 */
public class DefaultI18nResolver extends MessageSourceSupport implements I18nResolver {
	private static final ConcurrentMap<String, ResourceBundle> BUNDLE_MAP = new ConcurrentHashMap<String, ResourceBundle>();
	private static final List<String> DEFAULT_RESOURCE_BUNDLES = new CopyOnWriteArrayList<String>();
	private static final Log LOG = LogFactory.getLog(DefaultI18nResolver.class);
	@SuppressWarnings("rawtypes")
	protected Class clazz;
	/**
	 * 添加全局资源配置信息
	 *
	 * @param resourceBundleName the name of the bundle to add.
	 */
	public static void addDefaultResourceBundle(String resourceBundleName) {
		//make sure this doesn't get added more than once
		synchronized (DEFAULT_RESOURCE_BUNDLES) {
			DEFAULT_RESOURCE_BUNDLES.remove(resourceBundleName);
			DEFAULT_RESOURCE_BUNDLES.add(0, resourceBundleName);
		}

		if (LOG.isDebugEnabled()) {
			LOG.debug("Added default resource bundle '" + resourceBundleName + "' to default resource bundles = "
					+ DEFAULT_RESOURCE_BUNDLES);
		}
	}

	/**
	 * Creates a key to used for lookup/storing in the bundle misses cache.
	 *
	 * @param aBundleName the name of the bundle (usually it's FQN classname).
	 * @param locale      the locale.
	 * @return the key to use for lookup/storing in the bundle misses cache.
	 */
	private String createMissesKey(String aBundleName, Locale locale) {
		return aBundleName + "_" + locale.toString();
	}

	/**
	 * 从全局资源文件中读取文案信息
	 *
	 * @param aTextName 文案 key
	 * @param locale    the locale the message should be for
	 * @return 
	 */
	private String findDefaultText(String aTextName, Locale locale) {
		List<String> localList = DEFAULT_RESOURCE_BUNDLES;
		for (String bundleName : localList) {
			ResourceBundle bundle = findResourceBundle(bundleName, locale);
			if (bundle != null) {
				try {
					return bundle.getString(aTextName);
				} catch (MissingResourceException e) {
					// ignore and try others
				}
			}
		}
		return null;
	}

	/**
	 * 根据资源名称和locale信息查找资源信息
	 * @param aBundleName the name of the bundle (usually it's FQN classname).
	 * @param locale      the locale.
	 * @return the bundle, <tt>null</tt> if not found.
	 */
	protected ResourceBundle findResourceBundle(String aBundleName, Locale locale) {
		String key = createMissesKey(aBundleName, locale);
		ResourceBundle bundle = BUNDLE_MAP.get(key);
		if (bundle == null) {
			bundle = ResourceBundle.getBundle(aBundleName, locale, Thread.currentThread().getContextClassLoader());
			BUNDLE_MAP.put(key, bundle);
		}
		return bundle;
	}

	private Locale getLocale() {
		return LocaleContextHolder.getLocale();
	}

	@Override
	public String getMessage(String code) {
		return getMessage(code, new Object[] {});
	}

	/**
	 * 获取资源消息对应的值,先从指定的bundleName的资源中获取文案,如果找不到,从globalResources中读取
	 * @param bundleName
	 * @param locale
	 * @param key
	 * @param args
	 * @return
	 * @see #findResourceBundle
	 */
	private String getMessage(String bundleName, Locale locale, String key, Object[] args) {
		ResourceBundle bundle = findResourceBundle(bundleName, locale);
		if (bundle == null) {
			return null;
		}

		String orginalMessage = null;
		try {
			orginalMessage = bundle.getString(key);
		} catch (MissingResourceException e) {
			// read text from global resources
			orginalMessage = findDefaultText(bundleName, locale);
		}
		return this.formatMessage(orginalMessage, args, locale);
	}

	@Override
	public String getMessage(String code, Object[] args) {
		return getMessage(resolveBunFile(), getLocale(), code, args);
	}

	@Override
	public String getMessage(String code, Object[] args, String defaultMessage) {
		return StringUtils.defaultIfBlank(getMessage(code, args), defaultMessage);
	}

	@Override
	public String getMessage(String code, String arg) {
		String[] args = new String[] { arg };
		return getMessage(code, args);
	}

	protected String resolveBunFile() {
		String pack = this.clazz.getName();
		return pack.replaceAll("[.]", "/");
	}

	@SuppressWarnings("rawtypes")
	public void setClass(Class clazz) {
		this.clazz = clazz;
	}
}
加一个静态工厂类来获取解析器

/**
 * 资源解析器工厂类
 * @author WangXuzheng
 *
 */
public final class I18nResolverFactory {
	@SuppressWarnings("rawtypes")
	private static final ConcurrentMap<Class, I18nResolver> I18N_RESOVER_MAP = new ConcurrentHashMap<Class, I18nResolver>();
	private I18nResolverFactory(){
	}
	
	@SuppressWarnings("rawtypes")
	public static I18nResolver getDefaultI18nResolver(Class clazz){
		I18nResolver resolver = I18N_RESOVER_MAP.get(clazz);
		if(resolver == null){
			resolver = new DefaultI18nResolver();
			resolver.setClass(clazz);
			I18N_RESOVER_MAP.put(clazz, resolver);
		}
		return resolver;
	}
}
4.调用实现-非常类似log4j的调用方式
public class UserServiceImpl implements UserService {
    private static final I18nResolver I18N_RESOLVER = I18nResolverFactory.getDefaultI18nResolver(UserServiceImpl.class);
    private UserDAO userDAO;
    private RoleDAO roleDAO;

@Override
    public ExecuteResult<User> login(String userName, String password) {
        ExecuteResult<User> executeResult = new ExecuteResult<User>();
        User userInfo = userDAO.getUserByName(userName);
        if(userInfo == null){
            executeResult.addErrorMessage( I18N_RESOLVER.getMessage("user.notexisted"));
            return executeResult;
        }
        if(!userInfo.getPassword().equals(password)){
            executeResult.addErrorMessage(I18N_RESOLVER.getMessage("user.wrongpassword"));
            return executeResult;
        }
        executeResult.setResult(userInfo);
        return executeResult;
    }



Spring 系列后端多语言(国际化)处理 日期、数字、货币公告、消息推送模板。 阅读详情

相关推荐

系统国际化设计方案

系统国际化设计方案 在经济全球化的驱动下,系统的国际化成为了系统各项能力形态中至关重要的一种形态。下面简单就国际化的业务以及实现提供几种思路供参考,其中部分已经在产品中进行了验证。 一、国际化设计要点之业务需求 1、首先描述业务场景: 1.1、用户通过当前的语言环境自动的选择界面展示的语种; 1.2、管理人员在录入相应的基础数据(包括物料、字典等信息)时,可以选择对应的语种进...

文凡小筑 4440

背靠中台实现基于大数据驱动的国际化电商架构(一)

0.让大数据应用无处不在 计算机的伟大之处就是为人类带来了效率。而今天随着大数据技术的日益成熟,也促进了计算机技术的发展。我们可以利用大数据技术实现更加智能的计算,一方面使得效率极大进步;另一方面也智能化也让计算机的应用领域变宽,解决以前无法解决的问题。这两方面最终升化了计算机的计算效率。 如今阿里集团的大数据技术积累以及计算资源的极大丰富也为我们实现更...

weixin_34067049的博客 424

国际化设计方案概要

前言:该文主要是对之前《系统国际化设计方案》的细化及概要说明。 国际化设计 该部分主要从业务架构设计、应用架构设计、技术架构设计等方面进行描述,确保技术设计的全面性以及可实现性。 1.1 国际化目标 针对历史在运项目以及新建项目,统一考虑国际化设计以及实现方案,用于系统集成后的全面国际化。 其一:历史项目,针对现有的系统复杂度比较高的情况,实现系统功能的国际化,制定功能国际化的...

文凡小筑 2557

8.企业应用架构模式 --- 通盘考虑

极限编程,持续集成,测试驱动开发,重构。 1.从领域层开始 三种模式最简单的是事务脚本模式。比较符合大多数人的习惯。它将每种系统事务的逻辑很好的封装在功能完善的脚本中,而且比较适合于在关系数据库之上构建。 它的主要问题是对复杂业务逻辑的支持不够,尤其是不善于处理重复代码。 最复杂的是领域模型模式。缺点是难以学会使用领域模型。第二个缺点就是它与数据库的连接。 表模块模式是这2...

enlyhua的专栏 860

Struts国际化处理深入解析与实践

本文还有配套的精品资源,点击获取 简介:Struts是Apache软件基金会维护的Java Web开发MVC框架。本资料包深入探讨Struts的国际化处理机制,强调在多语言环境下应用的构建。内容包括资源文件的使用、配置文件的设置、HTTP请求的语言偏好获取、Action和JSP页面中消息的处理、动态语言切换、JSTL fmt库的运用以及国际化功能的测试与调试。掌握这些知识能...

weixin_36235398的博客 841

spring显示国际化信息

spring显示国际化信息!!!!这是一个完整的范例!!!!

代码 国际化架构图_图为代码,基础架构为代码

代码 国际化架构图If you’re working with infrastructure, you’re undoubtedly leveraging infrastructure as code, right? I sure hope so. Otherwise, start now. I’ll be waiting. 如果您使用的是基础架构,那么毫无疑问,您是在利用基础架构作为代码 ,对吧...

weixin_26712121的博客 449

国际化架构设计.rar

ArchSummit全球架构师峰会2017 国际化架构设计.rar ppt

一文搞懂国际化(二)架构设计

现代前端工程框架 react、vaue等都是有自己支持一套国际化方案的,在前端工程中的保存有i18n翻译资源包(例如VUE的i18n框架),随着项目打包发布。即如果语言被改了,不会立即生效,登出后再登入,刷新登录态,此时获取到最新语言,才会生效。3)翻译运营工具:上线生产后,为了快速实现,持续可拓展的新增语言功能,急需一个翻译运营工具,即如何设计实现一个新增/维护语言的【翻译运营工具】。后端:请求到后端时,在入参解析时,框架根据header中的timezone(缓存本地),转换成UTC8时区时间。

weixin_55010563的博客 568

从零到一:手把手教你用海康VisionMaster完成第一个字符识别项目(附完整流程与避坑点)

本文详细介绍了如何使用海康VisionMaster完成首个字符识别项目,涵盖从环境搭建、图像预处理到系统集成的完整流程。特别分享了Blob分析配置、字符识别优化等实战技巧,帮助开发者避开常见陷阱,快速掌握VisionMaster的基本操作,提升工业自动化场景下的字符识别效率。

weixin_29313423的博客 664

OpenCV实战:从边缘检测到透视校正,打造智能文档扫描仪(Python代码详解)

本文详细介绍了如何使用OpenCV和Python从零开始构建一个智能文档扫描仪。通过边缘检测、透视校正和文字增强等核心技术,将倾斜、模糊的文档照片转换为清晰、方正的高质量扫描图像。文章提供了完整的代码实现和参数调优经验,帮助开发者打造稳定、高效的文档处理工具。

flower的专栏 694

一文搞懂国际化(一)背景概览

专题目录 一文搞懂国际化(一)背景概览 一文搞懂国际化(二)架构设计 一文搞懂国际化(三)落地实践 一文搞懂国际化(四)总结提升 一、背景   软硬件产品(互联网、产业互联网等等),在2019年后,特别是2020年后,由于国内行业过于卷(低价、噱头、资本、政策),很多业务都开始从国内转向国外、从新兴业务转向传统业务。笔者经历的就有消费金融、虚拟币平台、母婴社交、农牧养殖。本文的背景就是一个深耕...

博客园搬家测试账号 1295

边缘AI硬件风向:从专用芯片到端侧大模型

边缘AI硬件正呈现五大发展趋势:专用AI加速芯片(如华为昇腾、谷歌EdgeTPU)凭借高能效比成为主流;端侧大模型催生支持Transformer架构的新硬件需求;存算一体技术突破内存墙限制;FPGA和可重构计算提供灵活性;异构集成与Chiplet技术实现模块化设计。这些发展需要配套软件工具链支持,未来边缘设备将演变成具备本地智能的"智能体",竞争焦点转向算力、能效、易用性和成本的多维平衡。

qq_37027335的博客 347

国际化 java 业务数据_业务层资源国际化处理

用过struts2等mvc框架开发的同学都知道,使用struts2处理国际化的消息非常简单直观,但是mvc框架的定位是在展示层(jsp,action)等,在一个典型的3层结构中,处于最上层的位置,按照分层设计原则,下层组件是不可以调用上层组件的,这样就存在一个问题,我们在业务层中可能也会出现一些需要国际化处理的消息信息,这些信息如何设置呢?在这篇文章中,我们将借鉴struts2的国际化处理机制,但...

weixin_39995108的博客 285

从“要啥”到“咋测”:手把手拆解Aspice SWE.1需求分析,为你的车载/嵌入式项目避坑

本文深入解析Aspice SWE.1需求分析在车载/嵌入式项目中的关键作用,帮助开发者将模糊的系统需求转化为可执行、可验证的软件需求。通过实战案例和结构化方法,详细拆解功能需求原子化、非功能需求工程化表达等核心技巧,为项目避坑提供实用指南。

weixin_31682031的博客 257

几何修复_ANSYS流体分析:几何前处理

建模及几何处理工具非常多,选择起来让人眼花缭乱。对于仿真而言,高效的对几何进行清理及生成符合仿真要求的几何至关重要。ANSYS SpaceClaim Direct Modeler是基于直接建模思想的新一代3D高效率建模和几何编辑修补软件。SCDM提供了当今最快速、最富有创意的直接建模解决方案。由于它的直接建模能力即使对于导入的其它3D模型(如catia、proe、solid works创建的几何)...

weixin_35778830的博客 2416

Struts2国际化转移到业务层

      用过struts2等mvc框架开发的同学都知道,使用struts2处理国际化的消息非常简单直观,但是mvc框架的定位是在展示层(jsp,action)等,在一个典型的3层结构中,处于最上层的位置,按照分层设计原则,下层组件是不可以调用上层组件的,这样就存在一个问题,我们在业务层中可能也会出现一些需要国际化处理的消息信息,这些信息如何设置呢?      在这篇文章中,我们将借鉴stru...

煎饼里的蛋 126

S32K3的STCU2模块详解:从硬件FSM到MCAL配置,搞懂BIST在线测试全流程

本文深入解析S32K3系列MCU的STCU2模块,从硬件FSM架构到MCAL配置,详细讲解BIST在线测试全流程。通过分区的安全策略和实战代码示例,帮助开发者掌握汽车电子系统中的功能安全实现,提升系统可靠性和安全性。

weixin_42525328的博客 446

UserTest

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// web.xml &lt;?xml version="1.0" encoding="UTF-8"?&gt; &lt;web-app version="2.5"...

yongzehanson的专栏 370

Mac 下 OpenClaw 安装全记录:从零到一的保姆级图文教程

本文主要介绍了Mac 下 OpenClaw 安装全记录:从零到一的保姆级图文教程,希望能对学习使用OpenClaw 的同学们有所帮助。 文章目录 1. 背景 2. 安装教程 2.1 安装OpenClaw 2.2 配置模型 2.3 对话 3. 后续使用步骤 4. 关闭程序

weixin_43178406的博客 11万+

Test-Time Adaptation Induces Stronger Accuracy and Agreement-on-the-Line

TTA不管是否提高了OOD的泛化性都增强了线性的趋势,TTA的初始目的是为了增强OOD的表现,一个人可能会觉得TTA使得ACL更强的原因是他消除了ID和OOD之间的差距,因此所有模型间ID vs OOD的准确率接近于y=x这条线,但是实际上这个解释不能够反映我们的经验发现,因为虽然TTA将分布之间的偏移简化为一种放缩的分布,但是这个放缩的因子幅值可能很大,也就是α<<1但是r>>1,这就会导致很偏离y=x这条线,而且理论分析也是,虽然ID和OOD的差距很大,但是线性关系还在。

m0_62673638的博客 148

自动式异常处理

关于异常的处理,大概就有编程式的异常处理,自动式异常处理,声明式的异常处理,编程式的异常处理一般是不可能用,因为在项目开发的过程中,绝大部分是采用声明式的异常处理,每个项目都有自己的异常处理类。这里发个自动式异常处理,对理解声明式的异常处理有点帮助。 1.在业务逻辑层抛出异常:简单抛出个RuntimeException [code="java"] public void delOrg(in...

健者天行 183

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

业务系统国际化方案落地实践

国际化项目方案落地和最佳实践

大龄码农的碎碎念 1614

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

上一篇: eclipse 的jetty配置jndi数据源
下一篇: 一个通用的单元测试框架的思考和设计09-实现篇-视图操作
CrazyCoder2010
博客等级 码龄16年 298粉丝 110原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值