Loom is designed with i18n strongly in mind, which means that all messages must be stored in properties files.
// action classes can just invoke these methods
public Resolution save() {
info("save.success");
if (count == 0)
warn("noEntitiesProcessed");
}
// service classes may invoke them using MessageUtils
public void foo(int param1) {
MessageUtils.error("validationError").addArgument("param1", param1);
}
To display messages, use a l:messages tag:
<l:messages level="warn"/> <l:messages level="error"/>
Messages will be serialized on a redirect, in which case they will be displayed on the next page request.
Loom messages are stored in a MessagesRepository instance specific to each Locale and configured by MessagesRepositoryFactory. MessagesRepository is very similar to the ResourceBundle class included in the JDK.
If a ResourcesWatchdog class has been configured, any modification to these properties files will trigger a reload. Only changes to files that are not in the classpath will be detected, though.
By default MessagesRepository will use classpath:resources/loom-messages.properties (provided by loom.jar) and /WEB-INF/classes/resources/messages.properties. The latest is where the application is expected to store its own messages.
Note that if a specific Locale is not found the system will fallback to MessagesRepositoryFactory.defaultLocale (set by default to the OS locale).
Messages may include parameters:
loom.validation.dateMaxValueFailed=The value of ${propertyName} is bigger than the maximum allowed (${validator.maxValue.date})
When setting parameter values you must indicate if they should be translated or not:
Message message = new Message();
message.setMessageKey("loom.validation.dateMaxValueFailed");
message.addArg("validator", validator);
message.addTranslatedArg("propertyName", "customer.name");
The guessString() method is invoked to translate messages: when asked for "customer.manager.name" it will first search the whole string, then "manager.name", then "name". If none is found it will return "[missing: customer.manager.name]" and mark it as not found to skip further searches.
Guess results are calculated just once to improve performance.
The browser will get a JSON copy of the messsages bundle for the current user locale. This copy will include:
Thus, these two examples are equivalent:
spring.xml:
<loom:config> <loom:messages browserMessages="payment.commit payment.rollback edit.approve"/> </loom:config>
MyAction.java
@BrowserMessages({"payment.commit", "payment.rollback", "edit.approve"})
public class MyAction extends AbstractAction {
}
Both will generate a JSON object that can be used at the browser with:
<l:script action="Resources"/> <script> alert(loom.messages['payment.commit']); </script>
There are several ways to print i18n contents inside your JSP pages:
<!-- Print foo -->
<l:out value="foo" />
<!-- Print foo -->
${messages['foo']}
<!-- Print foo if printFoo is true; else, print bar -->
<l:out value="foo" if="${printFoo}" else="bar"/>
${messages[printFoo? 'foo' : 'bar']}