Translations

The Translations API makes it possible to get localized strings using $container.translations.

Translation bundles may now be defined declaratively in Application, Flow, or Page containers. The properties of the "translations" object are the names of the bundle, and the value must contain a "path" property that is the path to the bundle.

When you declare a bundle at the Application level, an optional "merge" property allows you to specify an existing bundle path, which this bundle should merge with and override. This allows overriding existing bundles in JET, or JET CCs, with Application-level bundles. Expressions for "merge" are supported, but they cannot reference Application artifacts, as this evaluation happens before the creation of the Application.

The following paths are supported for "path":

  • container relative: a path fragment prefixed by "./" (dot-slash) will be appended to the declaring container's (flow, page) path. Note that flows and pages are not allowed to reach outside of its container (the path cannot reference parent folders). This means that "../" is not allowed anywhere in the path. See the note about Using "merge" below.

  • application relative: a path fragment without a "./" prefix will be relative to the application root. This is discouraged for Flows or Pages, except where a RequireJS path mapping is being used.

  • absolute: paths that contain a host are used as-is.

The bundle must be in a folder named nls : the path can be any depth, but the last folder in the path must be nls, such that the root bundle is in the nls/ folder.

Translation bundles have the standard JET bundle format. String resolution uses the JET oj.Config.getLocale() to get the current locale for the context.

Caution:

Using "merge"

When using "merge", take care to use requireJS mapped references consistently. A common failure is when the "merge" property does not use a requireJS mapping, but the defining path to the bundle does use a mapping. For example,when a CCA is loaded using a requireJS path ("mapped/foo/loader") and it references the bundle using a relative path ("./resources/nls/strings"), the app flow MUST also use the mapping: ("merge": "mapped/foo/resources/nls/strings").

When a dot (".") is used as a prefix in the bundle paths, be aware that "merge" will not work. Internally, Visual Builder 'normalizes' bundle paths, so the actual paths used to define the bundle do not have a "dot" prefix.

For example, the declaration below defines a bundle, and then overrides it; note the use of the "dot" prefix everywhere except the "merge". If "merge" is used in a a declaration in app-flow.json, which is typical, the "dot" prefix on the "path" properties are optional.

"translations": {
  "translations" : {
    "app" : {
      "path" : "./resources/strings/app/nls/app-strings"
    },
    "appoverride" : {
      "merge": "resources/strings/app/nls/app-strings",
      "path" : "./resources/strings/override/nls/override-strings"
    }
  },

Example 1-76 Bundles

Two bundles, translations.js and moreTranslations.js, are defined in a Page model JSON, named "app" and "anotherBundle":

"translations": {
  "app": {
    "path": "./resources/nls/translations"
  },
  "anotherBundle": {
    "path": "./resources/nls/moreTranslations"
  }
},

The corresponding expression syntax would be as follows, with one expression per bundle:

<h4><oj-bind-text value="[[$page.translations.anotherBundle.description]]"</oj-bind-text></h4>
<span> 
  <oj-bind-text value="[[$page.translations.format('app', 'info.instructions', { pageName: 'index.html' }) ]]"</oj-bind-text> 
</span> 
<br/>

Example 1-77 Overriding both JET strings and a component's strings

{
  "id": "demoCardDemo",
  "description": "Custom Component, Demo Card, with methods",
  "defaultPage": "shell",
  "translations": {
    "main": {
      "path": "resources/nls/translations",
      "merge": "ojtranslations/nls/ojtranslations"
    },
    "dcoverride": {
      "path": "resources/nls/demo-card-overrides",
      "merge": "resources/components/democard/resources/nls/demo-card-translations"
    }
  },

Expression Language

Similar to variable references and other references, the objectcan be prefixed with the container (for example, application in the example below), or you can omit the container, in which case the current container is assumed.

<oj-bind-text
  value="[[$translations.format('myPageBundle', 'info.instructions', { pageName: 'index.html' })
        ]]">
</oj-bind-text>
<!-- or -->
<oj-bind-text
  value="[[$application.translations.format('myPageBundle', 'info.instructions', { pageName:
        'index.html' }) ]]">
</oj-bind-text>

In the example above, the format() function allows both named and positional replacement.

<oj-bind-text
  value="[[ $page.translations.shell.shell_header_title ]]">
</oj-bind-text>

Strings can be referenced directly, using $translations.<bundle>.<string id>.

Existing Applications That Use Translations

Applications that used translations prior to 18.2.3 must manually migrate their translations. Translations previously used the JET configuration, and therefore had one bundle for the entire app. You have several options:

  • Declare the bundle. You can choose to break the bundle up logically, but the simplest migration would be to use the exact example above in app-flow.json, which uses the path for the existing bundle provided for new apps.

  • Change the expression syntax to the new syntax. Assuming you declared your single bundle in the same manner as the Bundles example, named "app":

    • For just the translated string, change $application.translations.get(key) to $application.translations.app.key

    • For Strings that require replacement, change $application.translations.get(key, arguments) to $application.translations.format('app', key, arguments)

Specifying the Locale

By default, VB defers to JET to determine the current locale for the client. This is typically done by first looking at the <html> tag 'lang' attribute, and then falling back to some browser settings.

There is a "localization" declaration section in the Application model (app-flow.json) that contains a "locale" property, which allows the developer to specify an alternate locale. This configures the JET ojL10n plugin to use this locale.

Expressions may be used, but the application is not created at this point, and therefore no application functions or variables are available. Instead, the developer must provide the necessary JavaScript. The developer should also set the 'lang' attribute on the <html> tag, so that JET, and anything that uses JET, will also use this locale.

Example 1-78 Locale Example

{
  "id": "demoCardDemo",
  "description": "Custom Component, Demo Card, with methods",
  "defaultPage": "shell",
  "services": {},
  "translations": {
    "main": {
      "path": "resources/nls/translations",
    },
  },
  "localization": {
    "locale": "{{ determineLocale() }}"
  },
  "types": {}
}