Skip to main content
Skip table of contents

Internationalization and localization

NexJ CRM provides the following  internationalization and localization  capabilities:

  • NexJ CRM supports English, and was built to work with Unicode for supporting other languages.
  • Specific formatting is supported for dates, currency, and numbers.
  • Multi-language support is built into the business model for code values.
  • Display language is selectable at runtime.

String and locale files (files with extensions .string and .locale) are not part of the Model Description Language. They are lists of key and value pairs, and are considered to be properties files or property resources.

Locale files store local preferences for a language, such as date and number formats

String files store translated captions, messages, and other elements and properties that switch their display text depending on the current user's language preference.  

Overview

To support a new language you need to add resources with a new language tag. The language tags are based on the IETF standard RFC 4646. For more information, see RFC 4646 - Tags for Identifying Languages (ietf.org).

Some examples of language tags are en for English, en-CA for Canadian English, or es for Spanish.

Several types of files are used to record information about specific languages, including the SupportedLocaleEnum enumeration, one or more strings files for each supported language, and a locale file for each supported language.

SupportedLocaleEnum enumeration

An enumeration is a special type of class that contains a list of values along with localizable captions. Enumerations provide a standardized way to create lists of values, such as the days of the week. The values can be localized, for example by providing the terms for each day of the week in multiple languages. Enumerations can be associated in a parent-child relationship, such as country and state. For more information about enumerations, see Enumeration : Class (nexj.com)

The SupportedLocaleEnum enumeration contains a list of all supported locales. For each locale, it indicates the long and short descriptions in each supported language, as well as other information, such as whether the value can be updated at runtime.

For each new supported locale, you need to add a locale record and define its associated values. This example SupportedLocaleEnum enumeration describes two supported locales, English and French, and the short and long captions for each locale in both languages. The hasBehavior=true parameter indicates that there is business logic associated with this value.

Example SupportedLocaleEnum enumeration

XML
Enumeration description="Enumeration of supported locales" typeCode="SSUPPORTEDLOCALE">
   <Locales>
      <Locale caption="Enumeration of supported locales" name="en"/>
      <Locale caption="Énumeration des paramètres régionaux supportés" name="fr"/>
   </Locales>
   <Values>
      <Value hasBehavior="true" name="en" value="en">
         <Locales>
            <Locale caption="English" name="en" shortCaption="en"/>
            <Locale caption="anglais" name="fr" shortCaption="en"/>
         </Locales>
      </Value>
      <Value hasBehavior="true" name="fr" value="fr">
         <Locales>
            <Locale caption="French" name="en" shortCaption="fr"/>
            <Locale caption="français" name="fr" shortCaption="fr"/>
         </Locales>
      </Value>
   </Values>
</Enumeration>

String files

Text strings used in your application are stored in .strings files in the meta/strings directory for each project. Each strings file can contain one or more captions with their identifiers. The names of the files are in the <uniqueStringsArea>.<languageTag>.strings. For example, the idsc.Address.en.strings file might contain the English terms for different parts of an address, such as "City" or "PO Box", which are associated with identifiers such as idsc.Address.city or idsc.Address.poBox. If the French language is also supported, then there needs to be an idsc.Address.fr.strings file in the same directory. This file would contain the French captions for the same identifiers. For example, the isdc.Address.city value would be "Ville" and the idsc.Address.poBox value would be "C. P."

You can also use a .strings file for a single caption. For example, the idsa.ServiceRequest.en.strings contains only the following item:

idsa.ServiceRequest.contactSRFilter=Contact Service Requests

The matching idsa.ServiceRequest.fr.strings file contains only the following item:

idsa.ServiceRequest.contactSRFilter=Demandes de service du contact

String files can be populated using the export and import functions in the Strings Tool in NexJ Studio.

For more information about strings and the Strings Tool, see Strings.

Locale files

The /meta/locales folder contains a <languageTag>.locale file for every supported locale. The locale file 

Additionally, a new locale/string file is created in /mod/i18n/<languageTag>.js. 

Internationalization resources, plugins, and functions

Localized resources are organized in hierarchical maps, which are maintained in  nexj/i18n  plugins and can be looked up by id (string key) using the nexj/i18n() function. The most common resources are localized strings, used for UI captions and messages. You can also localize arbitrary objects, including functions.

Locale names follow the RFC 4646 standards and the corresponding resource maps form prototype inheritance chains for fallback lookup. For example, the , e.g. the "en-CA" map is based on the "en" map, which in turn is based on the "" (default) map. In addition to the I18N framework modules  nexj/i18n/<lang>  with files  nexj/i18n/<lang>.js, each module can have its own set of localization modules  < module>/i18n/<lang>  with files  < module-file>/i18n/<lang>.js . The AFL component  nexj.core.ui.web.AMDChainLoader  automatically includes the localization modules according to the user language.

Localization modules use functions like  nexj/i18n.add()  and  nexj/sys.prototype()  to create resource prototype chains, based on the data from the Unicode Common Locale Data Repository (CLDR). For more information, see Unicode CLDR.

Models can parse and format data in locale-specific formats, therefore they can be configured with specific formatting patterns. The actual parsing and formatting is implemented in  nexj/cvtnexj/cvt()  constructs a converter from a data type name, a pattern, a unit of measure and a localized resource map. The type name must match a class property in  nexj/cvt:

cvt("number"); // => default number converter
new ui.Value(0, "number") // => value model using a default number converter

Number format patterns can be configured to round values to thousands and millions using one or more  M  characters and an optional number. Without a number the scale is determined automatically,  3  and  6  lock the scale to thousands and millions. A single, double and triple  M  results in short, medium and long names respectively. For instance the pattern  # #0 MMM  will format the value  1e8  as  100   Million , pattern  # #0M3  will format the value as  100000k.

Parameterized UI messages are formatted using the  nexj/cvt.message  converter, and alternatively with  nexj/cvt.format()  or  nexj/cvt.message.format(). Each argument placeholder can specify its own converter, pattern and unit, including those supplied as other arguments. The functionality in  id choice  and  list  allows taking into account different word forms, language constructs and expressions for lists of items, directly in the localized resources, without any additional locale-dependent code:

// Resource map - typically it should be in a nexj/i18n plug-in
var locale = sys.prototype(i18n.en, {
	Entity: "Entity",
	"Entity.pl": "Entities",
	"app.delete.confirm": "Delete " +
	// The choice converter is given argument 1, i.e. value 3, along with an
	// interval [1..2] pattern with nested id converters and all the
	// cvt.message.format arguments (*), so that the nested converter
	// placeholders could work.
	// The first id converter is used if argument 1 is <2. It looks up the
	// resource string by the id provided in argument 2, i.e. "Entity", and
	// converts it to lower case ("l").
	// The second id converter is used if argument 1 is >=2. It appends
	// ".pl" to the string resource id before looking it up, thus getting
	// the plural form "Entities", which it then converts to lower case.
	"{1;choice;1|one {2;id;|l}|2|{$} {2;id;.pl|l};*}: " +
	// The list converter puts quotes around each item from the array in
	// argument 0 and separates them with ", ", except for the last two
	// items, which it separates with " and ".
	"{0;list;\"{$}\"|, | and }?"
	}),
	entities = ["root", "wheel", "admin"];
cvt.message.format("app.delete.confirm",
	[entities, entities.length, "Entity"], locale);
// => 'Delete 3 entities: "root", "wheel" and "admin"?'

Localized exceptions are constructed easily with  nexj/i18n.Error():


    // The exception is an instance of RangeError, the localized message id is
// "val.range" and the message arguments are lo and hi
throw i18n.Error.call(RangeError(),	["val.range", lo, hi]);
...
// Similar to above, but the constructed type is the generic i18n.Error
throw i18n.Error(["val.range", lo, hi]);

The commonly used patterns are stored as localized resources, and specifying the corresponding resource id directly as a pattern is a useful shortcut for calling  nexj/i18n():

 // the resource id "date.s" is used as a shortcut for
 // looking up the actual pattern
new ui.Value(new Date(), "date", "date.s");

// the above is equivalent to the following
new ui.Value(new Date(), "date", i18n("date.s"));

Dates and calendars

Date manipulation API is implemented in the nexj/date module. The nexj/date.Zone class provides time zone offset computation. By default, only the local time zone used in the browser and the UTC time zone are supported. However, most of the time zones in the world are added by including the nexj/date/olson module. This module relies on the Olson time zone database module nexj/date/olson/db, which is automatically generated by the build from files maintained by the Internet Assigned Numbers Authority (IANA). A new version of the database is released regularly. For more information about IANA, see Time Zone Database (iana.org)

The nexj/date.Gregorian class implements date arithmetic according to Gregorian calendar rules. More calendars can be added by deriving them from the nexj/date.Calendar class.


Configuring supported client locales

To configure supported client regional locales, you must modify the corresponding .locale file by adding the ids.format.regionalLocale property, if it does not already exist. The locale files can be found in the Resources > Locales tab in NexJ Studio. They can also be found in the meta/locales directory in the project folder. For example, finance/meta/locales.

The value for this property should be a valid combination of a language code and a region code separated by a hyphen, for example, en-CA (Canadian English).

The format and capitalization for the value of this property must follow a specific standard. The full property and value added to a .locale file should look similar to: 
ids.format.regionalLocale=en-CA

Each .locale file can have its own value for this property. For example, the property mentioned above can be added to the en.locale file, and the following property can be added to the fr.locale file:
ids.format.regionalLocale=fr-CA

By adding this property to the .locale files, the values attached to them build the supported client locales accepted by the application.

Setting locales

You can change locale via URL or via browser language. 

To change locale via URL, you must add the l parameter to the URL and the value specified should be an exact match for the value of the ids.format.regionalLocale property in the .locale file. For example, if an application's URL is http://www.nexj.com/nexj/ui/portal , then use the following URL to indicate that Canadian English should be used http://www.nexj.com/nexj/ui/portal?l=en-CA . If there is already an appended URL query parameter, add &l=en-CA to the URL instead.

To change the locale to match the browser, use the browser settings to change the browser language.

Configuring a project default locale

You can configure the default locale for the project. It will be used if the incoming locale (either entered as the URL or the current user’s browser locale) is not supported by the application. To set a desired project default locale, modify the FALLBACK_LOCALE attribute in the User class (User.meta).

Locale selection precedence

Because there are multiple different ways to set locale, which can have varying values, for example, URL, browser, project default, and so on, there is a priority system in place to determine which value has precedence.

The priority, from highest to lowest, is:

  • URL
  • User model (locale attribute in User.meta)
  • browser locale
  • project default
  • en-CA

A locale specified in the URL has the highest priority. As long as the locale value in the URL is supported, that client locale is used.

If there is no locale set in the URL, the user locale value is checked. If supported, the user locale value is used. Otherwise, the browser locale is checked. If the browser locale is supported, it is used. If the browser locale is not supported, the project default locale is used.

If the project default locale that has been configured is not a valid supported client regional locale (by misconfiguration, for example), the application defaults to en-CA.

Using a flag to determine User model precedence

You can use the ignoreUserLocale flag from the framework to determine when the User #locale value should take precedence or not. 

When the value for the flag is false, which is the default value, the User#locale is used. If the value is true, the User model value may not match the locale value that was set in the URL, and the client locale should be used instead. In this case, the returned value is the existing clientLocale value, as tracked by the framework.

The following example shows the usage of the flag in finance. It is taken from the locale attribute for User.meta in finance. The methods, which get and set the ignoreUserLocale flag, exist for the nexj.core.runtime.InvocationContext class, and therefore a nexj.core.runtime.InvocationContext instance is needed to call these methods.

SCHEME
(cond
   //If the flag is TRUE (locale set from URL), then we can’t use the User model value, therefore, use the client locale value tracked by the framework (InvocationContext#getClientLocale).
   (((invocation-context)'ignoreUserLocale) ((invocation-context)'clientLocale))
   //ELSE the flag is FALSE (locale not set in URL), continue to User model logic
   (else
      … 
   //Retrieve user model locale/validate/use fallback/etc.

Enabling en_US language support

By default, the en locale uses the Canadian format for dates (day/month/year). If you want to be able to display dates in the US format (month/day/year), you must enable the en_US locale.

To enable the en_US locale and to provide the ability for users to select either the en_US or the en_CA locale for English, do the following:

  1. In NexJ Studio, in the Business Model layer, open the Enumerations tab.
  2. Open the SupportedLocaleEnum enumeration.
    • If SupportedLocaleEnum is not already customized, right-click on SupportedLocaleEnum and select Customize Using > Base.
  3. In the Values section, add a new value and specify the following settings:

    Field nameSetting
    Nameen_US
    Valueen_US
    Has Behaviortrue (checkbox selected)
    Display Updatabletrue (checkbox selected)
  4. Select the new value. In the Value Captions section for en_US, add a new preferred value caption for each supported locale. For example, if you have en, fr, and en_US locales, specify the following settings: 

    Field nameSetting for en localeSetting for fr localeSetting for en_US locale
    Localeenfren_US
    CaptionEnglish (United States)anglais (États Unis)English (United States)
    Short Captionen_USen_USen_US

    This will be reflected in the source code in the following:

    XML
    <Value hasBehavior="true" name="en_US" value="en_US">
       <Locales>
          <Locale caption="English (United States)" name="en" shortCaption="en_US"/>
          <Locale caption="English (United States)" name="en_US" shortCaption="en_US"/>
          <Locale caption="anglais (États Unis)" name="fr" shortCaption="en_US"/>
       </Locales>
    </Value>
  5. For each of the existing supported locales, select the locale and add a new value caption entry for the new en_US locale. For example, for the fr locale, add a value caption with the following settings:

    XML
    <Locale caption="French" name="en_US" shortCaption="fr"/>
  6. Add an upgrade load step to Main.upgrade and increment the version number by 1.
  7. Update finance model version to match the latest Main.upgrade version.
  8. Upgrade the database.

The en_US locale is now enabled and en_US data can be seeded.

To disable the en_US locale, do the following:

  1. In the SupportedLocaleEnum enumeration, remove all references to the en_US locale.
  2. In the Main.upgrade file, add an upgrade load step and increment the version by 1.
  3. Update finance model version to match the version on the latest Main.upgrade file.
  4. Upgrade the database.
JavaScript errors detected

Please note, these errors can depend on your browser setup.

If this problem persists, please contact our support.