7

How can we pass parameters to the i18n model from within a XML view?

Without parameters

<Label text="{i18n>myKey}"/>

works but how can we pass a parameter in that expression?

The only piece of information I've found so far is http://scn.sap.com/thread/3586754. I really hope that this is not the proper way to do it since this looks more like a (ugly) hack to me.

z00bs
  • 7,518
  • 4
  • 34
  • 53
  • 1
    The current accepted solution makes use of the deprecated jQuery-function. For current best-practice, see https://stackoverflow.com/a/55587775/5846045. – Boghyon Hoffmann Aug 17 '19 at 13:56

6 Answers6

13

The trick is to use the formatter jQuery.sap.formatMessage like this

<Label text="{parts:['i18n>myKey', 'someModel>/someProperty'], 
                    formatter: 'jQuery.sap.formatMessage'}"/>

This will take the value /someProperty in the model someModel and just stick it in myKey of your i18n resource bundle.

Edit 2020-05-19: jQuery.sap.formatMessage is deprecated as of UI5 version 1.58. Please use sap/base/strings/formatMessage. See this answer on usage instructions.

Boghyon Hoffmann
  • 17,103
  • 12
  • 72
  • 170
Tristan Storch
  • 690
  • 8
  • 18
  • Works great in "normal" binding. :) Doesn't work for me within an expression binding. Would be nice to know if it's also possible to pass a static parameter instead of another binding like 'someModel>/someBinding' – Tim Malich Jul 26 '16 at 09:28
  • 1
    @TimMalich With UI5 1.61+, it's possible now to pass static values to a formatter. See https://stackoverflow.com/a/53609552/5846045. – Boghyon Hoffmann Aug 17 '19 at 13:53
4

At the moment this is not possible. But you can use this simple workaround, that works for me.

Preparations

First of all we create a general i18n handler in our Component.js. We also create a JSONModel with a simple modification, so that immediatly the requested path is returned.

sap.ui.define([
    "sap/ui/core/UIComponent",
    "sap/ui/model/json/JSONModel"
], function(UIComponent, JSONModel) {
    "use strict";

    return UIComponent.extend("your namespace", {
        /**
         * Add a simple "StringReturnModel" to the components' models
         */
        init: function() {
            // [...] your other code in the init method

            // String Return Model
            var stringModel = new JSONModel({});
            stringModel.getProperty = function(sPath) {
                return sPath;
            };
            this.setModel(stringModel, "string");
        },

        /**
         * Reads out a string from our text domain.
         * The model i18n is defined in your manifest.json
         *
         * @param param text parameter
         * @param arr array for parameters
         * @return string
         */
        i18n: function(param, arr) {
            var oBundle = this.getModel("i18n").getResourceBundle();
            return oBundle.getText(param, arr);
        },
    });
});

Now, a model with the context {string>} exists. To use the i18n function in the XML view, we create a formatter function. This function parses the parts of the binding and returns the localized string.

sap.ui.define([
], function() {
    "use strict";

    var formatter = {
        /**
         * First argument must be the property key. The other
         * one are the parameters. If there are no parameters, the
         * function returns an empty string.
         * 
         * @return string The localized text
         */
        i18n: function() {
            var args = [].slice.call(arguments);
            if (args.length > 1) {
                var key = args.shift();
                // Get the component and execute the i18n function
                return this.getOwnerComponent().i18n(key, args);
            }
            return "";
        }
    };
    return formatter;
});

How To Use:

Together with the string-model you can use the formatter to pass paramaters to your i18n:

<Text text="{ parts: ['string>yourProperty', 'string/yourFirstParamter', 'anotherModel/yourSecondParamter'], formatter: '.model.formatter.i18n' }" />

You can pass how many paramaters as you want, but be sure that the first "part" is the property key.

Matthias Günter
  • 617
  • 8
  • 24
2

What is written at the link is correct for complex formatting case. But if you want to combine two strings you can just write

<Label text="{i18n>myKey} Whatever"/> 
or
<Label text="{i18n>myKey1} {i18n>myKey2}"/>
aborjinik
  • 723
  • 3
  • 5
  • 2
    Concatenating translatable texts is not a good idea. The position of the different text parts may be completely different in different languages. – user2808624 Dec 04 '14 at 16:36
  • That doesn't work for me: `` renders out as "5 {i18n>hours}". – z00bs Dec 04 '14 at 16:37
  • 2
    @z00bs: You have to add data-sap-ui-xx-bindingSyntax="complex" to your bootstrap script (in index.html). – user2808624 Dec 04 '14 at 17:21
2

create file formatter.js

   sap.ui.define([
        "sap/base/strings/formatMessage"
    ], function (formatMessage) {
        "use strict";

        return {
            formatMessage: formatMessage
        };
    });

View

<headerContent>
            <m:MessageStrip
                text="{
                    parts: [
                        'i18n>systemSettingsLastLoginTitle',
                        'view>/currentUser',
                        'view>/lastLogin'
                    ],
                    formatter: '.formatter.formatMessage'
                }"
                type="Information"
                showIcon="true">
            </m:MessageStrip>
        </headerContent>

Controller

var oBundle = this.getModel("i18n").getResourceBundle();

MessageToast.show(this.formatter.formatMessage(oBundle.getText("systemSettingsLastLoginTitle"), "sInfo1", "sInfo2"));

i18n

systemSettingsLastLoginTitle=You are logged in as: {0}\nLast Login: {1}
1

As ugly as it may seem, the answer given in the link that you mentioned is the way to go. However it may seem complicated(read ugly), so let's break it down..

Hence, you can use the following for passing a single parameter,

<Label text="{path: 'someParameter', formatter: '.myOwnFormatter'}"/>

Here, the someParameter is a binding of a OData model attribute that has been bound to the whole page/control, as it is obvious that you wouldn't bind a "hardcoded" value in a productive scenario. However it does end with this, as you see there isn't a place for your i18n text. This is taken care in the controller.js

In your controller, add a controller method with the same formatter name,

myOwnFormatter : function(someParameter) { /* the 'someParameter' will be received in this function */ var i18n = this.i18nModel; /* However you can access the i18n model here*/ var sCompleteText = someParameter + " " + i18n.getText("myKey") /* Concatenate the way you need */ }

For passing multiple parameters,

Use,

<Label text="{parts:[{path : 'parameter1'}, {path :'parameter2'}], formatter : '.myOwnFormatter'}" />

And in your controller, receive these parameters,

myOwnFormatter : function(parameter1, parameter2) { } /* and so on.. */

When all this is done, the label's text would be displayed with the parameter and your i18n text.

Breakpoint
  • 1,511
  • 1
  • 18
  • 41
0

In principle it is exactly as described in the above mentioned SCN-Link. You need a binding to the key of the resource bundle, and additional bindings to the values which should go into the parameters of the corresponding text. Finally all values found by these bindings must be somehow combined, for which you need to specify a formatter.

It can be a bit shortened, by omitting the path-prefix inside the array of bindings. Using the example from SCN, it also works as follows:

<Text text="{parts: ['i18n>PEC_to',
       'promoprocsteps>RetailPromotionSalesFromDate_E',
       'promoprocsteps>RetailPromotionSalesToDate_E'}], 
        formatter: 'retail.promn.promotioncockpit.utils.Formatter.formatDatesString'}"/>

Under the assumption, that you are using {0},{1} etc. as placeholders, a formatting function could look like the following (without any error handling and without special handling of Dates, as may be necessary in the SCN example):

formatTextWithParams : function(textWithPlaceholders, any_placeholders /*Just as marker*/) {
    var finalText = textWithPlaceholders;
    for (var i = 1; i < arguments.length; i++) {
        var argument = arguments[i];
        var placeholder = '{' + (i - 1) + '}';
        finalText = finalText.replace(placeholder, arguments[i]);
    }
    return finalText;
},
user2808624
  • 2,502
  • 14
  • 28
  • 1
    Thanks for the answer but as stated in the question, I'm not looking for such a overly complex solution. Such 'features' need to be included nicely in a framework if developers should eventually start using it. That's my opinion. – z00bs Dec 04 '14 at 16:39
  • I agree - somewhat. But what I wanted to point out is, that you at least have to specify the binding to the text and the binding to the parameters. This can not be guessed by any framework. So the "superfluous" part is mainly the definition of the formatter. But even if it would be a build in function of the ResourceModel, you would need some special syntax, to pass the values into the model. – user2808624 Dec 04 '14 at 16:57