Search code examples
javaspringspring-mvclocalizationresourcebundle

get all keys from spring ReloadableResourceBundleMessageSource


I am using ReloadableResourceBundleMessageSource to store value list of my system. So i can use the i18N functionality

i am using multiple resources in the basenames of ReloadableResourceBundleMessageSource. I want to pass all the localized labels of the web UI to the client (front-end) in order to be cached locally at the client. Is there a way to load the entire keys of my resource bundles?

here is my ReloadableResourceBundleMessageSource bean definition:

<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
    <property name="basenames">
        <list>
            <value>classpath:resource1</value>
            <value>classpath:resource2</value>
        </list>
    </property>
    <property name="cacheSeconds" value="60"/>
    <property name="defaultEncoding" value="UTF-8"/>
    <property name="useCodeAsDefaultMessage" value="true"/>
</bean>

and this is my controller that pass all the keys:

@Component
@RequestMapping("/bundle")
public class ResourceBundleController {

    @Autowired
    private MessageSource messageSource;

    @RequestMapping(value = "/locales.js")
    public ModelAndView strings(HttpServletRequest request) {

        // Call the string.jsp view
        return new ModelAndView("/WEB-INF/includes/locales.jsp", "keys", ??? );// HERE IS MY PROBLEM. HOW SHOULD I GET ALL THE KEYS FROM MESSAGESOURCE
    }
}

and here is my the resource bundle keys for the client:

<%@page contentType="text/javascript" pageEncoding="UTF-8"%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@taglib prefix="spring" uri="http://www.springframework.org/tags"%>

var messages = {};

<c:forEach var="key" items="${keys}">
    messages["<spring:message text='${key}' javaScriptEscape='true'/>"] = "<spring:message code='${key}' javaScriptEscape='true' />";
</c:forEach>

Any help will be appreciated.


Solution

  • Updated information about spring configuration regards cache of resource bundle

    You could make something like this:

    Your own implementation of ReloadableResourceBundleMessageSource:

    public class ExposedResourceMessageBundleSource extends ReloadableResourceBundleMessageSource {
    
        private static final Logger LOGGER = Logger.getLogger(ExposedResourceMessageBundleSource.class);
    
        @Override
        protected Properties loadProperties(Resource resource, String fileName) throws IOException {
    
            LOGGER.info("Load " + fileName);
            return super.loadProperties(resource, fileName);
        }
    
        /**
         * Gets all messages for presented Locale.
         * @param locale user request's locale
         * @return all messages
         */
        public Properties getMessages(Locale locale){
            return getMergedProperties(locale).getProperties();
        }
    }
    

    Service definition to handle reasource operations:

    public interface MessageResolveService extends MessageSourceAware{
        String getMessage(String key, Object[] argumentsForKey, Locale locale);
        Map<String,String> getMessages(Locale locale);
    }
    

    And implementation:

        @Service
        public class MessageResolverServiceImpl implements MessageResolveService{
            private static final Logger LOGGER = Logger.getLogger(MessageResolverServiceImpl.class);
    
            private MessageSource messageSource;
    
            @Override
            public void setMessageSource(MessageSource messageSource) {
                LOGGER.info("Messages i18n injected");
                this.messageSource = messageSource;
            }
    
            public String getMessage(String key, Object[] arguments, Locale locale){
                String message = "";
                try{
                    message = messageSource.getMessage(key,arguments,locale);
                } catch(NoSuchMessageException e){
                    message = key;
                    LOGGER.warn("No message found: "+key);
                }
                return message;
            }
    
            public Map<String,String> getMessages(Locale locale){
                Properties properties =  ((XmlWebApplicationContext)messageSource).getBean("messageSource",
                        ExposedResourceMessageBundleSource.class).getMessages(locale);
                Map<String,String> messagesMap = new HashMap<String,String>();
                for(Map.Entry<Object,Object> entry: properties.entrySet()){
                    if(entry.getKey() != null && entry.getValue() != null) {
                        messagesMap.put(entry.getKey().toString(), entry.getValue().toString());
                    }
                }
                return messagesMap;
            }
        }
    

    Spring configuration:

    <bean id="messageSource" class="your.package.ExposedResourceMessageBundleSource">
              <property name="basenames">
               <list>
                    <value>classpath:yourFileName</value>
                    <value>classpath:yourNextFileName</value>
                </list>
              </property>
              <property name="defaultEncoding" value="UTF-8"/>
              <property name="cacheSeconds" value="10"/> //If you want reload message every couple seconds. In this case every 10 seconds.
    </bean>
    

    And your @Controller(similar to this):

    @Component
    @RequestMapping("/bundle")
    public class ResourceBundleController {
    
        @Autowired
        private MessageResolveService messageResolveService;
    
    
        @RequestMapping(value = "/locales.js")
        public ModelAndView strings(HttpServletRequest request) {
    
            // Call the string.jsp view
            return new ModelAndView("/WEB-INF/includes/locales.jsp", "keys", messageResolverService.getMessages(LocaleContextHolder.getLocale()));
    }