Gregor Srdic
Gregor Srdic

Reputation: 446

ngx-translate object interpolation

I have a structured translation file as follows:

 "myApp":{
  "errorMessages": {
    "unauthorized": "This {{serviceName}} account is not valid for the {{appName}} app.",
    "missing_role": "You can not use this account to access {{appName}} application"
  }
}

If I access single translation directly, I can easily use interpolation:

const appNameObject = { appName: 'My app', serviceName: 'My Service' };
const unauthorizedErrorMessage = translateService.instant('myApp.errorMessages.unauthorized', appNameObject);

But sometimes I would like to get all keys in a structured object at once - and interpolation seems not to work in this case

 const errorMessages = translateService.instant('myApp.errorMessages', appNameObject);

Can I get this working? Thanks

Upvotes: 4

Views: 4117

Answers (3)

Ceci Semble Absurde.
Ceci Semble Absurde.

Reputation: 530

You can interpolate your parameters using a regex. In the .replace() function, call them with an object or a table using the match string.

Use the regex /{{(\w+)}}/gm to match the string to replace

const appNameObject: any = {
  serviceName: 'potatoe'
  appName: 'jam'
}

const message: string = (
  await this.translateService
  .get('myApp.errorMessages.unauthorized')
  .toPromise()
)
.replace(
  /{{(\w+)}}/gm,
  (fullMatch, match) => appNameObject[match]
);

Edit

Please, refer to Atscub answer which apply a regex on every elements of the translation object (even nested ones) using JSON.stringify.

My implementation of a NgxInterpolation method without JSON import (es2019+):

const NgxInterpolation: any = (translationObject: any, interpolationObject: {[k: string]: any}) => {
    switch(typeof translationObject){
        case "object":
            return Object.fromEntries(
                Object.entries(translationObject).map(
                ([k, v], i) => [k, NgxInterpolation(v, interpolationObject)]
                )
            );
        case "string":
            return translationObject.replace(
                /{{(\w+)}}/gm,
                (fullMatch, match) => interpolationObject[match]
            );
        default:
            return translationObject;
    }
}

Use it as follow:

const errorMessages: any = NgxInterpolation(
  this.translateService.instant('myApp.errorMessages'),
  appNameObject
)

Ngx-Translate Parser

ngx-translate has a parser and the get function from the translation service accept an interpolation object.

for the json file en.json:

{
    "HOME": {
        "HELLO": "hello {{value}}"
    }
}

you can interpolate the key value set to world for the translation string HELLO as follow:

// With the interpolationObject parameter from the get (or instant) method
this.translateService.get('HELLO', { value: 'world' })

// With the interpolation method from the ngx-translate parser
this.translateService.parser.interpolate(
  this.translateService.instant("HELLO"),
  { value: 'world' }
)

The translation only works for translation string. If you want to apply interpolation on every translation string by using ngx-translate methods, you need to define a method as follow (es2019+):

NgxObjectInterpolation(
  translationObject: any,
  interpolationObject: { [k: string]: any }
) {
  switch (typeof translationObject) {
    case "object":
      return Object.fromEntries(
        Object.entries(translationObject).map(([k, v], i) => [
          k,
          this.NgxObjectInterpolation(v, interpolationObject),
        ])
      );
    case "string":
      return this.translateService.parser.interpolate(
        translationObject,
        interpolationObject
      );
    default:
      return translationObject;
  }
}

Use it like this:

this.NgxObjectInterpolation(
  this.translateService.instant("HELLO"),
  { value: "world" }
);

Upvotes: 1

Abraham Toledo
Abraham Toledo

Reputation: 411

Although it can't be done out of the box, you can easily accomplish what you need using JSON.stringify() / JSON.parse() and interpolate by yourself using regular expressions.

  1. Define a function in your utils service or any other place like this:

    const objectInterpolate = (obj: any, params: {[k: string]: any}) => {
      return JSON.parse(
        JSON.stringify(obj)
          .replace(/{{\s*([^}\s]+)\s*}}/gm, (_, group) => params[group] ?? "")
      );
    }
    
  2. Then use it in your anywhere:

     this.translate.get("myApp.errorMessages")
       .subscribe(value => {
         this.errorMessages = this.utils.objectInterpolate(
           value, 
           {
             paramName: paramValue,
             paramName2: paramValue2,
             ...
           }
         );
       );
    

Upvotes: 1

Vojtech
Vojtech

Reputation: 2816

The ngx-translate doesn't support this.

If you want/expect to get an object that looks like this

{
  "unauthorized": "This My Service account is not valid for the My app app.",
  "missing_role": "You can not use this account to access My app application"
}

You have to actually create it yourself the way you use interpolation in the example that works

Upvotes: 1

Related Questions