This is a subject that has been asked in many forums but I can't find any precise and concrete answer for it. Even the accepted answers are incomplete in my point of view, so I will try to post my complete attempt to solve this problem in hope to build a precise question + answer on the subject.
I'm trying to get Resource Bundles working in JSF. The Resource Bundles come from a Spring bean, which should be loaded from an arbitrary external system (ie a Database).
I will bypass the Database querying for now and use a mocked up Resource Bundle to keep things clear.
This is my Resource Bundle business implementation, which I managed to gather from other post on this forum:
public class TesteResBundle extends ReloadableResourceBundleMessageSource {
private final Map<String, Map<String, String>> properties = new HashMap<String, Map<String, String>>();
public TesteResBundle() {
reload();
}
@Override
protected MessageFormat resolveCode(String code, Locale locale) {
String msg = getText(code, locale);
MessageFormat result = createMessageFormat(msg, locale);
return result;
}
@Override
protected String resolveCodeWithoutArguments(String code, Locale locale) {
return getText(code, locale);
}
private String getText(String code, Locale locale) {
Map<String, String> localized = properties.get(code);
String textForCurrentLanguage = null;
if (localized != null) {
textForCurrentLanguage = localized.get(locale.getLanguage());
if (textForCurrentLanguage == null) {
textForCurrentLanguage = localized.get(Locale.ENGLISH.getLanguage());
}
}
return textForCurrentLanguage != null ? textForCurrentLanguage : code;
}
public void reload() {
properties.clear();
properties.putAll(loadTexts());
}
protected Map<String, Map<String, String>> loadTexts() {
Map<String, Map<String, String>> m = new HashMap<String, Map<String, String>>();
Map<String, String> v = new HashMap<String, String>();
v.put("en", "good");
v.put("pt", "bom");
v.put("en_US", "bom");
m.put("prop", v);
v = new HashMap<String, String>();
v.put("en", "bad");
v.put("pt", "mau");
v.put("en_US", "bom");
m.put("pror", v);
return m;
}
}
This is a custom EL resolver that I also grabbed around in a forum. It tries to gather the message if the base is an instance of MessageSource. If not it passes the resolution to the default Spring EL resolver:
public class MessageSourcePropertyResolver extends SpringBeanFacesELResolver {
public Object getValue(ELContext elContext, Object base, Object property)
throws ELException {
if (base instanceof MessageSource && property instanceof String) {
String result = ((MessageSource) base).getMessage(
(String) property, null, getLocale());
if (null != result) {
elContext.setPropertyResolved(true);
}
return result;
}
return super.getValue(elContext, base, property);
}
private Locale getLocale() {
FacesContext context = FacesContext.getCurrentInstance();
return context.getExternalContext().getRequestLocale();
}
}
The custom EL resolver is defined in faces-config.xml:
<el-resolver>pt.teste.pojo.MessageSourcePropertyResolver</el-resolver>
Finally in Spring configuration I have the messageSource bean defined as:
<bean id="messageSource" class="pt.teste.pojo.TesteResBundle">
</bean>
I can confirm that the messageSource bean is instantiated correctly and the HashMap is correctly loaded upon the application startup. I can confirm that the custom handler is being called and is passing to the default Spring resolver all the EL's that are not Resource Messages and is resolving correctly.
When I use a Resource Bundle in a xhtml JSF 2.0 page I'm doing it this way:
<h:outputText value="#{messageSource.prop}" />
During EL resolving, the custom resolver correctly detects the base as a MessageSource instance but fails at:
String result = ((MessageSource) base).getMessage((String) property, null, getLocale());
With the following exception:
org.springframework.context.NoSuchMessageException: No message found under code 'prop' for locale 'en_US'.
org.springframework.context.support.DelegatingMessageSource.getMessage(DelegatingMessageSource.java:65)
pt.teste.pojo.MessageSourcePropertyResolver.getValue(MessageSourcePropertyResolver.java:18)
com.sun.faces.el.DemuxCompositeELResolver._getValue(DemuxCompositeELResolver.java:176)
com.sun.faces.el.DemuxCompositeELResolver.getValue(DemuxCompositeELResolver.java:203)
org.apache.el.parser.AstValue.getValue(AstValue.java:169)
org.apache.el.ValueExpressionImpl.getValue(ValueExpressionImpl.java:189)
com.sun.faces.facelets.el.TagValueExpression.getValue(TagValueExpression.java:109)
javax.faces.component.ComponentStateHelper.eval(ComponentStateHelper.java:194)
javax.faces.component.ComponentStateHelper.eval(ComponentStateHelper.java:182)
javax.faces.component.UIOutput.getValue(UIOutput.java:169)
com.sun.faces.renderkit.html_basic.HtmlBasicInputRenderer.getValue(HtmlBasicInputRenderer.java:205)
com.sun.faces.renderkit.html_basic.HtmlBasicRenderer.getCurrentValue(HtmlBasicRenderer.java:355)
com.sun.faces.renderkit.html_basic.HtmlBasicRenderer.encodeEnd(HtmlBasicRenderer.java:164)
javax.faces.component.UIComponentBase.encodeEnd(UIComponentBase.java:875)
com.sun.faces.renderkit.html_basic.HtmlBasicRenderer.encodeRecursive(HtmlBasicRenderer.java:312)
com.sun.faces.renderkit.html_basic.GroupRenderer.encodeChildren(GroupRenderer.java:105)
javax.faces.component.UIComponentBase.encodeChildren(UIComponentBase.java:845)
javax.faces.component.UIComponent.encodeAll(UIComponent.java:1779)
com.sun.faces.renderkit.html_basic.CompositeRenderer.encodeChildren(CompositeRenderer.java:78)
javax.faces.component.UIComponentBase.encodeChildren(UIComponentBase.java:845)
javax.faces.component.UIComponent.encodeAll(UIComponent.java:1779)
javax.faces.render.Renderer.encodeChildren(Renderer.java:168)
javax.faces.component.UIComponentBase.encodeChildren(UIComponentBase.java:845)
javax.faces.component.UIComponent.encodeAll(UIComponent.java:1779)
javax.faces.component.UIComponent.encodeAll(UIComponent.java:1782)
com.sun.faces.application.view.FaceletViewHandlingStrategy.renderView(FaceletViewHandlingStrategy.java:402)
com.sun.faces.application.view.MultiViewHandler.renderView(MultiViewHandler.java:125)
org.springframework.faces.webflow.FlowViewHandler.renderView(FlowViewHandler.java:99)
com.sun.faces.lifecycle.RenderResponsePhase.execute(RenderResponsePhase.java:121)
com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101)
com.sun.faces.lifecycle.LifecycleImpl.render(LifecycleImpl.java:139)
org.springframework.faces.mvc.JsfView.renderMergedOutputModel(JsfView.java:85)
org.springframework.web.servlet.view.AbstractView.render(AbstractView.java:262)
org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1180)
org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:950)
org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:852)
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:882)
org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:778)
javax.servlet.http.HttpServlet.service(HttpServlet.java:734)
javax.servlet.http.HttpServlet.service(HttpServlet.java:847)
I think that I may be missing something here, specifically in the messageSource bean definition in Spring config. I suspect of this because no method of TesteResBundle gets called when resolving the Resource Bundle.
Thank you for any help provided on this subject.