Romanchuk / angular-i18next

angular v10+ integration with i18next v19.4+
MIT License
130 stars 33 forks source link

[Feature request] Make the pipe update the translation when the language changes #35

Closed Finesse closed 4 years ago

Finesse commented 5 years ago

I use components with HTML like this:

<div>
  {{ 'tranlation_key' | i18next }}
</div>

The key gets translated on a component mount but it isn't retranslated when I change the language by calling:

// ...
constructor(
  @Inject(I18NEXT_SERVICE) private i18NextService: ITranslationService
) {
  setTimeout(() => {
    this.i18NextService.changeLanguage('es');
  }, 5000);
}
// ...

A retranslated text is shown only when the component is updated by another reason. I intuitively expect the i18next pipe to update the translation when the language changes like the async pipe does.

I'm not an expert in Angular, but as far as I know there is a way to force a component update from pipe.

Maybe there is another way to retranslate the components on language change? I really don't want to reload the whole page like in your demo, I consider it as user unfriendly.

BTW, this.i18NextService.events.languageChanged.subscribe works well in my components.

Romanchuk commented 4 years ago

@Finesse Hello and sorry, somehow i missed your post. Yes, it's not user friendly as it could be. I've already answered on similar questions, here are the reasons why i18next pipe is a pure pipe:

https://github.com/Romanchuk/angular-i18next/issues/27#issuecomment-472099839

In early versions i tryied to implement impure pipes, but left this idea for several reasons:

Some custom controls or pipes can be dependent on angular LOCALE_ID token. So we want to change angular LOCALE_ID with i18next language. { provide: LOCALE_ID, deps: [I18NextService], useFactory: (i18next: I18NextService) => { return i18next.language; //string: 'en', 'ru'... } } The problem is that angular IOC container resolves dependencies as singleton, once LOCALE_ID was requested it will always resolve as the same value (first resolved). The only way to get actual LOCALE_ID value is to register LOCALE_ID as factory function, but than it will break libraries that expect LOCALE_ID to be 'string' (not function). Here is my stackoverflow question: Dynamicly get LOCALE_ID (resolve per request)

Custom components with own localization support mostly require complete reinit on language change.

Also most l10n, i18n and date format (momentjs) angular pipes are pure, and changing i18next language or LOCALE_ID won't trigger pipe change detection for them.

Possible memory leaks and perfomant issues. For example zonejs automatically trigger change detection as a result of async operations, this means using jquery libs with ajax could cause your page (full of impure pipes) freeze.

That is why i ended up with simply refreshing page with a new language. We only need to setup right language, locale and formats on angular initialization.

Also i had impure i18next pipe implementation that was statefull - it had memoisation that returned already translated result if input params and language did not change. It worked well. But in my real world projects language changed immediately, but "angular moment " pipes was not called (they are pure), third party controls also needed to be manually reinit...

So impure pipe would cause perfomance freezes (mostlty because of zonejs). And it wouldn't solve probles with already resolved LOCALE_ID and angular pipes dependent on it LOCALE_ID (it can be updated only rebootstraping angular or refreshing page).

Hack with the creating copy of value of translation key could help us:

let copy = { ...original }

But our key (for example: 'common:hello') is string and it is not a reference type (but a value type), you can't change reference pointer as you could do with an object. And changing language doesn't change a key, it is still the same.

Finesse commented 4 years ago

@Romanchuk Thank you for the explanation (though I haven’t comprehended it because I don’t know Angular guts). It’s sad that Angular restrains us with such ridiculous limitations.

Also i had impure i18next pipe implementation that was statefull - it had memoisation that returned already translated result if input params and language did not change. It worked well.

Maybe you can return this pipe as an extra pipe? For example

<div>
  {{ 'tranlation_key' | i18nextEager }}
</div>
scastaldi commented 4 years ago

Hi @Finesse please take a look at this simple implementation of i18next, shows how to change the language, plurals, etc... at least for me is always easier look at a sample code i18nextSample

Romanchuk commented 4 years ago

Maybe you can return this pipe as an extra pipe? For example

@Finesse It is possible, i'll think about it in first half of january

Finesse commented 4 years ago

@scastaldi As I can see, the component reloads the page when the language changes. This is not what I want to achieve.

@Romanchuk It would be great. Thank you!

Romanchuk commented 4 years ago

@Finesse Hi, i just released version 7.2.0-beta with a new i18nextEager pipe. It is impure pipe so i highly recommend to use it only in combine with OnPush change detection strategy, or else (default change detection) each pipe will retrigger more than one time. You can use it without reloading page. Also this release supports latest i18next version.

Finesse commented 4 years ago

@Romanchuk Thank you, I’ll try it today

i highly recommend to use it only in combine with OnPush change detection strategy

Could you please give me a link where I can read about this strategy (especially how to use it with a pipe)

Romanchuk commented 4 years ago

@Finesse https://netbasal.com/a-comprehensive-guide-to-angular-onpush-change-detection-strategy-5bac493074a4 https://indepth.dev/the-essential-difference-between-pure-and-impure-pipes-in-angular-and-why-that-matters/ https://angular.io/guide/pipes

Angular executes an impure pipe during every component change detection cycle. An impure pipe is called often, as often as every keystroke or mouse-move.

With zone.js and Default change detection enabled it will cause huge perfomance issues

Finesse commented 4 years ago

@Romanchuk It works as expected, thank you! The first link was helpful. The issue is resolved for me.

Romanchuk commented 4 years ago

Ok, i will release stable version and update docs to cover i18nextEager pipe usage

Romanchuk commented 4 years ago

Docs updated