1

Hi guys!

I'm learning AngularJS 2 for a while now and now creating my own app based on Laravel 5 REST API. Anyway - that isn't very important atm.

What is important is that I want to provide the translation for the whole application and I found an issue that is hard to solve for me.

So - from the beginning... I'm created my ResourcesService that's translating the string:

getTranslation ( key: string, replace: Array<TranslationReplace> = null, locale: string = null, fallback: boolean = null ): Observable<Resource> {

    var params = "key=" + key +
        ( replace ? "&replace=" + JSON.stringify(replace) : '') +
        ( locale ? "&locale=" + locale : '') +
        ( fallback ? "&fallback=" + fallback : '');
    var headers = new Headers({'Content-Type':'application/x-www-form-urlencoded'});

    return this.http.post(this.apiUrl + 'getTranslation', params, {headers: headers})
        .map(this.extractData)
        .startWith({ name: 'Loading...', value: 'Translating...' })
        .catch(this.handleError);

}

And I created a TranslateComponent that's providing the translation, here's the whole component:

import {Component, Input, Injectable, OnInit, OnChanges, SimpleChange} from "@angular/core";
import {ResourcesService} from "../services/resources.service";
import {TranslationReplace} from "../models/TranslationReplace";

@Component({
    selector: 'translate',
    template: `{{translation}}`
})

@Injectable()
export class TranslateComponent implements OnInit, OnChanges {

    @Input() ref: string;
    @Input() replace: Array<TranslationReplace>;
    @Input() locale: string;
    @Input() fallback: boolean;

    private translation: string;
    constructor(private resourcesService: ResourcesService) {}

    ngOnInit() : void {
        this.getTranslation();
    }

    ngOnChanges(changes: {[propKey: string]: SimpleChange}) {
        for (let propName in changes) {
            if(propName == 'replace') {
                this.getTranslation();
            }
        }
    }

    private getTranslation(): void {
        this.resourcesService.getTranslation(this.ref, this.replace, this.locale, this.fallback).forEach(translation => this.translation = translation.value );
    }
}

All is working just perfect and to call for the translation I have to simply call the selector like that:

<translate [ref]="'string.to_translate'"></translate>

But...

Now I'd like to use the translation in the attribute.

So I found the ugly way to achieve it by creating the reference of the translation and the call it in the attribute. But it's very nasty...

First of all I need to add this bit to my template:

<translate [ref]="'string.to_translate'" style="display:none;" #myStringTranslation></translate>

And next in my element call it and ask for the property by the reference:

<input type="text" [(ngModel)]="input" #input="ngModel [placeholder]="myStringTranslation.translation">

And I really don't like the idea.

What I'm looking for is to call it somehow, I don't know... emit it? And make it looks better. Don't create extra elements.

So my question is: Can I do it better? Can I somehow call the translation directly from the attribute without the reference?

** ----- UPDATE ----- **

Ok, I learn my lesson :) Thanks to Meir for showing me the right direction and also the Angular.io site for the tutorials.

So finally I added a TranslateDirective to my application:

import {Directive, Input, ElementRef, OnChanges, OnInit, SimpleChange, Renderer} from "@angular/core";
import {TranslationReplace} from "../models/TranslationReplace";
import {ResourcesService} from "../services/resources.service";

@Directive({
    selector: '[translate]'
})
export class TranslateDirective implements OnInit, OnChanges {

    @Input('translate') ref: string;
    @Input('translateReplace') replace: Array<TranslationReplace>;
    @Input('translateLocale') locale: string;
    @Input('translateFallback') fallback: boolean;
    @Input('translateAttr') attr: string;

    private translation: string;

    constructor(
        private elRef: ElementRef,
        private renderer: Renderer,
        private resourcesService: ResourcesService
    ) {}

    ngOnInit():void {
        this.getTranslation();
    }

    ngOnChanges(changes: {[propKey: string]: SimpleChange}):void {
        for (let propName in changes) {
            if(propName == 'replace') {
                this.getTranslation();
            }
        }
    }

    private getTranslation(): void {
        if(this.attr)
            this.resourcesService.getTranslation(this.ref, this.replace, this.locale, this.fallback).forEach(translation =>
            {
                this.translation = translation.value;
                this.renderer.setElementAttribute(this.elRef.nativeElement,this.attr,this.translation);
            });
    }
}

And now can easily add the translations to the attributes like that:

<input type="text" [(ngModel)]="input" #input="ngModel [translate]="'string.to_translate'" [translateAttr]="'placeholder'">

Thanks for your help!!

1
  • There's no 'AngularJS 2', the name applies to Angular 1.x. Commented Nov 2, 2016 at 12:40

1 Answer 1

2

You can turn it into an attribute directive:

@Directive({
  selector: 'translate'
})
export class TranslateDirectiev {
  @Input() translate: string;

  constructor(private elRef: ElementRef){}

  ngOnChanges(changes: SimpleChanges): void {
       if(this.translate){
          var translatedText: string = translateSvc.translate(this.translate);
          this.renderer.setElementProperty(this.elementRef.nativeElement, 'innerHTML', translatedText);
      }
  }

}

This is a simple example without the service injection. Also, for input fields you might need to have a different approach and update the value attribute and not the innerHtml

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks! Your answer and this angular.io/docs/ts/latest/guide/attribute-directives.html help me solve the issue. Many thanks, buddy!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.