Comments? Feedback?

This wiki does not yet support public comments (a limitation of Google Sites), so we encourage you to post your comments either:

On Twitter by responding to @orbeon.

On our community mailing list: subscribe sending an email to ops-users-subscribe@ow2.org (content of subject/body doesn't matter), you'll get a response with the email to use to send your message to the community mailing list.

Recent site activity

How-to guides‎ > ‎

Internationalizing and localizing your form


The problem

You would like your form to show text, labels, etc. in more than one language, based on user selection or preferences.

NOTES:
  • Internationalization with Form Builder / Form Runner – This how-to shows how you can handle internationalization in forms you create  by writing XForms "by hand". If you are using Form Builder and/or Form Runner, see this section about form localization in Form Builder.
  • You've got options – There are many ways to handle internationalization in your XForms application. What follows is a "recipe" documenting one specific way, which is efficient, relatively simple, and works well in most situations.

The solution

The resources

Store your resources in an XML file (say resources.xml) stored somewhere in your resources directory. That file will look like:

<?xml version="1.0" encoding="UTF-8" ?>
<resources>
    <resources xml:lang="en">
        <label>English</label>
        <registration>
            <first-name>First name</first-name>
            <last-name>Last name</last-name>
            <register>Register</register>
            <thank-you>You have been registered. Thank you.</thank-you>
        </registration>
        <language>
            <change>Change language:</change>
        </language>
    </resources>
    <resources xml:lang="fr">
        <label>Français</label>
        <registration>
            <first-name>Prénom</first-name>
            <last-name>Nom de famille</last-name>
            <register>Enregistrer</register>
            <thank-you>Vous vous être enregistré. Merci.</thank-you>
        </registration>
        <language>
            <change>Choisissez votre language:</change>
        </language>
    </resources>
    <resources xml:lang="zh-Hans">
        <label>中文 (简化字)</label>
        <registration>
            <first-name>名</first-name>
            <last-name>姓</last-name>
            <register>注册</register>
            <thank-you>谢谢你的注册</thank-you>
        </registration>
        <language>
            <change>选择语言:</change>
        </language>
    </resources>
</resources>

  • The resources for different languages (here English and French) are stored inside elements <resources xml:lang="en"> where the xml:lang attribute contains the ISO 639-1 code for that language.
  • Inside the <resources> element for a specific language, <label> contains the name of that language. (This label will be used later by the language selector.)
  • Then the resource per se are stored in elements where:
    • The element name is name of the resource, and hence the same in every language. Say: <first-name>.
    • The element content is the value of that resource for the current language. Say: First name.
  • You can group resources by functionality in your application by using a hierarchical structure. For instance, in the above code <first-name> is declared inside <registration> as it relates to the "registration" functionality.

The model

In your XForms model, load resources.xml in an instance. Make sure to make that instance read-only and cacheable: this way the instance will only be stored once in memory (it will be shared by all the users) and using a more efficient (because read-only) representation in memory.

<xforms:instance id="all-resources" xxforms:readonly="true" xxforms:cache="true"
                 src="oxf:/apps/my-app/resources.xml"/>

NOTE: Here we point to a local file with the oxf: protocol. This is usually yields more performance than using http:, because oxf: will reach a local file on disk. However, in the online version, we use the http:, because we want to load the resource from an online server!


Define an instance used to store the current language, e.g. en or fr.

<xforms:instance id="language"><language/></xforms:instance>

Define a variable ($resources), which points to <resources> for the current language. You will use this variable as a shortcut in your view to point to specific resources.

<xxforms:variable name="resources" select="instance('all-resources')/resources[@xml:lang = instance('language')]"/>

When the form loads, you want to determine what the current language is. The following code looks for a document stored in the session which contains the user's language. It uses the language set in the session if there is one, and otherwise take the first language from resources.xml. The logic you need to implement for your application might be different: for instance, you might have to call a service which figures out what the language is for the current user based on her preferences.

<xforms:action ev:event="xforms-model-construct-done load-resources">
    <xxforms:variable name="session-language" select="xxforms:get-session-attribute('language')"/>
    <xforms:setvalue ref="instance('language')" value="if (exists($session-language))
        then $session-language/string() else instance('all-resources')/resources[1]/@xml:lang/string()"/>
</xforms:action>

Using resources

In your view, you get resources from $resources. For instance, in a label, instead of writing <xforms:label>First name</xforms:label> you write <xforms:label ref="$resources/registration/first-name"/>, as for the following control:

<xforms:input ref="first-name">
    <xforms:label ref="$resources/registration/first-name"/>
</xforms:input>

A language selector

The code in the model you have seen earlier was getting the user's language from the session. If you want users to be able to change the current language, include in your page a control that lets them choose a language, and that stores that preference in the session when they make a new selection.

<xforms:select1 appearance="minimal" ref="instance('language')">
    <xforms:action ev:event="xforms-value-changed">
        <xforms:insert context="." origin="xxforms:set-session-attribute('language', .)"/>
        <xxforms:variable name="session-language" select="xxforms:get-session-attribute('language')"/>
        <xforms:dispatch target="main-model" name="load-resources"/>
    </xforms:action>
    <xforms:label ref="$resources/language/change"/>
    <xforms:itemset nodeset="instance('all-resources')/resources">
        <xforms:label ref="label"/>
        <xforms:value ref="@xml:lang"/>
    </xforms:itemset>
</xforms:select1>

Right-to-left and non-Latin characters

Orbeon Forms supports non-Latin characters out of the box:
  • In great part thanks to the Java platform Orbeon Forms runs on, all your strings are internally encoded in Unicode. Orbeon Forms is also careful not to rely on a "default" or "platform" encoding, to make sure that it behaves the same whatever platform it is deployed on.

  • Make sure:
    1. Your XML files all have an XML declaration that specifies what encoding is used for the file.
    2. Your editor supports XML and understands the encoding you specified in the XML declaration. If it doesn't, the declared encoding of the file (say, UTF-8) might now match the actual encoding (say, Windows-1252). Unless you have good reasons not to, use the UTF-8 encoding.
Languages that are written right to left, like Arabic, Persian, and Hebrew, pose an additional set of challenges. In great part you can support right to left languages by adding or overriding existing CSS. See for instance the updated CSS and related instructions for Arabic created by Rami Khader.

Run it and get the source