copy i18n plugin source, because the jar caused compile issues
This commit is contained in:
parent
f0b99146fe
commit
ae805a77da
11 changed files with 740 additions and 1 deletions
|
@ -95,7 +95,6 @@ dependencies {
|
|||
compile "com.querydsl:querydsl-apt:$queryDslVersion:jpa"
|
||||
//vaadin
|
||||
compile 'com.vaadin:vaadin-spring-boot-starter'
|
||||
compile 'org.vaadin.spring.addons:vaadin-spring-addon-i18n:2.0.0.RELEASE'
|
||||
compile 'com.vaadin:vaadin-push'
|
||||
compile('org.vaadin.addon:jfreechartwrapper:4.0.0') {
|
||||
exclude group: 'javax.servlet', module: 'servlet-api'
|
||||
|
|
129
src/main/java/org/vaadin/spring/i18n/CompositeMessageSource.java
Normal file
129
src/main/java/org/vaadin/spring/i18n/CompositeMessageSource.java
Normal file
|
@ -0,0 +1,129 @@
|
|||
package org.vaadin.spring.i18n;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.MessageSource;
|
||||
import org.springframework.context.support.AbstractMessageSource;
|
||||
|
||||
import java.text.MessageFormat;
|
||||
import java.util.Collection;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* Message source that resolves the messages by querying the {@link MessageProvider}s in
|
||||
* the application context. The resolved messages are cached by default. The caching can be turned off.
|
||||
*
|
||||
* @author Petter Holmström (petter@vaadin.com)
|
||||
*/
|
||||
public class CompositeMessageSource extends AbstractMessageSource implements MessageSource {
|
||||
|
||||
public static final String ENV_PROP_MESSAGE_FORMAT_CACHE_ENABLED = "vaadin4spring.i18n.message-format-cache.enabled";
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(CompositeMessageSource.class);
|
||||
private final Collection<MessageProvider> messageProviders;
|
||||
private final Map<Locale, Map<String, MessageFormat>> messageFormatCache = new ConcurrentHashMap<Locale, Map<String, MessageFormat>>();
|
||||
private boolean messageFormatCacheEnabled = true;
|
||||
|
||||
/**
|
||||
* Creates a new {@code CompositeMessageSource}.
|
||||
*
|
||||
* @param applicationContext the application context to use when looking up
|
||||
* {@link MessageProvider}s, must not be {@code null}.
|
||||
*/
|
||||
public CompositeMessageSource(ApplicationContext applicationContext) {
|
||||
LOGGER.info("Looking up MessageProviders");
|
||||
messageProviders = applicationContext.getBeansOfType(MessageProvider.class).values();
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
for (MessageProvider messageProvider : messageProviders) {
|
||||
LOGGER.debug("Found MessageProvider [{}]", messageProvider);
|
||||
}
|
||||
}
|
||||
LOGGER.info("Found {} MessageProvider(s)", messageProviders.size());
|
||||
setMessageFormatCacheEnabled(applicationContext.getEnvironment()
|
||||
.getProperty(ENV_PROP_MESSAGE_FORMAT_CACHE_ENABLED, Boolean.class, true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the caches of all message providers.
|
||||
*
|
||||
* @see MessageProvider#clearCache()
|
||||
*/
|
||||
public void clearMessageProviderCaches() {
|
||||
for (MessageProvider messageProvider : messageProviders) {
|
||||
messageProvider.clearCache();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the resolved messages are cached or not. Default is true.
|
||||
*/
|
||||
public boolean isMessageFormatCacheEnabled() {
|
||||
return messageFormatCacheEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables or disables caching of resolved messages. This can also be set by the
|
||||
* <code>{@value #ENV_PROP_MESSAGE_FORMAT_CACHE_ENABLED}</code>
|
||||
* environment property.
|
||||
*/
|
||||
public void setMessageFormatCacheEnabled(boolean messageFormatCacheEnabled) {
|
||||
this.messageFormatCacheEnabled = messageFormatCacheEnabled;
|
||||
if (messageFormatCacheEnabled) {
|
||||
LOGGER.info("MessageFormat cache enabled");
|
||||
} else {
|
||||
LOGGER.info("MessageFormat cache disabled");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MessageFormat resolveCode(String s, Locale locale) {
|
||||
MessageFormat messageFormat = queryCache(s, locale);
|
||||
if (messageFormat == null) {
|
||||
messageFormat = queryMessageProviders(s, locale);
|
||||
if (messageFormat != null) {
|
||||
cache(s, locale, messageFormat);
|
||||
}
|
||||
}
|
||||
return messageFormat;
|
||||
}
|
||||
|
||||
private MessageFormat queryCache(String s, Locale locale) {
|
||||
if (messageFormatCacheEnabled) {
|
||||
final Map<String, MessageFormat> cache = getMessageFormatCache(locale);
|
||||
return cache.get(s);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void cache(String s, Locale locale, MessageFormat messageFormat) {
|
||||
if (messageFormatCacheEnabled) {
|
||||
final Map<String, MessageFormat> cache = getMessageFormatCache(locale);
|
||||
cache.put(s, messageFormat);
|
||||
}
|
||||
}
|
||||
|
||||
private MessageFormat queryMessageProviders(String s, Locale locale) {
|
||||
LOGGER.debug("Querying message providers for code [{}] for locale [{}]", s, locale);
|
||||
for (MessageProvider messageProvider : messageProviders) {
|
||||
final MessageFormat messageFormat = messageProvider.resolveCode(s, locale);
|
||||
if (messageFormat != null) {
|
||||
LOGGER.debug("Code [{}] for locale [{}] found in provider [{}]", s, locale, messageProvider);
|
||||
return messageFormat;
|
||||
}
|
||||
}
|
||||
LOGGER.debug("Code [{}] for locale [{}] not found", s, locale);
|
||||
return null;
|
||||
}
|
||||
|
||||
private Map<String, MessageFormat> getMessageFormatCache(Locale locale) {
|
||||
Map<String, MessageFormat> cache = messageFormatCache.get(locale);
|
||||
if (cache == null) {
|
||||
cache = new ConcurrentHashMap<String, MessageFormat>();
|
||||
messageFormatCache.put(locale, cache);
|
||||
}
|
||||
return cache;
|
||||
}
|
||||
}
|
195
src/main/java/org/vaadin/spring/i18n/I18N.java
Normal file
195
src/main/java/org/vaadin/spring/i18n/I18N.java
Normal file
|
@ -0,0 +1,195 @@
|
|||
package org.vaadin.spring.i18n;
|
||||
|
||||
import com.vaadin.ui.UI;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.NoSuchMessageException;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Helper class for resolving messages in a Vaadin UI. This is effectively a wrapper around
|
||||
* {@link ApplicationContext}
|
||||
* that uses the locale of the current UI to lookup messages. Use it like this:
|
||||
*
|
||||
* <pre>
|
||||
* @VaadinUI
|
||||
* public class MyUI extends UI {
|
||||
*
|
||||
* @Autowired I18N i18n;
|
||||
*
|
||||
* ...
|
||||
*
|
||||
* void init() {
|
||||
* myLabel.setCaption(i18n.get("myLabel.caption"));
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* Please note, that you also need to configure a {@link org.springframework.context.MessageSource} inside your
|
||||
* application context
|
||||
* that contains all the messages to resolve.
|
||||
*
|
||||
* @author Petter Holmström (petter@vaadin.com)
|
||||
*/
|
||||
public class I18N {
|
||||
|
||||
private final ApplicationContext applicationContext;
|
||||
private final Logger logger = LoggerFactory.getLogger(getClass());
|
||||
private boolean revertToDefaultBundle = true;
|
||||
|
||||
/**
|
||||
* @param applicationContext the application context to read messages from, never {@code null}.
|
||||
*/
|
||||
@Autowired
|
||||
public I18N(ApplicationContext applicationContext) {
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether {@code I18N} will try the default bundle if a message cannot be resolved for the
|
||||
* current locale. By default, this is true.
|
||||
*/
|
||||
public boolean isRevertToDefaultBundle() {
|
||||
return revertToDefaultBundle;
|
||||
}
|
||||
|
||||
/**
|
||||
* See {@link #isRevertToDefaultBundle()}.
|
||||
*/
|
||||
public void setRevertToDefaultBundle(boolean revertToDefaultBundle) {
|
||||
this.revertToDefaultBundle = revertToDefaultBundle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to resolve the specified message for the current locale.
|
||||
*
|
||||
* @param code the code to lookup up, such as 'calculator.noRateSet', never {@code null}.
|
||||
* @param arguments Array of arguments that will be filled in for params within the message (params look like "{0}",
|
||||
* "{1,date}", "{2,time}"), or {@code null} if none.
|
||||
* @return the resolved message, or the message code prepended with an exclamation mark if the lookup fails.
|
||||
* @see ApplicationContext#getMessage(String, Object[], Locale)
|
||||
* @see #getLocale()
|
||||
*/
|
||||
public String get(String code, Object... arguments) {
|
||||
try {
|
||||
return getMessage(code, null, arguments);
|
||||
} catch (NoSuchMessageException ex) {
|
||||
logger.warn("Tried to retrieve message with code [{}] that does not exist", code);
|
||||
return "!" + code;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to resolve the specified message for the given locale.
|
||||
*
|
||||
* @param code the code to lookup up, such as 'calculator.noRateSet', never {@code null}.
|
||||
* @param locale The Locale for which it is tried to look up the code. Must not be null
|
||||
* @param arguments Array of arguments that will be filled in for params within the message (params look like "{0}",
|
||||
* "{1,date}", "{2,time}"), or {@code null} if none.
|
||||
* @throws IllegalArgumentException if the given Locale is null
|
||||
* @return the resolved message, or the message code prepended with an exclamation mark if the lookup fails.
|
||||
* @see ApplicationContext#getMessage(String, Object[], Locale)
|
||||
* @see Locale
|
||||
*/
|
||||
public String get(String code, Locale locale, Object... arguments) {
|
||||
if (locale == null) {
|
||||
throw new IllegalArgumentException("Locale must not be null");
|
||||
}
|
||||
try {
|
||||
return getMessage(code, locale, arguments);
|
||||
} catch (NoSuchMessageException ex) {
|
||||
logger.warn("Tried to retrieve message with code [{}] that does not exist", code);
|
||||
return "!" + code;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to resolve the specified message for the given locale.
|
||||
*
|
||||
* @param code the code to lookup up, such as 'calculator.noRateSet', never {@code null}.
|
||||
* @param locale The Locale for which it is tried to look up the code. Must not be null
|
||||
* @param arguments Array of arguments that will be filled in for params within the message (params look like "{0}",
|
||||
* "{1,date}", "{2,time}"), or {@code null} if none.
|
||||
* @throws IllegalArgumentException if the given Locale is null
|
||||
* @return the resolved message, or the message code prepended with an exclamation mark if the lookup fails.
|
||||
* @see ApplicationContext#getMessage(String, Object[], Locale)
|
||||
* @see Locale
|
||||
* @deprecated Use {@link #get(String, Locale, Object...)} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public String getWithLocale(String code, Locale locale, Object... arguments) {
|
||||
return get(code, locale, arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to resolve the specified message for the current locale.
|
||||
*
|
||||
* @param code the code to lookup up, such as 'calculator.noRateSet', never {@code null}.
|
||||
* @param defaultMessage string to return if the lookup fails, never {@code null}.
|
||||
* @param arguments Array of arguments that will be filled in for params within the message (params look like "{0}",
|
||||
* "{1,date}", "{2,time}"), or {@code null} if none.
|
||||
* @return the resolved message, or the {@code defaultMessage} if the lookup fails.
|
||||
* @see #getLocale()
|
||||
*/
|
||||
public String getWithDefault(String code, String defaultMessage, Object... arguments) {
|
||||
try {
|
||||
return getMessage(code, null, arguments);
|
||||
} catch (NoSuchMessageException ex) {
|
||||
return defaultMessage;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to resolve the specified message for the given locale.
|
||||
*
|
||||
* @param code the code to lookup up, such as 'calculator.noRateSet', never {@code null}.
|
||||
* @param locale The Locale for which it is tried to look up the code. Must not be null
|
||||
* @param defaultMessage string to return if the lookup fails, never {@code null}.
|
||||
* @param arguments Array of arguments that will be filled in for params within the message (params look like "{0}",
|
||||
* "{1,date}", "{2,time}"), or {@code null} if none.
|
||||
* @return the resolved message, or the {@code defaultMessage} if the lookup fails.
|
||||
*/
|
||||
public String getWithDefault(String code, Locale locale, String defaultMessage, Object... arguments) {
|
||||
if (locale == null) {
|
||||
throw new IllegalArgumentException("Locale must not be null");
|
||||
}
|
||||
try {
|
||||
return getMessage(code, locale, arguments);
|
||||
} catch (NoSuchMessageException ex) {
|
||||
return defaultMessage;
|
||||
}
|
||||
}
|
||||
|
||||
private String getMessage(String code, Locale locale, Object... arguments) {
|
||||
Locale actualLocale = locale == null ? getLocale() : locale;
|
||||
try {
|
||||
return applicationContext.getMessage(code, arguments, actualLocale);
|
||||
} catch (NoSuchMessageException ex) {
|
||||
if (isRevertToDefaultBundle()) {
|
||||
return applicationContext.getMessage(code, arguments, null);
|
||||
} else {
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the locale of the current Vaadin UI. If the locale can not be determinted, the default locale
|
||||
* is returned instead.
|
||||
*
|
||||
* @return the current locale, never {@code null}.
|
||||
* @see UI#getLocale()
|
||||
* @see Locale#getDefault()
|
||||
*/
|
||||
public Locale getLocale() {
|
||||
UI currentUI = UI.getCurrent();
|
||||
Locale locale = currentUI == null ? null : currentUI.getLocale();
|
||||
if (locale == null) {
|
||||
locale = Locale.getDefault();
|
||||
}
|
||||
return locale;
|
||||
}
|
||||
}
|
27
src/main/java/org/vaadin/spring/i18n/MessageProvider.java
Normal file
27
src/main/java/org/vaadin/spring/i18n/MessageProvider.java
Normal file
|
@ -0,0 +1,27 @@
|
|||
package org.vaadin.spring.i18n;
|
||||
|
||||
import java.text.MessageFormat;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* A {@code MessageProvider} provides messages for a {@link CompositeMessageSource}.
|
||||
* There can be multiple message provider beans in the same application context.
|
||||
*
|
||||
* @author Petter Holmström (petter@vaadin.com)
|
||||
*/
|
||||
public interface MessageProvider {
|
||||
|
||||
/**
|
||||
* Attempts to resolve the specified code for the specified locale.
|
||||
*
|
||||
* @param s the code of the message, must not be {@code null}.
|
||||
* @param locale the locale, must not be {@code null}.
|
||||
* @return a {@code MessageFormat} for the message, or {@code null} if not found.
|
||||
*/
|
||||
MessageFormat resolveCode(String s, Locale locale);
|
||||
|
||||
/**
|
||||
* Clears any internal caches, forcing the message provider to resolve the codes from the original message source.
|
||||
*/
|
||||
void clearCache();
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
package org.vaadin.spring.i18n;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.core.env.Environment;
|
||||
|
||||
import javax.annotation.PreDestroy;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* This class will start up a thread that will invoke {@link CompositeMessageSource#clearMessageProviderCaches()} on
|
||||
* a regular interval if enabled (by default it is disabled). This feature is intended to be used during development,
|
||||
* together with a tool such as JRebel, to prevent time consuming application restarts while tweaking the translations.
|
||||
* <p>
|
||||
* To enable, specify the interval in seconds in the environment property
|
||||
* <code>{@value #ENV_PROP_MESSAGE_PROVIDER_CACHE_CLEANUP_INTERVAL_SECONDS}</code>
|
||||
* and disable the message format cache of the {@link CompositeMessageSource}.
|
||||
*
|
||||
* @author Petter Holmström (petter@vaadin.com)
|
||||
* @see CompositeMessageSource#setMessageFormatCacheEnabled(boolean)
|
||||
*/
|
||||
public class MessageProviderCacheCleanupExecutor {
|
||||
|
||||
/**
|
||||
* An environment property specifying the interval of the cache cleanups. A value of 0 (the default) will
|
||||
* disable the cleanups completely.
|
||||
*/
|
||||
public static final String ENV_PROP_MESSAGE_PROVIDER_CACHE_CLEANUP_INTERVAL_SECONDS = "vaadin4spring.i18n.message-provider-cache.cleanup-interval-seconds";
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(MessageProviderCacheCleanupExecutor.class);
|
||||
private ScheduledExecutorService executorService;
|
||||
private int cleanupInterval;
|
||||
private ScheduledFuture<?> cleanupJob;
|
||||
|
||||
/**
|
||||
* Creates a new {@code MessageProviderCacheCleanupExecutor} and starts up the cleanup thread if cache cleanup
|
||||
* has been enabled.
|
||||
*/
|
||||
public MessageProviderCacheCleanupExecutor(final Environment environment,
|
||||
final CompositeMessageSource compositeMessageSource) {
|
||||
cleanupInterval = environment.getProperty(ENV_PROP_MESSAGE_PROVIDER_CACHE_CLEANUP_INTERVAL_SECONDS,
|
||||
Integer.class, 0);
|
||||
if (cleanupInterval > 0) {
|
||||
if (compositeMessageSource.isMessageFormatCacheEnabled()) {
|
||||
LOGGER.warn(
|
||||
"The message format cache is enabled so message provider cache cleanup will not have any effect, disabling");
|
||||
} else {
|
||||
LOGGER.info("Cleaning up the message provider caches every {} second(s)", cleanupInterval);
|
||||
executorService = Executors.newSingleThreadScheduledExecutor();
|
||||
cleanupJob = executorService.scheduleAtFixedRate(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
LOGGER.debug("Cleaning up message provider caches");
|
||||
compositeMessageSource.clearMessageProviderCaches();
|
||||
}
|
||||
}, cleanupInterval, cleanupInterval, TimeUnit.SECONDS);
|
||||
}
|
||||
} else {
|
||||
LOGGER.info("Message provider cache cleanup is disabled");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shuts down the cleanup thread.
|
||||
*/
|
||||
@PreDestroy
|
||||
public void destroy() {
|
||||
if (executorService != null) {
|
||||
LOGGER.info("Shutting down message provider cache cleanup thread");
|
||||
cleanupJob.cancel(true);
|
||||
executorService.shutdown();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,116 @@
|
|||
package org.vaadin.spring.i18n;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.Locale;
|
||||
import java.util.MissingResourceException;
|
||||
import java.util.PropertyResourceBundle;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
/**
|
||||
* Implementation of {@link MessageProvider} that reads messages
|
||||
* from {@link ResourceBundle}s with a specific base name.
|
||||
*
|
||||
* @author Petter Holmström (petter@vaadin.com)
|
||||
*/
|
||||
public class ResourceBundleMessageProvider implements MessageProvider {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(ResourceBundleMessageProvider.class);
|
||||
|
||||
private final String baseName;
|
||||
private final String encoding;
|
||||
|
||||
/**
|
||||
* Creates a new {@code ResourceBundleMessageProvider} with the given base name and UTF-8 encoding.
|
||||
*
|
||||
* @param baseName the base name to use, must not be {@code null}.
|
||||
*/
|
||||
public ResourceBundleMessageProvider(String baseName) {
|
||||
this(baseName, "UTF-8");
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@code ResourceBundleMessageProvider} with the given base name and encoding.
|
||||
*
|
||||
* @param baseName the base name to use, must not be {@code null}.
|
||||
* @param encoding the encoding to use when reading the resource bundle, must not be {@code null}.
|
||||
*/
|
||||
public ResourceBundleMessageProvider(String baseName, String encoding) {
|
||||
this.baseName = baseName;
|
||||
this.encoding = encoding;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageFormat resolveCode(String s, Locale locale) {
|
||||
final ResourceBundle resourceBundle = getResourceBundle(locale);
|
||||
final String message = getString(resourceBundle, s);
|
||||
return getMessageFormat(message, locale);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearCache() {
|
||||
ResourceBundle.clearCache(this.getClass().getClassLoader());
|
||||
}
|
||||
|
||||
private ResourceBundle getResourceBundle(Locale locale) {
|
||||
try {
|
||||
return ResourceBundle.getBundle(baseName, locale, this.getClass().getClassLoader(), new MessageControl());
|
||||
} catch (MissingResourceException ex) {
|
||||
LOGGER.warn("No message bundle with basename [{}] found for locale [{}]", baseName, locale);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static String getString(ResourceBundle bundle, String s) {
|
||||
if (bundle == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return bundle.getString(s);
|
||||
} catch (MissingResourceException ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static MessageFormat getMessageFormat(String message, Locale locale) {
|
||||
if (message == null) {
|
||||
return null;
|
||||
}
|
||||
return new MessageFormat(message, locale);
|
||||
}
|
||||
|
||||
private class MessageControl extends ResourceBundle.Control {
|
||||
@Override
|
||||
public ResourceBundle newBundle(String baseName, Locale locale, String format, ClassLoader loader,
|
||||
boolean reload) throws IllegalAccessException, InstantiationException, IOException {
|
||||
if ("java.properties".equals(format)) {
|
||||
final String resourceName = toResourceName(toBundleName(baseName, locale), "properties");
|
||||
final InputStream stream = loader.getResourceAsStream(resourceName);
|
||||
if (stream == null) {
|
||||
return null; // Not found
|
||||
}
|
||||
Reader reader = null;
|
||||
try {
|
||||
reader = new InputStreamReader(stream, encoding);
|
||||
return new PropertyResourceBundle(reader);
|
||||
} catch (UnsupportedEncodingException ex) {
|
||||
stream.close();
|
||||
throw ex;
|
||||
} finally {
|
||||
if (reader != null) {
|
||||
reader.close();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return super.newBundle(baseName, locale, format, loader, reload);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package org.vaadin.spring.i18n.annotation;
|
||||
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.vaadin.spring.i18n.config.I18NConfiguration;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Add this annotation to your application configuration to enable the {@link org.vaadin.spring.i18n.I18N} internationalization support.
|
||||
*
|
||||
* @author Gert-Jan Timmer (gjr.timmer@gmail.com)
|
||||
*
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Import(I18NConfiguration.class)
|
||||
public @interface EnableI18N {
|
||||
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
package org.vaadin.spring.i18n.config;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.vaadin.spring.i18n.CompositeMessageSource;
|
||||
import org.vaadin.spring.i18n.I18N;
|
||||
import org.vaadin.spring.i18n.MessageProviderCacheCleanupExecutor;
|
||||
|
||||
/**
|
||||
* Configuration class used by {@literal @}EnableVaadinI18N
|
||||
*
|
||||
* Spring configuration for the {@link CompositeMessageSource}. Please remember to
|
||||
* define {@link org.vaadin.spring.i18n.MessageProvider} beans that can serve the message source with messages.
|
||||
*
|
||||
* @author Gert-Jan Timmer (gjr.timmer@gmail.com)
|
||||
* @author Petter Holmström (petter@vaadin.com)
|
||||
* @see I18N
|
||||
* @see CompositeMessageSource
|
||||
*/
|
||||
@Configuration
|
||||
public class I18NConfiguration {
|
||||
|
||||
@Bean
|
||||
I18N i18n(ApplicationContext context) {
|
||||
return new I18N(context);
|
||||
}
|
||||
|
||||
@Bean
|
||||
CompositeMessageSource messageSource(ApplicationContext context) {
|
||||
return new CompositeMessageSource(context);
|
||||
}
|
||||
|
||||
@Bean
|
||||
MessageProviderCacheCleanupExecutor messageProviderCacheCleanupExecutor(Environment environment,
|
||||
CompositeMessageSource messageSource) {
|
||||
return new MessageProviderCacheCleanupExecutor(environment, messageSource);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
package org.vaadin.spring.i18n.support;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Interface to be implemented by all components that contain some kind of internationalized content that needs to be
|
||||
* updated on the fly when the locale is changed.
|
||||
*
|
||||
* @see TranslatableSupport
|
||||
* @author Petter Holmström (petter@vaadin.com)
|
||||
*/
|
||||
public interface Translatable extends Serializable {
|
||||
|
||||
/**
|
||||
* Called when the component should update all of its translatable strings, setting locales, etc. The locale to use
|
||||
*
|
||||
* @param locale the new locale to use.
|
||||
*/
|
||||
void updateMessageStrings(Locale locale);
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
package org.vaadin.spring.i18n.support;
|
||||
|
||||
import com.vaadin.ui.Component;
|
||||
import com.vaadin.ui.HasComponents;
|
||||
import com.vaadin.ui.UI;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Implementation of {@link Translatable} intended to be used as a delegate by an owning {@link UI}.
|
||||
* The {@link #updateMessageStrings(Locale)} method will traverse the entire component hierarchy of the UI and
|
||||
* update the message strings of any components that implement the {@link Translatable} interface.
|
||||
*
|
||||
* @author Petter Holmström (petter@vaadin.com)
|
||||
*/
|
||||
public class TranslatableSupport implements Translatable {
|
||||
|
||||
private final UI ui;
|
||||
|
||||
/**
|
||||
* Creates a new {@code TranslatableSupport}.
|
||||
*
|
||||
* @param ui the UI that owns the object.
|
||||
*/
|
||||
public TranslatableSupport(UI ui) {
|
||||
this.ui = ui;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateMessageStrings(Locale locale) {
|
||||
updateMessageStrings(locale, ui);
|
||||
}
|
||||
|
||||
private void updateMessageStrings(Locale locale, Component component) {
|
||||
if (component instanceof Translatable) {
|
||||
((Translatable) component).updateMessageStrings(locale);
|
||||
}
|
||||
if (component instanceof HasComponents) {
|
||||
for (Component child : (HasComponents) component) {
|
||||
updateMessageStrings(locale, child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
package org.vaadin.spring.i18n.support;
|
||||
|
||||
import com.vaadin.server.VaadinRequest;
|
||||
import com.vaadin.ui.UI;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Base class intended to make it easier to write UIs that needs to support changing the locale on the fly.
|
||||
* You are not required to extend this class to be able to use {@link Translatable} components. You can
|
||||
* easily plug in the {@link TranslatableSupport} into your existing UIs.
|
||||
*
|
||||
* @author Petter Holmström (petter@vaadin.com)
|
||||
*/
|
||||
public abstract class TranslatableUI extends UI {
|
||||
|
||||
private final TranslatableSupport translatableSupport = new TranslatableSupport(this);
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p>
|
||||
* This method will also update the message strings of all {@link Translatable} components currently attached
|
||||
* to the UI.
|
||||
*
|
||||
* @see #updateMessageStrings()
|
||||
*/
|
||||
@Override
|
||||
public void setLocale(Locale locale) {
|
||||
super.setLocale(locale);
|
||||
updateMessageStrings();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p>
|
||||
* This implementation will delegate the UI initialization to {@link #initUI(VaadinRequest)}, then update
|
||||
* the message strings of all {@link Translatable} components.
|
||||
*
|
||||
* @see #updateMessageStrings()
|
||||
*/
|
||||
@Override
|
||||
protected void init(VaadinRequest request) {
|
||||
initUI(request);
|
||||
updateMessageStrings();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by {@link #init(VaadinRequest)} to actually initialize the UI.
|
||||
*
|
||||
* @param request
|
||||
*/
|
||||
protected abstract void initUI(VaadinRequest request);
|
||||
|
||||
/**
|
||||
* Returns the {@link TranslatableSupport} delegate owned by this UI.
|
||||
*/
|
||||
protected TranslatableSupport getTranslatableSupport() {
|
||||
return translatableSupport;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the message strings of all {@link Translatable} components attached to this UI.
|
||||
*/
|
||||
protected void updateMessageStrings() {
|
||||
getTranslatableSupport().updateMessageStrings(getLocale());
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue