views:

568

answers:

3

Hi,

I'm in the process of internationalising some JSF files so am externalising strings (and the concatenation of strings using placeholders). I have very little experience with JSF (today and yesterday) so excuse me if there is an obviuos answer to my question!

I have been using the h:outputFormat tag (and the f:param tag) successfully for simple placeholders but now I'm looking to replace a placeholder with a commandLink component.

i.e.

<h:outputFormat value="#{adminMsgs.scheduleUpdateLink} ">
    <h:commandLink value="#{adminMsgs.update}" action="refresh" />
</h:outputFormat>

Properties file:

scheduleUpdateLink = After waiting a few seconds, please {0} the page.
update = update

And output along these lines:

After waiting a few seconds, please <a href="#" class="..." onclick="...;return false;">update</a> the page.

This doesn't work (the update link appears before the 'scheduleUpdateLink' text), does anyone know how I might do it?

Thanks in advance.

EDIT / UPDATE

Thanks for your reply McDowell - very useful, but still haven't completely solved my problem. I will also be needing to do the same with input boxes (h:inputText), and there may also be instances where there are multiple placeholders in one resource string. Hence I cannot guarantee the order in, say, Arabic, will be the same.

If I went with a Java function; do you know if there is a way I can pass, as a string, the JSF tags, e.g. <h:outputFormat value=... and use the faces context to obtain the rendered HTML which I can then insert into respective placeholders and return as plain HTML? Or any other ideas along these lines?

Cheers.

A: 

In order for plain texts to be rendered correctly by JSF, it must be generated by <html:outputText ... > (there is some obscure technical reason). I would do this with an outputText, a commandLink and an outputText.

In general, when dealing with JSF everything must be a tag, no plain text.

A handy reference: http://exadel.com/tutorial/jsf/jsftags-guide.html

Thorbjørn Ravn Andersen
Sorry; has this got anything to do with the question? I'm having difficulty understanding if so...thanks for the link though, quite useful.
Ed
ChrisAD has understood and answered in more detail.
Thorbjørn Ravn Andersen
A: 

To solve your problem you can use the <h:outputText> instead like Thorbjørn said.

Basicly your code and properties file become like this:

<h:outputText value="#{adminMsgs.scheduleUpdateLink1}" />
<h:commandLink value="#{adminMsgs.update}" action="refresh" />
<h:outputText value0"#{adminMsgs.scheduleUpdateLink2}" />

and the properties file like this:

scheduleUpdateLink1 = After waiting a few seconds, please
update = update
scheduleUpdateLink2 = the page.

Tbh I dont think outputFormat works with UiComponents, but only text.

ChrisAD
+3  A: 

The h:outputFormat tag will (as far as I know) only use f:param children as parameters.

If the message was a link (instead of a h:commandLink), you could use HTML literals:

<h:outputFormat escape="false" 
    value="...seconds, please &lt;a href='etc..." />.

I would not recommend trying to emulate h:commandLink by doing this. It might be possible to find a richer version of h:commandLink in a third party library that does exactly what you want.

Just splitting the string into two is possible, but makes life difficult for translators and the quality of translation may suffer (especially if you're doing this a lot).

I'd look at using a custom function like this:

<f:loadBundle basename="i18n.I18n" var="bundle" />
<h:outputText value="#{i18n:fragment(bundle.scheduleUpdateLink, 0)}" />
<h:commandLink>
  <h:outputText value="#{bundle.update}" />
</h:commandLink>
<h:outputText value="#{i18n:fragment(bundle.scheduleUpdateLink, 1)}" />

The rendered output is of the form:

After waiting a few seconds, please <script type="text/javascript"><!--
    /*JavaScript elided*/
//-->
</script><a href="#"
      onclick="return oamSubmitForm('someClientId');">update</a> the page.

The function is backed by a static method that splits the string and returns the requested fragment:

public static String fragment(String string, int index) {
  // TODO: regex probably not complete; ask stackoverflow!
  // see http://java.sun.com/javase/6/docs/api/java/text/MessageFormat.html
  final String regex = "\\{\\d.*?\\}";
  String[] fragments = string.split(regex, index + 2);
  return fragments[index];
  // TODO: error handling
}

Depending on your view technology (Facelets or JSP), the function declaration is slightly different:


EDIT: based on more info

JSF takes a declarative approach to building interfaces. You want a dynamic UI with children ordered according to the order they appear in a translated string. These two things are at odds, but all is not lost.

There are a few ways to approach this.

  • Redesign the UI to use text above, input/control table below (the popular form approach). This is the simplest solution, but might not make your UI designer happy.
  • Use the binding attribute on a panel to create and programmatically order everything. This might mean a lot of repetitive code in your backing beans.
  • Write a reusable custom control which might look like this:

.

<ed:formatPane string="click {0} after filling in forename {1} and surname {2}">
   <h:commandLink value="#{bundle.linkMsg}" />
   <h:inputText value="#{bean.prop1}" />
   <h:inputText value="#{bean.prop2}" />
</ed:formatPane>

It is probably possible (and less work) to just replace the renderer for h:outputFormat to support arbitrary children instead of creating a full custom control, but I'd argue against it because it may lead to confusion during long-term maintenance.

Someone may have already written a custom control that does what I suggest (it can hardly be an original idea).

McDowell
Thanks this has been a great help!I'm working on the custom component atm and wanted to have a look at the default renderer to base my renderer on, but I can't find it and no luck googling. Do you know what the default renderer for the javax.faces.component.html.HtmlOutputFormat is? The only thing I know is that the renderer type is "javax.faces.Format". Cheers and I'll be checking you blog...
Ed
There are two open source JSF implementations (Sun Mojarra and Apache MyFaces). Find the configuration document for your implementation and search for the renderer-class element that matches the renderer-type.
McDowell
An implementation of the custom control, for them that are interested: http://illegalargumentexception.blogspot.com/2009/08/jsf-custom-format-panel-control-for.html
McDowell