Open In App

Angular Internationalization

Last Updated : 23 Jul, 2025
Comments
Improve
Suggest changes
Like Article
Like
Report

Internationalization (i18n) is a key feature in Angular that allows applications to be adapted for different languages and regions without needing to rewrite the core code. This article will walk you through the process of implementing i18n in Angular, including setup, translation, and configuration.

What is Internationalization?

Internationalization (i18n) is the process of designing and preparing your application to be adaptable to different languages, regions, and cultures. In Angular, i18n simplifies translating text, handling number and date formats, and managing other locale-specific requirements.

Importance of i18n in Angular Apps

Implementing i18n ensures that your application can reach a global audience by providing localized versions for different languages and cultural contexts. This enhances user experience and broadens your app’s accessibility.

Setting Up Angular i18n

Step 1: Enabling i18n in Angular

To enable i18n in Angular, first ensure your project is set up with the Angular CLI. You’ll need to use Angular's i18n tools, which are integrated into the framework.

Step 2: Configuring the Angular CLI for i18n

Update your angular.json file to define locales and configure the build options for each language. For example, add configurations for languages such as en, fr, and es.

Marking Text for Translation

1. Using Angular’s i18n Attribute

Mark translatable text in your templates by adding the i18n attribute:

<h1 i18n="@@welcomeMessage">Welcome to our application!</h1>

This tells Angular to extract this string for translation.

2. Marking Static and Dynamic Text

You can also use the i18n attribute for dynamic content by adding descriptions and meaning to provide context for translators.

Extracting Translations

Extracting Translatable Content with Angular CLI

Use the Angular CLI command to extract marked text into a translation source file:

ng extract-i18n --output-path src/locale

This command generates a messages.xlf file containing all translatable content.

Understanding the messages.xlf File

The .xlf file is an XML file that includes all marked texts and placeholders. Translators will use this file to provide translations.

Translating the Application

Working with Translation Files

Each language will have its own translation file, such as messages.fr.xlf for French. Translators replace the source text with translated versions in these files.

Handling Pluralization and Gender-Specific Texts

Angular’s i18n supports pluralization and gender-specific translations using ICU expressions:

<p i18n>
{ count, plural,
=0 {No items}
=1 {One item}
other {{count} items}
}
</p>

Building and Serving Localized Versions

Building the Application for Different Locales

To build your app for a specific locale, use:

ng build --configuration=fr

This generates a build for the French locale based on configurations defined in angular.json.

Serving Different Language Versions

You can serve specific locales using the Angular CLI by specifying the configuration:

ng serve --configuration=es

Handling Dynamic Content

Localizing Dynamic Content and Dates

Use Angular pipes like DatePipe, CurrencyPipe, and DecimalPipe to automatically format dates, numbers, and currencies according to the current locale.

Using Angular Pipes for Localization

For example:

<p>{{ currentDate | date: 'long' }}</p>

This formats the date according to the active locale.

Steps To Implement Angular Internationalization

Step 1: Create an Angular Application using the following command

ng new angular-i18n-demo

Step 2: Install the required dependencies

ng add @angular/localize

Step 3: Extract translatable content into an XLF file using the Angular CLI

ng extract-i18n --output-path src/i18n

Folder Structure

asf
Folder Structure

Dependencies

"dependencies": {
"@angular/animations": "^17.3.0",
"@angular/common": "^17.3.0",
"@angular/compiler": "^17.3.0",
"@angular/core": "^17.3.0",
"@angular/forms": "^17.3.0",
"@angular/platform-browser": "^17.3.0",
"@angular/platform-browser-dynamic": "^17.3.0",
"@angular/router": "^17.3.0",
"rxjs": "~7.8.0",
"tslib": "^2.3.0",
"zone.js": "~0.14.3"
},
"devDependencies": {
"@angular-devkit/build-angular": "^17.3.6",
"@angular/cli": "^17.3.6",
"@angular/compiler-cli": "^17.3.0",
"@angular/localize": "^17.3.12",
"@types/jasmine": "~5.1.0",
"jasmine-core": "~5.1.0",
"karma": "~6.4.0",
"karma-chrome-launcher": "~3.2.0",
"karma-coverage": "~2.2.0",
"karma-jasmine": "~5.1.0",
"karma-jasmine-html-reporter": "~2.1.0",
"typescript": "~5.4.2"
}

Update your angular.json file

XML
// angular.json
{
    "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
    "version": 1,
    "newProjectRoot": "projects",
    "projects": {
        "angular-i18n-demo": {
            "projectType": "application",
            "schematics": {},
            "root": "",
            "sourceRoot": "src",
            "prefix": "app",
            "i18n": {
                "sourceLocale": "en",
                "locales": {
                    "fr": "src/i18n/messages.fr.xlf"
                }
            },
            "architect": {
                "build": {
                    "builder": "@angular-devkit/build-angular:application",
                    "options": {
                        "outputPath": "dist/angular-i18n-demo",
                        "index": "src/index.html",
                        "browser": "src/main.ts",
                        "polyfills": [
                            "zone.js"
                        ],
                        "tsConfig": "tsconfig.app.json",
                        "assets": [
                            "src/favicon.ico",
                            "src/assets"
                        ],
                        "styles": [
                            "src/styles.css"
                        ],
                        "scripts": []
                    },
                    "configurations": {
                        "production": {
                            "budgets": [
                                {
                                    "type": "initial",
                                    "maximumWarning": "500kb",
                                    "maximumError": "1mb"
                                },
                                {
                                    "type": "anyComponentStyle",
                                    "maximumWarning": "2kb",
                                    "maximumError": "4kb"
                                }
                            ],
                            "outputHashing": "all",
                            "localize": [
                                "fr"
                            ]
                        },
                        "development": {
                            "optimization": false,
                            "extractLicenses": false,
                            "sourceMap": true,
                            "localize": [
                                "fr"
                            ]
                        },
                        "fr": {
                            "localize": [
                                "fr"
                            ]
                        }
                    },
                    "defaultConfiguration": "production"
                },
                "serve": {
                    "builder": "@angular-devkit/build-angular:dev-server",
                    "configurations": {
                        "production": {
                            "buildTarget": "angular-i18n-demo:build:production"
                        },
                        "development": {
                            "buildTarget": "angular-i18n-demo:build:development"
                        },
                        "fr": {
                            "buildTarget": "angular-i18n-demo:build:fr"
                        }
                    },
                    "defaultConfiguration": "development"
                },
                "extract-i18n": {
                    "builder": "@angular-devkit/build-angular:extract-i18n",
                    "options": {
                        "outputPath": "src/i18n",
                        "format": "xlf"
                    }
                },
                "test": {
                    "builder": "@angular-devkit/build-angular:karma",
                    "options": {
                        "polyfills": [
                            "zone.js",
                            "zone.js/testing"
                        ],
                        "tsConfig": "tsconfig.spec.json",
                        "assets": [
                            "src/favicon.ico",
                            "src/assets"
                        ],
                        "styles": [
                            "src/styles.css"
                        ],
                        "scripts": []
                    }
                }
            }
        }
    }
}

Basic code structure

HTML
<!-- app.component.html -->

<h1 i18n="@@appTitle">Welcome to our Angular i18n Demo!</h1>
<p i18n="@@appDescription">This application supports multiple languages.</p>
HTML
<!-- index.html -->
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="utf-8">
    <title>Angular i18n Demo</title>
    <base href="/">
</head>

<body>
    <app-root></app-root>
</body>

</html>
JavaScript
// app.component.ts
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';

@Component({
    selector: 'app-root',
    standalone: true,
    imports: [CommonModule],
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.css']
})
export class AppComponent {
    today = new Date();
    price = 1234.56;
}

Implement i18n files

XML
<!-- src/i18n/messages.fr.xlf -->
<xliff version="1.2">
  <file source-language="en" target-language="fr" datatype="plaintext">
    <body>
      <trans-unit id="appTitle">
        <source>Welcome to our Angular i18n Demo!</source>
        <target>Bienvenue dans notre démonstration Angular i18n !</target>
      </trans-unit>
      <trans-unit id="appDescription">
        <source>This application supports multiple languages.</source>
        <target>Cette application prend en charge plusieurs langues.</target>
      </trans-unit>
    </body>
  </file>
</xliff>
XML
<!-- src/i18n/messages.xlf -->
<xliff version="1.2">
    <file source-language="en" datatype="plaintext">
       <body>
            <trans-unit id="appTitle">
                <source>Welcome to our Angular i18n Demo!</source>
            </trans-unit>
            <trans-unit id="appDescription">
                <source>This application supports multiple languages.</source>
            </trans-unit>
        </body>
    </file>
</xliff>

Step 4: Build and Serve Localized Versions

Build your application for different locales:

ng build --localize

Serve a specific locale version:

ng serve --configuration=fr

Output:

Advanced i18n Techniques

  • Lazy Loading Translations: To optimize performance, you can lazy load translation files only when needed, using Angular’s built-in support for lazy-loaded modules.
  • Using Third-Party Libraries for Advanced Features: For more complex scenarios, consider using libraries like ngx-translate that offer additional features such as runtime translation loading and language switching.

Testing and Debugging i18n

  • Testing Localized Applications: Testing is crucial to ensure your localized app functions correctly. Test each locale thoroughly, checking that translations display correctly and that text fits within UI elements.
  • Common Issues and Troubleshooting: Common issues include missing translations, incorrect formatting, and performance bottlenecks. Use Angular’s debugging tools and inspect translation files for accuracy.

Best Practices for Angular i18n

  • Structuring Translation Files: Organize your translation files in a clear and manageable structure, such as keeping them in a dedicated locales directory.
  • Maintaining and Updating Translations: Regularly update your translation files as your app evolves. Use unique identifiers for each translatable string to prevent issues with changed text.

Similar Reads