/** * Copyright French Prime minister Office/SGMAP/DINSIC/Vitam Program (2015-2019) * <p> * contact.vitam@culture.gouv.fr * <p> * This software is a computer program whose purpose is to implement a digital archiving back-office system managing * high volumetry securely and efficiently. * <p> * This software is governed by the CeCILL 2.1 license under French law and abiding by the rules of distribution of free * software. You can use, modify and/ or redistribute the software under the terms of the CeCILL 2.1 license as * circulated by CEA, CNRS and INRIA at the following URL "http://www.cecill.info". * <p> * As a counterpart to the access to the source code and rights to copy, modify and redistribute granted by the license, * users are provided only with a limited warranty and the software's author, the holder of the economic rights, and the * successive licensors have only limited liability. * <p> * In this respect, the user's attention is drawn to the risks associated with loading, using, modifying and/or * developing or reproducing the software by the user in light of its specific status of free software, that may mean * that it is complicated to manipulate, and that also therefore means that it is reserved for developers and * experienced professionals having in-depth computer knowledge. Users are therefore encouraged to load and test the * software's suitability as regards their requirements in conditions enabling the security of their systems and/or data * to be ensured and, more generally, to use and operate it in the same conditions as regards security. * <p> * The fact that you are presently reading this means that you have had knowledge of the CeCILL 2.1 license and that you * accept its terms. */ package fr.gouv.vitam.common.i18n; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.URL; import java.net.URLConnection; import java.text.MessageFormat; import java.util.HashMap; import java.util.Locale; import java.util.Map; import java.util.MissingResourceException; import java.util.PropertyResourceBundle; import java.util.ResourceBundle; import java.util.ResourceBundle.Control; import fr.gouv.vitam.common.CharsetUtils; import fr.gouv.vitam.common.PropertiesUtils; import fr.gouv.vitam.common.logging.SysErrLogger; /** * Internationalization Messages support */ public class Messages { private static final String LFC_PREFIX = "LFC."; private final String bundleName; /** * Default Locale */ public static final Locale DEFAULT_LOCALE = Locale.FRENCH; private final ResourceBundle resourceBundle; private Locale locale; /** * Constructor using default Locale (FRENCH) * * @param bundleName */ public Messages(String bundleName) { this(bundleName, DEFAULT_LOCALE); } /** * Constructor * * @param bundleName * @param locale */ public Messages(String bundleName, Locale locale) { this.bundleName = bundleName; this.locale = locale; resourceBundle = init(); } /** * Enable UTF-8 Property files */ private static final class UTF8Control extends Control { /** * Specific constructor of RessourceBundler * * @param baseName * @param locale * @param format * @param loader * @param reload */ @Override public ResourceBundle newBundle(String baseName, Locale locale, String format, ClassLoader loader, boolean reload) throws IllegalAccessException, InstantiationException, IOException { // The below is a copy of the default implementation. final String bundleName = toBundleName(baseName, locale); final String resourceName = toResourceName(bundleName, "properties"); ResourceBundle bundle = null; InputStream stream = null; if (reload) { final URL url = loader.getResource(resourceName); if (url != null) { final URLConnection connection = url.openConnection(); if (connection != null) { connection.setUseCaches(false); stream = connection.getInputStream(); } } } else { stream = loader.getResourceAsStream(resourceName); } if (stream != null) { try { // Only this line is changed to make it to read properties files as UTF-8. bundle = new PropertyResourceBundle(new InputStreamReader(stream, CharsetUtils.UTF8)); } finally { stream.close(); } } return bundle; } } /** * Change the Message to the given Locale * * @param locale */ private final ResourceBundle init() { if (locale == null) { locale = Locale.FRENCH; } // First check if this file is in config directory final File bundleFile = PropertiesUtils.fileFromConfigFolder(bundleName + "_" + locale.toLanguageTag() + ".properties"); if (bundleFile.canRead()) { try (FileInputStream inputStream = new FileInputStream(bundleFile)) { return new PropertyResourceBundle(new InputStreamReader(inputStream, CharsetUtils.UTF8)); } catch (final IOException e) { SysErrLogger.FAKE_LOGGER.ignoreLog(e); } } // If necessary update Static enum of VitamCode return ResourceBundle.getBundle(bundleName, locale, new UTF8Control()); } /** * Retrieve all the messages * * @return map of messages */ public Map<String, String> getAllMessages() { final Map<String, String> bundleMap = new HashMap<>(); for (final String key : resourceBundle.keySet()) { final String value = resourceBundle.getString(key); bundleMap.put(key, value); } return bundleMap; } /** * * @param key the key of the message * @return the associated message */ public final String getString(String key) { try { return resourceBundle.getString(key); } catch (final MissingResourceException e) { SysErrLogger.FAKE_LOGGER.ignoreLog(e); return getFakeMessage(key); } } /** * * @param key the key of the message * @param args the arguments to use as MessageFormat.format(mesg, args) * @return the associated message */ public final String getString(String key, Object... args) { try { final String source = resourceBundle.getString(key); return MessageFormat.format(source, args); } catch (final MissingResourceException e) { SysErrLogger.FAKE_LOGGER.ignoreLog(e); return getFakeMessage(key, args); } } /** * return a fake message when the message is unknown in the property file * * @param key * @param args * @return the default fake message using the key */ private String getFakeMessage(String key, Object... args) { final StringBuilder builder = new StringBuilder("!").append(key).append('!'); if (args != null) { for (final Object object : args) { builder.append(" ").append(object); } } return builder.toString(); } /** * * @param key the key of the message * @param args the arguments to use as MessageFormat.format(mesg, args) * @return the associated message, !key! if value is null or empty */ public final String getStringNotEmpty(String key, Object... args) { try { final String source = resourceBundle.getString(key); if (source == null || source.isEmpty()) { // find in plugin message properties return getFakeMessage(key, args); } return MessageFormat.format(source, args); } catch (final MissingResourceException e) { SysErrLogger.FAKE_LOGGER.ignoreLog(e); // Remove the LFC prefix to get message value in plugin properties String pluginKey = ""; if (key.contains(LFC_PREFIX)) { pluginKey = key.replace(LFC_PREFIX, ""); } else { pluginKey = key; } final String source = PluginPropertiesLoader.getString(pluginKey); if (source == null || source.isEmpty()) { // Cannot find any resource or value for this key return getFakeMessage(key, args); } return MessageFormat.format(source, args); } } /** * * @return the current Locale */ public Locale getLocale() { return locale; } }