angularangular-localize

Plural support for typescript in Angular 18


Currently, I am upgrading my app to angular 18 and also am migrating i18n from ngx-translate to angular/localize. Unfortunately, I couldn't achieve plural in typescript, I can do the same in html. Below is my translation text, can someone suggest whether is it support? if yes guide me how to do that properly,

const i18NValue = $localize:@@MessageID:Found {count, plural, =1 {item} other {items}};

Expected text: Found item or Found items.

After extract when I check the trans-unit

Found {count, plural, =1 {item} other {items}}

this doesn't recognise as ICU.

Tried changing the localization expression, e.g.:

$localize:@@MessageID:Found ${count}:{count, plural, =1 {item} other {items}};

$localize:@@MessageID:Found {${count}, plural, =1 {item} other {items}};

$localize:@@MessageID:Found ${count, plural, =1 {item} other {items}};

but nothing worked.


Solution

  • First install the localize package either by doing ng add:

    ng add @angular/localize
    

    I had trouble installing the localize pipe, so I went for manual installation.

    First add the @angular/localize to the package.json.

     npm i @angular/localize
    

    After adding the package, open tsconfig.app.json and add the types.

    /* To learn more about this file see: https://angular.io/config/tsconfig. */
    {
      "extends": "./tsconfig.json",
      "compilerOptions": {
        "outDir": "./out-tsc/app",
        "types": ["@angular/localize"]
      },
      "files": ["src/main.ts"],
      "include": ["src/**/*.d.ts"]
    }
    

    After typing added, open main.ts and add the below import.

    import '@angular/localize/init';
    

    You should also have the CommonModule added to the imports so that ngPlural directive is accessible.

    Also make sure you have added I18nPluralPipe to the providers array of the application.

    bootstrapApplication(App, {
      providers: [I18nPluralPipe],
    });
    

    After adding these you should be able to access $localize and you can perform plural translations in the TS with the below code snippet.

    ngOnInit() {
      const numericVariable = 1;
      const plural = this.i18nPluralPipe.transform(
        numericVariable,
        {
          '=0': $localize`No results found.`,
          '=1': $localize`One result found.`,
          other: $localize`${numericVariable} results found.`,
        },
        this.locale
      );
    
      // do what you want with this pluralized variable
      console.log(plural);
    }
    

    Full Code:

    import {
      Component,
      Inject,
      LOCALE_ID,
      importProvidersFrom,
    } from '@angular/core';
    import { bootstrapApplication } from '@angular/platform-browser';
    
    import { ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/core';
    import { I18nPluralPipe, CommonModule } from '@angular/common';
    
    import '@angular/localize/init';
    
    @Component({
      selector: 'app-root',
      standalone: true,
      imports: [CommonModule],
      changeDetection: ChangeDetectionStrategy.OnPush,
      template: `
        <h1>Pluralization</h1>
    <p>{{ locale }}</p>
    
    <h2>English</h2>
    <h3>Example-1</h3>
    <div [ngPlural]="itemNum">
      <ng-template ngPluralCase="0">There is nothing</ng-template>
      <ng-template ngPluralCase="1">There is one item</ng-template>
      <ng-template ngPluralCase="other">There are {{ itemNum }} items</ng-template>
    </div>
    <h3>Example-2</h3>
    <div [ngPlural]="days">
      <ng-template ngPluralCase="=0">No days</ng-template>
      <ng-template ngPluralCase="one">1 day</ng-template>
      <ng-template ngPluralCase="=2">Two days</ng-template>
      <ng-template ngPluralCase="other">{{ days }} days</ng-template>
    </div>
    <h3>Example-3</h3>
    <div [ngPlural]="minutes">
      <ng-template ngPluralCase="=0">just now</ng-template>
      <ng-template ngPluralCase="=1">after one minute</ng-template>
      <ng-template ngPluralCase="other">after {{ minutes }} minutes</ng-template>
    </div>
    
    <!-- Set LOCALE_ID to de_DE in app.module.ts to see this correctly -->
    <h2>German</h2>
    <h3>Example-4</h3>
    <div [ngPlural]="days">
      <ng-template ngPluralCase="one">{{ days }} Tag</ng-template>
      <ng-template ngPluralCase="other">{{ days }} Tage</ng-template>
    </div>
    
    <!-- Set LOCALE_ID to hr_HR in app.module.ts to see this correctly-->
    <h2>Croatian</h2>
    <h3>Example-5</h3>
    <div [ngPlural]="hours">
      <ng-template ngPluralCase="one">{{ hours }} sat</ng-template>
      <ng-template ngPluralCase="few">{{ hours }} sata</ng-template>
      <ng-template ngPluralCase="other">{{ hours }} sati</ng-template>
    </div>
    
      `,
    })
    export class App {
      // For testing change value to 0,4,5, 5.2
      itemNum = 0;
      days = 5;
      minutes = 20;
      hours = 10;
      messageCount = 9;
      name = 'Arman';
      welcome!: string;
    
      constructor(
        private i18nPluralPipe: I18nPluralPipe,
        @Inject(LOCALE_ID) public locale: string
      ) {}
    
        ngOnInit() {
          const numericVariable = 1;
          const plural = this.i18nPluralPipe.transform(
            numericVariable,
            {
              '=0': $localize`No results found.`,
              '=1': $localize`One result found.`,
              other: $localize`${numericVariable} results found.`,
            },
            this.locale
          );
    
          // do what you want with this pluralized variable
          console.log(plural);
        }
    }
    
    bootstrapApplication(App, {
      providers: [I18nPluralPipe],
    });
    

    Stackblitz Demo