The <spring:message> tag is often used in JPSs to output localized messages that are read from property files. In most cases it outputs the expected message without problem, but if you have apostrophe(s) in a message, there could be some unexpected results. The following could happen:
- The apostrophes in the message are stipped, not visible.
- Some of the parameters in the messages don’t get replaced by the arguments passed in. As a result the paramter placeholder is displayed.
Let’s see some examples that demonstrate this problem. First, we create a ReloadableResourceBundleMessageSource object that will let us access the messages in our property files. This object is created like this in the application context xml:
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="defaultEncoding" value="UTF-8"/>
<property name="basenames">
<list>
<value>classpath:i18n/messages</value>
</list>
</property>
</bean>
We also create a property file that will contain the messages and we output them using the <spring:message> tag in our JSP files. Our property file is placed here to match the configuration of the messageSource bean:
/src/main/resources/i18n/messages.properties
Now let’s see some example properties and the result of them. Our properties are:
home.paragraph1=There is no apostrophe in this message.
home.paragraph2=There is no apostrophe in this message, but it has {0} and {1} parameters.
home.paragraph3=Look it's an apostrophe, but no params.
home.paragraph4=Look it's an apostrophe, and it has {0} and {1} params.
home.paragraph5=Look it's it's two apostrophes, and it has {0} and {1} params.
home.paragraph6=Look it's two apostrophes, {0} but they're between the parameters {1}.
In the JSP we diplay them like this:
<p><spring:message code="home.paragraph1"/></p>
<p><spring:message code="home.paragraph2" arguments="P1,P2"/></p>
<p><spring:message code="home.paragraph3"/></p>
<p><spring:message code="home.paragraph4" arguments="P1,P2"/></p>
<p><spring:message code="home.paragraph5" arguments="P1,P2"/></p>
<p><spring:message code="home.paragraph6" arguments="P1,P2"/></p>
And the result is:
There is no apostrophe in this message.
There is no apostrophe in this message, but it has P1 and P2 parameters.
Look it's an apostrophe, but no params.
Look its an apostrophe, and it has {0} and {1} params.
Look its its two apostrophes, and it has P1 and P2 params.
Look its two apostrophes, {0} but theyre between the parameters P2.
As you can see, when apostrophes and parameters are both present, the apostrophes always disappear, but only some of the parameters are not evaluated.
The logic behind the magic
Before a message is printed, the ReloadableResourceBundleMessageSource object that we use passes messages through a call to the MessageFormat.format() method. This substitutes the arguments and formats them property. During this call the apostrophe has a special meaning, it encloses parts of the string that should not be evaluated. Also, after parsing the string all single apostrophes are removed.
If you look at the previous examples you can see that where parameters are present the apostrophes really disapear, and if a parameter is after an “opening” apostrophe it is not evaluated.
However, note that the third example has apostrophe in it but it is not removed from the string when it is printed. This is because for optimization reasons only messages that contain parameters are passed through the formatter method. The rest of the messages are just printed as is.
Using apostrophes and parameters together, the right way
If you would like for an apostrophe to display in a string where there are also parameters, you must write double apostrophes. They will be converted to single ones, and parameters will not be harmed.
So let’s take our fourth example and modify it to get the expected result. We just have to add an extra apostrophe:
home.paragraph7=Look it''s an apostrophe, and it has {0} and {1} params.
Look it's an apostrophe, and it has P1 and P2 params.
So sometimes you have to add double apostrophes, and sometimes not. Won’t this cause a confusion?
Yes, probably it will. But there is a way to force the ReloadableResourceBundleMessageSource object to work the same way every time. You can set a parameter on it that will cause all messages to go through the MessageFormat.format() method. So this way you can be sure that you can use double apostrophes every time you need to print an apostrophe. To enable this option, modify the bean definition like this:
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="defaultEncoding" value="UTF-8"/>
<property name="alwaysUseMessageFormat" value="true"/>
<property name="basenames">
<list>
<value>classpath:i18n/messages</value>
</list>
</property>
</bean>
Download source
You can download the source files used in this project here.