SAPUI5: The Definitive Guide to i18n (Internalization)

This is the definitive guide to i18n in SAPUI5.

You’ll learn:

  • What i18n is
  • How to use i18n in XML views and controllers in SAPUI5
  • Lots more

So if you want to master i18n in SAPUI5, then this article is for you.

It’s time for the first step!

What Means i18n?

The SAPUI5 framework includes internationalization functionality out of the box.

Internationalization means that all the texts of an application are available in one central location, and that a translator can easily translate all the texts into different languages.

i18n is the abbreviation for internationalization.

Let’s break i18n down into three parts to understand what internationalization is:

  1. i for the first letter of the word internationalization
  2. n for the last letter of the word internationalization
  3. And 18 for the eighteen letters between the first letter i and the last letter n of the word internationalization: i > n (#1) > t (#2) > e (#3) > r (#4) > n (#5) > a (#6) > t (#7) > i (#8) > o (#9) > n (#10) > a (#11) > l (#12) > i (#13) > z (#14) > a (#15) > t (#16) > i (#17) > o (#18) > n.

Therefore, i18n means: i + 18 letters + n equals internationalization.

How i18n Works in SAPUI5

SAPUI5’s i18n mechanism is made up of three parts:

  1. Resource bundles
  2. Resource model
  3. Controls that consume the resource model

The resource bundles are the actual language files that contain the key for translation and the translation itself.

The resource model glues the resource bundles and the controls that consume the resource bundles together.

Those controls consume the resource bundles and therefore language data through a binding that refers to the key of the i18n text.

SAPUI5 i18n Resource Bundles

A resource bundle is a file that contains the translated texts. For each language, there’s one resource bundle, plus a default resource bundle that’s used if no resource bundle exists for the language.

Resource bundles stores translations as key-value pairs. A resource bundle key-value pair consists of a key and a value separated by an equal sign—see below for examples.

The key is consistent resource bundles—it always stays the same—the value changes to each language.

The keys are in CamelCase. The value is the actual text that should be displayed.

For example, a key-value pair in a resource bundle for English:

# in your resource bundle for English

appTitle=My Application

The same text is packaged in a resource bundle in French:

# in your resource bundle for French

appTitle=Mon application

Or, in German (SAP is German, by the way):

# in your resource bundle for German

appTitle=Meine Application

The structure of a resource bundle is simple; there’s no hierarchy—the structure is flat.

The resource bundles are in the i18n folder in an SAPUI5 application:

|— webapp
|—— controller
|—— i18n
|——— i18n.properties
|——— i18n_en.properties
|——— i18n_en_US.properties
|——— i18n_de.properties
|——— i18n_de_CH.properties
|——— i18n_fr.properties
|—— localService
|—— model
|—— test
|—— view

All resource bundles end in .properties. Their names begin with an i18n, followed by an underscore, and the language’s acronym, like i18n_en.properties.

All lower case: i18n_[language acronym].properties.

Sometimes a language can be split into more specific language varieties, like English being split into American English and British English.

A resource bundle for a more specific language will have an underscore after the first language acronym followed by the more specific language name in capital letters, such as US for American English: i18n_en_US.properties.

The SAPUI5 framework always asks for the most specific language file first. For example, if the browser language is set to US then the SAPUI5 framework requests the i18n_en_US.properties resource bundle.

If the i18n_en_US.properties file doesn’t exist, then the application will ask for the next most specific resource bundle:

In case of the example from above, it’s the i18n_en.properties resource bundle.

If i18n_en.properties doesn’t exist, SAPUI5 uses the default language: i18n.properties. That’s the language determination fallback process.

SAPUI5 i18n Resource Model

The resource model is the bridge between the language data resource bundle and the controls that consume that language data.

The resource model gets initiated in the manifest.json:

// in your manifest.json
{
  
  "_version": "1.12.0",
  
  "sap.app": {

    "id": "my.app",
    "type": "application",
    "i18n": "i18n/i18n.properties",
    "title": "{{appTitle}}",
    "description": "{{appDescription}}",
    "applicationVersion": {
      "version": "1.0.0"
    }

  },
  
  ...
  
  "sap.ui5": {

    ...

    "models": {
      "i18n": {
        "type": "sap.ui.model.resource.ResourceModel",
        "settings": {
          "bundleName": "my.app.i18n.i18n"
        }
      }
    }

    ...

  }

  ...

}

Once the resource model is defined in manifest.json, the SAPUI5 framework loads resources bundles into the model, and the language data is provided globally.

i18n in SAPUI5 Controls

The controls consume the language files. Each control is an SAPUI5 element, such as a button or a table.

SAPUI5 controls are initiated via an XML view (SAPUI5 best practices) and adapted in a controller.

For example, an sap.m.Button control in an XML view:

<!-- in your XML view -->

...

<button 
  id="button-id" 
  text="foo" 
  press="onButtonPress">
</button>

...

An sap.m.Button control in a controller:

// in your controller

...

var oButton = this.byId("button-id");
oButton.setText("bar");

...

The SAPUI5 internalization framework works for both controls initiated in an XML view and controls adapted in a controller as you’’ll see:

SAPUI5 i18n in XML Views

In SAPUI5, you should use XML views to instantiate controls like buttons and tables. To use the i18n framework in an XML view is pretty easy.

A control uses i18n data through the standard binding syntax. Binding is how SAPUI5 connects data from a model to a control.

For example, there’s a model named myModel with data for a button. It’s a JSON model that holds the following data:

// data of myModel

{
  "buttonText": "Press me"
}

The button text data gets bound to the button’s text property in an XML view this way:

<!-- in your XML view -->
...

<Button id="button-id" text="{myModel>/buttonText}">

...

Therefore, the general binding syntax is {modelName>pathToData}. The binding syntax is made up of five parts:

  1. {
  2. model name
  3. >
  4. data path
  5. }

The model name of the internationalization model in SAPUI5 is i18n as declared in the manifest.json.

The structure of a resource bundle file is flat. Therefore, the data structure in the i18n model is flat as well. The language data key of the resource bundle follows the model name without any path.

To bind language data to a control, the binding syntax is {i18n>resourceBundleKey}:

  1. {
  2. i18n as model name
  3. >
  4. resourceBundleKey as data path
  5. }

For example, the resource bundle file i18n_en.property has the following key and value language pair:

# in your i18_en.property

pageTitle=Foo Bar

Then Foo Bar in the English language would be the page title in an XML view like this:

<!-- in your XML view -->

...
  
<Page title="{i18n>pageTitle}"></Page>

...

The SAPUI5 framework takes care of the rest—loading the data from the right resource bundle based on the user’s browser language and displaying the text.

SAPUI5 i18n in Controllers

Adapting an SAPUI5 control is more complicated than adding i18n text in an XML view:

  • First, you have to manually retrieve the i18n bundle from the controller in order to access a text.
  • Second, to get the control that’s supposed to consume the text—only then can the i18n text be extracted and attached to the control.

There are three steps to bind an i18n text to a control in a controller:

  1. Retrieve the i18n resource bundle from the i18n model.
  2. Get the SAPUI5 control.
  3. Get the i18n text and glue it to the control.

First, get the resource bundle from the i18n model:

// in your controller
...

foo: function() {

  // retrieve the i18n resource bundle
  var oResourceBundle = this.getView().getModel("i18n").getResourceBundle();

},

...

Then second, get the control from the view:

// in some method in the controller
...

foo: function() {

  // retrieve the i18n resource bundle
  var oResourceBundle = this.getView().getModel("i18n").getResourceBundle();

  // retrieve the control from the view
  var oButton = this.byId("button-id");

}

...

Third, retrieve the i18n text from the resource bundle and set it to the control:

// in your controller
...

foo: function() {

  // retrieve the i18n resource bundle via the i18n model
  var oResourceBundle = this.getView().getModel("i18n").getResourceBundle();

  // retrieve the control via its id "button-id" from the controller's view
  var oButton = this.byId("button-id");

  // retrieve the i18n text via its key "bar" from the resource bundle
  // and set it to the control
  oButton.setText(oResourceBundle.getText("bar"));

},

...

SAPUI5 i18n and Parameters

You can use parameters in an i18n text—to dynamically add words or sentences:

For example, if an i18n text says Products but is supposed to dynamically show the count of the products after Products. This count comes from the back-end via an OData service and therefore an SAPUI5 OData model.

For i18n parameters, two steps are needed:

  1. Create an i18n key/value pair with parameters in i18n resource bundles.
  2. Glue the i18n text to a control and pass the parameters.

First, set a key/value pair with a parameter in the i18n resource bundles.

For instance, the default resource bundle i18n.properties:

# in your default resource bundle i18n.properties

products=Products {0}

The syntax for a parameter in an i18n text is {<number>}; if there would be three parameters then the text would be, for example, Products overall {0}, products sold {1}, and products in production {2}:

# in your default resource bundle i18n.properties

productsOverallSoldProduction=Products overall {0}, products sold {1}, and products in production {2}

A parameter number starts with 0 and increments with each new parameter. For a new text, the first parameter starts again at 0.

Second, after the key/value pair with parameters is set in the resource bundles, the i18n text is glued to a control and the parameters are passed onto the text.

The binding and passing of parameters happens in an XML view or a controller through binding.

i18n text with parameters binding in XML view:

<!-- in your XML view -->

...
  
  <Page
    id="foo-bar"
    title="{
    parts:[
      {path:'i18n>productsOverallSoldProduction'},
      {path:'odataModel>/argument1'}, 
      {path:'odataModel>/argument2'},
      {path:'odataModel>/argument3'}
    ], 
    formatter:'jQuery.sap.formatMessage'}">
  </Page>

...

i18n text with parameters in a controller:

// in your controller
...

foo: function() {

  // retrieve the i18n resource bundle via the i18n model
  var oResourceBundle = this.getView().getModel("i18n").getResourceBundle();

  // retrieve the dynamic values from the OData model
  var sProductsC = this.getView().getModel("oDataModel").getProperty("/Products/count");
  var sProductsSoldC = this.getView().getModel("oDataModel").getProperty("/ProductsSold/count");
  var sProductsProdC = this.getView().getModel("oDataModel").getProperty("/ProductsProduction/count");
  
  // set the i18n text to the control via its key
  // pass the dynamic arguments to the i18n text
  this.byId("foo-bar").setText(oResourceBundle.getText("bar", [
  
    sProductsC,
    sProductsSoldC,
    sProductsProdC    

  ]));

}

...

Full Example of SAPUI5 i18n in an XML View and a Controller Each With and Without Parameters

The requirement is to provide a French translation for the texts foo bar and shop <closed, open> at <date>. Whereby, <closed, open> and <date> are two parameters which are dynamically filled in with data from an OData model.

For example, the text could be shop closed at 11.11.2019.

Plus, when a button is clicked both texts are supposed to switch (one time, let’s not exaggerate)—the foo bar text switches with the shop <closed, open> at <date> and vice versa.

The steps to take:

  1. Create i18n resource bundle file i18n_fr.properties.
  2. Key/value pair for the text foo bar in the i18n_fr.properties.
  3. Key/value pair for the text shop <closed, open> at <date> in the i18n_fr.properties.
  4. Set up i18n framework in the manifest.json.
  5. Set i18n text foo bar in the XML view.
  6. Set i18n text shop <closed, open> at <date> in the XML view.
  7. Implement the switch of the i18n text on a button press in the controller.

First, the i18n resource bundle and the keys:

# in your i18n_fr.properties resource bundle file

fooBar=foo bar
shopClosedOpenAt=boutique {0} à {1}

Second, the i18n setup in manifest.json:

// in your manifest.json

{
  "_version": "1.12.0",
  
  "sap.app": {
    "id": "my.app",
    "type": "application",
    "i18n": "i18n/i18n.properties",
    "title": "{{appTitle}}",
    "description": "{{appDescription}}",
    "applicationVersion": {
      "version": "1.0.0"
    }
  },

  ...
  
  "sap.ui5": {
  
    ...

    "models": {
      "i18n": {
        "type": "sap.ui.model.resource.ResourceModel",
        "settings": {
          "bundleName": "my.app.i18n.i18n"
        }
      }
    }

    ...

  }

}
<!-- in your XML view -->

<mvc:View 
  height="100%"
  xmlns:mvc="sap.ui.core.mvc"
  xmlns="sap.m">
  
  <Text
    id="text1" 
    text="{i18n>fooBar}"></Text>

  <Text
    id="text2" 
    text="{ 
    parts:[
      {path:'i18n>shopClosedOpenAt'},
      {path:'ODataModel>/state'}, 
      {path:'ODataModel>/date'}
    ], 
    formatter:'jQuery.sap.formatMessage'}>
  </Text>

  <Button
    text="Change Texts"
    press="onButtonPress">
  </Button>

</mvc:View>

Third, set the i18n text in the XML view:

// in your controller
...

onButtonPress: function() {

  // retrieve the i18n resource bundle via the i18n model
  var oResourceBundle = this.getView().getModel("i18n").getResourceBundle();

  // retrieve the dynamic values from the OData model
  var sState = this.getView().getModel("ODataModel").getProperty("/state");
  var sDate = this.getView().getModel("ODataModel").getProperty("/date");
  
  // switch the i18n text
  // set the i18n text to the control via its key
  // pass the dynamic arguments to the i18n text with parameters
  this.byId("text2").setText(oResourceBundle.getText("fooBar");

  this.byId("text1").setText(oResourceBundle.getText("shopClosedOpenAt", [
  
    sState,
    sDate

  ]));

},

...

Fourth, adapt the texts on a button click in the controller:

SAPUI5 i18n and Right-to-Left Languages

All texts in the i18n resource bundle files are ISO-8859-1 (LATIN-1). This affects right-to-left languages such as Arabic, Hebrew, and Urdu.

LATIN-1 assumes left to right text, so right-to-left languages must be entered in the resource bundle file through Unicode, not through the script of the native language. Plus, the characters need to be reversed.

For example, the text is supposed to say Shalom Olam. That’s Hebrew and means Hello World.

  • First, there needs to be a Hebrew resource bundle file i18n_he.properties.
  • Second, the text needs to be entered in Unicode and not the Hebrew script.
  • Third, the text needs to be entered in the reverse order.

Therefore, it has to look like this:

# in your i18n resource bundle file i18n_he.properties

helloWorld=\u05E9\u05DC\u05D5\u05DD \u05E2\u05D5\u05DC\u05DD

And NOT entering the Hebrew text without Unicode:

# in your i18n resource bundle file i18n_he.properties

helloWorld=םולש םלוע

Leave a Comment