Is there a shorthand for ?

2019-01-22 03:32发布

问题:

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)

回答1:

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.



回答2:

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" >
...


回答3:

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


回答4:

I find a way without any interceptor:

${pageScope["org.springframework.web.servlet.tags.REQUEST_CONTEXT"]
    .getMessage("hello")}

but it's too long, and the key "org.springframework.web.servlet.tags.REQUEST_CONTEXT" may not be compatible for all versions of spring.



回答5:

I use a properties file for each language, and the following works well:

<input type="button" value="${msg['myKey']}" />



回答6:

You can set the msg request variable in a servlet or filter with the resource bundle to get the messages via EL expression in JSP code by this way ${msg.messageKey}

import javax.servlet.jsp.jstl.core.Config;
import java.util.ResourceBundle;

...

public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {

  String STR_LOCALE = "es-MX"; // you can get this value from the http request to get the default browser locale.
  String LOCALE_BASENAME = "my.package.basename";
  Locale locale = Locale.forLanguageTag(STR_LOCALE);
  req.setAttribute("msg", ResourceBundle.getBundle(LOCALE_BASENAME, locale));

  // And you can setup the 'fmt' tag library from here to resolve parameterized messages.
  Config.set(req, Config.FMT_LOCALE, locale);
  Config.set(req, Config.FMT_LOCALIZATION_CONTEXT, LOCALE_BASENAME);

}

Example:

 ...
 <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
 ...
 <h1>${msg.header}</h1>
 <h2>${msg[variableHeader]}</h2>
 <fmt:message key='messageWithVariable'>
    <fmt:param>The value</fmt:param>
 </fmt:message>

This is working in my java servlet 2.5 environment.



标签: java jsp jstl el