Dynamically translating a PrimeNG menu using ngx-translate

How to translate a PrimeNG menu dynamically

Dynamically translating a PrimeNG menu using ngx-translate

Recently, for our awesome startup project, we’ve decided to move away from Angular Material and to switch to Tailwind + PrimeNG.

The reason for this move is the fact that our designer’s work feels too distant from Material Design and we’re not willing to swim against the flow. We feel like overriding all the Angular Material styles is not going to be easy and would be hacky (at best).

As part of this transition to PrimeNG, we had to rewrite our mat-menu usages to Prime NG’s “equivalent” menu component.

In this article, I’ll explain how we’ve managed to translate the PrimeNG menu dynamically.

The problem

The boon and bane of Prime’s menu system is that it is backed by a powerful menu model.

The nice thing about that model is that we can statically define the menu’s configuration, pass it as input to the p-menu and let it render everything for us.

What’s less nice is the fact that we have very limited control over how the menu items are rendered, as opposed to Angular Material’s mat-menu component, which uses content projection and thus lets us insert whatever we want into the menu easily.

This is problematic at least in the context of internationalization, because the labels that we pass to the menu aren’t dynamic. They’re set once and don’t get automatically updated. Bummer!

With mat-menu, we simply has to use ngx-translate’s translate pipe and it worked fine. With PrimeNG’s menu, we had resort to translateService.instant(...), which does a one-time translation that doesn’t get updated automatically.

Here’s what our menu looks like once converted to PrimeNG:

And the template:

The problem is that with this in place, the menu translations are interpolated only once at construction time (during ngOnInit), but those don’t get re-evaluated when the language is changed. Bummer!

The solution

After looking into it a bit, only two solutions stood out:

  • Fork PrimeNG’s menu to bend it to our will and be able to use the translate pipe directly against the labels provided in the menu model
  • Rebuild the menu model whenever the language changes so that it contains the correct translated text

For now, we’ve started with the second option. The first one would be better for other reasons, but also problematic because it would introduce technical debt.

So here’s our plan: subscribe to language changes events and rebuild the whole menu each time the language changes.

In our application, we store the current language in the NGRX store and our facade simply exposes a selector to let us watch for value changes, which makes this really easy, but the same can be achieved using the onTranslationChange event emitter exposed by the TranslateService service of ngx-translate:

With this, we get warned whenever the language changes. When that happens, we need to rebuild the menu. Note that the example code above doesn’t handle the subscription correctly; don’t forget it for your applications though or you’ll introduce a memory leak ;-)

In order to do this easily, we’ve simply moved the construction code of our menu model in a factory method:

Whenever the language change callback is invoked, we just have to call our buildUserProfileMenu method to get a new version of the menu with the correct translations.

Here’s an overview of the code:

Problem solved!

Conclusion

In this small article, I’ve described how we’ve handled the internationalization of our new PrimeNG menu.

I’m not really happy about how we had to do it, but this is the cleanest approach I could find.

That's it for today! ✨