[go: up one dir, main page]

DEV Community

Cover image for Angular + @ngx-translate + Typings
Carlos Caballero for Angular

Posted on • Originally published at carloscaballero.io

Angular + @ngx-translate + Typings

Introduction

When you decide to use Angular in a project, it means that you are building a large SPA (otherwise, you should think of another framework). Large projects are usually intended for users in different countries and are carried out with translations of the application.

The library that, in my opinion,is the best to implement translation in an application belonging to Angular’s ecosystem is @ngx-translate. I have used this library since the beginning of the same because it is simple, powerful and very useful.

However, the biggest drawback that I find this library is that the translations are based on a key-value translation object in which to identify the key that you want to translate you must specify a string. The fact of using a string causes us to lose all the type control of the variables that we want to translate.

What are the problems that arise from this?

  1. We can not know if a key is lost, incomplete or has typo.

  2. We do not have autocomplete, despite having nested objects that can be complex.

  3. You can not change the name of a key automatically (refactoring) between all the languages, we must modify each file of languages one by one.

In this post we will build a small service that will allow us to considerably improve the use of @ngx-translate in our software development.

Before starting, you can see the final result of what we are going to build in the next gif:

Installation and configuration of @ngx-translate

First, you will configure @ngx-translate in a conventional way, as recommended by the author on the GitHub page.

First you need to install the npm module:

You have to import TranslateModule.forRoot() in the root NgModule of your application.

The forRoot static method is a convention that provides and configures services at the same time. Make sure you only call this method in the root module of your application, most of the time called AppModule. This method allows you to configure the TranslateModule by specifying a loader, a parser and/or a missing translations handler.

By default, there is no loader available. You can write your own loader, or import an existing one. For example you can use the TranslateHttpLoader that will load translations from files using HttpClient.

To use it, you need to install the http-loader package from @ngx-translate:

Once you’ve imported the TranslateModule, you can put your translations in a json file that will be imported with the TranslateHttpLoader. The following translations should be stored in eng.json and span.json.

You can either use the TranslateService, the TranslatePipe or the TranslateDirective to get your translation values.

In your component, define param like this:

Typing @ngx-translate

The first thing we need to do is create a new service named translations which will resolve the path of where the translation key is being specified is located.

Of course, the code can be refactored later but the objective of this post is to illustrate how @ngx-translate can be typed.

First of all, the GenericClass function has been defined, which is used to provide properties to any class that extends from it:

The second step is to create the service extending from GenericClass in which you specify that the properties belonging to the eng.ts file will be the properties of the Translations class.

The next step is to build the transformObjectToPath function, which is responsible for building an object with the keys' path of the .ts files, where the value would be the complete path so that @ngx-translate receives the string it expects.

At the end of executing this method, the class will have the following compound object inside:

Finally, the code associated with the service is the following one:

The files with the translations are .TS files instead of .json files, so they are as follows:

Another modification that must be made in the code is to define our own loader for @ngx-translate, since the .json files are not going to be downloaded using the httpClient service but rather a charger, that will be created to allow perform the loading of TS files as shown below:

The loader has been called WebpackTranslateLoader because Webpack is the one in charge of analyzing the possible files that are imported with the keyword import (...) and packages them as independent sources in order to carry out their request dynamically. Therefore, at the moment the file is requested, the request is made to the file /assets/i18n/language.js.

The last and most pleasant step is the use of @ngx-translate with typing in the component that you want to inject multi-language texts. It is necessary to inject the translations service, which must be public to be able to interact with the template.

Finally, magic can be seen in what is available:

  1. Autocomplete.

  1. Detection of lost keys.

Another interesting point that can be the icing on the cake is to be able to refactor all the language files easily.

In this case, we can apply two techniques:

  1. Strong Typing. Warn if a key is not in the language file.

2. Typing. A casting that gives you typing, but does not warn you if a key is missing

The incredible final result is the one shown in the following GIF.

The GitHub branch of this post is https://github.com/Caballerog/angular-ngx-translate-typing

Originally published at https://carloscaballero.io/angular-ngx-translate-typings

Top comments (6)

Collapse
 
vojtech profile image
Vojtech Mašek

I wrote a similar post on translation using lazyloading and dependency injection. Setup is easy and no library is required.
blog.angularindepth.com/angular-ty...

Collapse
 
carlillo profile image
Carlos Caballero

Hi @vojtech !

I've been reading your article and it's great! A pity I have not discovered it before. Both proposals are similar, although different...

In your proposal, interpolation is direct and cleaner (you do not have to use the translate pipe). However, in your proposal you need a module per language and, in addition, that the language is on the route. In my opinion, if a translation receives a parameter it is more inefficient ( the simple fact of being a function). Another problem that I have detected is that when you change the language, you have to reconstruct the view component tree in which the user is, this is caused by

  1. The movement of the route.
  2. It is necessary for the new language to be injected satisfactorily.

Anything that I can help or share, count on me.
Greetings.

Collapse
 
vojtech profile image
Vojtech Mašek

Thanks for the feedback.

Yes, the change of the language is reconstructing the whole view, but it something we are willing to accept as a trade of as users are not changing the language that often. For some products, there is even a logic in cloud functions serving that page which language version will you be redirected to based on the request.

Why we decided to use the DI instead of the pipe was that we wanted to omit the impure translate pipe in our projects.
But some projects have the setup bit adjusted and have translations in ngrx store, displayed using async pipe. They are even combining and "patching" translations that are dynamically received from CMS (where editors write them) with local ones that we keep as the default and for the development. That means there should never be a missing translation in development nor production and everything new is just being merged with the default.

Overall I've seen multiple variations and they are perfectly fine, the best thing is the typings and errors in compile-time :D

Collapse
 
isaacplmann profile image
Isaac Mann • Edited

This looks incredibly useful. Thanks for writing this up.

Does this technique bundle all the language files with the app?

Collapse
 
carlillo profile image
Carlos Caballero

Hi @isaacplmann ,

Only the default language is included in the bundle of the app (something that should work that way). The rest of the languages are downloaded using the WebpackLoader which are downloaded with the names 0.js 1.js ... etc (since this task has been delegated to Webpack).

Thanks!

Collapse
 
phillyvanilly profile image
philly-vanilly

Why would you use ngx-translate in Angular 8 though? The lib is buggy and it's not clear how long it will be supported, considering the official i18n-solution is in beta now and will be released in Angular 9.