How to use MatPaginatorIntl?

AngularTypescriptAngular Material

Angular Problem Overview


I'm using MatPaginator component and I'm trying to figure out how to translate those labels (documentation isn't clear enough about this).

I've found this article showing how to do this and I followed the steps:

1 - I created a file called custom-paginator.ts and put the following there:

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

export class CustomPaginator extends MatPaginatorIntl {
  constructor() {
    super();
    this.nextPageLabel = ' My new label for next page';
    this.previousPageLabel = ' My new label for previous page';
    this.itemsPerPageLabel = 'Task per screen';
  }
}

2 - In app.module.ts I put:

@NgModule({
  // ...
  providers: [
    {
      provide: MatPaginatorIntl,
      useClass: CustomPaginator
    }
  ]
})
export class AppModule

However, it simply doesn't change nothing. What am I missing?

Angular Solutions


Solution 1 - Angular

You can do it like this: I'm providing you with croatian labels:

customClass.ts

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

@Injectable()
export class MatPaginatorIntlCro extends MatPaginatorIntl {
  itemsPerPageLabel = 'Stavki po stranici';
  nextPageLabel     = 'Slijedeća stranica';
  previousPageLabel = 'Prethodna stranica';

  getRangeLabel = (page: number, pageSize: number, length: number) => {
    if (length === 0 || pageSize === 0) {
      return '0 od ' + 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 + ' od ' + length;
  };
}

and AppModule.ts:

providers: [{ provide: MatPaginatorIntl, useClass: MatPaginatorIntlCro}],

Solution 2 - Angular

Based on Vinko Vorih code, I made a paginator working with ngx-translate, here is the code :

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

export class PaginatorIntlService extends MatPaginatorIntl {
  translate: TranslateService;

  getRangeLabel = function (page, pageSize, length) {
    const of = this.translate ? this.translate.instant('paginator.of') : 'of';
    if (length === 0 || pageSize === 0) {
      return '0 ' + 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 + ' ' + of + ' ' + length;
  };

  injectTranslateService(translate: TranslateService) {
    this.translate = translate;

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

    this.translateLabels();
  }

  translateLabels() {
    super.itemsPerPageLabel = this.translate.instant('paginator.items_per_page');
    super.nextPageLabel = this.translate.instant('paginator.next_page');
    super.previousPageLabel = this.translate.instant('paginator.previous_page');
    this.changes.next();
  }

}

And then in your module providers entry :

{
  provide: MatPaginatorIntl,
  useFactory: (translate) => {
    const service = new PaginatorIntlService();
    service.injectTranslateService(translate);
    return service;
  },
  deps: [TranslateService]
}

This way, you can store translations in your usual i18n file and if your web app is able to dynamically change locale, paginator will be translated on demand.

Solution 3 - Angular

If you want a dynamic language switch with ngx-translate to work for you, here’s the solution, it works for me.

mat-paginator-i18n.service.ts

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

const ITEMS_PER_PAGE = 'Items per page:';
const NEXT_PAGE = 'Next page';
const PREV_PAGE = 'Previous page';
const FIRST_PAGE = 'First page';
const LAST_PAGE = 'Last page';

@Injectable()
export class MatPaginatorI18nService extends MatPaginatorIntl {
public constructor(private translate: TranslateService) {
  super();

  this.translate.onLangChange.subscribe((e: Event) => {
	this.getAndInitTranslations();
  });

  this.getAndInitTranslations();
}

public getRangeLabel = (page: number, pageSize: number, length: number): string => {
  if (length === 0 || pageSize === 0) {
	return `0 / ${length}`;
  }

  length = Math.max(length, 0);

  const startIndex: number = page * pageSize;
  const endIndex: number = startIndex < length
	? Math.min(startIndex + pageSize, length)
	: startIndex + pageSize;

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

public getAndInitTranslations(): void {
  this.translate.get([
	ITEMS_PER_PAGE,
	NEXT_PAGE,
	PREV_PAGE,
	FIRST_PAGE,
	LAST_PAGE,
  ])
	.subscribe((translation: any) => {
	  this.itemsPerPageLabel = translation[ITEMS_PER_PAGE];
	  this.nextPageLabel = translation[NEXT_PAGE];
	  this.previousPageLabel = translation[PREV_PAGE];
	  this.firstPageLabel = translation[FIRST_PAGE];
	  this.lastPageLabel = translation[LAST_PAGE];

	  this.changes.next();
	});
}
}

In your module AppModule

@NgModule({
  // ...
  providers: [
	{
	  provide: MatPaginatorIntl,
	  useClass: MatPaginatorI18nService,
	},
  ],
})
export class AppModule {
// ...

en.json, etc.

{
  "Items per page:": "Items per page:",
  "Next page": "Next page",
  "Previous page": "Previous page",
  "First page": "First page",
  "Last page": "Last page",
}

Solution 4 - Angular

in file: matPaginatorIntlCroClass.ts

import {MatPaginatorIntl} from '@angular/material';
export class MatPaginatorIntlCro extends MatPaginatorIntl {
  itemsPerPageLabel = 'Items par page';
  nextPageLabel     = 'Page Prochaine';
  previousPageLabel = 'Page Précedente';
}

in File: AppModule.ts:

import { MatPaginatorModule, MatPaginatorIntl} from '@angular/material';
import { MatPaginatorIntlCro } from './matPaginatorIntlCroClass'

@NgModule({
  imports: [],
  providers: [{ provide: MatPaginatorIntl, useClass: MatPaginatorIntlCro}],
  ..
})

Source: https://material.angular.io/components/paginator/api

Solution 5 - Angular

I did some modifications to fix the end index when start index exceed the list length. I also add the translation for first and last page. It is for @angular/material 5.2.4 pagination component.

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

@Injectable()
export class MatPaginationIntlService extends MatPaginatorIntl {
  translate: TranslateService;
  firstPageLabel = 'First page';
  itemsPerPageLabel = 'Items per page';
  lastPageLabel = 'Last page';
  nextPageLabel = 'Next page';
  previousPageLabel = 'Previous page';

  getRangeLabel = (page: number, pageSize: number, length: number): string => {
    const of = this.translate ? this.translate.instant('mat-paginator-intl.of') : 'of';
    if (length === 0 || pageSize === 0) {
      return '0 ' + of + ' ' + length;
    }
    length = Math.max(length, 0);
    const startIndex = ((page * pageSize) > length) ?
      (Math.ceil(length / pageSize) - 1) * pageSize:
      page * pageSize;

    const endIndex = Math.min(startIndex + pageSize, length);
    return startIndex + 1 + ' - ' + endIndex + ' ' + of + ' ' + length;
  };

  injectTranslateService(translate: TranslateService) {
    this.translate = translate;

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

    this.translateLabels();
  }

  translateLabels() {
    this.firstPageLabel = this.translate.instant('mat-paginator-intl.first_page');
    this.itemsPerPageLabel = this.translate.instant('mat-paginator-intl.items_per_page');
    this.lastPageLabel = this.translate.instant('mat-paginator-intl.last_page');
    this.nextPageLabel = this.translate.instant('mat-paginator-intl.next_page');
    this.previousPageLabel = this.translate.instant('mat-paginator-intl.previous_page');
  }
}

Solution 6 - Angular

In order to refresh the label, you can fire a change event after the label change:

translateLabels() {
    this.firstPageLabel = this.translate.instant('mat-paginator-intl.first_page');
    ...
    this.changes.next();
}

Solution 7 - Angular

Additionally you can use injected services by marking the Intl to be an injectable itself. See example (ignore specifics of DoneSubject and LocalizeService as those are custom implementations):

	import { Injectable, OnDestroy } from '@angular/core';
	import { MatPaginatorIntl } from '@angular/material';
	import { LocalizeService } from 'app/general';
	import { DoneSubject } from 'app/rx';
	import { takeUntil } from 'rxjs/operators';

	@Injectable()
	export class MatPaginatorIntlLoc extends MatPaginatorIntl implements OnDestroy {
	  constructor(private readonly localizer: LocalizeService) {
		super();

		localizer.locale$.pipe(takeUntil(this.done$)).subscribe(() => {
		  this.itemsPerPageLabel = localizer.translate('mat paginator label: items per page');
		  this.nextPageLabel = localizer.translate('mat paginator label: next page');
		  this.previousPageLabel = localizer.translate('mat paginator label: previous page');
		  this.firstPageLabel = localizer.translate('mat paginator label: first page');
		  this.lastPageLabel = localizer.translate('mat paginator label: last page');
		});
	  }

	  private readonly done$ = new DoneSubject();

	  ngOnDestroy() { this.done$.done(); }

	  getRangeLabel = (page: number, pageSize: number, length: number) => this.localizer
		.translate('mat paginator label: x of y', [
		  length > 0 && pageSize > 0 ? (page * pageSize + 1) + ' - ' + Math.min((page + 1) * pageSize, Math.max(length, 0)) : 0,
		  Math.max(length, 0),
		]);
	}

And don't forget to provide it in your module:

	providers: [
		...
		{ provide: MatPaginatorIntl, useClass: MatPaginatorIntlLoc },
		...
	  ]

Solution 8 - Angular

Based on answers above, using ngx-translate

import { Injectable } from '@angular/core';
import { MatPaginatorIntl } from '@angular/material/paginator';
import { TranslateService } from '@ngx-translate/core';
import { take } from 'rxjs/operators';

@Injectable()
export class MatPaginatorIntlNl extends MatPaginatorIntl {
  itemsPerPageLabel!: string;
  nextPageLabel!: string;
  previousPageLabel!: string;
  ofLabel!: string;

  constructor(private readonly translateService: TranslateService) {
    super();

    this.getTranslations();
  }

  async getTranslations() {
    const translations = await this.translateService
      .get(['TABLE.ITEMS_PER_PAGE', 'TABLE.NEXT_PAGE', 'TABLE.PREVIOUS_PAGE', 'COMMON.OF'])
      .pipe(take(1))
      .toPromise();

    this.itemsPerPageLabel = translations['TABLE.ITEMS_PER_PAGE'];
    this.nextPageLabel = translations['TABLE.NEXT_PAGE'];
    this.previousPageLabel = translations['TABLE.PREVIOUS_PAGE'];
    this.ofLabel = translations['COMMON.OF'];
  }

  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;
    // 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.ofLabel} ${length}`;
  };
}

Solution 9 - Angular

Inject MatPaginatorIntl anywhere in your application, set desired translations and call changes.next(). Repeat on every language change (e.g. by subscribing to onLangChange when using ngx-translate).

Solution 10 - Angular

I had the same issue, and then I changed in app.module.ts in the imports statement TranslateModule to TranslateModule.forRoot()

So it looks like this:

imports: [
    ...
    TranslateModule.forRoot()
    ...
]

Quote from NPM's site: "The forRoot static method is a convention that provides and configures services at the same time. Make sure you only call this method in the root module of your application, most of the time called AppModule. This method allows you to configure the TranslateModule by specifying a loader, a parser and/or a missing translations handler."

Here is the whole article: https://www.npmjs.com/package/@ngx-translate/core

Reading this can help resolving many issues with TranslateModule!

Solution 11 - Angular

One important thing to mention is to use useClass instead of useValue, when providing custom implementations as classes. This seems quite obvious, but can be easily overlooked as some guides still use useValue and provide functions.

{
  provide: MatPaginatorIntl, 
  useClass: LocalizedPaginator
}

Solution 12 - Angular

After checking many options what works in my case and I think that is most robust solutions for translating paginator strings dynamically is given below.

mat-paginator-translator.ts // Add this file in project

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

@Injectable()
export class MatPaginatorTranslator extends MatPaginatorIntl {

itemsPerPageLabel: string;
nextPageLabel: string;
previousPageLabel: string;
firstPageLabel: string;
lastPageLabel: string;

constructor(private translateService: TranslateService) {
    super();
}

getRangeLabel = (page: number, pageSize: number, length: number) => {
    this.getTranslations();
    return ((page * pageSize) + 1) + ' - ' + ((page * pageSize) + pageSize)
        + ' ' + this.translateService.instant('of') + ' ' + length;
};

private getTranslations() {
    this.itemsPerPageLabel = this.translateService.instant('Items per page');
    this.nextPageLabel = this.translateService.instant('Next page');
    this.previousPageLabel = this.translateService.instant('Previous page');
    this.firstPageLabel = this.translateService.instant('First page');
    this.lastPageLabel = this.translateService.instant('Last page');
}
}

app.module.ts // Add following changes in app module

providers: [{
    provide: MatPaginatorIntl,
    useClass: MatPaginatorTranslator
}],

de.json // This can be any translation json file

"Next page": "Nächste Seite",
"Previous page": "Vorherige Seite",
"First page": "Nächste Seite",
"Last page": "Nächste Seite",
"of": "von",

Put these files at right place and you will have the solution.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questiondev_054View Question on Stackoverflow
Solution 1 - AngularVinko VorihView Answer on Stackoverflow
Solution 2 - AngularZe Big DuckView Answer on Stackoverflow
Solution 3 - AngularPlusMinusView Answer on Stackoverflow
Solution 4 - AngularAlanView Answer on Stackoverflow
Solution 5 - AngularSébastien CôtéView Answer on Stackoverflow
Solution 6 - Angularuser3706681View Answer on Stackoverflow
Solution 7 - AngularAlex RempelView Answer on Stackoverflow
Solution 8 - AngularRubenView Answer on Stackoverflow
Solution 9 - AngularMarcin MajkowskiView Answer on Stackoverflow
Solution 10 - AngularGefilte FishView Answer on Stackoverflow
Solution 11 - AngularlajumaView Answer on Stackoverflow
Solution 12 - AngularRahul DudhaneView Answer on Stackoverflow