lephyrus / ngx-translate-messageformat-compiler

Advanced pluralization (and more) for ngx-translate, using standard ICU syntax which is compiled with the help of messageformat.js.
MIT License
93 stars 29 forks source link

translateParams input not triggered #35

Open MitkoTschimev opened 6 years ago

MitkoTschimev commented 6 years ago

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
    });
lephyrus commented 6 years ago

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?

artuska commented 5 years ago

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

artuska commented 5 years ago

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

Eugeny commented 2 years ago

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,
    },
lephyrus commented 7 months ago

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.