Sasxa
Sasxa

Reputation: 41294

Using Renderer to do i18n?

I read recently very interesting article by @yearofmoo about Angular2 Renderer. It gave me an idea that it might be possible to do i18n with Renderer. Basically use this function:

  createText(parentElement: any, value: string): any {
    // this is called for every text node in the template
  }

and simply transform value by mapping it to different language:

let es = { "Hello": "Hola" }

value = "Hello"
value = es[value]

I looked briefly at the issues and design docs, but before going down this rabbit hole I wanted to check if anyone has any experience with this.

Are there any issues that could prevent this from working? Some breaking changes on the way I'm not aware of? Opinions about this approach?

Upvotes: 7

Views: 285

Answers (2)

Jay
Jay

Reputation: 323

This is an old question but maybe others will stumble across it. :)

Also I realize Angular has built-in i18n mechanisms, but we didn't want to use them for a few reasons. Primarily because we already have thousands of translated phrase mappings in another product, and we want to reuse all that. So we convert the existing mappings into json, and then in our angular project:

  1. Create a service (e.g. I18nService) with a function like:
export class I18nService {
    phraseMap: Map<string, string>; // fill this however you like

    translatePage(elements: QueryList<ElementRef>, renderer: Renderer2) {
        elements.forEach((el: ElementRef) => {
           let key:string = el.nativeElement.innerText;
           let phrase = this.phraseMap.get(key);
           if (phrase) {
              renderer.setProperty(el.nativeElement, 'innerText', phrase);
           }
        }
    }

// ...

this.phraseMap is a Map<string, string> with values like key: "PH_HELLO" value: "Bonjour"

In your component template, decorate your elements with a template variable (I used #i18n but can be anything), and replace the text with your phrase key, for example:

   <span #i18n>PH_HELLO</span>
   <div #i18n>PH_LOGOUT</div>

In your component class, collect all the appropriate elements using ViewChildren. Inject your service and an Angular Renderer2, and implement AfterViewInit:

export class MyComponent implements AfterViewInit {

   @ViewChildren('i18n', {read:ElementRef}) i18nElements: QueryList<ElementRef>;

   constructor(
      private i18nService: I18nService,
      private renderer: Renderer2 // from @angular/core
   ) {}

   ngAfterViewInit() {
      this.i18nService.translatePage(this.i18nElements, this.renderer);
   }

Upvotes: 0

Maarten Mensink
Maarten Mensink

Reputation: 67

We use Renderer to set a translation that has been provided by a attribute.

import { Directive, ElementRef, Input, Renderer, OnInit} from '@angular/core';
import { TranslateService } from './translate.service';

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

    @Input() translate :string;

   constructor(private el: ElementRef, private renderer: Renderer, private _translateService : TranslateService) {}

   ngOnInit(): void {
        this.renderer.setText(this.el.nativeElement,this._translateService.instant(this.translate));
   }
}

See plunker for demo

Hopefully this is what you where looking for.

Upvotes: 1

Related Questions