In this article, you will learn with examples how to get started with Angular i18n using the built-in internationalization module. We will cover the following topics:
Setting up the Angular application and configuring the built-in localize module.
Performing simple translations and providing additional translation data.
Extracting translations to XLF files using a special tool.
Working with pluralization and gender information.
Working with Angular pipes.
Performing translations within components.
Adding a language switcher.
Working with Angular routes.
Building the app using the AOT compiler and deploying to production.
For the purposes of this Angular i18n internationalization/localization tutorial, we'll be using version 15, which is the most recent version at the time of writing. Most of the concepts I'm going to show you are applicable for versions 9.1 and above. These updates include enhancements in translation and localization capabilities to make multi-language support more efficient.
Understanding software internationalization is essential for leveraging Angular’s built-in i18n features effectively, allowing your application to cater to a diverse global audience.
We'll require the Angular CLI, so be sure to install it by running:
npm install -g @angular/cli
Next, create a new Angular 15 application using the following command:
ng new i18n-angular-lokalise
Follow the installation wizard's instructions:
Do not enable Angular routing (yet—we'll discuss this topic later in the article).
Use CSS for style sheets (this is up to you really; I'm not going to apply any styling).
Next, simply wait a couple of minutes and then make sure the app is starting without any issues:
cd i18n-angular-lokaliseng serve --open
The ng serve --open command should open the application in your browser. After you've made sure everything is working, stop the server and install a localize package:
ng add @angular/localize
This is a new package introduced in Angular 9, which will add internationalization support to the app.
Next, we'll need to modify angular.json. I've pinpointed the relevant lines here:
Translations for this language will live in the src/i18n/messages.ru.xlf file. Note that in the previous versions of Angular you would provide the --i18n-locale option when creating translations using command-line interface, but this option is no longer used.
Now we can proceed to working with Angular internationalization examples!
Getting started with Angular internationalization
Marking the text for translation
In order to translate text, use the i18n attribute. Let's see it in action by replacing the src/app/app.component.html content with the following:
<h1 i18n>Hello world!</h1>
i18n is a special attribute that is recognized by the localize package. During the compilation it will be removed, and the tag content will be replaced with the proper translations.
This attribute may contain translation metadata such as this:
This additional data provides context for your translators. Specifically, you may also explain on what pages the translation will be displayed, what tone should be used, and so on.
Creating translation files
Now, where and how do we store translations for Angular i18n? Usually, they live in the src/i18n or src/locale folder. As for the translation format, there are multiple options:
Let's stick with the default option, but the next question is: How do we actually create translation files? Should we do it manually? No! There is a special command-line tool called extract-i18n, which does the heavy lifting for us and extracts all the translations to a separate file.
Let's configure this tool by opening the angular.json file and finding the extract-i18n section inside it:
// ... other options are omitted ..."extract-i18n": { "builder": "@angular-devkit/build-angular:extract-i18n", "options": { "browserTarget": "i18n-angular-lokalise:build", "format": "xlf", "outputPath": "src/locale" }}// ...
Please note that if you're using Angular 17+, the browserTarget option should be renamed to buildTarget.
Alternatively, you can pass these options from the command line as shown in the docs.
Extracting translations
Now simply run the following command:
ng extract-i18n
In the src/locales folder, you will find a messages.xlf file. This is the base translation file with the following contents:
trans-unit is the tag containing a single translation; id is a translation identifier and has a special meaning — extract-i18n generates the id for us so do not modify it here! We will discuss this attribute later in more detail.
source contains translation source text.
context-group specifies where exactly the given translation can be found.
context-type="sourcefile" shows the file where the translation lives.
context-type="linenumber" shows the actual line of code.
Also, there are two note tags that provide the translation description and meaning, respectively.
Next, you can copy the messages.xlf file and name your copy messages.ru.xlf. This new file is going to store Russian translations. In order to translate something, add a target tag immediately after the source in the trans-unit as follows:
Also, it is a good idea to provide the target-language attribute for the file tag so that translation management systems can detect the locale properly – see below:
We're adding a new configuration specifically for the ru locale.
This configuration should be available when serving the app in development. Please note that if you're using Angular 17+, the browserTarget option should be renamed to buildTarget.
Now we can easily use the ng serve command and instruct Angular to show us the Russian version of the app:
ng serve --configuration=ru --open
Ensure that the translation is displayed properly, and proceed to the next section.
Translation identifiers
I've already mentioned that translation IDs (the id attribute for the trans-unit tag) have special meanings. These IDs are unique and extract-i18n generates them based on the combination of the source text and its meaning. Therefore, whenever you update the translation source or its meaning, the ID will change. For instance, let's modify our translation text as follows:
Note that if you have multiple occurrences of the same source text and meaning, all of them will have the same translation. This is due to the fact that their identifiers are similar.
Custom translation identifiers
So, having auto-generated translation IDs is not very convenient because they depend on the source text and the meaning. However, it is possible to provide custom identifiers using the @@ prefix – for example:
Be sure to assign unique custom identifiers for different translations! If you provide the same IDs, only the first translation will be extracted:
<h1 i18n="main header|Friendly welcoming message@@welcome">Hello everyone!</h1><!-- "Other text" will be ignored. Translation for "Hello everyone!" will be used here instead: --><p i18n="@@welcome">Other text</p>
The Angular i18n module allows us to translate both tag content and attributes. Suppose there is a link to your portfolio in the src/app/app.component.html:
The link text will be translated properly, but what about the title and href? To deal with these attributes, provide the i18n-ATTRIBUTE_NAME attributes in the following way:
We have translated the link text, the URL, and the title!
Performing pluralization
We'll continue our Angular localization tutorial by discussing pluralization. Imagine that we'd like to show how many ongoing tasks the user has. The built-in i18n module utilizes an ICU message format which may seem a bit complex at first:
<span i18n> {tasksCount, plural, zero {no tasks} one {one task} other {{{tasksCount}} tasks}}</span>
tasksCount is the variable that we'll create in a moment.
plural is the name of an ICU expression.
zero provides text when there are no tasks.
one contains text when there is 1 task.
other covers all other cases.
Don't forget to create a new variable in the src/app/app.component.ts file as below:
// ...export class AppComponent { tasksCount = 3;}
Extract the new translations and update the src/i18n/messages.ru.xlf file as follows:
Another useful ICU expression is select. It allows you to choose one of the translations based on a value. For example, it is very helpful when working with gender information:
Once a button is clicked, the message with the gender info will be updated instantly.
Translation without a tag
All the examples we've seen previously required some sort of tag. Sometimes, you may need to translate plain text without rendering any tags at all. This can be done with a ng-container, for instance:
<ng-container i18n>Copyright 2023</ng-container>
Upon page display, ng-container will be gone, and you'll have simple plain text. Provide the corresponding Russian translation as usual:
Now the locale data are loaded and we can use the pipes mentioned above. For instance, let's perform date localization:
<p>{{today | date:'fullDate'}}</p>
Create a new today variable in the src/app/app.component.ts file:
// ...export class AppComponent { today: number = Date.now(); // ...}
Upon running the Russian version of the app, you will see the localized date!
Translations within components
Sometimes you may need to perform translations within your components. This can be done by using the $localize() function directly. For example, add the following line to src/app/app.component.ts:
// ...export class AppComponent { company = "Lokalise"; created_by = $localize`Created by ${this.company}`;}
Use this variable in the src/app/app.component.html template:
<p>{{ created_by }}</p>
Now run the extractor again — it should detect the newly added content properly.
Add Russian the translation to the src/i18n/messages.ru.xlf file. Don't forget to provide placeholders, for instance:
Now the created_by variable has the correct translation!
$localize accepts a custom ID, meaning, and description as well. The format is the same: meaning|description@@ID. Please note that this data should be wrapped using colons:
created_by = $localize`:used on the main page|explains who created the app@@created_by:Created by ${this.company}`
Managing translation files with Lokalise
As you can see, Angular i18n translation files have a pretty complex format. While developers won't have any problems working with it, translators may be confused by the weird tags and attributes. Moreover, your translator could accidentally remove an important tag and break the app. We don't want that to happen, right?
Luckily, there is a translation management system here to save the day, and this system is called Lokalise. It allows management of translation and localization files with a very convenient graphical interface, enables multiple developers to collaborate on a single project, provides integrations with developer tools and services like GitHub, Amazon S3, CLI, and has a robust API. Plus, there are more integrations with project management tools like Jira and 50+ others.
It is possible to perform all manipulations with the GUI, but I propose following the "developer's way" and utilizing the command-line interface instead. Therefore, do the following:
Log in to Firebase locally using the following command:
firebase login
Performing the deployment
At this point, we're ready to get rolling. Compile your application with an ahead-of-time compiler (which is the preferred method):
ng build
This will create both English and Russian versions of the app under the dist directory in one go. This is very convenient because in previous Angular versions we had to build each application separately and this took significantly more time. It still possible to provide a specific version of the app to build, like so:
ng build --configuration=production,ru
Once the build is finished, let's add a special package for Firebase:
ng add @angular/fire
Once the package is installed, you'll need to answer a couple of questions:
Features to use: choose "hosting"
What Firebase account to use
Which project and website to use (you'll have an option to create a new Firebase project)
Having done that, run the following command:
ng deploy
After a few moments your site will be up and running!
Now you can browse your application. In my case, there are two URLs:
At this point your application should work fine on Firebase. However, if you are going to implement a routing system, additional steps have to be taken.
Creating components and routes
Let's create two simple routes – /home and /about – that should utilize HomeComponent and AboutComponent, respectively. Start by creating these two components:
ng generate component homeng generate component about
Next, open app.module.ts, import the router module, and provide the actual routes:
The most important step here is configuring Firebase properly. As long as we're using the AOT compiler, Firebase will effectively serve two different applications (for the Russian and English locales). Thus, to make Firebase play nicely with our Angular routes, we should adjust the rewrite rules. To achieve this, open firebase.json and add the following lines:
Another problem is that opening http://lokalise-angular-i18n.web.app directly (without providing the locale part in the URL) results in a 404 error. To fix that issue, let's redirect all such requests to the English version of the app as shown here:
This is the final version of our firebase.json config file.
Deploying once again
We have now dealt with all the Firebase issues, so you can build and deploy your application again:
ng deploy
Great job!
Next steps and further reading
So, in this article we have seen how to introduce internationalization in Angular apps with a built-in localize module. We have learned how to configure the Angular app, perform simple translations, work with pluralization and gender information, and how to introduce localization and translate texts within components. Finally, we have built the demo application and deployed it to Firebase. Well done!
That's all for today, folks. I hope you found this article useful. Thanks for staying with me, and I'll see you next time!
Easily manage angular i18n translation files
Grab a FREE Lokalise trial and start internationalizing your Angular app
Ilya is a lead of content/documentation/onboarding at Lokalise, an IT tutor and author, web developer, and ex-Microsoft/Cisco specialist. His primary programming languages are Ruby, JavaScript, Python, and Elixir. He enjoys coding, teaching people and learning new things. In his free time he writes educational posts, participates in OpenSource projects, goes in for sports and plays music.
Ilya is a lead of content/documentation/onboarding at Lokalise, an IT tutor and author, web developer, and ex-Microsoft/Cisco specialist. His primary programming languages are Ruby, JavaScript, Python, and Elixir. He enjoys coding, teaching people and learning new things. In his free time he writes educational posts, participates in OpenSource projects, goes in for sports and plays music.
Building an AI-powered translation flow using Lokalise API and webhooks
Managing translations in a growing product can quickly become repetitive and error-prone, especially when dealing with frequent content updates or multiple languages. Lokalise helps automate this process, and with the right setup you can build a full AI-powered translation pipeline that runs with minimal manual input. In this guide, you’ll learn how to: Upload translation files to Lokalise automaticallyCreate AI-based translation tasksUse webhooks to downloa
Build a smooth translation pipeline with Lokalise and Vercel
Internationalization can sometimes feel like a massive headache. Juggling multiple JSON files, keeping translations in sync, and redeploying every time you tweak a string… What if you could offload most of that grunt work to a modern toolchain and let your CI/CD do the heavy lifting? In this guide, we’ll wire up a Next.js 15 project hosted on Vercel. It will load translation files on demand f
Hands‑on guide to GitHub Actions for Lokalise translation sync: A deep dive
In this tutorial, we’ll set up GitHub Actions to manage translation files using Lokalise: no manual uploads or downloads, no reinventing a bicycle. Instead of relying on the Lokalise GitHub app, we’ll use open-source GitHub Actions. These let you push and pull translation files directly via the API in an automated way. You’ll learn how to: Push translation files from your repo to LokalisePull translated content back and open pull requests automaticallyWork w