All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.nervousync.utils.MultilingualUtils Maven / Gradle / Ivy

There is a newer version: 1.2.1
Show newest version
/*
 * Licensed to the Nervousync Studio (NSYC) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.nervousync.utils;

import jakarta.annotation.Nonnull;
import org.nervousync.beans.ip.path.TargetPath;
import org.nervousync.commons.Globals;
import org.nervousync.i18n.MessageResource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.net.URL;
import java.net.URLDecoder;
import java.nio.charset.Charset;
import java.util.*;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**
 * 

Internationalization Utilities

*

国际化工具集

*.0 * @author Steven Wee [email protected] * @version $Revision: 1.0.0 $ $Date: Jul 19, 2023 16:39:41 $ */ public final class MultilingualUtils { /** * Logger instance * 日志实例 */ private final static Logger LOGGER = LoggerFactory.getLogger(MultilingualUtils.class); /** * Registered resources map * 已注册的资源信息映射表 */ private static final Map> REGISTERED_RESOURCES = new HashMap<>(); private static final Map IDENTIFY_KEY_MAP = new HashMap<>(); /** * Default locale instance, usually value is default locale for this instance of the Java Virtual Machine. * 默认区域设置实例,通常值是 Java 虚拟机实例的默认区域设置。 */ private static String DEFAULT_LANGUAGE_CODE = toLanguageCode(Globals.DEFAULT_LOCALE); /** * XML Schema file mapping resource path * XML约束文档的资源映射文件 */ private static final String BUNDLE_RESOURCE_PATH = "META-INF/nervousync.i18n"; private static final String MESSAGE_RESOURCE_PREFIX = "META-INF/i18n/"; static { try { ClassUtils.getDefaultClassLoader().getResources(BUNDLE_RESOURCE_PATH) .asIterator() .forEachRemaining(MultilingualUtils::registerBundle); } catch (IOException ignore) { } } /** *

Private constructor for MultilingualUtils

*

国际化工具集的私有构造方法

*/ private MultilingualUtils() { } /** *

Convert locale instance to string.

*

将语言环境实例转换为字符串。

* * @param locale locale instance * 区域设置实例 * * @return Converted string * 转换后的字符串 */ public static String toLanguageCode(@Nonnull final Locale locale) { return Optional.of(locale.getCountry()) .filter(StringUtils::notBlank) .map(countryCode -> locale.getLanguage() + "-" + countryCode) .orElse(locale.getLanguage()); } /** *

Configure default locale

*

设置默认语言

* * @param locale Default locale instance * 默认区域设置实例 */ public static void defaultLocale(@Nonnull final Locale locale) { DEFAULT_LANGUAGE_CODE = toLanguageCode(locale); } /** *

Generate multilingual agent instance

*

生成国际化代理实例对象

* * @param groupId Resource group id * 资源的组ID * @param bundle Resource bundle * 资源的标识 * * @return Generated instance * 生成的实例对象 */ public static Agent newAgent(final String groupId, final String bundle) { return new Agent(groupId, bundle); } /** *

Generate multilingual agent instance

*

生成国际化代理实例对象

* * @param clazz Class instance * 类实例对象 * * @return Generated instance * 生成的实例对象 */ public static Agent newAgent(final Class clazz) { return new Agent(clazz); } /** *

Remove message resources by given argument languageCode if registered.

*

根据给定的参数 languageCode 移除已注册的国际化信息资源

* * @param languageCode Resource language code * 资源语言代码 */ public static void removeResource(@Nonnull final String languageCode) { if (StringUtils.notBlank(languageCode)) { for (String bundle : REGISTERED_RESOURCES.keySet()) { Map bundleMap = REGISTERED_RESOURCES.getOrDefault(bundle, new HashMap<>()); bundleMap.remove(languageCode); REGISTERED_RESOURCES.put(bundle, bundleMap); } } } /** *

Remove bundle resources by given argument bundle if registered.

*

根据给定的参数 bundle 移除已注册的国际化信息资源

* * @param groupId Resource group id * 资源的组ID * @param bundle Resource bundle * 资源的标识 */ public static void removeBundle(@Nonnull final String groupId, @Nonnull final String bundle) { if (StringUtils.notBlank(groupId) && StringUtils.notBlank(bundle)) { REGISTERED_RESOURCES.remove(groupId + ":" + bundle); } } /** *

Remove message resources by given argument bundle and languageCode if registered.

*

根据给定的参数 bundle 和 languageCode 移除已注册的国际化信息资源

* * @param groupId Resource group id * 资源的组ID * @param bundle Resource bundle * 资源的标识 * @param languageCode Resource language code * 资源语言代码 */ public static void removeResource(@Nonnull final String groupId, @Nonnull final String bundle, @Nonnull final String languageCode) { if (StringUtils.notBlank(groupId) && StringUtils.notBlank(bundle) && StringUtils.notBlank(languageCode)) { String identifyKey = groupId + ":" + bundle; Map bundleMap = REGISTERED_RESOURCES.getOrDefault(identifyKey, new HashMap<>()); bundleMap.remove(languageCode); REGISTERED_RESOURCES.put(identifyKey, bundleMap); } } /** *

Register i18n message resource

*

注册国际化信息资源

* * @param identifyKey Resource identified key * 资源唯一识别码 * @param languageCode Resource language code * 资源语言代码 * @param properties Resource information properties instance * 资源信息属性实例对象 * * @return Registered result, Boolean.TRUE for success, Boolean.FALSE for failed * 注册结果,成功返回 Boolean.TRUE,失败返回 Boolean.FALSE */ private static boolean registerResource(@Nonnull final String identifyKey, @Nonnull final String languageCode, @Nonnull final Properties properties) { if (StringUtils.isEmpty(identifyKey) || properties.isEmpty()) { return Boolean.FALSE; } Map bundleMap = REGISTERED_RESOURCES.getOrDefault(identifyKey, new HashMap<>()); MessageResource messageResource; if (bundleMap.containsKey(languageCode)) { messageResource = bundleMap.get(languageCode); messageResource.updateResource(properties); } else { messageResource = new MessageResource(properties); } bundleMap.put(languageCode, messageResource); REGISTERED_RESOURCES.put(identifyKey, bundleMap); return Boolean.TRUE; } /** *

Read resource files and register

*

读取资源文件并注册

* * @param identifyKey Resource identified key * 资源唯一识别码 * @param languageCode Resource language code * 资源语言代码 * @param dataBytes Message resource data bytes * 信息资源文件二进制数组 * * @return Registered result, Boolean.TRUE for success, Boolean.FALSE for failed * 注册结果,成功返回 Boolean.TRUE,失败返回 Boolean.FALSE */ private static boolean registerResource(@Nonnull final String identifyKey, @Nonnull final String languageCode, @Nonnull final byte[] dataBytes) { try (ByteArrayInputStream inputStream = new ByteArrayInputStream(dataBytes)) { return registerResource(identifyKey, languageCode, PropertiesUtils.loadProperties(inputStream, Boolean.TRUE)); } catch (IOException e) { return Boolean.FALSE; } } /** *

Retrieve internationalization information content and formatted by given collections

*

读取国际化资源信息详情并使用给定的参数集合格式化资源信息

* * @param identifyKey Resource identified key * 资源唯一识别码 * @param messageKey Message identify key * 信息识别键值 * @param languageCode Resource language code * 资源语言代码 * @param collections given parameters of information formatter * 用于资源信息格式化的参数 * * @return Formatted resource information or joined string by character '/' if not found * 格式化的资源信息,如果未找到则返回使用'/'拼接的字符串 */ private static String findMessage(final String identifyKey, final String messageKey, final String languageCode, final Object... collections) { if (StringUtils.notBlank(identifyKey) && StringUtils.notBlank(languageCode)) { return Optional.ofNullable(REGISTERED_RESOURCES.get(identifyKey)) .map(bundleMap -> bundleMap.containsKey(languageCode) ? bundleMap.get(languageCode) : bundleMap.get(DEFAULT_LANGUAGE_CODE)) .map(messageResource -> messageResource.findMessage(messageKey, collections)) .filter(StringUtils::notBlank) .orElse(languageCode + Globals.DEFAULT_URL_SEPARATOR + messageKey); } return messageKey; } /** *

Retrieve resource identify key by given class

*

根据给定的类查找资源唯一识别码

* * @param clazz Class instance * 类实例对象 */ private static String identifyKey(final Class clazz) { String jarPath = URLDecoder.decode(clazz.getProtectionDomain().getCodeSource().getLocation().getFile(), Charset.defaultCharset()); return IDENTIFY_KEY_MAP.getOrDefault(jarPath, Globals.DEFAULT_VALUE_STRING); } private static void registerBundle(final URL url) { String basePath = url.getPath().substring(0, url.getPath().length() - BUNDLE_RESOURCE_PATH.length()); if (basePath.startsWith(FileUtils.FILE_URL_PREFIX)) { basePath = basePath.substring(FileUtils.FILE_URL_PREFIX.length()); } final String resourcePath; if (basePath.endsWith(FileUtils.JAR_URL_SEPARATOR)) { basePath = basePath.substring(0, basePath.length() - FileUtils.JAR_URL_SEPARATOR.length()); resourcePath = basePath + FileUtils.JAR_URL_SEPARATOR + MESSAGE_RESOURCE_PREFIX; } else { resourcePath = basePath + MESSAGE_RESOURCE_PREFIX; } if (LOGGER.isDebugEnabled()) { LOGGER.debug("Base resource path: {}", resourcePath); } Properties properties = new Properties(); // Map dataMap = ConvertUtils.toMap(url); try (InputStream inputStream = url.openStream()) { properties.load(inputStream); } catch (IOException e) { LOGGER.error("Load nervousync.i18n file error! "); return; } String groupId = Optional.ofNullable(properties.getProperty("groupId")).orElse(Globals.DEFAULT_VALUE_STRING); String bundle = Optional.ofNullable(properties.getProperty("bundle")).orElse(Globals.DEFAULT_VALUE_STRING); String languages = Optional.ofNullable(properties.getProperty("languages")).orElse(Globals.DEFAULT_VALUE_STRING); if (!groupId.isEmpty() && !bundle.isEmpty() && !languages.isEmpty()) { String identifyKey = groupId + ":" + bundle; IDENTIFY_KEY_MAP.put(basePath, identifyKey); while (!languages.isEmpty()) { int index = languages.indexOf(","); String languageCode = (index > 0) ? languages.substring(0, index) : languages; languages = languages.substring(languageCode.length()); if (languages.startsWith(",")) { languages = languages.substring(1); } languageCode = languageCode.trim(); String filePath = resourcePath + languageCode + ".xml"; boolean result = registerResource(identifyKey, languageCode, readBytes(filePath)); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Group ID: {}, bundle: {}, language: {}, result: {}", groupId, bundle, languageCode, result); } } } } private static byte[] readBytes(final String filePath) { TargetPath targetPath = TargetPath.parse(filePath); if (targetPath == null) { try (InputStream inputStream = new FileInputStream(filePath)) { return readBytes(inputStream); } catch (IOException ignored) { } } else { try (JarFile jarFile = new JarFile(new File(targetPath.getFilePath()))) { JarEntry packageEntry = jarFile.getJarEntry(targetPath.getEntryPath()); if (packageEntry != null) { return readBytes(jarFile.getInputStream(packageEntry)); } } catch (IOException ignored) { } } return new byte[0]; } private static byte[] readBytes(final InputStream inputStream) { byte[] dataBytes; int readLength; byte[] readBuffer = new byte[Globals.READ_FILE_BUFFER_SIZE]; try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) { while ((readLength = inputStream.read(readBuffer)) != Globals.DEFAULT_VALUE_INT) { byteArrayOutputStream.write(readBuffer, Globals.INITIALIZE_INT_VALUE, readLength); } dataBytes = byteArrayOutputStream.toByteArray(); } catch (IOException e) { LOGGER.error("Read data error! "); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Stack message: ", e); } dataBytes = new byte[0]; } return dataBytes; } /** *

Multilingual Agent

*

国际化代理

*/ public static final class Agent { /** * Resource identified key * 资源唯一识别码 */ private final String identifyKey; /** *

Constructor method for MultilingualUtils.Agent

*

国际化代理的构造方法

* * @param groupId Resource group id * 资源的组ID * @param bundle Resource bundle * 资源的标识 */ private Agent(final String groupId, final String bundle) { this.identifyKey = groupId + ":" + bundle; } /** *

Constructor method for MultilingualUtils.Agent

*

国际化代理的构造方法

* * @param clazz Class instance * 类实例对象 */ private Agent(final Class clazz) { this.identifyKey = MultilingualUtils.identifyKey(clazz); } /** *

Retrieve internationalization information content and formatted by given collections

*

读取国际化资源信息详情并使用给定的参数集合格式化资源信息

* * @param messageKey Message identify key * 信息识别键值 * @param collections given parameters of information formatter * 用于资源信息格式化的参数 * * @return Formatted resource information or joined string by character '/' if not found * 格式化的资源信息,如果未找到则返回使用'/'拼接的字符串 */ public String findMessage(final String messageKey, final Object... collections) { return MultilingualUtils.findMessage(this.identifyKey, messageKey, DEFAULT_LANGUAGE_CODE, collections); } /** *

Retrieve internationalization information content and formatted by given collections

*

读取国际化资源信息详情并使用给定的参数集合格式化资源信息

* * @param messageKey Message identify key * 信息识别键值 * @param locale locale instance * 区域设置实例 * @param collections given parameters of information formatter * 用于资源信息格式化的参数 * * @return Formatted resource information or joined string by character '/' if not found * 格式化的资源信息,如果未找到则返回使用'/'拼接的字符串 */ public String findMessage(final String messageKey, final Locale locale, final Object... collections) { return MultilingualUtils.findMessage(this.identifyKey, messageKey, toLanguageCode(locale), collections); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy