Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a shorthand for <fmt:message key="key" />?

Tags:

java

jsp

jstl

el

It's tedious and ugly to write things like:

<input type="button" value="<fmt:message key="submitKey" />" />

And in case you want to nest the message tag in another tag's attribute it becomes even worse.

Is there any shorthand for that. For example (like in JSF):

<h:commandButton value="#{msg.shareKey}" />

(spring-mvc-only solutions applicable)

like image 850
Bozho Avatar asked Jul 04 '10 10:07

Bozho


3 Answers

This feels like a bit of a hack, but you could write a custom implementation of java.util.Map which, when get(key) is called, fetches the message from the Spring MessageSource. This Map could be added to the model under the msg key, allowing you to dereference the messages using ${msg.myKey}.

Perhaps there's some other dynamic structure than is recognised by JSP EL that isn't a Map, but I can't think of one offhand.

public class I18nShorthandInterceptor extends HandlerInterceptorAdapter {

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

    @Autowired
    private MessageSource messageSource;

    @Autowired
    private LocaleResolver localeResolver;

    @Override
    public boolean preHandle(HttpServletRequest request,
            HttpServletResponse response, Object handler) throws Exception {

        request.setAttribute("msg", new DelegationMap(localeResolver.resolveLocale(request)));

        return true;
    }

    private class DelegationMap extends AbstractMap<String, String> {
        private final Locale locale;

        public DelegationMap(Locale locale) {
            this.locale = locale;
        }

        @Override
        public String get(Object key) {
            try {
                return messageSource.getMessage((String) key, null, locale);
            } catch (NoSuchMessageException ex) {
                logger.warn(ex.getMessage());
                return (String) key;
            }
        }
        @Override
        public Set<Map.Entry<String, String>> entrySet() {
            // no need to implement this
            return null;
        }

    }
}

As an alternative:

<fmt:message key="key.name" var="var" />

Then use ${var} as a regular EL.

like image 160
skaffman Avatar answered Nov 09 '22 16:11

skaffman


If all you want is a shortcut, you can create a tag file, say btn.tag

<%@tag%>
<%@ attribute name="key" required="true" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%>

<input type="button" value="<fmt:message key="${key}" />" />

And use it like this

<%@ taglib tagdir="/WEB-INF/tags" prefix="tags"%>
...
<tags:btn key="submitKey" >
...
like image 39
svachon Avatar answered Nov 09 '22 15:11

svachon


There isn't. Since I don't do Spring, I'll leave it out of the context in this answer. For the Spring-targeted answer, see @skaffman. You can create a class which extendsResourceBundle, manage the loading yourself with help of a Filter (based on request path?) and store it in the session scope. The ResourceBundle is accessible the usual JSP EL way. You can access it as if it's a Map. The handleGetObject() method will be invoked on every access.

Here's a kickoff example:

package com.example;

import java.util.Enumeration;
import java.util.Locale;
import java.util.ResourceBundle;

import javax.servlet.http.HttpServletRequest;

public class Text extends ResourceBundle {

    private static final String TEXT_ATTRIBUTE_NAME = "text";
    private static final String TEXT_BASE_NAME = "com.example.i18n.text";

    private Text(Locale locale) {
        setLocale(locale);
    }

    public static void setFor(HttpServletRequest request) {
        if (request.getSession().getAttribute(TEXT_ATTRIBUTE_NAME) == null) {
            request.getSession().setAttribute(TEXT_ATTRIBUTE_NAME, new Text(request.getLocale()));
        }
    }

    public static Text getCurrentInstance(HttpServletRequest request) {
        return (Text) request.getSession().getAttribute(TEXT_ATTRIBUTE_NAME);
    }

    public void setLocale(Locale locale) {
        if (parent == null || !parent.getLocale().equals(locale)) {
            setParent(getBundle(TEXT_BASE_NAME, locale));
        }
    }    

    @Override
    public Enumeration<String> getKeys() {
        return parent.getKeys();
    }

    @Override
    protected Object handleGetObject(String key) {
        return parent.getObject(key);
    }

}

The Filter:

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
    Text.setFor((HttpServletRequest) request);
    chain.doFilter(request, response);
}

JSP:

<p>${text['home.paragraph']}</p>

If you want to change the locale from inside some servlet or filter:

Text.getCurrentInstance(request).setLocale(newLocale);

Related/interesting-to-know:

  • Overriding ResourceBundle#Control to read files using UTF-8
  • List all available ResourceBundle files
  • Iterating through multiple properties from ResourceBundle
like image 3
BalusC Avatar answered Nov 09 '22 15:11

BalusC