How to Create a Localized Date Pipe in Angular 📅
Mamadou Ouologuem5 min read
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
(notepure: false
, we will come back to that later) - the injection of the app’s
TranslateService
- the
transform
method of thePipeTransform
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 👨🏽💻