church.i18n.resources.bundles.PolyglotMultiResourceBundle Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of church.i18n.resources.bundles Show documentation
Show all versions of church.i18n.resources.bundles Show documentation
Project name: church.i18n.resources.bundles
Improved Resource bundles that are able to hold multiple different localization files and provide
functionality to handle multiple language mutations at once.
.
The newest version!
/*
* Copyright (c) 2019 Juraj Jurčo
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
* associated documentation files (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge, publish, distribute,
* sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or
* substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
* NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package church.i18n.resources.bundles;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Holder of different resource bundles for multiple languages. The class allows adding resource
* bundles 'on the fly' and loads locales lazily, when they are required.
*/
public class PolyglotMultiResourceBundle implements MultiResourceBundle {
private static final Logger log = LoggerFactory.getLogger(PolyglotMultiResourceBundle.class);
private final Map locales = new HashMap<>();
private final List resources = new ArrayList<>();
private final ReadWriteLock lock = new ReentrantReadWriteLock();
private final Locale defaultLocale;
/**
* Load multiple resource bundles with messages.
*
* @param defaultLocale Set default locale that will be used by this class. If {@code null},
* {@link Locale#getDefault()} is used instead.
* @param resourceFilenames List of resource bundle names to load. If you do not specify any file,
* no message will be retrieved. You can freely add resource files later.
* The order of added resource bundles is respected during message
* lookup.
*/
public PolyglotMultiResourceBundle(final Locale defaultLocale,
final String... resourceFilenames) {
log.trace("PolyglotMultiResourceBundle(defaultLocale = [{}], resourceFilenames = [{}])",
defaultLocale, resourceFilenames);
lock.writeLock().lock();
try {
this.defaultLocale = (defaultLocale == null) ? Locale.getDefault() : defaultLocale;
if (resourceFilenames != null) {
resources.addAll(Arrays.asList(resourceFilenames));
}
//by default, always load root locale - no language, no country
loadLocale(Locale.ROOT);
loadLocale(this.defaultLocale);
} finally {
lock.writeLock().unlock();
}
}
/**
* Load multiple resource bundles with messages. When you call this constructor without any
* specific locale, the default locale {@link Locale#getDefault()} will be used.
*
* @param resourceFilenames List of resource bundles names to load. If you do not specify any
* file, no message will be retrieved. You can freely add resource files
* later. The order of added resource bundles is respected during message
* lookup.
*/
public PolyglotMultiResourceBundle(final String... resourceFilenames) {
this(Locale.getDefault(), resourceFilenames);
}
/**
* Retrieve the first localized message. The order of locales is respected and the first found
* localized message is returned. If the specified locale was not loaded yet, it tries to load
* messages with required locale first and then it looks for a message. It always tries to load
* also the message from the default locale if none from specified one was found.
*
* @param key Message code reference from resource bundle.
* @param prioritizedLocales Ordered list of locales. If the enumeration is {@code null} or empty,
* default and root locale are used instead
* @return {@link Optional#empty()} is returned if the code is null or the message was not found
* in any locale in any loaded resource bundle. Otherwise it returns the message in the first
* locale and the first first resource bundle file found.
*/
public Optional getString(final String key, final List prioritizedLocales) {
log.trace("getString(key = [{}], prioritizedLocales = [{}])", key, prioritizedLocales);
if (key == null) {
return Optional.empty();
}
List searchLocales = new ArrayList<>();
if (prioritizedLocales != null) {
searchLocales.addAll(prioritizedLocales);
}
searchLocales.add(defaultLocale);
searchLocales.add(Locale.ROOT);
lock.writeLock().lock();
Optional result;
try {
result = searchLocales.stream()
.filter(Objects::nonNull)
.map(locale -> getString(key, locale))
.flatMap(Optional::stream)
.findFirst();
} finally {
lock.writeLock().unlock();
}
return result;
}
/**
* Retrieve the first localized message. The order of locales is respected and the first found
* localized message is returned. If the specified locale was not loaded yet, it tries to load
* messages with required locale first and then it looks for a message. It always tries to load
* also the message from the default locale if none from specified one was found.
*
* @param key Message code reference from resource bundle.
* @param locales Ordered list of locales. If the enumeration is {@code null} or empty, default
* and root locale are used instead
* @return {@link Optional#empty()} is returned if the code is {@code null} or the message was not
* found in any locale in any loaded resource bundle. Otherwise it returns the message in the
* first locale and the first first resource bundle file found.
*/
public Optional getString(final String key, final Enumeration locales) {
log.trace("getString(key = [{}], locales = [{}])", key, locales);
if (locales == null) {
return getString(key, Collections.emptyList());
} else {
return getString(key, Collections.list(locales));
}
}
/**
* Retrieve a localized message from the message bundle. If the specified locale was not loaded
* yet, it tries to load messages first and then it looks for a message.
*
* @param key Message code reference from resource bundle.
* @param locale Locale to use for message lookup.
* @return {@link Optional#empty()} is returned if the code is {@code null} or the message was not
* found in default locale in any loaded resource bundle. Otherwise it returns the message in
* the default locale and the first first resource bundle file found.
*/
public Optional getString(final String key, final Locale locale) {
log.trace("getString(key = [{}], locale = [{}])", key, locale);
if (locale == null || key == null) {
return Optional.empty();
}
if (locales.get(locale) == null) {
loadLocale(locale);
}
lock.readLock().lock();
try {
try {
return Optional.of(locales.get(locale).getString(key));
} catch (final MissingResourceException ex) {
log.info("Cannot find key {} for locale: {}", key, locale, ex);
}
} finally {
lock.readLock().unlock();
}
return Optional.empty();
}
/**
* Add locale among other locales. This loads all resource files that were previously added with
* specified locale and all resource files added later will be loaded with this new locale as
* well.
*
* @param locale Locale you want to add.
* @return This instance.
*/
public PolyglotMultiResourceBundle loadLocale(final Locale locale) {
log.trace("loadLocale(locale = [{}])", locale);
lock.writeLock().lock();
try {
if (locales.containsKey(locale)) {
return this;
}
SingleLocaleMultiResourceBundle singleLocaleMultiResourceBundle =
new SingleLocaleMultiResourceBundle(locale);
singleLocaleMultiResourceBundle.addMessageSources(resources.toArray(new String[0]));
locales.put(locale, singleLocaleMultiResourceBundle);
} finally {
lock.writeLock().unlock();
}
return this;
}
/**
* Get list of loaded locales.
*
* @return Set of locales that this instance has already loaded.
*/
public Set getLocales() {
log.trace("getLocales()");
return locales.keySet();
}
@Override
public MultiResourceBundle addMessageSources(final String... fileLocations) {
log.trace("addMessageSources(fileLocations = [{}])", (Object) fileLocations);
if (fileLocations == null) {
return this;
}
lock.writeLock().lock();
try {
locales.forEach((k, v) -> v.addMessageSources(fileLocations));
Arrays.stream(fileLocations).filter(Objects::nonNull).forEach(resources::add);
} finally {
lock.writeLock().unlock();
}
return this;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy