- #default language options
- language=EN
- #English
- EN_label_server=Server
- EN_label_servers=Servers
- EN_label_readers=Restrict to
- EN_label_command=Command
- EN_label_commands=Commands
- EN_label_create=Add
- EN_label_edit=Edit
- EN_label_submit=Submit
- #Spanish
- SP_label_server=Servidor
- SP_label_servers=Servidores
- SP_label_readers=Limitar a los
- SP_label_command=Comando
- SP_label_commands=Comandos
- SP_label_create=A\u00F1adir
- SP_label_edit=Edici\u00F3n
- SP_label_submit=Presentar
- #German
- DE_label_server=Server
- DE_label_servers=Server
- DE_label_readers=Beschr\u00E4nken auf
- DE_label_command=Befehl
- DE_label_commands=Erstellen
- DE_label_create=Hinzuf\u00FCgen
- DE_label_edit=Editieren
- DE_label_submit=Einreichen
dominoGuru.com
Your Development & Design Resource
Multi-Lingual Support for XPage Controls via Properties Files
01/26/2011 05:00 AM by Chris Toohey
While working on Remote Console HD, I thought I would heed the advice of a friend from the community and look into providing language skins for my apps. Of course, there are absolutely tried and tested methods for doing this using traditional IBM Lotus Notes Domino Development techniques... but with .Properties File Resources, it's infinitely easier with XPages!
The idea is simple: Field/Input labels, View Column Headers, and any in-app content would have a translation that could be applied across the application. In other words, the Readers Field for Remote Console HD would have an English label (Restrict to), a Spanish label (Limitar a los), or even a German label (Beschränken auf).
Again, certainly techniques available using traditional Design Elements for something like a label (Computed Text looking up a Language Profile is most common...), but when you get into View Column Headers, it gets really tricky.
XPage Controls, however, don't fall victim of such limitations.
Jeremy Hodge recently wrote an excellent article on using .Properties files to create Global Variables for your XPage apps, and it was that article that inspired the technique I used for my own localization of Remote Console HD.
To start, I created a new File Resource named language.properties, with the following content:
[line numbers below have been added for reference purposes, and not part of the text added to the .Properties File Resource]
Lines 18, 19, 24, and 27 above contain the escaped unicode characters for ñ, ó, ä and ü respectively. A complete list of unicode characters [which you'll need to reference in order to truly go multi-lingual] can be found at http://en.wikipedia.org/wiki/List_of_Unicode_characters.
Once you have created your .Properties File, you can add it as a Resource to your XPage.
Once you have selected Add\Resource Bundle... from the XPage Resources section of the XPage Properties Pane, you will select your .Properties File and define a Resource Bundle Name (which acts as a variable declaration for use in your XPage).
Added to the XPage, you can now reference any value in the .Properties File via the following syntax:
Resource Bundle Variable[.Properties File Name/Value Name]
... or, for a less-confusing way to put it:
preferences['EN_label_server']
Fairly straight forward from here really. Let's take a look at a demo block of labels for Remote Console HD:
<div class="card">
<h3>Static English</h3>
<xp:text escape="true" id="computedField17"
styleClass="example">
<xp:this.value><![CDATA[#{javascript:preferences['EN_label_server']}]]></xp:this.value>
</xp:text>
<xp:text escape="true" id="computedField18"
styleClass="example">
<xp:this.value><![CDATA[#{javascript:preferences['EN_label_servers']}]]></xp:this.value>
</xp:text>
<xp:text escape="true" id="computedField19"
styleClass="example">
<xp:this.value><![CDATA[#{javascript:preferences['EN_label_readers']}]]></xp:this.value>
</xp:text>
<xp:text escape="true" id="computedField20"
styleClass="example">
<xp:this.value><![CDATA[#{javascript:preferences['EN_label_command']}]]></xp:this.value>
</xp:text>
<xp:text escape="true" id="computedField21"
styleClass="example">
<xp:this.value><![CDATA[#{javascript:preferences['EN_label_commands']}]]></xp:this.value>
</xp:text>
<xp:text escape="true" id="computedField22"
styleClass="example">
<xp:this.value><![CDATA[#{javascript:preferences['EN_label_create']}]]></xp:this.value>
</xp:text>
<xp:text escape="true" id="computedField23"
styleClass="example">
<xp:this.value><![CDATA[#{javascript:preferences['EN_label_edit']}]]><
span class="sc3"></xp:this.value>
</xp:text>
<xp:text escape="true" id="computedField24"
styleClass="example">
<xp:this.value><![CDATA[#{javascript:preferences['EN_label_submit']}]]></xp:this.value>
</xp:text>
</div>
With some CSS, we get the following:
Taking this technique one step further, we can use the Default Language Option defined in the language.properties File:
<xp:text escape="true" id="computedField1"
styleClass="example">
<xp:this.value><![CDATA[#{javascript:preferences[preferences['language'] +
'_label_server']}]]></xp:this.value>
</xp:text>
From here, the Lotus Professional who purchased their copy of Remote
Console HD would only need to change the .Properties File Resource [which
could even be done programmatically via Java] language
value from
EN to SP to change all labels, view column titles, and all
other content wired to the language.properties Resource Bundle.
You could even provide a user profiling facility in the application to allow for individual language preferences, or base the leveraged name/value pairs on a UI Trigger:
<div class="card">
<h3>Default, with UI Trigger</h3>
<h4>
<xp:comboBox id="languagepicker">
<xp:this.defaultValue><![CDATA[#{javascript:preferences['language']}]]></xp:this.defaultValue>
<xp:selectItem itemLabel="English" itemValue="EN"></xp:selectItem>
<xp:selectItem itemLabel="Spanish" itemValue="SP"></xp:selectItem>
<xp:selectItem itemLabel="German" itemValue="DE"></xp:selectItem>
<xp:eventHandler event="onchange" submit="true"
refreshMode="partial"
refreshId="uitrigger_elements">
</xp:eventHandler>
</xp:comboBox>
</h4>
<xp:panel id="uitrigger_elements">
<xp:text escape="true" id="computedField25"
styleClass="example">
<xp:this.value><![CDATA[#{javascript:var fv =
getComponent("languagepicker").getValue();
if (fv == "") {
fv = preferences['language']
}
preferences[fv + '_label_server']}]]></xp:this.value>
</xp:text>
...
</xp:panel>
</div>
Here we have a simple comboBox Control that performs a Partial Refresh on the uitrigger_elements panel Control, which is wired to the value of said comboBox Control. The logic here is pretty simple: If the languagepicker comboBox value is blank, use the Resource Bundle's default language designation, otherwise use the language defined by the comboBox.
Combine this with the .Properties Resource Bundle read/write capabilities available vi a Java (which I believe Jeremy is working on a follow-up to the previously-mentioned article that inspired this one illustrating a Java read/write/save example for Resource Bundles), and you can really get a feeling for the awesome potential of this technique.
You can view a demo of this technique in action now at http://guru.gbs.com/demo/demo.nsf/babelfish.xsp, and a huge thanks to Jeremy Hodge, who not only inspired this article but who also helped me figure out the need to escape unicode special characters.