flea-common使用之本地国际化实现

Posted Huazie

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了flea-common使用之本地国际化实现相关的知识,希望对你有一定的参考价值。

本地国际化实现 — 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-common使用之本地国际化实现的主要内容,如果未能解决你的问题,请参考以下文章

怎样实现WPF Prism Module的国际化和本地化?

iOS开发之本地化国际化语言设置

JavaWeb-国际化之ResourceBundle

实现iOS语言本地化/国际化

3分钟实现iOS语言本地化/国际化(图文详解)

按测试地域的角度划分:国际化测试本地化测试