Skip to content

How to Create a Localized Date Pipe in Angular 📅

October 11, 2021Mamadou Ouologuem4 min read

Usage of localized date pipe against standard date pipe

TLDR:

To translate dates in Angular, we can think about three approaches: setting the app global locale, passing a specific locale to Angular date through arguments, or creating a custom localizedDate pipe. The localizedDate pipe combines Angular date pipe and the lib ngx-translate. This solution prevents us from explicitly passing the locale as an argument in the template while still being able to dynamically change the dates' locale without reloading the app. Those aspects make the localizedDate pipe impure like [ngx-translate]'s translate pipe.

Use a pipe in the template

We use pipes either in a service or a template (the HTML code). In the following example, we are using the template syntax of the slice pipe and the uppercase pipe.

<div>{{ 'Grogu' | slice:1:4 | uppercase }}</div>
<!-- output: <div>ROG</div> -->

This is equivalent to 'Grogu'.slice(1, 4).toUpperCase() in Javascript.

Sadly, there is only a short list of built-in pipes in Angular. Wait a second, could we create our own?

That's fairly easy to do because a pipe is simply a data transformer.

Different ways to translate dates in a template

Angular provides a built-in date pipe. It transforms a date into a string value (eg. 2021-03-21 -> Mar 21, 2021). The date pipe uses the app locale as its default locale (eg. 2021-03-21 -> 21 mars 2021 for a french locale).

To translate dates in a template, we can think about three approaches:

  • by setting the default app locale
  • by passing the locale as arguments of the date pipe
  • by combining ngx-translate and the date pipe in a new custom pipe

Approach 1: Setting the default locale

The date pipe uses the LOCALE_ID to determine the default locale to use. By default, LOCALE_ID is set to en-US. We can change the app locale by LOCALE_ID setting in the AppModule.

@NgModule({
  providers: [
    { provide: LOCALE_ID, useValue: 'fr-FR' }
  ]
})

With the static string value fr-FR, the LOCALE_ID value can not be dynamically changed. Instead of statically setting the LOCALE_ID value, we can provide a service as a value.

@NgModule({
  providers: [
    { provide: LOCALE_ID, useFactory: appLocaleService },
  ]
})

For changes made to appLocaleService to be reflected in our app, we need to reload the Angular app. LOCALE_ID is required in Angular build process. Reloading the app may reset the app state and re-trigger HTTP requests.

Approach 2: Passing the locale as arguments of the date pipe

The locale used in the the built-in date pipe can be set through the pipe's arguments.

<div>{{ '2021-03-21' | date:undefined:undefined:'en' }}</div>
<!-- output: <div>Mar 21, 2021</div> -->
<div>{{ '2021-03-21' | date:undefined:undefined:'fr' }}</div>
<!-- output: <div>21 mars 2021</div> -->

Same as the previous approach, the static locale can be replaced by value resolved in a service. That way, we can dynamically change the locale without reloading the entire app.

The repetitive syntax makes this approach frustrating in the syntax.

Approach 3: Using a custom pipe localizedDate

The internationalization library ngx-translate provides a translate pipe that can be used as follows.

<div>{{ 'my.traduction.key.for.hello-world' | translate }}</div>
<!-- output (if the current locale is EN): <div>Hello world</div> -->
<!-- output (if the current locale is FR): <div>Bonjour le monde</div> -->

The goal here is to create a localizedDate similar to translate. It will subscribe to any change in the application locale and behave accordingly.

<div>{{ '2021-03-21' | localizedDate }}</div>
<!-- output (if the current locale is EN): <div>Mar 21, 2021</div> -->
<!-- output (if the current locale is FR): <div>21 mars 2021</div> -->

With the localizedDate, we gain in maintainability in the syntaxe and by using a single internationalization library for dates and contents. As others, it has its cost, this approach adds extra computation in the app. More details next in the article.

Our own custom localized date pipe

The idea is the use the built-in date pipe along with ngx-translate. Sounds complicated? Let's figure that out!

@Pipe({
  name: 'localizedDate',
  pure: false
})
export class LocalizedDatePipe implements PipeTransform {

  constructor(private translateService: TranslateService) { }

  transform(value: Date, format = 'mediumDate'): string {
    const datePipe = new DatePipe(this.translateService.currentLang);
    return datePipe.transform(value, format);
  }
}

Yeah, as simple as that. In this implementation, there is:

  • the declaration of the new pipe with @Pipe (note pure: false, we will come back to that later)
  • the injection of the app's TranslateService
  • the transform method of the PipeTransform interface

In the transform method, we are using the service syntax of DatePipe. Hence, we can specify the current locale during instantiation.

Pure vs Impure pipe

Ahmed Ghoul wrote a super article about Pure vs Impure Pipe in Angular. Pure pipes (like the built-in date pipe) are called only if the pipe's inputs changed. On the other hand, impure pipes are called on every change detection cycle.

The localizedDate can not be pure. The main purpose of this pipe is to avoid passing the current locale as input. Therefore, it has to be an impure pipe to listen to the current locale changes triggered by other components.

For the same reason, the translate pipe from ngx-translate is impure as well.

A Stackblitz is worth a thousand words

The source code of the localizedDate in a concrete example can be found here :

The last word

The approach you may want to choose to translate dates depends on your application and needs.

If you have a specific URL per locale and redirect the user when he changes locale, the first approach should work just fine. If you are already using ngx-translate, creating a localizedDate starts to make sense.

To help you with your decision, here is a quick comparison between the three approaches:

Syntaxe Reusability Reload required Performance
Setting LOCALE_ID ⭐️⭐️⭐️ ⭐️⭐️⭐️ YES ⭐️⭐️⭐️
Using date pipe argument - ⭐️ NO ⭐️⭐️⭐️
Custom localizedDate ⭐️⭐️⭐️ ⭐️⭐️⭐️ NO ⭐️

Happy coding 👨🏽‍💻