Multi-Language Date Formats with Angular Material

Kristina Hertmann
Mar 3 · 5 min read

Recently in my project, I needed to implement a scenario where the date format would change with the language. In my dual language website, we used German and English languages. Both use different date formats — Germany uses dates with dot (dd.mm.yyyy), and British English, like most of the Europe, uses strokes (dd/mm/yyyy). The date format needed to change in Angular Material Datepicker, but also in written text that used Angular DatePipe.

I did lot of research, tried multiple ways, and finally achieved the desired behaviour. I used many different examples from various tutorials to figure out how to implement this, and to get it working correctly. Since this solution was not clear or easy to find, I’ve decided to share my method in this article

Date format change with language

Lets get started

What is used:

Angular 2+

Angular Material

First things first, your application should have a working translation system. I’m not going to give a full explanation of how I added my translation, as there’s a great tutorial on Angular.io.

For this example I added 3 different locales to app.module.

registerLocaleData(localeEn, 'en');
registerLocaleData(localeDe, 'de', localeDeExtra);
registerLocaleData(localeFr, 'fr', localeFrExtra);

I then set ‘en’ as my default language.

export class AppModule {
constructor(private translate: TranslateService){
translate.addLangs(['en', 'de', 'fr']);
translate.setDefaultLang('en');
translate.use('en');
}
}

After creating a working translation system, the date format in Angular Material Datepicker or in Angular DatePipe was not following the language, instead using ‘en’ locale.

only language is changing

For Datepicker to follow the language, we need to change its locale. For this to happen, we need to import Angular Material DateAdapter to the component where the language change is happening, and set locale based on the chosen language.

constructor(private translate: TranslateService, private dateAdapter: DateAdapter<Date>) {}useLanguage(language: Languages): void{
this.translate.use(language);
this.dateAdapter.setLocale(language);
}

In this language change function, we change the translation language, but also now the Datepicker locale. The Datepicker format now changes based on locale we are giving.

If your Datepicker has manual date adding enabled, it still uses MDY order, which is used in ‘en’, but both German and French use DMY order. To prevent this from happening, we need a DateAdapter provider for our app.module, and to create our custom datepicker adapter.

providers: [{provide: DateAdapter, useClass: CustomDatePickerAdapter}],

In date-adapter.ts we create our custom date adapter class, that is listening to locale for manual input, and checks the input as well as changes the order if necessary.

export class CustomDatePickerAdapter extends NativeDateAdapter {
parse(value: string | number): Date | null {
if ((typeof value === ‘string’) && (value.indexOf(‘.’) > -1)) {
const str: string[] = value.split(‘.’);
if (str.length < 2 || isNaN(+str[0]) || isNaN(+str[1])
||isNaN(+str[2])) {
return null;
}
return new Date(Number(str[2]), Number(str[1]) — 1,
Number(str[0]));
}
const timestamp: number = typeof value === ‘number’ ? value :
Date.parse(value);
return isNaN(timestamp) ? null : new Date(timestamp);
}
}
Datepicker dateformat follows the language change

Now the only thing left to do is make Angular DatePipe listen to the current language, and adjust to that date format correctly. For this example, I used DatePipe ‘shortDate’.

<p>
{{selectedDate | date: 'shortDate': '': translate.currentLang}} (with DatePipe)
</p>
DatePipe date format follows the language change

One last small thing. Datepicker dateformat is not the same as the DatePipe ‘shortDate’ format. It uses its default locale format. So in order for DatePicker to also follow ‘shortDate’ format, or any other custom format we choose, we need to add formatting to DateAdapter too.

First, in our date-adapter.ts we create our custom date format const. This default const has dateInput already set, but to create our own, we need to change dateInput to a custom one to later use it in formatting.

export const CUSTOM_DATE_FORMATS = {
parse: {
dateInput: {month: 'short', year: 'numeric', day: 'numeric'}
},
display: {
//dateInput: { month: 'short', year: 'numeric', day: 'numeric'},
dateInput: 'customInput',
monthYearLabel: {year: 'numeric', month: 'short'},
dateA11yLabel: {year: 'numeric', month: 'long', day: 'numeric'},
monthYearA11yLabel: {year: 'numeric', month: 'long'},
}
};

In the CustomDatePickerAdapter class that we created before, we format the date also. If the display is ‘customInput’, we add our own custom format, which is a new DatePipe with the format ‘shortDate’.

format(date: Date, display: string | DateDisplay): string {if (display === 'customInput') {
return new DatePipe(this.locale).transform(date, 'shortDate')
} else {
return new DatePipe(this.locale).transform(date, 'MMM yyyy');
}
}

An else statement in that formatting is setting how we see DatePicker inside.

return new DatePipe(this.locale).transform(date, ‘MMM yyyy’);

This and display input can be customized however one likes. Since it is listening to locale, it can have multiple if statements with different returns, so every locale can have a different output.

if (this.locale === 'en') {
return doSomething
}

And lastly, in app.module we need to provide Angular Material Date Formats, and use our custom Date Format that we created instead.

providers: [{provide: DateAdapter, useClass: CustomDatePickerAdapter},{provide: MAT_DATE_FORMATS, useValue: CUSTOM_DATE_FORMATS}],

Testing

To test, it is important to provide DateAdapter with a custom class to the component test where the locale is set.

providers: [{provide: DateAdapter, useClass: CustomDatePickerAdapter}],

Test DateAdapter call with a spy.

let dateAdapter;
dateAdapter = TestBed.get(DateAdapter);
spyOn(dateAdapter, 'setLocale');
it('should test useLanguage function when language is de', () => {
component.useLanguage('de');
expect(dateAdapter.setLocale).toHaveBeenCalledWith('de');
});

And that’s it! Now we have a working Angular project where the language change also changes date formats.

I hope this was helpful for someone, as when I did this, I struggled to find good example.

Working StackBlitz for this example can be found HERE.

^^^