Internationalization (i18n) & Resource Bundles Usage in ColdBox

ColdBox comes with two plugins and wires in the coldbox.xml that will facilitate any project to be internationalized. This guide will not explain what internationalization is or a full in depth guide on resource bundles. For that, see the Resources section below. A project can be internationalized and use resource bundles for displaying per locale strings or just use internationalization without resource bundles. Another features is that you can use the plugins separate from the coldbox.xml declarations. So basically you can programattically use i18n and resource bundles. The ColdBox bundle download also includes a i18N sample application that you can learn from. Also, the samples gallery is an internationalized application that you can learn from.

Credit

These plugins are based on Mr. Paul Hastings work and all credit goes to him. I only converted them to a ColdBox plugin and wired them for usage in the framework. http://www.sustainablegis.com/blog/cfg11n/

Coldbox.xml Declarations

In the coldbox.xml you can declare in the <i18n> section the default resource bundle to use, the default locale of the application, and the storage space of the locale by the framework.

<i18N>
        <!--Default Resource Bundle without locale and properties extension-->
        <DefaultResourceBundle>includes/main</DefaultResourceBundle>
        <!--Java Standard Locale-->
        <DefaultLocale>en_US</DefaultLocale>
        <!--session or client-->
        <LocaleStorage>session</LocaleStorage>
</i18N>

The code above will tell the framework to load the resource bundle main for locale en_US and save the locale information in the session scope. The only available scopes are client and session. You can also use the config declaration for a i18n project with no resource bundles by using the following declaration:

<i18N>
        <!--Java Standard Locale-->
        <DefaultLocale>en_US</DefaultLocale>
        <!--session or client-->
        <LocaleStorage>session</LocaleStorage>
</i18N>

You eliminate the resource bundle element or leave it blank. The framework will not load the bundle then.

From this point, the framework loads any default resource bundle into the ColdBox cache and sets the locale you will be using. It is then up to you (DEVELOPER) to code the necessary hooks for internationalization.

Coding for i18n

Now that you have completed the initialization of the i18n tags in the configuration file, you are ready to code for them.

Note: Please note that the i18N plugin gets cached by default on the !ColdBox Cache indefinitely. You do not need to do any persistence on this object.
<cffunction name="onAppInit" access="public" returntype="void" output="false">
  <cfargument name="event" type="any">
  <!--- Set a default Locale, just in case --->
  <cfset getPlugin("i18n").setfwLocale(getSetting("DefaultLocale"))>
</cffunction>

<cffunction name="onRequestStart" access="public" returntype="void" output="false">
  <cfargument name="event" type="any">
  <!--- place plugin object on request collection for easy reference --->
  <cfset event.setValue("localeUtils",getPlugin("i18n"))>
</cffunction>

I call setfwLocale(getSetting("DefaultLocale")) on the application start method. This code tells the plugin what locale to instantiate the java classes in. You now have a locale specific i18n Plugin loaded for usage. You can look at the plugins API to see all the methods the i18n plugin contains. On my on request start method I then place the plugin on the event scope in order to be accessed from an alias on my views. I can actually skip this step and just use consecutive getPlugin("i18n") calls. So there are alternatives.

You can then use this utility for i18n specific methods, below is a simple example:

<cfoutput >
<strong>Locale:</strong> <br />#rc.localeUtils.getfwLocale()#<br />
<strong>Language:</strong> <br />#rc.localeUtils.showLanguage()#<br />
<strong>Country:</strong> <br />#rc.localeUtils.showCountry()#<br />
<strong>TimeZone:</strong> <br />#rc.localeUtils.getServerTZ()#<br />
<strong>i18nDateFormat:</strong> <br />#rc.localeUtils.i18nDateFormat(rc.localeUtils.toEpoch(now()),1)#<br />
<strong>i18nTimeFormat:</strong> <br />#rc.localeUtils.i18nTimeFormat(rc.localeUtils.toEpoch(now()),2)#<br />
<hr>
<strong>I18NUtilVersion:</strong> <br />#rc.localeUtils.getVersion().I18NUtilVersion#<br>
<strong>I18NUtilDate:</strong> <br />#rc.localeUtils.dateLocaleFormat(rc.localeUtils.getVersion().I18NUtilDate)#<br>
<strong>Java version:</strong> <br />#rc.localeUtils.getVersion().javaVersion#<br>
</cfoutput>

Resource Bundle Usage

You have declared the i18n section in your config and now you want to display locale specific text on your layouts,views, event handlers, etc. Well, all you need to do is use the getResource("Key") method. This method can be called from anywhere within the framework to retrieve a key from the current set locale's resource bundle. If you have not used resource bundles before or have no clue of what they are, please see the Resources at the end of this guide. It is a basic concept of getting key values from a structure.

Resource Bundle Tools

Below you can see some screenshots of the eclipse resource bundle editor plugin. I really recommend this tool also

Note: Make sure your files are all utf-8 encoding. It's also good i18n practice to liberally use cfprocessingdirective

Here is a simple example of how to use the getResource() method:

<p>&nbsp;</p>
        <h2>#getresource("about")# ColdBox </h2>
        <p>#getResource("aboutcoldbox")# </p>
</div>

It makes a call to the getResource method with a key to retrieve. ColdBox then retrieves the appropriate key from the loaded resource bundle. If no key is found it will return an _UNKNOWNTRANSLATION value.

How do I change Locales?

Well, it would not make any sense to use i18n for just one locale, would it? That is why you need to be able to change the framework's locale programmatically. You can do this in two ways:

  1. Using the i18n plugin and calling the setfwLocale() method.
  2. Using the i18n plugin instance that you loaded into the application scope (or any scope you loaded it in) and calling the setfwLocale method on it.

Below you can see an example taken from the samples gallery:

<cffunction name="doChangeLocale" access="public" returntype="void" output="false">
  <cfargument name="event" type="any">
  <!--- Change Locale --->
  <cfset getPlugin("i18n").setfwLocale(event.getValue("locale"))>
  <cfset dspHome()>
</cffunction>

It calls the plugin instance and tells it to load the locale submitted. If an incorrect or unsupported locale (by the JVM) then the framework will throw an invalid locale exception.

Notes

The i18n plugin contains several methods that you can use for i18n purposes, from language codes, timezone support, offsets, etc. You also have the resourceBundle plugin that you can use to load on the fly resource bundle files, parse them, etc. Please the cfc documentation for all the possibilities with these plugins.

Best Practices

  • eclipse (not cfeclipse) doesn't add a BOM to UTF-8 encoded files.
  • Always use cfprocessingdirective
    <cfprocessingdirective pageencoding="utf-8">
    
  • ColdBox uses the core java resource bundle flavor so you have to use a proper resource bundle tool to manage these.

Resources


Copyright 2006 ColdBox Framework by Luis Majano