/** * The contents of this file are subject to the license and copyright * detailed in the LICENSE file at the root of the source * tree and available online at * * https://github.com/keeps/roda */ package org.roda.core.common; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.nio.charset.Charset; import java.nio.file.Files; import java.nio.file.Path; import java.util.Enumeration; 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 org.apache.commons.io.IOUtils; import org.roda.core.data.common.RodaConstants; import org.roda.core.storage.fs.FSUtils; public class Messages { private static final String MESSAGES_BUNDLE = "ServerMessages"; private ResourceBundle resourceBundle; private Map<String, Map<String, Object>> translationsCache; public Messages(Locale locale, Path folder) { this.resourceBundle = ResourceBundle.getBundle(MESSAGES_BUNDLE, locale, new FolderBasedUTF8Control(folder)); this.translationsCache = new HashMap<>(); } /** * Get translation * * @param key * @return */ public String getTranslation(String key) { return resourceBundle.getString(key); } public String getTranslationWithArgs(String key, Object... args) { return String.format(getTranslation(key), args); } public String getTranslation(String key, String fallback) { String ret; try { ret = getTranslation(key); } catch (MissingResourceException e) { ret = fallback; } return ret; } /** * * prefix will be replaced by "i18n." for simplicity purposes */ public <T extends Object> Map<String, T> getTranslations(String prefix, Class<T> valueClass, boolean replacePrefixFromKey) { // try cache first if (translationsCache.get(prefix) != null) { return (Map<String, T>) translationsCache.get(prefix); } Map<String, T> map = new HashMap<>(); Enumeration<String> keys = resourceBundle.getKeys(); String fullPrefix = prefix + "."; while (keys.hasMoreElements()) { String key = keys.nextElement(); if (key.startsWith(fullPrefix)) { map.put(replacePrefixFromKey ? key.replaceFirst(fullPrefix, "i18n.") : key, valueClass.cast(resourceBundle.getString(key))); } } // cache it translationsCache.put(prefix, (Map<String, Object>) map); return map; } private class FolderBasedUTF8Control extends Control { private static final String CONFIG_I18N_PATH = "/config/i18n/"; private Path folder; public FolderBasedUTF8Control(Path folder) { this.folder = folder; } @Override public long getTimeToLive(String baseName, Locale locale) { // ask not to cache return TTL_DONT_CACHE; } @Override public Locale getFallbackLocale(String baseName, Locale locale) { if (baseName == null) { throw new NullPointerException(); } // 20160712 hsilva: the following line is needed otherwise default locale // is used and this can be incoherent with other parts of the code where // the default locale is ENGLISH Locale defaultLocale = Locale.ENGLISH; return locale.equals(defaultLocale) ? null : defaultLocale; } @Override public ResourceBundle newBundle(String baseName, Locale locale, String format, ClassLoader loader, boolean reload) throws IllegalAccessException, InstantiationException, IOException { if (!"java.properties".equals(format)) { return null; } String bundleName = toBundleName(baseName, locale) + ".properties"; ResourceBundle bundle = null; InputStreamReader reader = null; InputStream is = null; try { Path bundlePath = folder.resolve(bundleName); // Also checks for file existence if (FSUtils.exists(bundlePath)) { is = Files.newInputStream(bundlePath); } else { is = this.getClass().getResourceAsStream(CONFIG_I18N_PATH + bundleName); } reader = new InputStreamReader(is, Charset.forName(RodaConstants.DEFAULT_ENCODING)); bundle = new PropertyResourceBundle(reader); } finally { IOUtils.closeQuietly(reader); IOUtils.closeQuietly(is); } return bundle; } } }