To set locale from a service, you need to add the LOCALE_ID
provider with the factory to app.module
, like in Amol Bhor's answer:
{
provide: LOCALE_ID,
deps: [SettingsService], //some service handling global settings
useFactory: (settingsService) => settingsService.getLanguage() //returns locale string
}
Unfortunately, you cannot change the language for DatePipe JIT. The Angular compiler requires LOCALE_ID
during bootstrapping.
There are some bug reports for Angular:
There are several workarounds for this:
Workaround #1
Re-bootstrapping the 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 isn't any 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 a library which already handles localization with custom Pipes, for example:
Workaround #4
Every pipe which uses LOCALE_ID
has a private field locale or _locale, so you may override this field at those pipes on a language change, as there is one instance of the pipe.
That will work because TypeScript is just syntactic sugar for JavaScript. And in JavaScript there aren’t any private fields.
Also remember to process the change detection in the application by using the 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 the application when the language is changed.
window.location.reload()
Unfortunately, all of the above are workarounds.
But there is also another solution. You can have multiple bundles for each language, which probably will be a better approach as the app will be faster. But this solution is not applicable for every application and doesn't answer the question.