Reputation: 1376
I currently have apps built in angular 4 and would like to upgrade to angular 5. I was reading about how they handle locale in their pipes and it seems like upgrading is no longer an option. From what I can tell they expect you to either 1. manually import a different culture 2. build a separate app for each culture
The problem is that I work for an international company that supports 351 different cultures and we have 15 apps. Is the angular team really saying I have to now build 5265 different applications if I want to keep upgrading as they do? If I went to my boss with that I would probably be thrown out of the room.
In our angularJs apps we just downloaded the $locale service we needed at runtime when the user logged in and set the provider to that. is there nothing like that for Angular? If not I am not sure how an enterprise level app could ever use this language unless the devs were lucky enough to only have to support one culture.
Upvotes: 3
Views: 6047
Reputation: 41
i was facing the same problem, and found a workaround.
I decided to publish angular locales data (coming from cldr) in an asset folder. Cldr data come from node_mopdules\@angular\common\locales (what you would have imported in typescript). Just before the bootstrap, using APP_INITIALIZER, i load the currect culture data from assets given a specific locale_id.
The trick is to avoid using the import
key word, because dynamic imports are not support in EC2015.
To get the culture data from the js file, i wrote this 'dirty' code :
import { Injectable, Inject, LOCALE_ID } from '@angular/core';
import { HttpClient } from '@angular/common/http';
@Injectable()
export class LoaderService {
constructor(protected httpClient: HttpClient,
@Inject(LOCALE_ID) protected locale: string) { }
private async loadAngularCulture(locale) {
let angularLocaleText = await this.httpClient.get(`assets/angular-locales/${locale}.js`).toPromise();
// extracting the part of the js code before the data,
// and i didn't need the plural so i just replace plural by null.
const startPos = angularLocaleText.indexOf('export default ');
angularLocaleText = 'return ' + angularLocaleText.substring(startPos + 15).replace('plural', null);
// The trick is here : to read cldr data, i use a function
const f = new Function(angularLocaleText);
const angularLocale = f();
// console.log(angularLocale);
// And now, just registrer the object you just created with the function
registerLocaleData(angularLocale);
}
And is my mainModule, using the APP_INITIALIZER :
export function configFactory(intlLoader: SfkIntlLoaderService) {
return intlLoader.initializer();
}
export function localFunction() {
// the rule to get you language according.
// Here, for demo, i just read it from localstorage
return localStorage.getItem('LANGUGAGE');
}
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
HttpClientModule
)
],
providers: [
LoaderService,
{
provide: LOCALE_ID,
deps: [],
useFactory: localFunction
},
{
provide: APP_INITIALIZER,
useFactory: configFactory,
deps: [LoaderService],
multi: true
}
],
bootstrap: [AppComponent]
})
export class AppModule { }
Hope this will help !
Upvotes: 0
Reputation: 1376
After communicating on github about this issue an idea given that have started using is to create a service that I call in APP_INITIALIZER which you can see below. Because of the dynamic string at build time the angular-cli will create a chunk for every culture and then load the correct one at run time once I supply the culture string I received from a service call.
setLocale(localeId: string): Promise<any> {
this.culture = localeId;
return new Promise((resolve, reject) => {
System.import(`@angular/common/locales/${localeId}.js`).then(
module => {
registerLocaleData(module.default);
resolve();
},
() => {
System.import(`@angular/common/locales/${localeId.split('-')[0]}.js`).then(module => {
registerLocaleData(module.default);
resolve();
}, reject);
}
);
});
}
Upvotes: 2
Reputation: 19288
I had the same problem with angular4 I wanted to use AOT with multple languages. This is what I did:
Simple wrapper around a pipe to set the locale.
@Pipe({name: 'datepipe', pure: true})
export class MyDatePipe extends DatePipe implements PipeTransform {
constructor(private win: WindowRef) {
super(win.ln);
}
transform(value: any, pattern?: string): string | null {
return super.transform(value, pattern);
}
}
Service for the locale:
function _window(): any {
// return the global native browser window object
return window;
}
@Injectable()
export class WindowRef {
get nativeWindow(): any {
return _window();
}
//default locale
public ln = 'en';
constructor() {
try {
if (!isNullOrUndefined(this.nativeWindow.navigator.language) && this.nativeWindow.navigator.language !== '') {
this.ln = this.nativeWindow.navigator.language;
}
}finally {}
}
}
This worked for angular4 but with angular5 I had to import the supported locales in my main.ts
import { registerLocaleData } from '@angular/common';
import localeFr from '@angular/common/locales/fr';
registerLocaleData(localeFr);
Upvotes: 0