Skip to the solution
MediumFrontend10 min read

How do you handle internationalization (i18n) in Angular?

335 views

The problem

Question presented to candidate: "We need our Angular app in English and French, with correct plurals, dates and currencies, and the marketing team wants French URLs to be indexable. How do you internationalise an Angular app, and what does the build produce?"

What a strong answer should cover:

  • @angular/localize (ng add @angular/localize); mark templates with i18n, attributes with i18n-<attr>, code with $localize.
  • Message IDs: generated by default, custom with @@id; meaning and description for translators.
  • ICU expressions for plural and select.
  • ng extract-i18n produces XLIFF (or JSON, ARB, XMB); translators return per-locale files.
  • angular.json i18n.sourceLocale / locales and localize: true: one build per locale, own folder, <base href> and lang.
  • LOCALE_ID drives DatePipe, CurrencyPipe, DecimalPipe.
  • Trade-offs: build-time (fast, needs a reload to switch) vs runtime libraries; missing-translation handling; SSR hydration with withI18nSupport().

Clarifying questions expected:

  • "Must users switch language without reloading the page?"
  • "How will the server route /fr/ and /en-US/ to the right build?"

Code / implementation expected: Yes β€” a marked-up template with an ICU plural, and the translation workflow.

  • i18n

Try it yourself

Sketch your approach before you read the solution. That is what the interview tests.

Stuck? Get a hint.

A short nudge toward the approach. It will not give the answer away.

Solution

The solution is hidden so you can try first

10 min read Β· 11 sections

Read the code

AngularA component marked for translation: i18n attributes, an ICU plural, $localize and locale-aware pipes
import { Component, LOCALE_ID, inject, signal } from '@angular/core';
import { CurrencyPipe, DatePipe, DecimalPipe } from '@angular/common';

@Component({
  selector: 'app-root',
  imports: [CurrencyPipe, DatePipe, DecimalPipe],
  template: `
    <h1 i18n="Shop header|Greeting at the top of the home page@@homeWelcome">Welcome to the shop</h1>
    <img src="logo.png" i18n-alt alt="Shop logo" width="40" height="40" />
    <p i18n>You have {count(), plural, =0 {no items} =1 {one item} other {{{count()}} items}} in your basket.</p>
    <p>{{ title }}</p>
    <p>{{ 1234567.891 | number }} | {{ 1299.5 | currency: 'EUR' }} | {{ day | date: 'fullDate' }}</p>
    <small>LOCALE_ID = {{ locale }}</small>
  `,
})
export class App {
  protected readonly count = signal(3);
  protected readonly title = $localize`:@@pageTitle:Products on sale`;
  protected readonly day = new Date(Date.UTC(2026, 9, 16, 12));
  protected readonly locale = inject(LOCALE_ID);
}

Discussion

Other approaches, follow-ups and how it went in real interviews.

Sign in to join the discussion.

No comments yet. Share how you would answer it.

Question 69 of 121 in Angular

Keep your momentum going.

Mark this question solved to track your progress, then take the next one while it is fresh.

All Angular questions
Up nextMedium

What is an HTTP interceptor?

Continue