Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
Finesse opened this issue Nov 12, 2019 · 11 comments

Comments

@Finesse
Copy link

Finesse commented Nov 12, 2019

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
Copy link
Owner

Romanchuk commented Dec 24, 2019

@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:

#27 (comment)

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
Copy link
Author

Finesse commented Dec 24, 2019

@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
Copy link

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
Copy link
Owner

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
Copy link
Author

Finesse commented Dec 25, 2019

@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
Copy link
Owner

Romanchuk commented Jan 28, 2020

@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
Copy link
Author

Finesse commented Jan 28, 2020

@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
Copy link
Owner

Romanchuk commented Jan 28, 2020

@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
Copy link
Author

Finesse commented Jan 28, 2020

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

@Romanchuk
Copy link
Owner

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

@Romanchuk
Copy link
Owner

Docs updated

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants