ngx-translate-messageformat-compiler icon indicating copy to clipboard operation
ngx-translate-messageformat-compiler copied to clipboard

translateParams input not triggered

Open MitkoTschimev opened this issue 7 years ago • 5 comments

Hello,

i have a problem with following syntax:

<span [translateParams]="{ copyrightYear: copyrightYear }"
            translate="footer.copyright">
   {{copyrightYear}} blie bla blub
</span>

-> https://stackblitz.com/edit/angular-tjigun

set translateParams input setter is never triggered

Following format is working:

<span translate [translateParams]="{ copyrightYear: copyrightYear }">footer.copyright</span>

-> https://stackblitz.com/edit/angular-crnhjq

If I don't use this plugin everything works like expected.

-> https://stackblitz.com/edit/angular-cxxfui

What i can see is that the @input setter of translateParams is not called before the translation process is triggered and than no params are available

Object.defineProperty(TranslateDirective.prototype, "translateParams", {
        /**
         * @param {?} params
         * @return {?}
         */
->        set: function (params) {
            if (!equals(this.currentParams, params)) {
                this.currentParams = params;
                this.checkNodes(true);
            }
        },
        enumerable: true,
        configurable: true
    });

MitkoTschimev avatar Sep 12 '18 21:09 MitkoTschimev

You're right, there's a difference in behaviour between using pre-compiled interpolation functions (like when using this plugin) and regular interpolation strings (the ngx-translate default) which leads to this problem.

The way the TranslateDirective is implemented, it tries to update the view with a translation as soon as the translate setter is called. This is before the translateParams setter is called, so the params will be undefined. This behaviour is questionable, and the reason it crashes with messageformat is this:

// translate.parser.ts (in ngx-translate/core)
  private interpolateFunction(fn: Function, params?: any) {
    return fn(params);
  }

  private interpolateString(expr: string, params?: any) {
    if (!params) {
      return expr;
    }
    // ...
  }

For a string, it is safe to say that without an interpolation parameter, it won't change. (It can lead to rendering a string with non-interpolated parameters, which will be updated to the correct value once translateParams has been set.) If you have a function however, you need to invoke it to get the resulting string, even if no parameter is needed. If the function does need and tries to accesses the parameter, however, it will naturally throw an error.

For me, the best way to fix this is in the TranslateDirective. Before trying to get a translation for the key passed to the translate input, Angular should have had a chance to set the translateParams input. If the params still do not satisfy what's needed to successfully interpolate then, throwing an error is fine. This would also get rid of unnecessarily rendering incomplete translations.

Another way to fix this would be to catch errors when params is undefined in TranslateParser::interpolateFunction and returning the error message (or some placeholder string) instead, which would be (temporarily) rendered.

@ocombe Do you have any thoughts on this, maybe?

lephyrus avatar Sep 14 '18 09:09 lephyrus

The same issue with the same problem — https://github.com/lephyrus/ngx-translate-messageformat-compiler/issues/28

artuska avatar Nov 07 '18 13:11 artuska

This does not work even in latest versions of translate plugins.

Angular — 7.2.5 Translate — 11.0.1 Translate Messageformat Compiler — 4.4.0

artuska avatar Feb 20 '19 09:02 artuska

A workaround is to wrap the TranslateService:

export class TranslateServiceWrapper extends TranslateService {
    getParsedResult (translations: any, key: any, interpolateParams?: any): any {
        // here -> setting an empty object as fallback for interpolateParams
        return super.getParsedResult(translations, key, interpolateParams ?? {})
    }
}

and then override it in the module providers list:

    {
        provide: TranslateService,
        useClass: TranslateServiceWrapper,
    },

Eugeny avatar Feb 10 '22 21:02 Eugeny

Note that while the upstream TranslateParser could still be improved, the default behaviour in this plugin since v7.0.0 is to catch these errors and log them, rather than let them break all translations.

lephyrus avatar Feb 09 '24 13:02 lephyrus