Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dynamically change locale for DatePipe in Angular 2

I'm making an Angular project where the user has the ability to switch languages. Is it possible to make the locale dynamic?

I have seen that you can add it in the NgModule but i'm guessing it's not dynamic when i put it there? Or can i change it somehow through a service or something?

like image 803
Martijn van den Bergh Avatar asked May 31 '17 14:05

Martijn van den Bergh


People also ask

What is locale in DatePipe?

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.

Which one of the following angular pipes formats a date value according to the locale settings?

The data transformation pipes use the LOCALE_ID token to format data based on rules of each locale. Formats a date value.

What is locale in angular?

Angular uses the Unicode locale identifier (Unicode locale ID) to find the correct locale data for internationalization of text strings. Unicode locale ID. A locale ID conforms to the Unicode Common Locale Data Repository (CLDR) core specification.


3 Answers

To set locale from service you need to add LOCALE_ID provider with factory to app.module, like in @AmolBhor answer

{   provide: LOCALE_ID,   deps: [SettingsService],      //some service handling global settings   useFactory: (settingsService) => settingsService.getLanguage()  //returns locale string } 

Unfortunately you cannot change language for DatePipe JIT. Angular compiler requires LOCALE_ID during bootstrapping.

There are some bug reports for Angular:

  • https://github.com/angular/angular/issues/15039 (closed - not resolved)
  • https://github.com/angular/angular/issues/16960 (closed with workaround in comments)

There are several workarounds for this:

Workaround #1

Re-bootstrapping angular module:

let _platformRef: NgModuleRef<Object>; if(_platformRef) { _platformRef.destroy(); } platformBrowserDynamic(providers)     .bootstrapModule(AppModule, {providers})     .then(platformRef => {         _platformRef = platformRef;     }) 

*This won't work for Hybrid Angular/AngularJS as there is no way do destroy AngularJS using UpgradeModule.

Workaround #2

To overwrite DatePipe, NumberPipe - whatever You need:

@Pipe({name: 'datepipe', pure: true}) export class MyDatePipe implements PipeTransform {   transform(value: any, pattern?: string): string | null {     // transform value as you like (you can use moment.js and format by locale_id from your custom service)     return DateUtils.format(value);   } } 

Workaround #3

To use library which already handle localization with custom Pipes for ex:

  • https://github.com/urish/angular2-moment
  • https://github.com/robisim74/angular-l10n

Workaround #4

Every pipe which use LOCALE_ID has private field locale or _locale, so You may override this field at that pipes on language change, as there is one instance of pipe.

That will work because TypeScript is just syntax sugar for JavaScript. And in JavaScript there are no private fields.

Also remember to process change detection in application by using tick() method in ApplicationRef.

@Injectable() export class DynamicLocaleService {   private i18nPipes: PipeTransform[];    constructor(     datePipe: DatePipe,     currencyPipe: CurrencyPipe,     decimalPipe: DecimalPipe,     percentPipe: PercentPipe,     private applicationRef: ApplicationRef,   ) {     this.i18nPipes = [       datePipe,       currencyPipe,       decimalPipe,       percentPipe,     ]   }    setLocale(lang: string): void {     this.i18nPipes.forEach(pipe => {       if(pipe.hasOwnProperty("locale")) {         pipe["locale"] = lang;       } else if (pipe.hasOwnProperty("_locale")) {         pipe["_locale"] = lang       }     })     this.applicationRef.tick()   } } 

Workaround #5

To reload application when language is changed.

window.location.reload() 

Unfortunately all of above are workarounds.

But there is also another solution - you can have multiple bundles for each language, which probably will be better approach as app will be faster. But this solution is not applicable for every application and doesn't answer the question.

like image 55
Anton Temchenko Avatar answered Sep 22 '22 12:09

Anton Temchenko


Using providers you can change your default locale in your NgModule. to do this You need to import LOCALE_ID from angular/core and fetch your locale language to pass the same to providers.

import { LOCALE_ID } from '@angular/core';  @NgModule({     imports: [//your imports],     providers: [         { provide: LOCALE_ID, useValue: "en-US" }     ] })  ... ... {   provide: LOCALE_ID,   deps: [SettingsService],      //some service handling global settings   useFactory: (settingsService) => settingsService.getLanguage()  //returns locale string } 

Hope this will help you.

like image 39
Amol Bhor Avatar answered Sep 21 '22 12:09

Amol Bhor


Have your Service like

import { Injectable } from '@angular/core';

@Injectable()
export class LocaleService {

  //Chosse Locale From This Link
  //https://github.com/angular/angular/tree/master/packages/common/locales
  constructor() { }

  private _locale: string;

  set locale(value: string) {
    this._locale = value;
  }
  get locale(): string {
    return this._locale || 'en-US';
  }

  public registerCulture(culture: string) {
    debugger;
    if (!culture) {
      return;
    }
    switch (culture) {
      case 'en-uk': {
        this._locale = 'en';
        console.log('Application Culture Set to English');
        break;
      }
      case 'zh-hk': {
        this._locale = 'zh-Hant';
        console.log('Application Culture Set to Traditional Chinese');
        break;
      }
      case 'zh-cn': {
        this._locale = 'zh-Hans';
        console.log('Application Culture Set to Simplified Chinese');
        break;
      }
      default: {
        this._locale = 'en';
        console.log('Application Culture Set to English');
        break;
      }
    }
  }
}

And in App.module.ts

first import localization that you need , say

import localeEN from '@angular/common/locales/en';
import localezhHant from '@angular/common/locales/zh-Hant';
import localezhHans from '@angular/common/locales/zh-Hans';

Than under providers section

{
  provide: LOCALE_ID,
  deps: [LocaleService],
  useFactory: (LocaleService: { locale: string; }) => LocaleService.locale
}

At the end

registerLocaleData(localeEN);
registerLocaleData(localezhHant);
registerLocaleData(localezhHans);

Hope it will help someone

if you want to change locale dynamically , inject LocaleService in your desired component and use registerCulture method , pass your required culture into this

like image 29
Saurabh Avatar answered Sep 25 '22 12:09

Saurabh