本地国际化实现 — Flea I18N

百度百科针对 国际化 的解释:

本地国际化,就是指应用程序根据所处语言环境的不同【如 Java 中可用 国际化标识类 java.util.Locale 区分不同语言环境】,自动匹配应用内置的相应的语言环境下的资源配置【如 Java 中可用 资源包类 java.util.ResourceBundle 来匹配】,从而获取并对外展示相应的语言环境下的资源信息。

话不多说,直接上干货:

1. 依赖

 <!-- FLEA COMMON--><dependency><groupId>com.huazie.fleaframework</groupId><artifactId>flea-common</artifactId><version>2.0.0</version></dependency>

2. 实现

上面提到了 Java 中 的 国际化标识类 java.util.Locale资源包类 java.util.ResourceBundle,这两者就是本地国际化实现的关键所在。

2.1 定义国际化资源相关配置 — flea-config.xml

这里用于特殊配置国际化资源的路径和文件前缀。

<flea-config><!-- flea-common --><config-items key="flea-i18n-config" desc="Flea国际化相关配置"><config-item key="error" desc="error国际化资源特殊配置,指定路径和文件前缀,逗号分隔">flea/i18n,flea_i18n</config-item></config-items>
</flea-config>

2.2 定义Flea I18N 配置类 — FleaI18nConfig

在使用 FleaI18nConfig 之前,我们先了解下Flea国际化资源文件的组成,主要有如下 5 部分:


上述国际化资源也可以配置默认资源文件,即文件名中不需要包含国际化标识 。例如: flea/i18n/flea_i18n_error.properties

注意: 国际化资源文件扩展名必须为 properties

好了,基础的认知有了,我们开始了解 FleaI18nConfig,如下贴出了实现:

/*** Flea I18N 配置类,用于获取指定语言环境下的指定资源对应的国际化数据。** <p> 它默认读取资源路径为 flea/i18n,资源文件前缀为 flea_i18n,当然* 也可以在 flea-config.xml 中为指定资源文件配置路径和前缀,从而可以* 实现读取任意位置的资源数据。** @author huazie* @version 2.0.0* @since 1.0.0*/
public class FleaI18nConfig {private static final FleaLogger LOGGER = FleaLoggerProxy.getProxyInstance(FleaI18nConfig.class);private static volatile FleaI18nConfig config;private ConcurrentMap<String, String> resFilePath = new ConcurrentHashMap<>(); // 资源文件路径集private ConcurrentMap<String, ResourceBundle> resources = new ConcurrentHashMap<>(); // 资源集/*** 只允许通过 getConfig() 获取 Flea I18N 配置类实例*/private FleaI18nConfig() {init(); // 初始化资源文件相关配置}/*** 获取 Flea I18N 配置类实例** @return Flea I18N 配置类实例* @since 1.0.0*/public static FleaI18nConfig getConfig() {if (ObjectUtils.isEmpty(config)) {synchronized (FleaI18nConfig.class) {if (ObjectUtils.isEmpty(config)) {config = new FleaI18nConfig();}}}return config;}/*** 初始化资源名和资源文件相关属性的映射关系** @since 1.0.0*/private void init() {ConfigItems fleaI18nItems = FleaConfigManager.getConfigItems(CommonConstants.FleaI18NConstants.FLEA_I18N_CONFIG_ITEMS_KEY);if (ObjectUtils.isNotEmpty(fleaI18nItems) && CollectionUtils.isNotEmpty(fleaI18nItems.getConfigItemList())) {for (ConfigItem configItem : fleaI18nItems.getConfigItemList()) {if (ObjectUtils.isNotEmpty(configItem) && StringUtils.isNotBlank(configItem.getKey()) && StringUtils.isNotBlank(configItem.getValue())) {String[] valueArr = StringUtils.split(configItem.getValue(), CommonConstants.SymbolConstants.COMMA);if (ArrayUtils.isNotEmpty(valueArr) && CommonConstants.NumeralConstants.INT_TWO == valueArr.length) {// 获取资源文件路径String filePath = StringUtils.trim(valueArr[0]);// 获取资源文件前缀String fileNamePrefix = StringUtils.trim(valueArr[1]);if (StringUtils.isNotBlank(filePath) && StringUtils.isNotBlank(fileNamePrefix)) {String configResFilePath;// 如果资源文件路径最后没有 "/",自动添加if (CommonConstants.SymbolConstants.SLASH.equals(StringUtils.subStrLast(filePath, 1))) {configResFilePath = filePath + fileNamePrefix;} else {configResFilePath = filePath + CommonConstants.SymbolConstants.SLASH + fileNamePrefix;}resFilePath.put(configItem.getKey(), configResFilePath);}}}}}// 添加默认资源文件路径String defaultResFilePath = CommonConstants.FleaI18NConstants.FLEA_I18N_FILE_PATH +CommonConstants.FleaI18NConstants.FLEA_I18N_FILE_NAME_PREFIX; // 默认资源文件路径(仅包含公共的部分)resFilePath.put(CommonConstants.SymbolConstants.ASTERISK, defaultResFilePath);}/*** 通过国际化数据的key,获取当前系统指定资源的国际化资源;* 其中国际化资源中使用 {} 标记的,需要values中的数据替换。** @param key     国际化资源KEY* @param values  待替换字符串数组* @param resName 资源名* @param locale  国际化标识* @return 国际化资源数据* @since 2.0.0*/public FleaI18nData getI18NData(String key, String[] values, String resName, Locale locale) {return new FleaI18nData(key, this.getI18NDataValue(key, values, resName, locale));}/*** 通过国际化数据的key,获取当前系统指定资源的国际化资源** @param key     国际化资源KEY* @param resName 资源名* @param locale  国际化标识* @return 国际化资源数据* @since 1.0.0*/public FleaI18nData getI18NData(String key, String resName, Locale locale) {return new FleaI18nData(key, this.getI18NDataValue(key, resName, locale));}/*** <p> 通过国际化数据的key,获取当前系统指定资源的国际化资源数据 </p>** @param key     国际化资源KEY* @param values  国际化资源数据替换内容* @param resName 资源名* @param locale  国际化标识* @return 国际化资源数据* @since 1.0.0*/public String getI18NDataValue(String key, String[] values, String resName, Locale locale) {String value = getI18NDataValue(key, resName, locale);if (ArrayUtils.isNotEmpty(values)) {StringBuilder builder = new StringBuilder(value);for (int i = 0; i < values.length; i++) {StringUtils.replace(builder, CommonConstants.SymbolConstants.LEFT_CURLY_BRACE + i + CommonConstants.SymbolConstants.RIGHT_CURLY_BRACE, values[i]);}value = builder.toString();}return value;}/*** <p> 通过国际化数据的key,获取当前系统指定资源的国际化资源数据 </p>** @param key     国际化资源KEY* @param resName 资源名* @param locale  国际化标识* @return 国际化资源数据* @since 1.0.0*/public String getI18NDataValue(String key, String resName, Locale locale) {Object obj = null;if (LOGGER.isDebugEnabled()) {obj = new Object() {};LOGGER.debug1(obj, "Find the key     : {}", key);LOGGER.debug1(obj, "Find the resName : {}", resName);LOGGER.debug1(obj, "Find the locale  : {} , {}", locale == null ? Locale.getDefault() : locale, locale == null ? Locale.getDefault().getDisplayLanguage() : locale.getDisplayLanguage());}ResourceBundle resource = getResourceBundle(resName, locale);String value = null;if (ObjectUtils.isNotEmpty(resource)) {value = resource.getString(key);if (StringUtils.isBlank(value)) { // 如果取不到数据,则使用key返回value = key;}}if (LOGGER.isDebugEnabled()) {LOGGER.debug1(obj, "Find the value   : {} ", value);}return value;}/*** <p> 根据资源名和国际化标识获取指定国际化配置ResourceBundle对象 </p>** @param resName 资源名* @param locale  国际化标识* @return 国际化配置ResourceBundle对象* @since 1.0.0*/private ResourceBundle getResourceBundle(String resName, Locale locale) {String key = generateKey(resName, locale);Object obj = null;if (LOGGER.isDebugEnabled()) {obj = new Object() {};LOGGER.debug1(obj, "Find the resKey  : {}", key);}ResourceBundle resource = resources.get(key);// 获取资源文件名StringBuilder fileName = new StringBuilder(getResFilePath(resName));if (StringUtils.isNotBlank(resName)) {fileName.append(CommonConstants.SymbolConstants.UNDERLINE).append(resName);}if (LOGGER.isDebugEnabled()) {if (ObjectUtils.isEmpty(locale)) {LOGGER.debug1(obj, "Find the expected fileName: {}.properties", fileName);} else {LOGGER.debug1(obj, "Find the expected fileName: {}_{}.properties", fileName, locale);}}// 获取资源文件if (ObjectUtils.isEmpty(resource)) {if (ObjectUtils.isEmpty(locale)) {resource = ResourceBundle.getBundle(fileName.toString());} else {resource = ResourceBundle.getBundle(fileName.toString(), locale);}resources.put(key, resource);}if (LOGGER.isDebugEnabled()) {Locale realLocale = resource.getLocale();if (ObjectUtils.isEmpty(locale) || StringUtils.isBlank(realLocale.toString())) {LOGGER.debug1(obj, "Find the real fileName: {}.properties", fileName);} else {LOGGER.debug1(obj, "Find the real fileName: {}_{}.properties", fileName, realLocale);}}return resource;}/*** <p> 获取国际化资源文件KEY </p>* <p> 如果资源名不为空,则资源名作为key,同时如果国际化标识不为空,则取资源名+下划线+国际化语言作为key;** @param resName 资源名* @param locale  国际化标识* @return 国际化资源文件KEY* @since 1.0.0*/private String generateKey(String resName, Locale locale) {String key = "";if (StringUtils.isNotBlank(resName)) {key = resName;if (ObjectUtils.isNotEmpty(locale)) {key += CommonConstants.SymbolConstants.UNDERLINE + locale;}}return key;}/*** <p> 根据资源名,获取资源文件路径 </p>** @param resName 资源名* @return 资源文件路径* @since 1.0.0*/private String getResFilePath(String resName) {// 首先根据资源名,从 资源文件路径集中获取String resFilePathStr = resFilePath.get(resName);if (ObjectUtils.isEmpty(resFilePathStr)) {// 取默认资源文件路径resFilePathStr = resFilePath.get(CommonConstants.SymbolConstants.ASTERISK);}return resFilePathStr;}}

2.3 定义Flea I18N 工具类 — FleaI18nHelper

Flea I18N 工具类 封装了 I18N 资源数据获取的静态方法,主要包含如下4种:

 public static String i18n(String key, String resName, Locale locale) {return FleaI18nConfig.getConfig().getI18NDataValue(key, resName, locale);}public static String i18n(String key, String[] values, String resName, Locale locale) {return FleaI18nConfig.getConfig().getI18NDataValue(key, values, resName, locale);}// 实际在调用该方法之前,可以通过 FleaFrameManager.getManager().setLocale(Locale) 设置当前线程的国际化标识。public static String i18n(String key, String resName) {return i18n(key, resName, FleaFrameManager.getManager().getLocale());}// 实际在调用该方法之前,可以通过 FleaFrameManager.getManager().setLocale(Locale) 设置当前线程的国际化标识。public static String i18n(String key, String[] values, String resName) {return i18n(key, values, resName, FleaFrameManager.getManager().getLocale());}// 其他是对具体资源的封装,如错误码资源error、授权资源auth 和 公共信息资源common

2.4 定义Flea I18N资源枚举 — FleaI18nResEnum

/*** Flea I18N 资源枚举** @author huazie* @version 1.0.0* @since 1.0.0*/
public enum FleaI18nResEnum {ERROR("error", "异常信息国际码资源文件类型"),ERROR_CORE("error_core", "FLEA CORE异常信息国际码资源文件类型"),ERROR_DB("error_db", "FLEA DB异常信息国际码资源文件类型"),ERROR_JERSEY("error_jersey", "FLEA JERSEY异常信息国际码资源文件类型"),ERROR_AUTH("error_auth", "FLEA AUTH异常信息国际码资源文件类型"),AUTH("auth", "FLEA AUTH 国际码资源文件类型"),COMMON("common", "公共信息国际码资源文件类型");private String resName;private String resDesc;/*** <p> 资源文件类型枚举构造方法 </p>** @param resName 资源名* @param resDesc 资源描述* @since 1.0.0*/FleaI18nResEnum(String resName, String resDesc) {this.resName = resName;this.resDesc = resDesc;}public String getResName() {return resName;}public String getResDesc() {return resDesc;}}

简单的介绍之后,初步了解了本地国际化的实现,下面就需要来实际测试一下了。

话不多说,开始操刀:

3. 自测

首先,我们先添加几个国际化配置文件,如下:

资源文件 国际化标识(语言环境)
flea/i18n/flea_i18n_error.properties 默认
flea/i18n/flea_i18n_error_zh_CN.properties 中文(简体)
flea/i18n/flea_i18n_error_en_US.properties 英文(美式)

注意: 笔者电脑的本地语言环境为 中文(简体)

3.1 匹配指定语言

 @Testpublic void fleaI18nHelperTest1() {String value = FleaI18nHelper.i18n("ERROR0000000001", "error", Locale.US);LOGGER.debug("Value = {}", value);}

测试结果:

3.2 匹配本地语言

 @Testpublic void fleaI18nHelperTest() {String value = FleaI18nHelper.i18n("ERROR0000000001", "error", Locale.FRANCE);LOGGER.debug("Value = {}", value);}

测试结果:

3.3 匹配默认资源

首先,我们将本地语言的资源文件删除,如下:

 @Testpublic void fleaI18nHelperTest() {String value = FleaI18nHelper.i18n("ERROR0000000001", "error", Locale.FRANCE);LOGGER.debug("Value = {}", value);}

测试结果:

3.4 无资源匹配

首先,我们将本地语言默认 的 资源文件删除,如下:

 @Testpublic void fleaI18nHelperTest() {String value = FleaI18nHelper.i18n("ERROR0000000001", "error", Locale.FRANCE);LOGGER.debug("Value = {}", value);}

测试结果:

4. 接入

上面演示了 如何通过 FleaI18nHelper 获取本地国际化的资源数据,下面我们来看看在异常类中接入错误码国际化资源。

4.1 定义通用异常类 — CommonException

/*** Flea I18N 通用异常,由子类传入具体的国际化资源枚举类型** @author huazie* @version 1.0.0* @since 1.0.0*/
public abstract class CommonException extends Exception {private static final long serialVersionUID = 1746312829236028651L;private String key;                     // 国际化资源数据关键字private Locale locale;                  // 国际化区域标识private FleaI18nResEnum i18nResEnum;    // 国际化资源类型public CommonException(String mKey, FleaI18nResEnum mI18nResEnum) {// 使用服务器当前默认的国际化区域设置this(mKey, mI18nResEnum, FleaFrameManager.getManager().getLocale());}public CommonException(String mKey, FleaI18nResEnum mI18nResEnum, String... mValues) {// 使用服务器当前默认的国际化区域设置this(mKey, mI18nResEnum, FleaFrameManager.getManager().getLocale(), mValues);}public CommonException(String mKey, FleaI18nResEnum mI18nResEnum, Locale mLocale) {// 使用指定的国际化区域设置this(mKey, mI18nResEnum, mLocale, new String[]{});}public CommonException(String mKey, FleaI18nResEnum mI18nResEnum, Locale mLocale, String... mValues) {// 使用指定的国际化区域设置super(convert(mKey, mValues, mI18nResEnum, mLocale));key = mKey;locale = mLocale;i18nResEnum = mI18nResEnum;}public CommonException(String mKey, FleaI18nResEnum mI18nResEnum, Throwable cause) {// 使用服务器当前默认的国际化区域设置this(mKey, mI18nResEnum, FleaFrameManager.getManager().getLocale(), cause);}public CommonException(String mKey, FleaI18nResEnum mI18nResEnum, Throwable cause, String... mValues) {// 使用服务器当前默认的国际化区域设置this(mKey, mI18nResEnum, FleaFrameManager.getManager().getLocale(), cause, mValues);}public CommonException(String mKey, FleaI18nResEnum mI18nResEnum, Locale mLocale, Throwable cause) {// 使用指定的国际化区域设置this(mKey, mI18nResEnum, mLocale, cause, new String[]{});}public CommonException(String mKey, FleaI18nResEnum mI18nResEnum, Locale mLocale, Throwable cause, String... mValues) {// 使用指定的国际化区域设置super(convert(mKey, mValues, mI18nResEnum, mLocale), cause);key = mKey;locale = mLocale;i18nResEnum = mI18nResEnum;}private static String convert(String key, String[] values, FleaI18nResEnum i18nResEnum, Locale locale) {if (ObjectUtils.isEmpty(locale)) {locale = FleaFrameManager.getManager().getLocale(); // 使用当前线程默认的国际化区域设置}if (ObjectUtils.isEmpty(i18nResEnum)) {i18nResEnum = FleaI18nResEnum.ERROR; // 默认使用 国际化资源名为 error}if (ArrayUtils.isNotEmpty(values)) {return FleaI18nHelper.i18n(key, values, i18nResEnum.getResName(), locale);} else {return FleaI18nHelper.i18n(key, i18nResEnum.getResName(), locale);}}public String getKey() {return key;}public Locale getLocale() {return locale;}public FleaI18nResEnum getI18nResEnum() {return i18nResEnum;}
}

4.2 定义业务逻辑层异常类 — ServiceException

/*** 业务逻辑层异常类,定义了业务逻辑层抛出的异常,* 其对应的国际化资源名为【error】** @author huazie* @version 1.0.0* @since 1.0.0*/
public class ServiceException extends CommonException {public ServiceException(String key) {super(key, FleaI18nResEnum.ERROR);}public ServiceException(String key, String... values) {super(key, FleaI18nResEnum.ERROR, values);}public ServiceException(String key, Throwable cause) {super(key, FleaI18nResEnum.ERROR, cause);}public ServiceException(String key, Throwable cause, String... values) {super(key, FleaI18nResEnum.ERROR, cause, values);}}

好了,Flea框架下的本地国际化实现已经介绍完毕,欢迎大家使用!

flea-common使用之本地国际化实现相关推荐

  1. Swift之本地国际化与App内切换语言

    添加语言库 点击项目 -> PROJECT -> Info -> Localizations -> + , 添加需要的语言; 添加完成的语言库如下所示: 创建Localizab ...

  2. ios开发语言本地国际化_开发人员软件本地化最终语言指南

    ios开发语言本地国际化 There are lots of great guides out there for how to prep your product for international ...

  3. 三招通过Apollo和nacos的能力进行国际化热更新

    通过Apollo和nacos的能力进行国际化热更新 1.apollo的自动刷新 Apollo(阿波罗)是一款可靠的分布式配置管理中心,有了它,我们可以用来做很多事情:配置的热更新,配置监听,灰度发布, ...

  4. 07-PDI(Kettle)源码编译8.2.0.0.R版本

    文章目录 07-PDI(Kettle)源码编译8.2.0.0.R版本 1.安装PDI8.2.0.0.R的parent工程到本地 1.1配置Maven的settings.xml文件 1.2安装PDI源码 ...

  5. 2022Java面试题,非常全面

    JAVA基础 1.基本数据类型 : byte short int long float double char boolean byte char short 平级 int float long do ...

  6. 6426C Lab6 部署和配置RMS

    共5个练习 练习1:安装和配置AD RMS (附加)练习A1:测试AD RMS的功能 练习2:配置AD RMS模板 练习3:配置AD RMS信任策略 练习4:测试AD RMS功能 练习5:生成AD R ...

  7. AFNetworking 3.0 源码解读(一)之 AFNetworkReachabilityManager

    做ios开发,AFNetworking 这个网络框架肯定都非常熟悉,也许我们平时只使用了它的部分功能,而且我们对它的实现原理并不是很清楚,就好像总是有一团迷雾在眼前一样. 接下来我们就非常详细的来读一 ...

  8. 部署Rsync服务器-差异化数据同步

    目录: 1.搭建Rsync服务器 2.客户端访问 3.同步备份数据脚本 Rsync(remote sync)是Unix及类Unix平台下一款神奇的数据镜像备份软件,她不像FTP或其他文件传输服务那样需 ...

  9. 从零开始带你一步一步使用YOLOv3训练自己的数据

    红色石头的个人网站:redstonewill.com 知乎:https://www.zhihu.com/people/red_stone_wl 公众号:AI有道(redstonewill) YOLOv ...

  10. redis 集群 实操 (史上最全、5w字长文)

    文章很长,建议收藏起来慢慢读! 总目录 博客园版 为大家准备了更多的好文章!!!! 推荐:尼恩Java面试宝典(持续更新 + 史上最全 + 面试必备)具体详情,请点击此链接 尼恩Java面试宝典,34 ...

最新文章

  1. Linux下 apache 配置 wsgi 以使用 python-flask (2016年2月)
  2. 使用windbg定位内存问题【入门级】
  3. response.sendRedirect()重新定向的乱码问题
  4. __clone class php_PHP 对象克隆 clone 关键字与 __clone() 方法
  5. 编译Android源码前的一个步骤
  6. R-FCN/Faster-rcnn使用snapshot继续训练
  7. pyecharts第二节、饼图
  8. 计算机网络 | 应用层 :HTTP协议详解
  9. 【EasyUI】关于EasyUI中numberbox onblur事件失效的问题
  10. 40种Javascript中常用的使用小技巧【转】
  11. 键盘出现与消失的监听方法
  12. iPhone 15 Pro有望实现屏下面容识别 三星已在研发相关技术
  13. 超详细的QSS样式表入门Demo
  14. [保姆级教程] 从原理到应用,超级详细的MPU6050传感器整理,看完这一篇就够了
  15. java 2048思路_浅谈2048
  16. 学计算机做纸质笔记,详细图文教你康奈尔大学推荐的超级笔记法,只要一张A4纸张,你也可以做学霸...
  17. 张小龙:通过微信谈产品
  18. 光纤熔接操作步骤——详细图文光纤熔接教程
  19. 饿了吗 ui 行拖拽处理
  20. 第5章-CSS盒子模型

热门文章

  1. 庞贝古城千年废墟复活:VR模型与眼动追踪复现被火山灰掩埋的建筑
  2. Tacotron2 NVIDIA版本使用Biao-Bei数据集
  3. android 录屏工具,ShareREC for Android全系统手机录屏软件原理解析
  4. 雪花飘落代码java_个人网站html5雪花飘落代码JS特效下载
  5. 三天打鱼两天晒网问题
  6. iphone导出视频 无法连接到设备_iPhone内存不足?深度清理方法了解一下
  7. powershell 汉洛塔
  8. mfc treectrl设置背景透明_微信透明头像怎么弄 专用透明头像图片更换设置教程闽南网...
  9. matlab微积分如何计算器,如何用matlab对这个函数进行积分。 请问这个公式是如何算出来的?使用微积分吗?...
  10. 2db多少功率_功率换算(dB与W).doc