Resource Bundles

For those who follow this group that implement internationalization,
how do you organize your resource bundles and views?

Do you have a single view that has many/most strings replaced at
runtime with the CB toolkit? If so, do you jam all your strings for
the entire site into a single file since the RB plugin can only have
one bundle loaded at a time?

Or, do you use the CB toolkit to replace strings within layouts/views
that are constant (header/footer for example) and construct separate
views for each language?

I can imagine a project I am working on having lots of strings and was
curious how others have solved the problem in the past.

Thanks for you time.

Aaron Greenlee

Aaron,

I have done it two ways. First, as you suggested, I have a one large Resource Bundle file per lang that I was implementing and then just used CB to replace it with the correct string. This works nice as everything gets loaded at once and to make a change for any lang, its all in one place. The disadvantage is when you have to change something for all langs. This wasn't a big concern as there were only 3 langs in that project.

The other way I have done it is to use CB to switch and control the current lang and time/money formats, and then use a DB to look up based on that lang the desired string. Make sense. This approach also works well.

Hows that for a definiative answer?

Curt Gratz
Computer Know How

We switched over to using a DB table for our i18n stuff and it's very
easy to do this in ColdBox. We have a table with locale, key and
translation and we have our own ResourceBundle CFC that extends
ColdBox's standard plugin:

<cfcomponent extends="coldbox.system.plugins.ResourceBundle"
       output="false"
       cache="true"
       cachetimeout="0">

  <cffunction name="getResourceBundle" access="public" returntype="any"
output="false">
    <cfargument name="rbFile" required="true" type="any"/>
    <cfargument name="rbLocale" required="false" type="any" default="en_US"/>
    <cfset var dsn = getSetting( 'dsn' ) />
    <cfset var rbq = 0 />
    <cfquery name="rbq" datasource="#dsn#"
cachedwithin="#createTimeSpan( 0, 6, 0, 0 )#">
      SELECT * FROM i18n WHERE locale = <cfqueryparam
cfsqltype="cf_sql_varchar" value="#arguments.rbLocale#"/>
    </cfquery>
    <cfset var rb = { } />
    <cfloop query="rbq">
      <cfset rb[ rbq.key ] = rbq.translation />
    </cfloop>
    <cfreturn rb />
  </cffunction>
  
</cfcomponent>

That's it! Then key management is a simple CRUD admin task.

We use dotted key names like profile.view.heading for event-specific
stuff and _global.cancel for global stuff.

while i'm only kibitzing when it comes to coldbox:

we still use the old old icu4j/IBM rbmanager s/w for our rb. we know it well & we've managed, for the most part, to train the herd of cats that are our translators into using rb files. this gets complicated fast when dealing with many locales. so maybe spare a few seconds of thought into how you're going to manage all this over time.

we'll normally have 1 global rb for the app's common strings & then individual rb for each module/part. there might be some minor overlap between the module rb but normalization will only buy you so much when it comes to complex apps across many locales when the design is still in "flux". since developers can easily get lost swapping between the common/global rb & individual rb, we usually do the normalization process after the fact.

you have an app for that? how do you track coverage/completeness? is this unicode instead of escaped unicode? what file formats do your translators provide?

i actually like the idea of a db backed rb, especially if we want to share rb with a flex frontend (it's "rb" wants utf-8 encoded text) but never really found anything like rbManager for db & never followed through on attempts to develop one.

Great approaches guys, one more for you.

If you deal not only with string replacement, but let’s say view content, different layouts per locale etc. The best approach is to package layouts/views into their own locale specifc packages. This way, if you are blessed with translators, they can translate the views that are prepared for them.

This approach is for rich content translations, not simple replacements.

Luis F. Majano
President
Ortus Solutions, Corp

ColdBox Platform: http://www.coldbox.org
Linked In: http://www.linkedin.com/pub/3/731/483
Blog: http://www.luismajano.com
IECFUG Manager: http://www.iecfug.com

Ernst, didn’t you develop this if I remember correctly? Even had a flex front end for it?

Maybe its time to open it up?

Luis F. Majano
President
Ortus Solutions, Corp

ColdBox Platform: http://www.coldbox.org
Linked In: http://www.linkedin.com/pub/3/731/483
Blog: http://www.luismajano.com
IECFUG Manager: http://www.iecfug.com

Thank you all!

I made an extension of ResourceBundle, that's called
ResourceBundleUpdater. It generates keys and updates the properties
file if the key is not found.
Also in udfs.cfc i made a function "_" for translate, and a function
called "__" for translateMessage. This makes the text in the view more
easily readable.
I got these idea's from some other i18n framework, forgot which one.

In my view i only have to do this:

_("Forgot password")

The ResourceBundle will generate a key for me, it hashes the string,
and prepends a part of the text to the key so its still human
readable:

if-your-forgot-your-password----2082E800872425B1823F2B8DCFA0C1AB=if
your forgot your password.
Click-here----------------------936CCDB97115E9F35A11D35E3D5B5CAD=Click here

It updates a preset amount of locale-property-files.

greets,
klaas

Awesome why don't you submit this to forgebox?

I havent really tested it yet in production, the project im using it
for got paused for a while..