Internationalization in Angular (i18n) and the powerful *ngFor directive

Within my Angular application, I have a basic component that takes a list of strings and generates a radio group based on these strings:

@Component({
  selector: 'radio-group',
  templateUrl: 
           `<div *ngFor="let item of items">
                 <label for="input_{{item}}">{{item}}</label>
                 <input type="radio" id="input_{{item}}" value="{{item}}" [formControl]="radioControl">
           </div>`,
  styleUrls: ['./radio-group.component.scss']
})
export class RadioGroupComponent {
    @Input()
    items: string[];
    @Input()
    radioControl: FormControl;
}

I am looking to internationalize the labels within the radio group.
The official i18n documentation provided by Angular focuses mainly on static HTML.

Is there a way to apply internationalization to dynamic components (such as entries generated with *ngFor) using i18n template translation?

While I am familiar with the approach using ngx-translate, I am specifically interested in utilizing i18n.

Answer №1

After searching for a solution and wanting to avoid using ngx-translate, I came across ocombe, who appears to be leading the way in Angular's internationalization efforts. In GitHub issue #16477, ocombe shared a roadmap for i18n in Angular.

▢ Utilize translation strings outside of a template - #11405 [dependent on runtime i18n]

This feature is not currently integrated into Angular but is in the works. The roadblock preventing its implementation is making progress:

▢ Runtime i18n (one bundle for all locales with AOT) - [in progress]

I vaguely recall reading that ocombe mentioned they aim to introduce this feature in Angular 5, possibly before spring 2018 (according to the Angular roadmap which indicates Angular 6 will debut in spring 2018)

Date: March/April 2018; Stable Release: 6.0.0

As of today, ocombe just posted this update:

I'm not overseeing this feature at present. Given the holiday break, more information will be provided when available

If you can't wait, your best option is to utilize ngx-translate or keep track of these GitHub issues #11405 & #16477 for updates until the feature is released, hopefully by early 2018 :)

PS: It seems dynamic translation is also on their agenda, though it may not be ready until Angular 6.

UPDATE:

It has been confirmed: Runtime i18n won't be rolled out until after Angular 6 (refer to: 11405#issuecomment-358449205)

EDIT:
I stumbled upon a workaround for this. You can create hidden div tags for your translations and store them in a map using ViewChildren for future use. You could even subscribe to these elements for updates.

@ViewChildren('test') myChildren; // the class needs to implement AfterViewInit
myMap = new Map();
ngAfterViewInit() {
  this.myChildren.forEach(value => this.myMap.set(value.nativeElement.getAttribute('name'), value.nativeElement.innerHTML));
  console.log(this.myMap);
}

Answer №2

If you're working with Angular 9, there's a useful global function called $localize that allows you to translate strings easily. Just use it like this:

$localize`String to be translated`

I found a really informative article on this topic:

Answer №3

Currently, I am utilizing @ngx-translate for translation purposes. My approach involves declaring the content to be translated in both language files - specifically, es.json and en.json.

es.json:


        "CONTENTS": {
    "CONT_1": "TEXTO_1",
    "CONT_2": "TEXTO_1",
    "CONT_3": "TEXTO_1",
    "CONT_4": "TEXTO_1",
    "CONT_5": "TEXTO_1",
    "CONT_6": "TEXTO_1",
    "CONT_7": "TEXTO_1",
    "CONT_8": "TEXTO_1",
    "CONT_9": "TEXTO_1",
    "CONT_10": "TEXTO_1",
    "CONT_11": "TEXTO_1"
}

en.json:


    "CONTENTS": {
    "CONT_1": "TEXT_1",
    "CONT_2": "TEXT_1",
    "CONT_3": "TEXT_1",
    "CONT_4": "TEXT_1",
    "CONT_5": "TEXT_1",
    "CONT_6": "TEXT_1",
    "CONT_7": "TEXT_1",
    "CONT_8": "TEXT_1",
    "CONT_9": "TEXT_1",
    "CONT_10": "TEXT_1",
    "CONT_11": "TEXT_1"
}

Within the component where translation is desired (e.g., contents.component.ts), the following steps are taken: i) an array of contents is declared; ii) the translate Service is injected; iii) it is set; iv) a loop is initiated to construct an object as follows,


import { Component, OnInit } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
@Component({
  selector: 'app-contents',
  templateUrl: './contents.component.html',
  styleUrls: ['./contents.component.css']
})
export class ContentsComponent implements OnInit {
  // i) Declare an array
  public contents = [];
  // inject the translateService
  constructor(public translateService: TranslateService) {
    // iii) set it
    translateService.addLangs(['es', 'en']);
    translateService.setDefaultLang('es');
    const browserLang = translateService.getBrowserLang();
    translateService.use(browserLang.match(/es|en/) ? browserLang : 'es');
    // loop for build a content object and push to the array
    for (let index = 1; index <= 11; index++) {
      const contentObj = {
        id: index,
        content: 'CONTENTS.CONT_' + index.toString()
      };
      this.contents.push(contentObj);
    }
  }
  ngOnInit() {
  }
}

Ultimately, the structure of my contents.component.html appears as follows

<ul>
    <li *ngFor= "let content of contents" >
        {{  content.content | translate }}
    </li>
</ul>

This code can be further refined and optimized for enhanced performance. I trust that you will find these insights valuable.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Is there a way to incorporate a loading spinner into a MaterialUI DataTable without having to specify a fixed height for the parent component?

Currently, I am using a MaterialUI DataTable with the following setup: <div style = {{height: 300}}> <DataGrid loading={true} getRowHeight={() => "auto"} getEstimatedRowHeight={() => 250} ...

Utilizing the power of Angular 4 in combination with mailgun

I need assistance with setting up an email form on my website using Angular 4 and Mailgun as the mail service. I have a method in my mail service file to send messages, but I keep encountering a Bad Request error stating that 'from' is not presen ...

"Encountered an issue with Next-Auth session returning as undefined in getServerSideProps using NextJS version 13.2

When inspecting the code below, session is found to be undefined upon logging from the client side after being transferred from getServerSideProps. import { getServerSession } from 'next-auth/next'; import { authOptions } from './api/auth/[. ...

Error: The OOP class value for translateX in the Web Animation API is returning as undefined

I'm currently working on a basic animation project using JavaScript. I have utilized the Animation class from the Web Animation API. My goal is to create multiple instances of this class in order to animate different elements with varying values and r ...

Is there a way to imitate a method that initiates an AJAX request?

I am currently working on writing tests for my Angular application and I need to mock a method in order to avoid making actual requests to the server. Within my grid.service.ts file, here is the method I am trying to mock: loadAccountListPromise(id: str ...

How to declare and initialize a variable in Angular 2 using TypeScript

I'm currently working with angular 2 and I'm having trouble understanding how to set a variable. The variable _isLoading is being set in "this.observable.filter((event)" but it doesn't seem to change in the template. This is my TypeScript co ...

Creating a personalized menu using Nextron (electron) - Step by step guide

I'm currently in the process of developing an app with Nextron (Electron with Nextjs and Typescript). Although I have the foundation of my Next app set up, I've been encountering issues when attempting to create a custom electron menu bar. Every ...

Angular validation for password and confirmation password fields

I have been working on implementing password and confirm password validation within an angular project. I recently came across a helpful answer on this thread Confirm password validation in Angular 6 that I tried to follow. Unfortunately, I am encountering ...

Identify the category of the component

Using a Link component from version 4.0.0-beta.2, I am exploring its capability to override the root element with a field called component. My goal is to wrap the Link component in a new component called MyLink and pass a custom component through props: ...

Can you use the useCallback function within a nested callback function?

within component A: const retrieveOnClick = useCallback( (rec: GenericRec): (() => void) => () => { setSelectedRecord(rec); }, [], ); inside component B which is a child of A: const displayRecord = useCallback( (row: Row& ...

Selecting a radio button by clicking on its corresponding label within an Angular Material dialog

I recently implemented a custom rating bar in Angular, which worked perfectly fine in a basic component. However, when I tried to move it to a MatDialog component, I encountered some issues. In the initial setup, my input was set to display: none so that t ...

The NgRx Effect unit test revealed that the actions$ stream does not trigger any effects

Upon running my unit test, I observed that Jasmine logged a message indicating that there were no expectations in my test. This led me to realize that the unit test was not functioning properly as the action did not seem to trigger the expected effect. It ...

The test session failed to launch due to an error in initializing the "@wdio/cucumber-framework" module. Error message: [ERR_PACKAGE_PATH_NOT_EXPORTED]

I added @wdio/cli to my project using the command 'npm i --save-dev @wdio\cli'. Next, I ran 'npx wdio init' and chose 'cucumber', 'selenium-standalone-service', 'typescript', 'allure' along w ...

Exploring Angular 2 Routing across multiple components

I am facing a situation where I have an app component with defined routes and a <router-outlet></router-outlet> set. Additionally, I also have a menu component where I want to set the [routerLink] using the same routes as the app component. How ...

The alignment of the first and second steps in Intro.js and Intro.js-react is off

There seems to be an issue here. Upon reloading, the initial step and pop-up appear in the upper left corner instead of the center, which is not what I anticipated based on the Intro.js official documentation. https://i.stack.imgur.com/ICiGt.png Further ...

'This' loses its value within a function once the decorator is applied

Scenario: Exploring the creation of a decorator to implement an interceptor for formatting output. Issue at Hand: Encountering 'this' becoming undefined post application of the decorator. // custom decorator function UseAfter(this: any, fn: (.. ...

Implementing redux-persist with redux toolkit using TypeScript

Currently, I have been utilizing Redux Persist in my next js application but now I am interested in incorporating redux toolkit with TypeScript. While I have managed to grasp the syntax for implementing redux-persist in redux toolkit, I am struggling to ...

Compilation failure due to Typescript initialization issue

Encountering a TypeScript error in my IntelliJ-Idea 2017.1.1 IDE I have enabled JavaScript, NodeJS, and TypeScript Compiler. I have exhausted all solutions but the issue persists, perhaps I am missing something. Error: Initialization error (typescript ...

Is it Feasible to Use Component Interface in Angular 5/6?

My main goal is to create a component that can wrap around MatStepper and accept 2..n steps, each with their own components. In other languages, I would typically create an interface with common behavior, implement it in different components, and then use ...

Combining two streams in RxJS and terminating the merged stream when a particular input is triggered

I am currently developing an Angular application and working on implementing a system where an NGRX effect will make requests to a service. This service will essentially perform two tasks: Firstly, it will check the local cache (sqlite) for the requested ...