Skip to content

Angular i18n Explained — Internationalization and Localization

DodaTech Updated 2026-06-28 6 min read

In this tutorial, you will learn about Angular i18n Explained. We cover key concepts, practical examples, and best practices to help you master this topic.

Angular i18n (Internationalization) lets you build applications that support multiple languages, regional formats, and cultural conventions using Angular's built-in translation pipeline.

What You'll Learn

  • The difference between i18n and l10n
  • How to mark text for translation in templates
  • How to generate and manage translation files
  • How to handle pluralization and dates
  • How to build and deploy for multiple locales

Why It Matters

Internationalization opens your application to global audiences. A translated app increases user engagement in non-English markets and is often required for enterprise or government contracts.

Real-World Use

Durga Antivirus Pro supports 12 languages. The security dashboard, threat alerts, and settings panels automatically display in the user's preferred language based on browser settings or account preferences.

flowchart LR
    A[Source Templates] --> B[Extract Messages]
    B --> C[XLIFF / JSON Files]
    C --> D[Translators]
    D --> E[Translated Files]
    E --> F[Build per Locale]
    F --> G[en-US / es / fr / ja / ...]
    style A fill:#f97316,color:#fff

Marking Text for Translation

Use the i18n attribute to mark translatable text:

import { Component } from "@angular/core";

@Component({
  selector: "app-welcome",
  standalone: true,
  template: `
    <h1 i18n="Welcome header|Greeting message@@welcomeHeader">
      Welcome to our application!
    </h1>
    <p i18n="Intro paragraph@@introText">
      This application helps you manage your tasks efficiently.
    </p>
    <p i18n>
      This text has a simple marker without description or ID.
    </p>
  `
})
export class WelcomeComponent {}

Expected output: Angular extracts the marked text into translation files. The i18n attribute value can include a description, meaning, and unique ID.

The format is: i18n="{meaning}|{description}@@{id}". The @@id is an optional unique identifier. If omitted, Angular generates one from the text content.

Handling Interpolations

Translate text with dynamic values:

@Component({
  selector: "app-user-greeting",
  standalone: true,
  template: `
    <p i18n>
      Hello {{ username }}! You have {{ taskCount }} tasks pending.
    </p>

    <!-- ICU Message Format for pluralization -->
    <p i18n>
      {taskCount, plural,
        =0 {You have no tasks pending.}
        =1 {You have one task pending.}
        other {You have {{taskCount}} tasks pending.}
      }
    </p>
  `
})
export class UserGreetingComponent {
  username = "Alice";
  taskCount = 3;
}

Expected output: "Hello Alice! You have 3 tasks pending." In plural form: "You have 3 tasks pending." If taskCount were 1: "You have one task pending."

Angular uses ICU Message Format for pluralization and gender rules. Translators get the full ICU expression to adapt to their language's plural rules (some languages have 2, 3, or 4 plural forms).

Generating Translation Files

Extract messages and create translation files:

# Extract messages from templates
ng extract-i18n --output-path src/locale

# This generates messages.xlf in src/locale/

Expected output: An XLIFF (.xlf) file containing all translatable texts from templates.

The XLIFF file looks like:

<trans-unit id="welcomeHeader" datatype="html">
  <source>Welcome to our application!</source>
  <target state="new">Welcome to our application!</target>
  <note priority="1" from="description">Greeting message</note>
  <note priority="1" from="meaning">Welcome header</note>
</trans-unit>

Translators edit the <target> elements to provide translations.

Building for Different Locales

Configure and build for multiple languages:

// angular.json (partial)
{
  "projects": {
    "my-app": {
      "i18n": {
        "sourceLocale": "en-US",
        "locales": {
          "es": { "translation": "src/locale/messages.es.xlf" },
          "fr": { "translation": "src/locale/messages.fr.xlf" },
          "ja": { "translation": "src/locale/messages.ja.xlf" }
        }
      },
      "architect": {
        "build": {
          "configurations": {
            "production": {
              "localize": true
            }
          }
        }
      }
    }
  }
}

Build all locales:

ng build --localize

Expected output: Angular creates separate output directories for each locale: dist/my-app/en-US/, dist/my-app/es/, dist/my-app/fr/, etc.

Each locale build has the translated templates and the correct locale data (date/number formats). You deploy these separately or use a CDN to serve the appropriate locale based on the user's language.

Locale-Specific Pipes

Dates, numbers, and currencies adapt to locale:

import { Component, Inject, LOCALE_ID } from "@angular/core";
import { CommonModule } from "@angular/common";

@Component({
  selector: "app-locale-demo",
  standalone: true,
  imports: [CommonModule],
  template: `
    <p>Date: {{ today | date:"fullDate" }}</p>
    <p>Number: {{ value | number:"1.2-2" }}</p>
    <p>Currency: {{ price | currency }}</p>
    <p>Percent: {{ percent | percent }}</p>
    <p>Current locale: {{ currentLocale }}</p>
  `
})
export class LocaleDemoComponent {
  today = new Date();
  value = 1234567.8912;
  price = 99.99;
  percent = 0.758;
  currentLocale = "";

  constructor(@Inject(LOCALE_ID) locale: string) {
    this.currentLocale = locale;
  }
}

Expected output: In the Spanish locale, the date appears as "domingo, 28 de junio de 2026". Currency shows as "99,99 EUR" with European formatting.

Angular ships locale data for over 120 locales. Register additional locales in angular.json or import them manually.

Runtime Locale Switching

For dynamic locale switching without reload:

import { Injectable } from "@angular/core";
import { BehaviorSubject } from "rxjs";

@Injectable({ providedIn: "root" })
export class LocaleService {
  private currentLocale = new BehaviorSubject<string>("en-US");
  locale$ = this.currentLocale.asObservable();

  setLocale(locale: string) {
    this.currentLocale.next(locale);
    localStorage.setItem("preferred_locale", locale);
  }

  getLocale(): string {
    return this.currentLocale.value;
  }
}

For true runtime switching without rebuild, use the $localize tag with lazy-loaded translation files. More commonly, apps use the built locale Strategy where each locale is a separate deploy.

Common Mistakes

  1. Forgetting to extract translations — New texts added after the initial extraction are not translated until you re-run ng extract-i18n.

  2. Missing ICU plural rules — English has 3 plural forms (0, 1, other). Other languages have different rules. Always use ICU format, not if/else logic.

  3. Not translating dynamic texts in TypeScripti18n only works in templates. Use $localize tag for TypeScript strings: $localize`Hello ${name}`.

  4. Assuming translated text fits the UI — German and Russian text can be 30-50% longer than English. Design flexible layouts that accommodate variable text lengths.

  5. Translating technical terms — Some terms like "API", "GET", "POST", or brand names should not be translated. Mark these with i18n but provide translator notes.

Practice Questions

  1. What attribute marks text for translation in Angular? i18n attribute in the template. For example: <h1 i18n>Hello</h1>.

  2. What is ICU Message Format? A standard syntax for handling pluralization, gender, and select rules in translations.

  3. How do you generate translation files? Run ng extract-i18n to extract all i18n-marked texts into an XLIFF file.

  4. What does --localize do when building? It builds separate bundles for each configured locale with translated templates and locale data.

  5. How do you access the current locale in code? Inject LOCALE_ID token: constructor(@Inject(LOCALE_ID) public locale: string).

Challenge

Build a MultiLangCheckoutComponent with: product name, price, quantity, total, "Add to Cart" button, and a summary section. Mark all user-facing text for translation. Use ICU pluralization for cart item count. Extract the XLIFF file. Translate to Spanish and French (even if using Google Translate). Build and verify both locale outputs.

FAQ

Does Angular i18n support right-to-left (RTL) languages?

Yes, Angular supports RTL. The locale data includes directionality. You may need to adjust CSS for RTL layouts.

What is the $localize function?

A runtime function used to translate strings in TypeScript code. It is a tag function for template literals.

Can I lazy load translations?

Yes, load translation files dynamically and use $localize for runtime translation without rebuilding.

How do I handle date/time formatting per locale?

Angular's DatePipe automatically formats according to the app's locale. No additional work needed.

What browsers support Angular i18n?

All modern browsers. Angular compiles translations at build time, so there is no runtime impact.

Mini Project

Create an Internationalized DashboardComponent that displays: a welcome message with user name, today's date (full format), currency amounts (user's salary), percentage (completion rate), and a notification count with pluralization. Configure 3 locales: en-US, es (Spanish), and de (German). Extract translations, provide translated XLF files (use Google Translate if needed), build with --localize, and verify the output structure has separate directories for each locale.

What's Next

Continue with state management with NgRx:

Angular State Management, Angular Signals, Angular Standalone

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro