At the moment I work a lot with Angular packages, which contain different components built upon default material components by myself. Those smaller packages are served to make the overall project a little bit cleaner and to differentiate the various sub projects.
Eventually you get to the point where you have to remember that the components may need to become multilingual.
Considering of translation in the Angular context, you first encounter concepts such as Internationalization (i18n) or ngx-translate. Even if the i18n-out-of-the-box-solution is not suitable for our application, ngx-translate has emerged as the better option for me personally, as it is much easier to handle and does not generate files on the go.
Reusability comes first
Since this topic was created for one reason only (reusability), we attach great importance to this as well. Therefore, it is necessary to create a solid base, where we can built upon. So we are creating a new Angular application and a library project as well — really fast forward.
ng new translation
ng generate library ngx-translation
Other options (angular routing, hammerJS, stylesheet type) do not matter, because we won’t generate reusable components in this package.
Because the project will be based on ngx-translate we will need to install those two dependencies:
npm install @ngx-translate/core @ngx-translate/http-loader --save
After a few seconds of waiting, you should be good to go.
These dependencies should best be declared in the “package.json” of the library as dependencies or at least as peer dependencies, since this necessarily depends on it.
I’ve also written a medium blog post regarding dependencies and versioning.
Also we are deleting the component files from the
./lib/ folder, because we only need a module and a service.
This is the base of the project. Next we will continue with the base of the translation.
The idea behind
We will write a custom loader which is reading the basic translation files (
*.json) from the application and merges them with the translation provided by each component. Components, which need a translation should register at a registry service so we can read out the translation for each component.
Basic translation with ngx-translate
As a next step, we will need a module, which we can later import in other custom modules or projects. Due the good documentation of ngx-translate for shared modules it is very simple. So first, we will basically generate a shared module in our library, which will later import, export and provide us with everything we need, to translate custom components.
It basically imports the TranslateModule of @ngx-translate/core, defines the TranslationLoader (which is our own) to use, provides the application with a TranslationRegistryService and an initialization function (for setting default parameters).
The TranslationRegistryService should be a simple service which is providing the Loader with the translations of components. So basically it consists of two methods. One method needs to save a binding to the registered component and another method needs to collect up the translations.
To identify each component, I’ve added an identification (“id”) property to it. Also I chose a subject to notify my translation service, when a new component “registered”.
The TranslationLoader is doing the real work. It has to load the normal translation files (under ./assets/i18n/*.json) and merge them with the provided translation objects of registered components. Let’s go to the actual coding.
The most necessary thing here is the merge function. Everything stands and falls with the merge function, which combines the basic translation object from the JSON file with the translation objects of the components.
A good example can be found here:
Since we provided our custom loader in the module, each time, the translation is getting reloaded (by trigger or something else), the loader is getting called. The rest of our code of the custom loader is looking like the following, which is using our merge functions:
Our translation loader requests the newest version of the translation files (
*.json) and uses the merge functions to combine those objects with the language object of the translation registry.
Last but not least, we could provide the developer with an abstract class, so he is not missing out the necessary functions:
The developer can now extend this abstract class from his components. He has to provide an ‘id’, which just can be the name of the component.
Caution: It’s not working with this.constructor.name because this is not available for production builds.
Furthermore its required to pass in a TranslationRegistry-Instance for registering the translation. If this is all done, it can be used as simple as the following code.
In some cases, it is necessary to reload the current language, which can be done by ngx-translate service.
If this is all done, the translation can be used normally done by ngx-translate.
Due to this implementation and the extension of the custom loader, its pretty easy to translate custom components which ship their own translation and still preserve the advantages of the basic ngx-translate. Go multilingual!
Special thanks to Thomas Sebastian Jensen, which provided me with feedback.
Please also feel free to provide feedback.