Back to the homepage
Angular

Internationalization: How To Open an Application to the World – part 2

GO TO FIRST PART

What is pluralization?

A popular problem in the i18n realm is pluralization — displaying different text depending on the value of a variable. In most libraries, we will encounter an implementation of the ICU format, and thus two forms of pluralization: select and plural.

Note: For @angular/localize and ngx-translate, the ICU format is implemented by default. We can add it to Transloco by installing the transloco-messageformat package.

Amount plural

We will use this format when we want to display text depending on the value of a number. In most languages we encounter two forms — singular and plural. It is worth remembering that some languages differ in this aspect. Ukrainian, for example, has four forms.

Nevertheless, in our applications, we will most often encounter a situation where we display text in three different cases:

  • No data
  • A single record
  • Multiple records

Plural syntax:

As we can see above, we open the plural expression with brackets. The first argument is the name of the parameter —”amount,” in our example. For the second argument, we pass the type of the plural: plural or select. The remaining arguments are conditional.

  • ={amount} is a condition, followed by the text in curly brackets, which will be returned if the condition is met 
  • other is an else — he text inside it will be shown if no other condition matches
  • In each condition we can use the character # , which is replaced with a number passed as an argument.

Select plural

The select type format is useful if we want to display text depending on the value of the string.

Just like previously, the syntax starts with curly brackets, then the name of the parameter, and the type of the plural — “select”. Other arguments are the conditions and their respective translations. 

Pluralization in practice

Let’s add the transloco-messageformat package to our application. We can do this by executing the command

npm and @ngneat/transloco-messageformat

After installing the package, we need to overwrite the Transloco transpiler. We can do this by providing MessageFormatTranspiler in the AppModule.

After the initial setup, let’s create the sample data that we will display in our storage application. Let’s start by creating an interface in the types/storage-item.interface.ts file:

Next, let’s create some sample data in our component:

We’re going to display it in the template using the ngFor directive. While creating the paragraphs we will pass our parameters In the second argument of the “t” function.

After this process, we should see a view similar to the one on the screenshot below.

Problems with scaling i18n applications

Scaling applications that use a runtime library can present several challenges. These include zombie keys, increased initial load time or missing translation keys.

Zombie keys

Keeping translation files in order is not an easy task. Zombie keys are translation keys that aren’t used anywhere in the repository. You may ask yourself, where did they come from? The answer is simple — someone forgot to remove them from the i18n files when they removed their use cases in the code. Having a few unused keys is not a big deal. Only with the expansion of this small “epidemic” will we increasingly feel the unnecessarily increased bundle size. Remember to monitor the use of translation keys on a regular basis. We can do this through editor add-ons that will help us find and remove zombie keys present in our application.

Lazy loading translations

As the number of keys in our application increases, we may encounter another big problem. The time it takes to load a json file increases in proportion to its size. This is especially problematic if we already have thousands of keys in a single file — the performance impact can be significant. In that case we can use the simple solution available in most libraries — split translations into modules and lazy-load translations along with the module.

Missing translation keys

While implementing large translations for multiple languages, we may sometimes forget to implement a key. Fortunately for us, fixing this problem is not very difficult. What’s more, we have several options:

  • Use an editor extension that shows the missing keys. This solution will work well for small projects with a small number of translation files.
  • Many external translation management services show statistics on the number of translated keys and which keys are not yet translated. This solution works especially great for medium and large projects, but can be implemented in smaller projects too. More information on that later in this article.
  • We can also create a script to check if any file is missing a translation.

Create a script that finds missing keys

One way to find missing keys in a project is to create a simple script. The advantage of this solution is its flexibility. We can run the script at any time, and even combine it with a husk to block commits if the script finds a missing translation. 

Missing-keys-finder.js

The above script collects the translations from files located inside the folder declared in the i18nFolderPath variable. After loading all of the translations, it detects missing keys and then displays the search results in the console.

Now that we know how our script works, let’s add it to package.json so that it can be run using a npm run command.

To test the script, run the following command in the terminal

npm run find-missing-keys

You should see an output similar to the one in the screenshot below.

What else is worth remembering?

  • We should translate every available text on our site, especially attributes such as placeholder, title, aria-label or alt.
  • Add alternative versions for images and infographics that contain text. Include the path for these images in the translations file, and then reference them with our library in the src attribute of the img element.
  • If you are using the Angular Material library, you should translate all the elements that the user interacts with. In addition to the obvious things such as text inside tooltips and snack bars, you shouldn’t forget about the mat-paginator. This component comes with default tooltips, which can be translated using the MatPaginatorIntl provider.
  • Set the page title based on the user’s language. We can do this by creating a custom class extending the TitleStrategy class. In it, we override the updateTitle method to set the title depending on the page title declared in the route. Take a look at this example implementation to learn more.

Internationalization of displayed data formats

Is that all we should know about i18n? Not really — internationalization of an application is more than just translating text into different languages. The process is much more complicated and consists of many steps. One of the things that goes into internationalization is adjusting the format of the displayed data to regional standards.

Angular provides various pipes that allow us to display internationalized data with ease. We have the following tools at our disposal:

  • DatePipe – formats the displayed dates
  • CurrencyPipe – formats the displayed currencies
  • DecimalPipe – formats the displayed numbers
  • PercentPipe – formats the displayed percentages

Why is LOCALE_ID so important?

LOCALE_ID is an injection token with which we can set the global language code in our application. The aforementioned pipes use this token to format displayed data.

In the above screenshot, we can see how much formatting can vary depending on the locale, especially for currencies and dates. Thankfully, all we need to do is change the token value while the application is running. It can’t be that difficult, right?

Changing LOCALE_ID while the application is running

The problems with changing LOCALE_ID start at the very beginning. This token stores a string value, and thus we cannot edit it after app initialization as we would with reference type values — for example, objects.

This problem does not occur in Angular’s native library, as it uses different builds for each supported language. This is worth taking into account if you plan to choose a runtime library.

Despite this, it is possible to create a dynamic LOCALE_ID

Editing keys inside the IDE and external services

Editing keys through the GUI

Working on translation files can be problematic — it frequently requires us to scroll through extremely large files to add new translations or edit existing ones. To make life easier for ourselves, we can use IDE extensions that allow us to edit translations from the GUI.

These extensions greatly improve the DX, especially when they uses a columns-based UI to display translations. I guarantee you that when you start using a UI editor, the time spent on unnecessary jumping between files will be drastically reduced. You will also be able to view, add and edit new keys with ease.

VS Code add-ons

  • i18n Ally — a very rich add-on containing many i18n features. It allows us to extract, edit or preview translations. In my opinion, it is the best solution available for VS Code. 
  • i18n json editor — an extension that allows us to edit translations in a simple UI editor.

Add-ons for JetBrains IDE

  • Easy i18n — this add-on allows us to edit translation keys in a table or tree view. It also does not lack such features as highlighting missing keys or editing translations directly from the template.

External key management services

Many external translation management services are available to developers. Personally, I think that this technology is a game changer. This is especially true for huge projects that involve dozens of people — including translators. It is in these types of projects that developers are often forced to mindlessly copy translations received from translators into json files. In the long run, this results in wasted time and opens the application to micro typing errors.

An external service can take our work to the next new level. Below you will find some of the benefits that translation management services offer us.:

  • Edit history for individual translations
  • Application release management
  • Integration with CI/CD
  • Automatic translation of files using DeepL, Google Translate or Amazon Translate
  • Various types of warnings, such as missing translations
  • Grouping translations, adding tags, comments and setting flags to keys. This allows you to expand the context for translators without impacting the sizes of translation files.

Possibility of self-hosting

Examples of external translations management services include:

Summary

As you surely noticed, despite appearing simple, application internationalization has its secrets. Before introducing i18n into your application, you should think about what kind of library you need and what advantages and limitations it provides you with.

What experiences have you had with application internationalization? Which libraries and services have you used?

About the author

Dawid Kostka

Angular Developer at House of Angular. For me software development is something more than just writing code. I love exploring and playing around with finest code. I’m passionate about adrenaline and positive attitude. In spare time I skate on aggressive skates.

Don’t miss anything! Subscribe to our newsletter. Stay up-to-date with the latest trends, tips, meetups, courses and be a part of a thriving community. The job market appreciates community members.

Leave a Reply

Your email address will not be published. Required fields are marked *