In many ways Java applications "start on 3rd base" with regards to I18N, being as the decision was made at an early stage to base the language and JVM on Unicode, and standard Java properties bundles have been part of the landscape for a long while. Spring takes this support a little further with its MessageSource hierarchy and uniform approach to resource loading.
RSF takes care to preserve this heritage maximally, rounding it off with a few architectural enhancements resulting from its use of request-scope IoC which make best practice and universal portability truly effortless.
I18N in RSF is enabled through three methods: template rsf:id convention (msg=), the concrete UIMessage class, and the MessageLocator interface.
Template Convention (msg=)#
Use of the msg=message.key as the rsf:id will automatically replace the content of the html tag with that rsf:id with the internationalized message.
<span rsf:id="msg=page.user.message.key">This is an internationalized message.</span>
UIMessage#
Use of UIMessage is the preferred means for localisation in RSF ViewProducers- this method is clearer than direct use of the underlying messageLocator bean since it requires no injection. As of RSF 0.7.1 every RSF primitive component and decorator is fully enabled to make use of UIMessage or otherwise function without explicit injection of a MessageLocator.Here is the most basic usage:
public void fillComponents(UIContainer tofill, ViewParameters viewparams, ComponentChecker checker) { .... UIMessage.make(tofill, "scale-title-note", "scaleaddmodify.scale.title.note"); ...Here we have created a label component with an rsf:id of scale-title-note, which will fetch the message with key scaleaddmodify.scale.title.note from the message bundle registered into the RSF messageLocator. To complete the picture, here is the corresponding template section
<span rsf:id="scale-title-note" style="font-weight:bold;">Title</span>and the message key from the message file (by default in RSF expected under the path /WEB-INF/messages/message_xxxx.properties, where xx represents the locale string):
scaleaddmodify.scale.title.note=Title
For more complex message formatting, one may use another constructor of UIMessage as follows:
public static UIMessage make(UIContainer parent, String ID, String messagekey, Object[] arguments)This signature operates just the same semantics as the standard Java class MessageFormat (which will be in actual fact most likely be used to render the final markup) - consult those Javadocs for the ultimate reference.
UILink and UICommand#
The UILink and UICommand primitive components both have constructors capable of accepting a UIMessage (or any other variety of UIBoundString) thereby creating a link or command control with localized text. For exampleUICommand saveReorderButton = UICommand.make(form2, "saveReorderButton", UIMessage.make("modifytemplate.button.save.order"), "#{templateBBean.saveReorder}");
will create a command control displaying the localised message with the key modifytemplate.button.save.order.
Localizing decorators#
All the String-valued RSF Decorators will accept a UIMessage object in place of the direct String. For this application, you must use the shorter UIMessage constructors that do not accept the UIContainer parent, since the component is not being added to the tree directly. For example, to add a tooltip decorator to the command button we created above, so that hovering the mouse over it will show a localised message, one could use the following line:
saveReorderButton.decorators = new DecoratorList( new UITooltipDecorator( UIMessage.make("modifytemplate.button.save.order.title") ) );
Other BoundString-accepting decorators include UIAlternativeTextDecorator.
Localising selection controls#
Selection controls represented by UISelect are conveniently initialised with String arrays. Looping over these for message lookup would be highly tedious, so the primitive UISelect supports a very useful shorthand notation for expressing that the String supplied as the labels argument represents an array of message keys, rather than an array of literal labels:
String[] instructorOptLabels = { "evalsettings.instructors.label.opt.out", "evalsettings.instructors.label.opt.in", "evalsettings.instructors.label.required" }; UISelect.make(tofill, "instructorOpt", EvaluationConstant.INSTRUCTOR_OPT_VALUES, instructorOptLabels, "#{evaluationBean.eval.instructorOpt}").setMessageKeys();
As can be seen, the only extra formality required to signal that instructorOptLabels should be interpreted as an array of messsage keys is to finalise the construction with a call to the instance method setMessageKeys() - this returns the freshly constructed selection control (in the famous "chaining" style so famous from other frameworks) in case it is required.
RSF internals afficionados may be interested to learn that the entire implementation of setMessageKeys() consists of the following code:
public UISelect setMessageKeys() { optionnames.resolver = new ELReference("#{messageLocator}"); return this; }
MessageLocator#
The RSF bean messageLocator, of type MessageLocator, is the underlying implementation to which all the above message components pass during the fixup phase to acquire the correctly localised text. Occasionally it may be necessary to make use of this bean directly, but since RSF 0.7.1 all the principal use cases have been handled with the above methods, which are to be preferred, since they result in no intrusion on the target producer with an extra dependency. MessageLocator and its relationship to Spring MessageSource is explained on its own page - here we show examples of the main styles of usage.Direct resolution of a simple message#
This is a deprecated usage showing a UIOutput whose text is initialised by direct call to a MessageLocator, injected as a dependency. For this usage, use a UIMessage instead.UIOutput.make(tofill, "task-list-title", messageLocator.getMessage("task_list_title"));
Indirect resolution via EL#
Where necessary, and where the required message takes no arguments (that is, the Object array argument to MessageLocator or UIMessage would not be required), messages may be resolved via EL expressions using the message key as a property name of messageLocator:UIOutput.make(tofill, "sequence-num-1", null, "#{messageLocator.gt_num_1}");
Take care to escape any periods that occur within the key name here using the proper RSF utilities for working with EL. Again, this particular usage should be replaced with a UIMessage, although this capability may be useful in other contexts.
Default naming and location of the Messages.properties file#
The default location for the Messages.properties file in RSF tools is tool/src/webapp/WEB-INF/messages but this is configurable via a bean in applicationContext.xml like so.
<!-- Configure the location of the messages files --> <bean id="messageSource" parent="messageSourceParent"> <property name="basename" value="classpath:org/sakaiproject/evaluation/tool/bundle/messages" /> </bean>