Reputation: 9511
I want to extend ngx-translate's pipe to make it potentially multi purpose within my app.
My pipe:
import { Pipe, PipeTransform } from '@angular/core';
import { TranslatePipe } from "@ngx-translate/core";
@Pipe({
name: 'msg'
})
export class MsgPipe extends TranslatePipe implements PipeTransform {
transform(value: any, args: any[]): any {
return super.transform(value, args)
}
}
In order to wait for the relevant translate modules to load, I have used Angular's APP_INITIALIZER:
app.module:
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { HttpClientModule, HttpClient } from '@angular/common/http';
import { TranslateHttpLoader } from '@ngx-translate/http-loader';
import { appRoutes } from './app.routes';
import { AppComponent } from './root/app.component';
import { PageNotFoundComponent } from './shared/components/page-not-found/page-not-found.component';
import { HomePageComponent } from './shared/components/home-page/home-page.component';
import { MsgPipe } from './shared/pipes/msg.pipe';
import { ChangeDetectorRef } from '@angular/core';
import { TranslateModule, TranslateLoader } from "@ngx-translate/core";
import { Injector, APP_INITIALIZER } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
import { LOCATION_INITIALIZED } from '@angular/common';
export function HttpLoaderFactory(http: HttpClient) {
return new TranslateHttpLoader(http);
}
export function appInitializerFactory(translate: TranslateService, injector: Injector) {
return () => new Promise<any>((resolve: any) => {
const locationInitialized = injector.get(LOCATION_INITIALIZED, Promise.resolve(null));
locationInitialized.then(() => {
const langToSet = 'en'
translate.setDefaultLang('en');
translate.use(langToSet).subscribe(() => {
console.info(`Successfully initialized '${langToSet}' language.'`);
}, err => {
console.error(`Problem with '${langToSet}' language initialization.'`);
}, () => {
resolve(null);
});
});
});
}
@NgModule({
declarations: [
AppComponent,
PageNotFoundComponent,
HomePageComponent,
MsgPipe
],
imports: [
BrowserModule,
RouterModule.forRoot(appRoutes),
HttpClientModule,
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useFactory: HttpLoaderFactory,
deps: [HttpClient]
}
}),
],
providers: [
{
provide: APP_INITIALIZER,
useFactory: appInitializerFactory,
deps: [TranslateService, Injector],
multi: true
}
],
bootstrap: [AppComponent]
})
export class AppModule { }
(The code above was taken from here)
My pipe still doesn't work unless pure is set to false, causing multiple unnecessary calls. No errors, it just doesn't change the content.
Upvotes: 3
Views: 9054
Reputation: 1266
The TranslatePipe
from ngx-translate
is unpure. And that for a reason. So you have to set pure
to false
too:
@Pipe({
name: 'msg',
pure: false
})
Now although this would be enough for you to work with, I'll extend my answer a bit.
The translate pipe has not been engineered to translate only once and get done. Even if you preload the translations for one or even more languages, the user may switch the language later on. In reality you have to handle 3 different events as of today:
Now, if you look at the source of the original translation pipe, you'll see, they already update their translated value on these events. Every event will call the updateValue()
method. See https://github.com/ngx-translate/core/blob/master/projects/ngx-translate/core/src/lib/translate.pipe.ts. The updateValue()
then would mark the pipe as dirty to request a rerendering.
Upvotes: 0
Reputation: 283
Example of pipe which accepts array of string enums and transforms it into array of translated strings
import { ChangeDetectorRef, Pipe, PipeTransform } from '@angular/core';
import { TranslatePipe, TranslateService } from '@ngx-translate/core';
import { AlertEnum } from '../alert.enum';
@Pipe({
name: 'translateAlertArray',
pure: false
})
export class TranslateErrorsArrayPipe
extends TranslatePipe
implements PipeTransform {
constructor(
private translateService: TranslateService,
private changeDetectionRef: ChangeDetectorRef
) {
super(translateService, changeDetectionRef);
}
transform(alerts: any): string[] {
return alerts?.map((alert: AlertEnum) =>
super.transform('alerts.' + AlertEnum[alert])
);
}
}
Upvotes: 0
Reputation: 21
the translatepipe itself is impure (see: https://github.com/ngx-translate/core/blob/master/projects/ngx-translate/core/src/lib/translate.pipe.ts), because it needs to react on changes to the translations-observable.
you should also just call super.transform(key, ...args) instead of instant(...). this approach worked in our project. or please state why you need to use instant instead.
Upvotes: 0
Reputation: 1333
If you do the pipe impure works... but I think that is not the best way to solve that.
@Pipe({
name: 'msg',
pure: false
})
export class TranslationPipe extends TranslatePipe {
Upvotes: 2
Reputation: 2930
You do not any extra library for translations. As an example, you only need a pipe file translation-pipe.ts
file and translation.provder.ts
file and you can extend whatever you want. Please check the below files
translation-pipe.ts
import { Pipe, PipeTransform } from '@angular/core';
import {TranslateProvider} from '../../providers/translate/translate';
@Pipe({
name: 'translation',
})
export class TranslationPipe implements PipeTransform {
constructor(private _translateService: TranslateProvider) { }
transform(value: string, ...args) {
return this._translateService.instant(value);
}
}
TranslateProvider.ts
import {Injectable} from '@angular/core';
import {LANG_EN_TRANS} from './languages/en';
import {LANG_TR_TRANS} from './languages/tr';
@Injectable()
export class TranslateProvider {
private _currentLang: string;
// If you want to use dictionary from local resources
private _dictionary = {
'en': LANG_EN_TRANS,
'tr': LANG_TR_TRANS
};
// inject our translations
constructor( private _db: DbProvider) { }
public get currentLang() {
if ( this._currentLang !== null) {
return this._currentLang;
} else return 'en';
}
public use(lang: string): void {
this._currentLang = lang;
} // set current language
// private perform translation
private translate(key: string): string {
// if u use local files
if (this._dictionary[this.currentLang] && this._dictionary[this.currentLang][key]) {
return this._dictionary[this.currentLang][key];
} else {
return key;
}
// if u do not want local files then get a json file from database and add to dictionary
}
public instant(key: string) { return this.translate(key); }
}
Here private translate()
function is main function to change the local key to language. You can import local files like en.ts, sp.ts, au.ts etc. or you can modify this function to connect database and get the key value pairs...
Example of local translation file is
en.ts
export const LANG_EN_TRANS = {
'about': 'About',
}
or
tr.ts
export const LANG_TR_TRANS = {
'about': 'Hakkinda',
}
Have a nice coding...
Upvotes: 1