The previous post on localization outlined a solution to the issue of localization - i.e. the display of your pages in multiple languages (and where appropriate currencies, time formats, etc).

In that post I highlighted the issue of memory usage when putting a resource bundle into each user’s session who differed from the default site language. Suggestion 1 was to use Coldspring to inject all resource bundles into a structure and use the locale as a key to retrieve the bundle from this structure in the Application scope. Thus, the only thing we need to store in the session is the locale string - just a few bytes per user.

This further extends Andy Jarret’s use of resourceBundle.cfc and coldspring. Rather than rewriting resourceBundle.cfc, I took the approach of creating a wrapper component which, in its init() method, populates the resource bundle structure by looping through the required locale’s and, relying on convention, pumps the locale file locations into resourceBundle.cfc to create the resource bundle and then adds the resource bundle to a structure, using the locale as the key.

First let’s take a look at this new wrapper component:

<cfcomponent displayname="ResourceBundleCollection" hint="Creates a structure populated with resource bundles">

	<cffunction name="getResourceBundleByLocale" access="public" output="No" hint="returns the locale rb struct" returntype="struct">
		<cfargument name="locale" required="true" type="string" />
		<cfreturn variables.resourceBundleCollection[locale] />
	</cffunction>

	<cffunction name="init" access="public" output="false" returnType="void"
				hint="Loads a bundle">
		<cfargument name="rbFileDir" required="Yes" type="string" hint="This must be the path where all locale files reside" />
		<cfargument name="rbLocaleList" required="No" type="string" default="en_GB" hint="List of comma delimited local codes" />

		<cfset variables.resourceBundleCollection = StructNew() />

		<cfloop list="#rbLocaleList#" index="locale">
			<cfset variables.resourceBundleCollection[locale] = CreateObject("component","mgshop.model.resourceBundle") />
			<cfset variables.resourceBundleCollection[locale].init(rbFile=arguments.rbFileDir & locale, rbLocale=locale) />
		</cfloop>
	</cffunction>

</cfcomponent>

We can pump in a list of locales (rbLocaleList) and a directory in which to find the files (rbFileDir). The xml in coldspring.xml is similar:

	<bean id="resourceBundleCollection" class="mgshop.model.resourceBundleCollection">
		<constructor -arg name="rbFileDir"><value>/mgshop/model/locale_data/</value></constructor>
		<constructor -arg name="rbLocaleList"><value>en_GB,fr</value></constructor>
	</bean>
	<bean id="controller" class="controller.controller">
		<property name="resourceBundleCollection">
			<ref bean="resourceBundleCollection" />
		</property>
	</bean>

And the autowiring works in controller.cfc as follows:

	<cffunction name="setResourceBundleCollection" returntype="void" access="public" output="false">
    	<cfargument name="resourceBundleCollection" required="true" type="any" />
    	<cfset variables.resourceBundleCollection = arguments.resourceBundleCollection />
	</cffunction>

	<cffunction name="getResourceBundleCollection" returntype="any" access="public" output="false">
  	  <cfreturn variables.resourceBundleCollection />
	</cffunction>

Not much different to what came before. Where the major change is is with changeLocale(). Firstly, I have changed to lookup a Locale table in the database before changing the session variable. And, of course, that session variable is now a small string.

	<cffunction name="changeLocale" access="public" returnType="void" hint="I change the locale resource bundle">
		<cfargument name="event" type="any" />

		<cfset var locale = arguments.event.getValue("locale") />

		
		<cfif getModelGlue().getORMService().createRecord("Locale").init(LocaleId=locale).load().Exists()>
			<cfset session.locale = locale />
			<cfset arguments.event.addResult("success") />
		<cfelse>
			<cfset arguments.event.addResult("failure") />
		</cfelse></cfif>
	</cffunction> 

And finally, the onQueueComplete code changes to the following:

	  	
	  	<cfif isDefined("session.locale")>
		  	
			<cfset arguments.event.setValue("rb", variables.ResourcebundleCollection.getResourceBundleByLocale(session.locale)) />
		<cfelse>
			
			<cfset arguments.event.setValue("rb", variables.ResourcebundleCollection.getResourceBundleByLocale(variables._genConfig.getConfig().defaultLocale)) />
		</cfelse></cfif>

If the session variable is set, then get the resource bundle for that locale. Otherwise default to a locale I have stored on my simple config bean where I store a lot of application config settings.

I hope someone finds this tutorial (part I followed by part II) useful.


One Response to “Localization in Model-Glue - Part II”  

  1. 1 Localization in Model-Glue - Part I at Clogging up the Web


Leave a Reply