HST Dynamic Resource Bundles Support
Hippo's delivery tier (HST) supports dynamic Java resource bundles management in the repository and seamless integration with JSTL tag libraries. In order to provide a more flexible and convenient way than the current default practice dealing with localization issues, HST provides an option to store resource bundles in the repository and configure a default resource bundle per virtual host group, virtual host, mount or sitemap item, while supporting standard JSTL tag libraries seamlessly. This means that the resource bundles can be accessed using the standard JSTL tag libraries and API set, whether they are stored in the repository or as plain Java classpath resources.
Introduction
The HST supports dynamic Java resource bundles management in the repository and seamless integration with JSTL tag libraries.
You can set multiple resource bundle IDs, separated by a comma or white space character, per sitemap item, mount, virtual host, or virtual host group configuration. Next to that, the <hst:setBundle/> tag allows multiple resource bundle IDs, again separated by comma or white space character, as basename attribute value as well.
When building localized web page templates wih JSTL tag libraries, you might have used the <fmt:setBundle/> and <fmt:message/> tags as in the following example:
<fmt:setBundle basename="org.example.app.Messages" /> <fmt:message key="greeting.one" />
This kind of usage looks okay, but there is still some room for improvement. For example, the <fmt:setBundle/> tag in the above example assumes the following:
- All the files of this resource bundle are normally loaded from the classpath, e.g. classpath:org/example/app/Messages.properties, classpath:org/example/app/Messages_en.properties, classpath:org/example/app/Messages_en_US.properties, etc. Therefore, normally you will not be able to change the resource bundles at runtime. You will need to redeploy the application and restart the container for resource bundle changes by default.
- Also, you have to put the <fmt:setBundle/> tag in each template page. If you want to change the value of the basename attribute (also see JSTL format fmt setBundle), you will need to modify the template page and, if the template is a JSP, redeploy the application and restart the container.
- With the standard tag, you cannot set multiple resource bundles working as fallback where the first resource bundle has the highest priority.
When you manage resource bundle documents in the repository, your changes will be reflected on the website right away at runtime, no redeploy or restart needed.
Resource Bundle Documents
Resource Bundle Documents can be created and managed in the CMS using the Resource Bundle Editor plugin. This plugin is installed by default when creating a project using the Maven archetype.
Please note that the HST Resource Bundle feature supports context awareness based on preview or live mode meaning that if you visit a site in preview mode, the resource bundles are automatically loaded only from the preview variants of resource bundle documents. In turn, when visiting a site in live mode, the resource bundles are automatically loaded from the live variants of resource bundle documents.
Resource Bundle Configurations in HST
You can configure the basename value of the default resource bundle the application should use per template, virtual host group, virtual host, mount or sitemap item.
Here are the CND snippets for each configuration node:
[hst:virtualhosts] > nt:base, mix:referenceable, mix:versionable // <SNIP>// single resource bundle base name or (comma or white space separated) multiple resource bundle names. - hst:defaultresourcebundleid (string) // <SNIP> [hst:virtualhost] > nt:base, mix:referenceable // <SNIP> // single resource bundle base name or (comma or white space separated) multiple resource bundle names. - hst:defaultresourcebundleid (string) // <SNIP> [hst:mount] > nt:base, mix:referenceable // <SNIP> // single resource bundle base name or (comma or white space separated) multiple resource bundle names. - hst:defaultresourcebundleid (string) // <SNIP> [hst:sitemapitem] > nt:base, mix:referenceable // <SNIP> // single resource bundle base name or (comma or white space separated) multiple resource bundle names. - hst:resourcebundleid (string) // <SNIP>
As you can see, you can configure either the hst:resourcebundleid or hst:defaultresourcebundleid property. These properties are intended for configuring as many (comma or white space separated) basename values per item as is needed.
For example, if you set the hst:resourcebundleid property to " org.example.app.Messages" for a specific sitemap item, it will have the same effect as adding <fmt:setBundle basename="org.example.app.Messages"/> in all the template pages that are executed through this specific sitemap item. In other words, this resolves the issue of having to manage the <fmt:setBundle/> tag by editing and (possibly) redeploying templates. The default resource bundle will be set for all templates automatically by the HST container. If you want to override the default resource bundle you can simply define <fmt:setBundle/> in your template and change the basename there.
Even better, you can set multiple resource bundle basenames on the hst:resourcebundleid and hst:defaultresourcebundleid properties. You can use this mechanism in many ways, for instance when you're using inheritance in your channels to override keys in a specific channel that are defined in a ResourceBundle in a common channel. Internally, the mechanism works as follows: if you set " org.example.app.Messages1, org.example.app.Messages2" as the basename value, the HST creates a new CompositeResourceBundle that uses each internal ResourceBundle instance to lookup the value for the key. In the example, bundle org.example.app.Messages1 will be searched first and org.example.app.Messages2 will be searched next if the key is not found in the first bundle. Don't forget to separate the resource bundle basenames by a comma or white space character(s).
Unless you add a resource bundle document associated with the basename in the repository (which is explained in the next section), the HST container will pick up a resource bundle from the default Java standard classpath resources using the basename value configured either on a sitemap item, mount, virtual host or virtual host group configuration node.
The hst:defaultresourcebundleid and hst:resourcebundleid property values are inherited by descending configuration nodes. For example, if you don't specify the hst:resourcebundleid property on a sitemap item, the HST container will use the parent sitemap item to look up the property. Also, if there's no hst:resourcebundleid property set in sitemap items, the HST container will look up the hst:defaultresourcebundleid property of the mount and use it if found. In the same way, the value of the hst:defaultresourcebundleid property of a virtual host group or virtual host configuration node will be inherited by its descending virtual host configuration nodes or child mount nodes.
In summary,
- You can set a single default resource bundle ID (a.k.a., basename of the resource bundle) or multiple (comma or white space separated) default resource bundle IDs on a sitemap item, mount, virtual host or virtual host group configuration node, in order to set the default resource bundle for the current request processing. You don't have to use the <fmt:setBundle/> tag to specify the default resource bundle using this configuration.
- By default, if you don't manage a resource bundle document in the repository, the resources will be picked up from the classpath. In other words, if the default resource bundle id is set to org.example.app.Messages and you don't have a resource bundle document associated with the basename in the repository, the HST container will pick up classpath resources like classpath:org/example/app/Messages.properties, classpath:org/example/app/Messages_en.properties, classpath:org/example/app/Messages_en_US.properties, etc. How to manage resource bundle documents in the repository will be explained in the next section.
- The default resource bundle configuration from either the hst:defaultresourcebundleid or hst:resourcebundleid property is passed from parent to child sitemap item, from mount to sitemap item, from virtual host to mount, and from virtual host group to virtual host configuration node.
- You can set multiple resource bundle IDs on a sitemap item, mount, virtual host or virtual host group configuration node (e.g, " org.example.app.Messages1, org.example.app.Messages2"). The order of the resource bundle IDs defines the order in which a key is looked up.
HST Tag Libraries supporting Dynamic Resource Bundles
HST tag libraries support an extended setBundle tag in order to allow you to use resource bundle documents in the repository if available. The <hst:setBundle/> tag basically inherits everything from the default standard <fmt:setBundle/> tag and is in addition able to look up the resource bundle(s) from the repository.
If you just use one default resource bundle for a sitemap item, mount, virtual host, or virtual host group, then normally you don't need to use the <hst:setBundle/> tag at all because a default resource bundle is automatically set just by configuring it in the repository. However, if you want to override the resource bundle in a template and use resource bundle documents from the repository instead of the classpath, you will need to use the <hst:setBundle/> tag in your templates.
By default, if a resource key is not found in the resource bundle defined by the <hst:setBundle/> tag, it will fallback to the resource bundle hierarchy as defined by the related sitemap item(s), mount, virtual host, or virtual host group if available. This default fallback behavior can be turned off by setting the fallbackToDefaultLocalizationContext attribute to false.
The <hst:setBundle/> tag is defined as follows:
<tag> <description> Loads a resource bundle and stores it in the named scoped variable or the bundle configuration variable </description> <name>setBundle</name> <tag-class>org.hippoecm.hst.tag.SetHstBundleTag</tag-class> <body-content>empty</body-content> <attribute> <description> Resource bundle base name. This is the bundle's fully-qualified resource name, which has the same form as a fully-qualified class name, that is, it uses "." as the package component separator and does not have any file type (such as ".class" or ".properties") suffix. </description> <name>basename</name> <required>true</required> <rtexprvalue>true</rtexprvalue> </attribute> <attribute> <description> Flag whether or not to fall back to the Java standard resource bundles if no resource bundle is found from the HST ResourceBundleRegistry by the basename. The default value is true. </description> <name>fallbackToJavaResourceBundle</name> <required>false</required> <rtexprvalue>true</rtexprvalue> </attribute> <attribute> <description> Flag whether or not to fall back to the default localization context, which might have been set in the virtual hosts, vitual host, mount or sitemap item level. The default value is true. </description> <name>fallbackToDefaultLocalizationContext</name> <required>false</required> <rtexprvalue>true</rtexprvalue> </attribute> <attribute> <description> Name of the exported scoped variable which stores the i18n localization context of type javax.servlet.jsp.jstl.fmt.LocalizationContext. </description> <name>var</name> <required>false</required> <rtexprvalue>false</rtexprvalue> </attribute> <attribute> <description> Scope of var or the localization context configuration variable. </description> <name>scope</name> <required>false</required> <rtexprvalue>false</rtexprvalue> </attribute> </tag>
Basically, the usage of <hst:setBundle/> is almost the same as for <fmt:setBundle/>, but you can do more with <hst:setBundle/> as follows:
- By setting the basename attribute of the <hst:setBundle/> tag, you can get the resource bundle from either a resource bundle document in the repository or a resource bundle file from the Java classpath. By default, the repository is searched first before the lookup falls back to the Java classpath.
- You can define multiple resource bundle IDs (separated by comma or white space character) as the basename attribute value. Each resource bundle will be searched in the order they are defined until a value for the key is found.
- You can use the fallbackToJavaResourceBundle attribute of the <hst:setBundle/> tag to control whether the classpath should be searched in case a value is not found in a resource bundle stored in the repository. The default value is true.
- You can use the fallbackToDefaultLocalizationContext attribute of the <hst:setBundle/> tag to control whether the resource bundles configured on the related sitemap item, mount, virtual host or virtual host group should be searched in case a value is not found in the specified bundle. The default value is true.
In summary, you can use the <hst:setBundle/> tag instead of the <fmt:setBundle/> tag in any case. Whether or not you want to use resource bundle documents managed in the repository, the <hst:setBundle/> tag will work in the same way as <fmt:setBundle/> by default. But, if you have resource bundle documents associated with the basenames, the <hst:setBundle/> tag will enable you to use resource bundles managed in the repository, too. Because <hst:setBundle/> supports a fallback mechanism to the default Java standard classpath resource bundle files by default, it is both safer and more convenient during normal web application development using the HST.
Dynamic Resource Bundles Reloading on Changes at Runtime
By default, the HST container maintains a JCR observation listener component which listens to any changes of all the resource bundle documents in the repository and invalidates the internal resource bundle cache accordingly.
Therefore when (de)publishing any resource bundle document in the CMS at runtime, the changes will be reflected on your website(s) right away.
Programmatic Accesses to Resource Bundles managed in the Repository
HST also provides a standard API to support programmatic access to resource bundles managed in the repository.
In most cases, you can simply use the utility method org.hippoecm.hst.resourcebundle.ResourceBundleUtils. ResourceBundleUtils#getBundle(String basename, Locale locale) to retrieve a resource bundle basename and locale. This method will fall back to the Java standard classpath resource bundles if no related resource bundle document is found in the repository. If you want to explicitly specify whether the fallback mechanism is used or not, use ResourceBundleUtils#getBundle(String basename, Locale locale, boolean fallbackToJavaResourceBundle) instead.