76

Do you have any ideas how can I translate "Items per page" in Angular's mat-paginator tag? The mat-paginator is an element from Material Design.

Roy
  • 7,811
  • 4
  • 24
  • 47
bigmeister
  • 1,137
  • 3
  • 13
  • 20

14 Answers14

173

You can use the MatPaginatorIntl for this. Will Howell made an example that no longer works, so here is an updated version (with Dutch) and step-by-step guidance.

  1. Import the MatPaginatorIntl from @angular/material into your application.
  2. Create a new paginator file for your locale (in this example I use Dutch) and import that: import { getDutchPaginatorIntl } from './app/dutch-paginator-intl'; in main.ts file
  3. Set a provider for the Paginator inside of the main.ts file, so it takes the translations of your local file (instead of English as default language):
providers: [
   { provide: MatPaginatorIntl, useValue: getDutchPaginatorIntl() }
]
  1. Inside your paginator-intl file, set the labels for the strings that can be translated and export these. Most important part of that file (see the example for more info):
paginatorIntl.itemsPerPageLabel = 'Items per pagina:';
paginatorIntl.firstPageLabel = 'Eerste pagina';
paginatorIntl.previousPageLabel = 'Vorige pagina';
paginatorIntl.nextPageLabel = 'Volgende pagina';
paginatorIntl.lastPageLabel = 'Laatste pagina';
paginatorIntl.getRangeLabel = dutchRangeLabel;

Example on StackBlitz with the paginator translations file as starting point.


June 2018 - Update to Angular 6.x
This updated Example on StackBlitz is upgraded to Angular 6.x to accommodate the latest version of the framework. Only packages have changed, nothing inside the paginator has changed.


June 2019 - Update to Angular 8.x
This updated Example on StackBlitz is upgraded to Angular 8.x to accommodate the latest version of the framework. Only packages have changed, nothing inside the paginator has changed.


February 2020 - Update to Angular 9.x
This updated Example on StackBlitz is upgraded to Angular 9.x to accommodate the latest version of the framework. Package versions have changed. Major change is the way to import from Angular Material. You cannot import from Material root anymore. You need to specify the import from the module (material/paginator) itself:

import { MatPaginatorModule, MatPaginatorIntl } from '@angular/material/paginator';

June 2020 - Update to Angular 10.x
This updated Example on StackBlitz is upgraded to Angular 10.x to accommodate the latest version of the framework. Only packages have changed, nothing inside the paginator has changed.


December 2020 - Update to Angular 11.x
This updated Example on StackBlitz is upgraded to Angular 11.x to accommodate the latest version of the framework. Only packages have changed, nothing inside the paginator has changed.


May 2021 - Update to Angular 12.x
This updated Example on StackBlitz is upgraded to Angular 12.x to accommodate the latest version of the framework. Only packages have changed, nothing inside the paginator has changed.


January 2022 - Update to Angular 13.x
This updated Example on StackBlitz is upgraded to Angular 13.x to accommodate the latest version of the framework. Only packages have changed, nothing inside the paginator has changed.


June 2022 - Update to Angular 14.x
This updated Example on StackBlitz is upgraded to Angular 14.x to accommodate the latest version of the framework. Only packages have changed, nothing inside the paginator has changed.


November 2022 - Update to Angular 15.x
This updated Example on StackBlitz is upgraded to Angular 15.x to accommodate the latest version of the framework. Only packages have changed, nothing inside the paginator has changed.

Roy
  • 7,811
  • 4
  • 24
  • 47
67

Modified solution (with Angular 6) based on accepted answer with @ngx-translate:

@NgModule({
  imports: [...],
  providers: [
    {
      provide: MatPaginatorIntl, deps: [TranslateService],
      useFactory: (translateService: TranslateService) => new PaginatorI18n(translateService).getPaginatorIntl()
    }
  ]
})
export class CoreModule {}

And the PaginatorI18n:

import { MatPaginatorIntl } from '@angular/material';
import { TranslateService } from '@ngx-translate/core';

export class PaginatorI18n {

    constructor(private readonly translate: TranslateService) {}

    getPaginatorIntl(): MatPaginatorIntl {
        const paginatorIntl = new MatPaginatorIntl();
        paginatorIntl.itemsPerPageLabel = this.translate.instant('ITEMS_PER_PAGE_LABEL');
        paginatorIntl.nextPageLabel = this.translate.instant('NEXT_PAGE_LABEL');
        paginatorIntl.previousPageLabel = this.translate.instant('PREVIOUS_PAGE_LABEL');
        paginatorIntl.firstPageLabel = this.translate.instant('FIRST_PAGE_LABEL');
        paginatorIntl.lastPageLabel = this.translate.instant('LAST_PAGE_LABEL');
        paginatorIntl.getRangeLabel = this.getRangeLabel.bind(this);
        return paginatorIntl;
    }

    private getRangeLabel(page: number, pageSize: number, length: number): string {
        if (length === 0 || pageSize === 0) {
            return this.translate.instant('RANGE_PAGE_LABEL_1', { length });
        }
        length = Math.max(length, 0);
        const startIndex = page * pageSize;
        // If the start index exceeds the list length, do not try and fix the end index to the end.
        const endIndex = startIndex < length ? Math.min(startIndex + pageSize, length) : startIndex + pageSize;
        return this.translate.instant('RANGE_PAGE_LABEL_2', { startIndex: startIndex + 1, endIndex, length });
    }
}

and cz.json

{
    "ITEMS_PER_PAGE_LABEL": "Počet řádků:",
    "NEXT_PAGE_LABEL": "Další stránka",
    "PREVIOUS_PAGE_LABEL": "Předchozí stránka",
    "FIRST_PAGE_LABEL": "První stránka",
    "LAST_PAGE_LABEL": "Poslední stránka",
    "RANGE_PAGE_LABEL_1": "0 z {{length}}",
    "RANGE_PAGE_LABEL_2": "{{startIndex}} - {{endIndex}} z {{length}}"
}  

Configure ngx-translate in app.module.ts:

import { TranslateLoader, TranslateModule, TranslateService } from '@ngx-translate/core';
const httpLoaderFactory = (http: HttpClient) => new TranslateHttpLoader(http, './assets/i18n/', '.json');
@NgModule({
  imports: [
    TranslateModule.forRoot({
      loader: { provide: TranslateLoader, useFactory: httpLoaderFactory, deps: [HttpClient] }
    })
  ],
  providers: [{ provide: LOCALE_ID, useValue: 'cs' }],
  bootstrap: [AppComponent]
})
export class AppModule { }
Camilo Terevinto
  • 31,141
  • 6
  • 88
  • 120
Felix
  • 3,999
  • 3
  • 42
  • 66
  • 13
    The only solution that propose dynamic translations - which is the way you should want it. This should be the accepted answer. – Michal Stepan Oct 11 '18 at 10:00
  • @MichalStepan I agree, through I tried to make it work on StackBlitz several times, but they're not accepting static assets yet. See https://github.com/stackblitz/core/issues/577 – Roy Oct 20 '18 at 15:03
  • @puntanet I have update the answer, see the very bottom. The `cz.json` is then located under `src/assets/i18n`. – Felix Nov 23 '18 at 15:12
  • 9
    This answer is good but it doesn't update the translations when the language is changed. Instead of returning the object in `getPaginatorIntl` it is better to make the class extend `MatPaginatorIntl` and return the object itself. It is also necessary to call `this.changes.next()` for existing tables to be refreshed. Check out this other post https://stackoverflow.com/questions/46869616/how-to-use-matpaginatorintl – iberbeu Dec 06 '18 at 11:22
  • To account for language switch, I used `this.translate.stream()` that reports a new value each time language changed. – Ahmad Shahwan Apr 29 '21 at 12:52
  • What if you want to dynamically switch between languages? – Ya. Jul 22 '21 at 13:24
  • as @iberbeu said, to i used another response from this post https://stackoverflow.com/a/49532776/1977459 – bilelz Jul 12 '23 at 10:02
24

For a quick and dirty solution use this.paginator._intl property.

In my ...component.ts I have:

@ViewChild(MatPaginator) paginator: MatPaginator;

ngOnInit() {
  ...
  this.paginator._intl.itemsPerPageLabel = 'My translation for items per page.';
  ...
}
GoTo
  • 5,966
  • 2
  • 28
  • 31
  • when using this method, how can we translate 'of'? "31-60 0f 20000" – Peyman Kheiri Mar 25 '20 at 16:35
  • 1
    `this.paginator._intl.getRangeLabel = (page: number, pageSize: number, length: number) => { if (length == 0 || pageSize == 0) { return `0 din ${length}`; } length = Math.max(length, 0); const startIndex = page * pageSize; const endIndex = startIndex < length ? Math.min(startIndex + pageSize, length) : startIndex + pageSize; return `${startIndex + 1} - ${endIndex} **of** ${length}`; };` – GoTo Mar 26 '20 at 19:38
  • this will get called after you fire it in every other compoent which uses paginator – Mohit Harshan Jul 30 '20 at 05:06
6

For angular 9.0.0, if you are using i18n package, you can do

require: ng add @angular/localization

Create a file called my-paginator-intl.ts

import { MatPaginatorIntl } from '@angular/material/paginator'

const matRangeLabelIntl = (page: number, pageSize: number, length: number) => {
    if (length === 0 || pageSize === 0) {
        return $localize`:@@paginator.zeroRange:0 in ${length}`
    }
    length = Math.max(length, 0)
    const startIndex = page * pageSize

    // If the start index exceeds the list length, do not try and fix the end index to the end.
    const endIndex = startIndex < length ?  Math.min(startIndex + pageSize, length) : startIndex + pageSize
    return $localize`:@@paginator.rangeOfLabel:${startIndex + 1} - ${endIndex} in ${length}`
}

export function MyPaginatorIntl() {
    const paginatorIntl = new MatPaginatorIntl()

    paginatorIntl.itemsPerPageLabel = $localize`:@@paginator.displayPerPage:Items per page`
    paginatorIntl.nextPageLabel = $localize`:@@paginator.nextPage:Next page`
    paginatorIntl.previousPageLabel = $localize`:@@paginator.prevPage:Prev page`
    paginatorIntl.getRangeLabel = matRangeLabelIntl

    return paginatorIntl
}

Import to app.modudle.ts

import { MatPaginatorIntl } from '@angular/material/paginator'
import { MyPaginatorIntl } from './shared/paginator-int/my-paginator-intl'

@NgModule({
    providers: [
        { provide: MatPaginatorIntl, useValue: MyPaginatorIntl() },
    ]
})

Copy following to your language xlf file

<trans-unit id="paginator.zeroRange">
  <source>0 of <x id="PH" /></source>
  <target>0 trong <x id="PH" /></target>
</trans-unit>
<trans-unit id="paginator.rangeOfLabel">
  <source><x id="PH" /> - <x id="PH_1" /> of <x id="PH_2" /></source>
  <target><x id="PH" /> - <x id="PH_1" /> trong <x id="PH_2" /></target>
</trans-unit>
<trans-unit id="paginator.displayPerPage">
  <source>Items per page</source>
  <target>Hiển thị/Trang</target>
</trans-unit>
<trans-unit id="paginator.nextPage">
  <source>Next page</source>
  <target>Trang kế</target>
</trans-unit>
<trans-unit id="paginator.prevPage">
  <source>Prev page</source>
  <target>Trang trước</target>
</trans-unit>
cocoseis
  • 1,443
  • 1
  • 13
  • 35
xCiCi
  • 147
  • 2
  • 9
3

You can hack your way into MatPaginator._intl and put your string there using ngx-translate.

forkJoin({
  itemsPerPageLabel: this.translate.get('paginator.itemsPerPageLabel'),
  nextPageLabel: this.translate.get('paginator.nextPageLabel'),
  previousPageLabel: this.translate.get('paginator.previousPageLabel'),
  firstPageLabel: this.translate.get('paginator.firstPageLabel'),
  lastPageLabel: this.translate.get('paginator.lastPageLabel'),
}).subscribe(values => {
  this.paginator._intl.itemsPerPageLabel = values.itemsPerPageLabel;
  this.paginator._intl.nextPageLabel = values.nextPageLabel;
  this.paginator._intl.previousPageLabel = values.previousPageLabel;
  this.paginator._intl.firstPageLabel = values.firstPageLabel;
  this.paginator._intl.lastPageLabel = values.lastPageLabel;

  // 1 – 10 of 100
  // https://github.com/angular/components/blob/master/src/material/paginator/paginator-intl.ts#L41
  this.paginator._intl.getRangeLabel = (page: number, pageSize: number, length: number): string => {
    length = Math.max(length, 0);
    const startIndex = page * pageSize;
    const endIndex = startIndex < length ? Math.min(startIndex + pageSize, length) : startIndex + pageSize;
    return this.translate.instant('paginator.getRangeLabel', {
      startIndex: startIndex + 1,
      endIndex,
      length,
    });
  };

  // Otherwise, the paginator won't be shown as translated.
  this.dataSource.paginator = this.paginator;
});
Nato Boram
  • 4,083
  • 6
  • 28
  • 58
3

I build on Felix' answers, who builds on Roy's answer.

To account for dynamic language switch, my factory method uses TranslateService's stream() method (instead of instance()) that returns an observable that fires each time language is set. I also call paginator's change.next() to force redraw.

TranslateService is able to return a "dictionary" of vocabularies. With the right keys in the JSON file, Object.assign() can do most of the job.

Finally, I do not re-implement paginator's getRangeLabel(). Rather I take the old value and replace occurrences of "of" with translated text.

public getPaginatorIntl(): MatPaginatorIntl {
    const paginatorIntl = new MatPaginatorIntl();
    this.translation.stream('paginator.paginatorIntl').subscribe(dict => {
        Object.assign(paginatorIntl, dict);
        paginatorIntl.changes.next();
    });
    const originalGetRangeLabel = paginatorIntl.getRangeLabel;
    paginatorIntl.getRangeLabel = (page: number, size: number, len: number) => {
        return originalGetRangeLabel(page, size, len)
            .replace('of', this.translation.instant('paginator.of'));
    };
    return paginatorIntl;
}

Here is the fr.json for example.

{
    "paginator": {
        "paginatorIntl" : {
            "itemsPerPageLabel": "Items par page",
            "nextPageLabel": "Page suivante",
            "previousPageLabel": "Page précédente",
            "firstPageLabel": "Première page",
            "lastPageLabel": "Dernière page"
        },
        "of": "de"
    }
}
Ahmad Shahwan
  • 1,662
  • 18
  • 29
1
this.dataSource.paginator._intl.itemsPerPageLabel = "Your string here";

this worked in latest angular8 + material8;

shaheer shukur
  • 1,077
  • 2
  • 12
  • 19
1

A laconic solution to change all labels of MatPaginator.

const rangeLabel: string = 'із';
const itemsPerPageLabel: string = 'Елементiв на сторiнцi:';
const firstPageLabel: string = 'Перша сторінка';
const lastPageLabel: string = 'Остання сторінка';
const previousPageLabel: string = 'Попередня сторінка';
const nextPageLabel: string = 'Наступна сторінка';

const getRangeLabel: (page: number, pageSize: number, length: number) => string = (
  page: number,
  pageSize: number,
  length: number
): string => {
  return new MatPaginatorIntl().getRangeLabel(page, pageSize, length).replace(/[a-z]+/i, rangeLabel);
};

export function getPaginatorIntl(): MatPaginatorIntl {
  const paginatorIntl: MatPaginatorIntl = new MatPaginatorIntl();

  paginatorIntl.itemsPerPageLabel = itemsPerPageLabel;
  paginatorIntl.firstPageLabel = firstPageLabel;
  paginatorIntl.lastPageLabel = lastPageLabel;
  paginatorIntl.previousPageLabel = previousPageLabel;
  paginatorIntl.nextPageLabel = nextPageLabel;
  paginatorIntl.getRangeLabel = getRangeLabel;
  
  return paginatorIntl;
}
Yarik256
  • 11
  • 1
1

Another version of @Felix's answer, but here you inject your service directly in the function without the need to make or instantiate a class:

In app.module.ts Add your provider:

providers: [
    YourCustomService,
    {provide: MatPaginatorIntl, useFactory: getPaginatorI18n, deps: [YourCustomService]}
  ]

Create a new ts file with your function (Example paginator-i18n.ts):

import {MatPaginatorIntl} from '@angular/material/paginator';

// This is the word that shows up in the range label
let paginPerRng = '';

const i18nRangeLabel = (page: number, pageSize: number, length: number) => {
  if (length == 0 || pageSize == 0) {
    return `0 ${paginPerRng} ${length}`;
  }

  length = Math.max(length, 0);

  const startIndex = page * pageSize;

  // If the start index exceeds the list length, do not try and fix the end index to the end.
  const endIndex = startIndex < length ?
    Math.min(startIndex + pageSize, length) :
    startIndex + pageSize;

  return `${startIndex + 1} - ${endIndex} ${paginPerRng} ${length}`;
};

export function getPaginatorI18n(YourCustomService: customService) {
  const paginatorIntl = new MatPaginatorIntl();

  // Call the localization methods in your service
  paginatorIntl.itemsPerPageLabel = customService.getResource(...);
  paginatorIntl.nextPageLabel = customService.getResource(...);
  paginatorIntl.previousPageLabel = customService.getResource(...);
  paginatorIntl.firstPageLabel = customService.getResource(...);
  paginatorIntl.lastPageLabel = customService.getResource(...);
  // We localize the word that shows up in the range label before calling the RangeLabel constant
  paginPerRng = customService.getResource(...);
  paginatorIntl.getRangeLabel = i18nRangeLabel;
  return paginatorIntl;
}
Yassir Khaldi
  • 1,452
  • 1
  • 17
  • 30
0

Following the current Angular Material documentation (10.2.0) to modify the labels and text displayed, create a new instance of MatPaginatorIntl and include it in a custom provider. You have to import MatPaginatorModule (you would need it anyway to display paginator component)

import { MatPaginatorModule } from '@angular/material/paginator';

and in your component (where you are using paginator) you could use it in following way:

 constructor(private paginator: MatPaginatorIntl) {
    paginator.itemsPerPageLabel = 'Your custom text goes here'; 
}
luk
  • 205
  • 3
  • 14
0

A simple solution using ngx-translate.

@ViewChild(MatPaginator, { static: true }) paginator: MatPaginator;

constructor(private translate: TranslateService){}

ngOnInit() {
  this.translatePaginator();
}

async translatePaginator() {
  const label = await this.translate.get('COMMON.ItemsPerPageLabel').toPromise();
  this.paginator._intl.itemsPerPageLabel = label;
}
Yadiana Molina
  • 121
  • 1
  • 2
  • 1
    This is an out of context snippet. No context, no full class definition and no example how to use it. – Ya. Jul 22 '21 at 13:08
0

If you want to change dynamically the languange, you can enter this code:

    this.translate.onLangChange.subscribe(() => {
            this.getPaginatorIntl(matPaginatorIntl);
        });

    private getPaginatorIntl(matPaginatorIntl: MatPaginatorIntl): void {
        matPaginatorIntl.itemsPerPageLabel = this.translate.instant('PAGINATOR.ITEM');
        matPaginatorIntl.nextPageLabel = this.translate.instant('PAGINATOR.NEXT_PAGE');
        matPaginatorIntl.previousPageLabel = this.translate.instant('PAGINATOR.PREVIOUS_PAGE');
        matPaginatorIntl.firstPageLabel = this.translate.instant('PAGINATOR.FIRST_PAGE');
        matPaginatorIntl.lastPageLabel = this.translate.instant('PAGINATOR.LAST_PAGE');
        matPaginatorIntl.getRangeLabel = this.getRangeLabel.bind(this);
        matPaginatorIntl.changes.next();
    }

    private getRangeLabel(page: number, pageSize: number, length: number): string {
        if (length === 0 || pageSize === 0) {
            return `0 ${this.translate.instant('PAGINATOR.OF')} ${ length }`;
        }
        length = Math.max(length, 0);
        const startIndex = page * pageSize;
        // If the start index exceeds the list length, do not try and fix the end index to the end.
        const endIndex = startIndex < length ? Math.min(startIndex + pageSize, length) : startIndex + pageSize;
        return `${startIndex + 1} - ${endIndex} ${this.translate.instant('PAGINATOR.OF')} ${length}`;
    }

It's import insert 'changes.next();' after the edit of the object matPaginatorIntl.

0

If you use transloco and you need to translate mat-paginator strings, add this typescript class to your project:

import { Injectable } from '@angular/core';
import { TranslocoService } from '@ngneat/transloco';
import { MatPaginatorIntl } from '@angular/material/paginator';

@Injectable()
export class CustomMatPaginatorIntl extends MatPaginatorIntl  {

    ofLabel: string = 'of';

    constructor(private _translate: TranslocoService) {
        super();
        this._translate.langChanges$.subscribe((lang) => {
            this.getAndInitTranslations();
        });
        this.getAndInitTranslations();
    }

    getAndInitTranslations(): void {
        this._translate.selectTranslate('paginator.itemsPerPageLabel').subscribe((value) => {
            this.itemsPerPageLabel = value;
            this.changes.next();
        });
        this._translate.selectTranslate('paginator.nextPageLabel').subscribe((value) => {
            this.nextPageLabel = value;
            this.changes.next();
        });
        this._translate.selectTranslate('paginator.previousPageLabel').subscribe((value) => {
            this.previousPageLabel = value;
            this.changes.next();
        });
        this._translate.selectTranslate('paginator.firstPageLabel').subscribe((value) => {
            this.firstPageLabel = value;
            this.changes.next();
        });
        this._translate.selectTranslate('paginator.lastPageLabel').subscribe((value) => {
            this.lastPageLabel = value;
            this.changes.next();
        });
        this._translate.selectTranslate('paginator.ofLabel').subscribe((value) => {
            this.ofLabel = value;
            this.changes.next();
        });
    }

    getRangeLabel = (
        page: number,
        pageSize: number,
        length: number,
    ): string => {
        if (length === 0 || pageSize === 0) {
            return `0 ${this.ofLabel} ${length}`;
        }
        length = Math.max(length, 0);
        const startIndex = page * pageSize;
        const endIndex =
            startIndex < length
                ? Math.min(startIndex + pageSize, length)
                : startIndex + pageSize;
        return `${startIndex + 1} - ${endIndex} ${
            this.ofLabel
        } ${length}`;
    };
}

Then in your Module call it like this:

@NgModule({
    imports: [
        ...
        TranslocoModule,
    ],
    providers: [
        { provide: MatPaginatorIntl, useClass: CustomMatPaginatorIntl } // >> add this line
    ]
})
AnasSafi
  • 5,353
  • 1
  • 35
  • 38
0

Local solution for ES internationalization (just change the values for the labels with the corresponding ones in your language), super direct and without touching the app.moulde:

import { MatPaginatorIntl } from '@angular/material/paginator';

...

  constructor(
    ...
    private paginator: MatPaginatorIntl
    ) {
      paginator.nextPageLabel = 'Siguiente';
      paginator.previousPageLabel = 'Anterior';
      paginator.lastPageLabel = 'Última Página';
      paginator.firstPageLabel = 'Primera Página';
      paginator.itemsPerPageLabel = 'Registros por Página';
      paginator.getRangeLabel = (page: number, pageSize: number, length: number) => {
        if (length == 0 || pageSize == 0) { return `0 de ${length}`; }
        length = Math.max(length, 0);
        const startIndex = page * pageSize;
        const endIndex = startIndex < length ? Math.min(startIndex + pageSize, length) : startIndex + pageSize;
        return `${startIndex + 1} - ${endIndex} de ${length}`;
      };
    }