13

I am trying to use resource bundles with Spring's Message Source. Here is the way I am doing it:

@Component
public class MessageResolver implements MessageSourceAware {

    @Autowired
    private MessageSource messageSource;

    public void setMessageSource(MessageSource messageSource) {
        this.messageSource = messageSource;
    }

    public String getMessage(){
        return messageSource.getMessage("user.welcome", new Object[]{"Rama"} , Locale.US);
    }

}

And here is my folder structure:

enter image description here

messages_en_US.properties contains just one line:

user.welcome=Welcome {0}

Here is the xml configuration used:

<bean name="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
    <property name="basename">
        <value>resourcebundles/messages</value>
    </property>
</bean>

Here is the error I am getting:

WARNING: ResourceBundle [resourcebundles/messages] not found for MessageSource: Can't find bundle for base name resourcebundles/messages, locale en_US
Exception in thread "main" org.springframework.context.NoSuchMessageException: No message found under code 'user.welcome' for locale 'en_US'.

But if I move my resource bundle to directly under the resources folder, it is working fine. In this case, here is the xml configuration I am using:

<bean name="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename">
    <value>messages</value>
</property>

Is is that if I have to use ResourceBundleMessageSource, I should put my resource bundles directly under the resources? If i have to keep it in specified folder only, is there any other way to get this one work?

Thanks!

Prasanth
  • 1,005
  • 5
  • 19
  • 39

13 Answers13

21

boy, maybe you can change the xml configuration as follows:

use

org.springframework.context.support.ReloadableResourceBundleMessageSource

instead of

org.springframework.context.support.ResourceBundleMessageSource

all configuration like this:

<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
    <property name="basename" value="classpath:resourcebundles/messages" />
    <property name="useCodeAsDefaultMessage" value="true" />
</bean>
jis117
  • 211
  • 2
  • 2
  • 2
    +1. I had similar to the OP's issue and this helped me. In my case it was important to use `classpath:..` __with no asterisk__ (just as is here). The asterisk used to break the loader and the message was not resolved. I have not tested this when the message properties are not in the final application (thus requiring `classpath*:`), as it was not my case. – Ivaylo Slavov Jan 29 '14 at 10:10
15

Change your configuration to the following for messageSource bean in your xml file.

<bean name="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource"> 
    <property name="basename"> 
        <value>classpath*:resourcebundles/messages</value> 
    </property> 
</bean>

Since all your properties files are in classpath of java you need to define the path with prefix classpath*: otherwise it will look into the web directory of your application.

Hope this helps you. Cheers.

Japan Trivedi
  • 4,445
  • 2
  • 23
  • 44
  • It is giving the same exception even now. In fact it is directly throwing the error, without even throwing an warning that it could not find the resource bundle specified. – Prasanth Jun 22 '12 at 08:34
  • 1
    Here is the exception thrown:Exception in thread "main" org.springframework.context.NoSuchMessageException: No message found under code 'user.welcome' for locale 'en_US'. – Prasanth Jun 22 '12 at 08:35
7

It's nearly 2015 now and I'm using Spring 4.1.2.RELEASE and there's definitely a problem with the way the messageSource bean needs to be configured so it picks up the target resource bundle.

1) If the messageSource bean is of type ReloadableResourceBundleMessageSource it won't work:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ReloadableResourceBundleMessageSource;

@Configuration
@ComponentScan(basePackages = { "com.intertech.service" })
//@ImportResource({"classpath:spring/applicationContext-i18n.xml"})
public class AppConfig {

  @Bean(name = "messageSource")
  public ReloadableResourceBundleMessageSource getMessageSource() {
      ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
      messageSource.setBasename("config/messages");
      messageSource.setDefaultEncoding("UTF-8");
      messageSource.setUseCodeAsDefaultMessage(true);
      return messageSource;
  }

//  @Bean(name = "messageSource")
//  public ResourceBundleMessageSource getMessageSource() {
//      ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
//      messageSource.setBasename("config/messages");
//      messageSource.setDefaultEncoding("UTF-8");
//      messageSource.setUseCodeAsDefaultMessage(true);
//      return messageSource;
//  }
}

2) If the messageSource bean is of type ResourceBundleMessageSource it will work:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ResourceBundleMessageSource;

@Configuration
@ComponentScan(basePackages = { "com.intertech.service" })
//@ImportResource({"classpath:spring/applicationContext-i18n.xml"})
public class AppConfig {

//  @Bean(name = "messageSource")
//  public ReloadableResourceBundleMessageSource getMessageSource() {
//      ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
//      messageSource.setBasename("config/messages");
//      messageSource.setDefaultEncoding("UTF-8");
//      messageSource.setUseCodeAsDefaultMessage(true);
//      return messageSource;
//  }

  @Bean(name = "messageSource")
  public ResourceBundleMessageSource getMessageSource() {
      ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
      messageSource.setBasename("config/messages");
      messageSource.setDefaultEncoding("UTF-8");
      messageSource.setUseCodeAsDefaultMessage(true);
      return messageSource;
  }
}

3) If you're using an XML configuration file combined with a configuration class - it will work (notice how the base bundle is configured in a class like qualification manner i.e. 'config.messages' not 'config/messages'): (applicationContext-i18n.xml)

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:p="http://www.springframework.org/schema/p"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="messageSource"
        class="org.springframework.context.support.ResourceBundleMessageSource"
        p:basename="config.messages"
        p:useCodeAsDefaultMessage="true"/>

    <!-- This will not work -->
    <!--
    <bean id="messageSource"
        class="org.springframework.context.support.ReloadableResourceBundleMessageSource"
        p:basename="config/messages"
        p:useCodeAsDefaultMessage="true"/>
     -->
</beans>

and:

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportResource;

@Configuration
@ComponentScan(basePackages = { "com.intertech.service" })
@ImportResource({"classpath:spring/applicationContext-i18n.xml"})
public class AppConfig {

//  @Bean(name = "messageSource")
//  public ReloadableResourceBundleMessageSource getMessageSource() {
//      ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
//      messageSource.setBasename("config/messages");
//      messageSource.setDefaultEncoding("UTF-8");
//      messageSource.setUseCodeAsDefaultMessage(true);
//      return messageSource;
//  }

//  @Bean(name = "messageSource")
//  public ResourceBundleMessageSource getMessageSource() {
//      ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
//      messageSource.setBasename("config/messages");
//      messageSource.setDefaultEncoding("UTF-8");
//      messageSource.setUseCodeAsDefaultMessage(true);
//      return messageSource;
//  }
}

4) Most importantly... if you're using a WebApplicationInitializer (no web.xml), you've got to register the configuration class that defines the 'messageSource' bean in the root context, not in the dispatcher servlet's context:

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;

import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.ContextLoaderListener;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;

public class WebAppInitializer implements WebApplicationInitializer {

    @Override
    public void onStartup(ServletContext container) throws ServletException {
    // Create the 'root' Spring application context
    AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
    rootContext.register(AppConfig.class);
    // Manage the lifecycle of the root application context
    container.addListener(new ContextLoaderListener(rootContext));
    // Create the dispatcher servlet's Spring application context
    AnnotationConfigWebApplicationContext dispatcherServlet = new AnnotationConfigWebApplicationContext();
    dispatcherServlet.register(MvcConfig.class);
    // Register and map the dispatcher servlet
    ServletRegistration.Dynamic dispatcher = container.addServlet("dispatcher", new DispatcherServlet(
            dispatcherServlet));
    dispatcher.setLoadOnStartup(1);
    dispatcher.addMapping("*.htm");
    }
}
Tom Silverman
  • 634
  • 1
  • 8
  • 7
2

In my case, using Spring 4.3.2.RELEASE and java config and a ReloadableResourceBundleMessageSource, I had to define my template engine as a bean otherwise my messages were not getting resolved.

Here's a sample of a working configuration.

AppConfig.java

import java.util.concurrent.TimeUnit;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ReloadableResourceBundleMessageSource;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.spring4.SpringTemplateEngine;
import org.thymeleaf.spring4.templateresolver.SpringResourceTemplateResolver;
import org.thymeleaf.spring4.view.ThymeleafViewResolver;
import org.thymeleaf.templatemode.TemplateMode;
import org.thymeleaf.templateresolver.ITemplateResolver;

@Configuration
@EnableWebMvc
@ComponentScan("myapp")
public class AppConfig extends WebMvcConfigurerAdapter implements ApplicationContextAware {

private ApplicationContext applicationContext;

private static final boolean CACHE_THYMELEAF_TEMPLATES = false;
private final String UTF8_ENCODING = "UTF-8";

@Override
public void setApplicationContext(ApplicationContext applicationContext) {
    this.applicationContext = applicationContext;
}

@Bean
public ViewResolver viewResolver() {
    ThymeleafViewResolver resolver = new ThymeleafViewResolver();
    resolver.setTemplateEngine(templateEngine());
    resolver.setCharacterEncoding(UTF8_ENCODING);
    resolver.setCache(CACHE_THYMELEAF_TEMPLATES);
    return resolver;
}

@Bean
public TemplateEngine templateEngine() {
    //this method must be defined as a bean otherwise i18n messages are not found
    //if method defined as private TemplateEngine templateEngine() messages are not found
    SpringTemplateEngine engine = new SpringTemplateEngine();
    engine.setEnableSpringELCompiler(true);
    engine.addTemplateResolver(templateResolver());
    return engine;
}

private ITemplateResolver templateResolver() {
    SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver();
    resolver.setApplicationContext(applicationContext);
    resolver.setPrefix("/WEB-INF/thymeleaf/");
    resolver.setTemplateMode(TemplateMode.HTML);
    resolver.setSuffix(".html");
    resolver.setCacheable(CACHE_THYMELEAF_TEMPLATES);
    resolver.setCharacterEncoding(UTF8_ENCODING);
    return resolver;
}   

@Bean
public MessageSource messageSource() {
    ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
    messageSource.setBasenames("WEB-INF/i18n/messages");
    messageSource.setUseCodeAsDefaultMessage(true);
    messageSource.setDefaultEncoding(UTF8_ENCODING);
    messageSource.setFallbackToSystemLocale(false);
    messageSource.setCacheSeconds((int)TimeUnit.HOURS.toSeconds(1));
    return messageSource;
}

}
Timothy Anyona
  • 342
  • 4
  • 14
1
 <!-- Application Message Bundle -->
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
    <property name="basename" value="resourcebundles/messages" />
</bean>

You have to configure your messages path as shown above. Also, check class name.

Sachin J
  • 2,081
  • 12
  • 36
  • 50
  • This is exactly the configuration I am using. But it is not working. In the question, you can see the xml I am using. – Prasanth Jun 22 '12 at 08:17
  • Have u changed class as ReloadableResourceBundleMessageSource? – Sachin J Jun 22 '12 at 08:21
  • Change ur properties file name as messages_en.properties. – Sachin J Jun 22 '12 at 08:25
  • 1
    Now it is working after changing the file name to messages_en.properties But if I am naming my file as messages_en_US and passing the locale as Locale.US, I did not understand why it is not working? – Prasanth Jun 22 '12 at 08:28
  • Refer this link http://www.mkyong.com/spring-mvc/spring-mvc-internationalization-example/ – Sachin J Jun 22 '12 at 08:32
  • :This should work fine as per this:http://docs.oracle.com/javase/1.3/docs/api/java/util/ResourceBundle.html But it is not. Though the proposed solution is working, I could not understand why it is working? – Prasanth Jun 25 '12 at 04:53
  • I think for the English language it search for *_en.properties file. – Sachin J Jun 25 '12 at 05:51
  • As per the documentation i have read, it should read in the following order. Initially it will look for exact locale i.e. combination of language and country. If it is not found, then it will look for language alone. If this is also not found, then it will look for just the base name. You can see the same documented in the link I provided in my previous comment. – Prasanth Jun 25 '12 at 06:36
  • Please, check your browser language setting . There are two options English(US) and English change it and try.Hope this will work. – Sachin J Jun 25 '12 at 09:39
1

YAML version for this

  spring:
     messages:
       basename: i18n/validation, i18n/message # for multiple properties just use comma separated values
       encoding: UTF-8

You can refer to documentation to see full description.

Also I should mention that the default MessageSource bean is a ResourceBundleMessageSource which is already reading form a classpath so there is no need to use nonation like classpath:i18n/validation.

Directory structure

enter image description here

Community
  • 1
  • 1
Edgar Asatryan
  • 687
  • 9
  • 13
1

Is is that if I have to use ResourceBundleMessageSource, I should put my resource bundles directly under the resources? If i have to keep it in specified folder only, is there any other way to get this one work?

You can define your messages in your own package, it does not need to be located in the resources folder.

Using Spring 5.2.2.RELEASE versioned components, this is the way I managed to make it work:

The qualified name of the file would be:

/tutproject/src/com/tutproject/app/messages/messages.properties

The bean is defined like this in the my Spring Bean Configuration File (XML):

<bean id="messageSource"
    class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename"
    value="/com/tutproject/app/messages/messages">
</property>
</bean>

The Java build path includes tutproject/src , this is the part of the location omitted in the XML definition.

Some additional useful information from the ResourceBundleMessageSource class:

  • The basenames follow {@link java.util.ResourceBundle} conventions: essentially, * a fully-qualified classpath location. If it doesn't contain a package qualifier * (such as {@code org.mypackage}), it will be resolved from the classpath root. * Note that the JDK's standard ResourceBundle treats dots as package separators: * This means that "test.theme" is effectively equivalent to "test/theme".

Balu
  • 522
  • 6
  • 16
0

I have used following configuration and it is working fine

<beans:bean id="messageSource class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
        <beans:property name="basename" value="classpath:resourcebundles/ScreenLabelResources" />
    </beans:bean>
Sanchi Girotra
  • 1,232
  • 13
  • 13
0

What worked for me was something really simple.

It was

<property name="basename">
            <value>locale\messages</value>
        </property>

I changed it to

<property name="basename">
            <value>locale/messages</value>
        </property>

Just a \ to / change fixed it for me. I am using a MAC.

I have not tried *classpath, that may not have worked for me.

Siddharth
  • 9,349
  • 16
  • 86
  • 148
0

I have used following configuration and it is working fine in my project. My messages.properties is in below path: ..\WebContent\WEB-INF\resources

<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
    <property name="basename" value="classpath:WEB-INF/resources/messages" />
    <property name="useCodeAsDefaultMessage" value="true" />
</bean>
0

I have used following configuration and it is working fine in my project.

<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
    <property name="basename" value="classpath:configurations/messages" />
    <property name="useCodeAsDefaultMessage" value="true" />
</bean>

location: src\main\resources\configurations\messages_en.properties

Lorelorelore
  • 3,335
  • 8
  • 29
  • 40
0

In Spring Boot 2.2.5 things have slightly changed. Classpath is not needed anymore.

    @Bean
public MessageSource messageSource() {
    ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
    messageSource.setBasename("messages");
    messageSource.setDefaultEncoding("UTF-8");
    return messageSource;
}
Aliis
  • 21
  • 1
0

I strongly suggest to keep property files out side of project so that we don't need to compile code for every property change.

Below configuration we are using in live project. setting property.location value in application.properties file

@Configuration
public class LocalizationConfiguration {

    private static final Logger logger = LoggerFactory.getLogger(LocalizationConfiguration.class);

    @Value("${property.location}")
    private String propertyLocation;

    @Bean
    public LocaleResolver localeResolver() {
        SessionLocaleResolver localeResolver = new SessionLocaleResolver();
        localeResolver.setDefaultLocale(Locale.ENGLISH); // change this
        return localeResolver;
    }

    @Bean
    public ReloadableResourceBundleMessageSource messageSource() {
        ReloadableResourceBundleMessageSource  resource = new ReloadableResourceBundleMessageSource();
        String messageFolderPath = propertyLocation + "/" + "i18n";
        resource.setBasename("file:"+messageFolderPath+"/messages");
        resource.setDefaultEncoding("UTF-8");
        resource.setCacheSeconds(10);
        return resource;
    }

    @Bean
    public LocalValidatorFactoryBean validatorFactoryBean() {
        LocalValidatorFactoryBean bean = new LocalValidatorFactoryBean();
        bean.setValidationMessageSource(messageSource());
        return bean;
    }

}