Localization In Angular 8 Employee App

Introduction

 
We will create an Employee application using Angular CLI commands and will add i18n attributes for major labels in HTML files for localization. Later, we will create a translation source file and translation content files for Hindi and Malayalam languages. We are using i18n internationalization and localization for displaying application in different languages. You can get more details on i18n internationalization from this official angular document.
 

Create Employee application with Angular CLI

 
We can use the below command to create a new employee application in angular CLI
 
ng new employeelocalization
 
It will take some moments to install all node dependencies to our project. After some time, our new project will be ready.
 
We need bootstrap, font-awesome and angular-in-memory-web-api libraries in our project. We can add all these libraries one by one.
  • npm i bootstrap
  • npm i font-awesome
  • npm i angular-in-memory-web-api
We have successfully added required three libraries to our project. You can see the new entries in the package.json file as well.
 
We can create Home component now. You can use below CLI command to create a new component.
 
ng g c home
 
Above command is the shortcut for ng generate component home
 
Copy the below code and paste to home.component.html file.
 
home.component.html
  1. <div style="text-align:center">  
  2.     <h1 i18n="@@welcome">  
  3.       Welcome to Employee Application!  
  4.     </h1>  
  5.     <img width="200" alt="Angular Logo"  
  6.       src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTAgMjUwIj4KICAgIDxwYXRoIGZpbGw9IiNERDAwMzEiIGQ9Ik0xMjUgMzBMMzEuOSA2My4ybDE0LjIgMTIzLjFMMTI1IDIzMGw3OC45LTQzLjcgMTQuMi0xMjMuMXoiIC8+CiAgICA8cGF0aCBmaWxsPSIjQzMwMDJGIiBkPSJNMTI1IDMwdjIyLjItLjFWMjMwbDc4LjktNDMuNyAxNC4yLTEyMy4xTDEyNSAzMHoiIC8+CiAgICA8cGF0aCAgZmlsbD0iI0ZGRkZGRiIgZD0iTTEyNSA1Mi4xTDY2LjggMTgyLjZoMjEuN2wxMS43LTI5LjJoNDkuNGwxMS43IDI5LjJIMTgzTDEyNSA1Mi4xem0xNyA4My4zaC0zNGwxNy00MC45IDE3IDQwLjl6IiAvPgogIDwvc3ZnPg==">  
  7.     
  8.     <div class="jumbotron">  
  9.       <h5 i18n="@@homemessage">  
  10.         Localization in Angular 8 using i18n  
  11.       </h5>  
  12.     </div>  
  13.   </div>  
You can notice that I have added two i18n attributes inside <h1> and <h5> tags. These attributes are accompanied by @@welcome and @@homemessage values. We will add these kinds of attributes and values in many html files and later we will create a translation source file for allthese i18n attributes.
 
We can create a common header component for our application.
 
ng g c ui\header
 
The above command will create a new header component inside the new “ui” folder.
 
Copy the below code and paste it inside the header.component.html file.
 
header.component.html
  1. <nav class="navbar navbar-dark bg-dark mb-5">  
  2.     <a class="navbar-brand" href="/">Employee App</a>  
  3.     <div class="navbar-expand mr-auto">  
  4.       <div class="navbar-nav">  
  5.         <a class="nav-item nav-link active" routerLink="home" routerLinkActive="active" i18n="@@home">Home</a>  
  6.         <a class="nav-item nav-link" routerLink="employees" i18n="@@app">Employee</a>  
  7.       </div>  
  8.     </div>  
  9.     <div class="navbar-expand ml-auto navbar-nav">  
  10.       <div class="navbar-nav">  
  11.         <a class="nav-item nav-link" href="https://github.com/sarathlalsaseendran" target="_blank">  
  12.           <i class="fa fa-github"></i>  
  13.         </a>  
  14.         <a class="nav-item nav-link" href="https://twitter.com/sarathlalsasee1" target="_blank">  
  15.           <i class="fa fa-twitter"></i>  
  16.         </a>  
  17.         <a class="nav-item nav-link" href="https://codewithsarath.com" target="_blank">  
  18.           <i class="fa fa-wordpress"></i>  
  19.         </a>  
  20.       </div>  
  21.     </div>  
  22.   </nav>  
You can see that a couple of i18n attributes are added in the above file also.
 
We can create a common footer component in the same way.
 
ng g c ui\footer
 
Copy the below code and paste inside the footer.component.html file.
 
footer.component.html
  1. <nav class="navbar navbar-dark bg-dark mt-5 fixed-bottom">  
  2.     <div class="navbar-expand m-auto navbar-text" i18n="@@developer">  
  3.       Developed with <i class="fa fa-heart"></i> by <a href="https://codewithsarath.com" target="_blank">Sarathlal</a>  
  4.     </div>  
  5.   </nav>  
We can create Layout component now.
 
ng g c ui\layout
 
Copy below code and paste inside the layout.component.html file.
 
layout.component.html
  1. <app-header></app-header>  
  2. <div class="container">  
  3.   <ng-content></ng-content>  
  4. </div>  
  5. <app-footer></app-footer>  
We have added header and footer components tags inside this file along with a container to display remaining application parts.
 
We will use a generic validation class to validate the employee name. We can create this class now.
 
ng g class shared\GenericValidator
 
The above command will create an empty class inside the shared folder. Copy the below code and paste it inside this class file.
 
generic-validator.ts
  1. import { FormGroup } from '@angular/forms';  
  2.   
  3. export class GenericValidator {  
  4.   
  5.   constructor(private validationMessages: { [key: string]: { [key: string]: string } }) {  
  6.   }  
  7.   
  8.   processMessages(container: FormGroup): { [key: string]: string } {  
  9.     const messages = {};  
  10.     for (const controlKey in container.controls) {  
  11.       if (container.controls.hasOwnProperty(controlKey)) {  
  12.         const c = container.controls[controlKey];  
  13.         // If it is a FormGroup, process its child controls.  
  14.         if (c instanceof FormGroup) {  
  15.           const childMessages = this.processMessages(c);  
  16.           Object.assign(messages, childMessages);  
  17.         } else {  
  18.           // Only validate if there are validation messages for the control  
  19.           if (this.validationMessages[controlKey]) {  
  20.             messages[controlKey] = '';  
  21.             if ((c.dirty || c.touched) && c.errors) {  
  22.               Object.keys(c.errors).map(messageKey => {  
  23.                 if (this.validationMessages[controlKey][messageKey]) {  
  24.                   messages[controlKey] += this.validationMessages[controlKey][messageKey] + ' ';  
  25.                 }  
  26.               });  
  27.             }  
  28.           }  
  29.         }  
  30.       }  
  31.     }  
  32.     return messages;  
  33.   }  
  34.   
  35.   getErrorCount(container: FormGroup): number {  
  36.     let errorCount = 0;  
  37.     for (const controlKey in container.controls) {  
  38.       if (container.controls.hasOwnProperty(controlKey)) {  
  39.         if (container.controls[controlKey].errors) {  
  40.           errorCount += Object.keys(container.controls[controlKey].errors).length;  
  41.           console.log(errorCount);  
  42.         }  
  43.       }  
  44.     }  
  45.     return errorCount;  
  46.   }  
  47. }  
We will create each employee with a unique Guid. Hence, we can create a Guid class.
 
ng g class shared\Guid
 
The above command will create an empty class. Copy the below code and paste to this class file.
 
guid.ts
  1. export class Guid {  
  2.     static newGuid() {  
  3.         return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {  
  4.             var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);  
  5.             return v.toString(16);  
  6.         });  
  7.     }  
  8. }  
We have added the logic for creating an automatic Guid.
 
We can create an employee interface using below command.
 
ng g interface employee\Employee
 
The above command will create an empty interface inside the employee folder. Copy the below code and paste to this file.
 
employee.ts
  1. export interface Employee {  
  2.     id: string,  
  3.     name: string,  
  4.     address: string,  
  5.     gender: string,  
  6.     company: string,  
  7.     designation: string  
  8. }  
In this application, we are using in-memory data to save and retrieve employee information. Hence, we can add below employee data class.
 
ng g class employee\EmployeeData
 
Copy below code and paste to this class file.
 
employee-data.ts
  1. import { InMemoryDbService } from 'angular-in-memory-web-api';  
  2. import { Employee } from './employee';  
  3.   
  4. export class EmployeeData implements InMemoryDbService {  
  5.   
  6.     createDb() {  
  7.         const employees: Employee[] = [  
  8.             {  
  9.                 'id''496f04a0-5c6d-400f-a0a8-0929088dc41e',  
  10.                 'name''Sarath Lal',  
  11.                 'address''Kakkanad',  
  12.                 'gender''Male',  
  13.                 'company''Orion Business Innovation',  
  14.                 'designation''Technical Lead'  
  15.             },  
  16.             {  
  17.                 'id''03810397-381c-4b9d-85f6-d7c87622e518',  
  18.                 'name''Baiju Mathew',  
  19.                 'address''Kakkanad',  
  20.                 'gender''Male',  
  21.                 'company''Orion Business Innovation',  
  22.                 'designation''Project Manager'  
  23.             }  
  24.         ];  
  25.         return { employees };  
  26.     }  
  27. }  
We can create employee service now.
 
ng g service employee\Employee
 
Copy the below code to this service class.
 
employee.service.ts
  1. import { Injectable } from '@angular/core';  
  2. import { HttpClient, HttpHeaders } from '@angular/common/http';  
  3. import { Employee } from './employee';  
  4. import { Observable, throwError, of } from 'rxjs';  
  5. import { catchError, map } from 'rxjs/operators';  
  6. import { Guid } from '../shared/guid';  
  7.   
  8. @Injectable({  
  9.   providedIn: 'root'  
  10. })  
  11. export class EmployeeService {  
  12.   private employeesUrl = 'api/employees';  
  13.   
  14.   constructor(private http: HttpClient) { }  
  15.   
  16.   getEmployees(): Observable<Employee[]> {  
  17.     return this.http.get<Employee[]>(this.employeesUrl)  
  18.       .pipe(  
  19.         catchError(this.handleError)  
  20.       );  
  21.   }  
  22.   
  23.   getEmployee(id: string): Observable<Employee> {  
  24.     if (id === '') {  
  25.       return of(this.initializeEmployee());  
  26.     }  
  27.     const url = `${this.employeesUrl}/${id}`;  
  28.     return this.http.get<Employee>(url)  
  29.       .pipe(  
  30.         catchError(this.handleError)  
  31.       );  
  32.   }  
  33.   
  34.   createEmployee(employee: Employee): Observable<Employee> {  
  35.     const headers = new HttpHeaders({ 'Content-Type''application/json' });  
  36.     employee.id = Guid.newGuid();  
  37.     debugger;  
  38.     return this.http.post<Employee>(this.employeesUrl, employee, { headers: headers })  
  39.       .pipe(  
  40.         catchError(this.handleError)  
  41.       );  
  42.   }  
  43.   
  44.   deleteEmployee(id: string): Observable<{}> {  
  45.     const headers = new HttpHeaders({ 'Content-Type''application/json' });  
  46.     const url = `${this.employeesUrl}/${id}`;  
  47.     return this.http.delete<Employee>(url, { headers: headers })  
  48.       .pipe(  
  49.         catchError(this.handleError)  
  50.       );  
  51.   }  
  52.   
  53.   updateEmployee(employee: Employee): Observable<Employee> {  
  54.     const headers = new HttpHeaders({ 'Content-Type''application/json' });  
  55.     const url = `${this.employeesUrl}/${employee.id}`;  
  56.     return this.http.put<Employee>(url, employee, { headers: headers })  
  57.       .pipe(  
  58.         map(() => employee),  
  59.         catchError(this.handleError)  
  60.       );  
  61.   }  
  62.   
  63.   private handleError(err) {  
  64.     let errorMessage: string;  
  65.     if (err.error instanceof ErrorEvent) {  
  66.       errorMessage = `An error occurred: ${err.error.message}`;  
  67.     } else {  
  68.       errorMessage = `Backend returned code ${err.status}: ${err.body.error}`;  
  69.     }  
  70.     console.error(err);  
  71.     return throwError(errorMessage);  
  72.   }  
  73.   
  74.   private initializeEmployee(): Employee {  
  75.     return {  
  76.       id: null,  
  77.       name: null,  
  78.       address: null,  
  79.       gender: null,  
  80.       company: null,  
  81.       designation: null  
  82.     };  
  83.   }  
  84. }  
We have added all CRUD actions logic inside above service class.
 
We can add employee list component to list all employee information.
 
ng g c employee\EmployeeList
 
Copy the below code and paste inside the component class file.
 
employee-list.component.ts
  1. import { Component, OnInit } from '@angular/core';  
  2. import { Employee } from '../employee';  
  3. import { EmployeeService } from '../employee.service';  
  4.   
  5. @Component({  
  6.   selector: 'app-employee-list',  
  7.   templateUrl: './employee-list.component.html',  
  8.   styleUrls: ['./employee-list.component.css']  
  9. })  
  10. export class EmployeeListComponent implements OnInit {  
  11.   pageTitle = 'Employee List';  
  12.   filteredEmployees: Employee[] = [];  
  13.   employees: Employee[] = [];  
  14.   errorMessage = '';  
  15.   
  16.   _listFilter = '';  
  17.   get listFilter(): string {  
  18.     return this._listFilter;  
  19.   }  
  20.   set listFilter(value: string) {  
  21.     this._listFilter = value;  
  22.     this.filteredEmployees = this.listFilter ? this.performFilter(this.listFilter) : this.employees;  
  23.   }  
  24.   
  25.   constructor(private employeeService: EmployeeService) { }  
  26.   
  27.   performFilter(filterBy: string): Employee[] {  
  28.     filterBy = filterBy.toLocaleLowerCase();  
  29.     return this.employees.filter((employee: Employee) =>  
  30.       employee.name.toLocaleLowerCase().indexOf(filterBy) !== -1);  
  31.   }  
  32.   
  33.   ngOnInit(): void {  
  34.     this.employeeService.getEmployees().subscribe(  
  35.       employees => {  
  36.         this.employees = employees;  
  37.         this.filteredEmployees = this.employees;  
  38.       },  
  39.       error => this.errorMessage = <any>error  
  40.     );  
  41.   }  
  42.   
  43.   deleteEmployee(id: string, name: string): void {  
  44.     if (id === '') {  
  45.       // Don't delete, it was never saved.  
  46.       this.onSaveComplete();  
  47.     } else {  
  48.       if (confirm(`Are you sure want to delete this Employee: ${name}?`)) {  
  49.         this.employeeService.deleteEmployee(id)  
  50.           .subscribe(  
  51.             () => this.onSaveComplete(),  
  52.             (error: any) => this.errorMessage = <any>error  
  53.           );  
  54.       }  
  55.     }  
  56.   }  
  57.   
  58.   onSaveComplete(): void {  
  59.     this.employeeService.getEmployees().subscribe(  
  60.       employees => {  
  61.         this.employees = employees;  
  62.         this.filteredEmployees = this.employees;  
  63.       },  
  64.       error => this.errorMessage = <any>error  
  65.     );  
  66.   }  
  67.   
  68. }  
Also, copy the corresponding HTML and CSS files.
 
employee-list.component.html
  1. <div class="card">  
  2.     <div class="card-header">  
  3.       {{pageTitle}}  
  4.     </div>  
  5.     
  6.     <div class="card-body">  
  7.       <div class="row">  
  8.         <div class="col-md-2" i18n="@@filter">Filter by:</div>  
  9.         <div class="col-md-4">  
  10.           <input type="text" [(ngModel)]="listFilter" />  
  11.         </div>  
  12.         <div class="col-md-3"></div>  
  13.         <div class="col-md-3">  
  14.           <button class="btn btn-primary mr-3" [routerLink]="['/employees/0/edit']" i18n="@@newemployee">  
  15.             New Employee  
  16.           </button>  
  17.         </div>  
  18.       </div>  
  19.       <div class="row" *ngIf="listFilter">  
  20.         <div class="col-md-6">  
  21.           <h4 i18n="@@filteredBy">Filtered by: {{listFilter}}</h4>  
  22.         </div>  
  23.       </div>  
  24.     
  25.       <div class="table-responsive">  
  26.         <table class="table mb-0" *ngIf="employees && employees.length">  
  27.           <thead>  
  28.             <tr>  
  29.               <th i18n="@@empName">Name</th>  
  30.               <th i18n="@@empAddress">Address</th>  
  31.               <th i18n="@@empGender">Gender</th>  
  32.               <th i18n="@@empCompany">Company</th>  
  33.               <th i18n="@@empDesignation">Designation</th>  
  34.               <th></th>  
  35.               <th></th>  
  36.             </tr>  
  37.           </thead>  
  38.           <tbody>  
  39.             <tr *ngFor="let employee of filteredEmployees">  
  40.               <td>  
  41.                 <a [routerLink]="['/employees', employee.id]">  
  42.                   {{ employee.name }}  
  43.                 </a>  
  44.               </td>  
  45.               <td>{{ employee.address }}</td>  
  46.               <td>{{ employee.gender }}</td>  
  47.               <td>{{ employee.company }}</td>  
  48.               <td>{{ employee.designation}} </td>  
  49.               <td>  
  50.                 <button class="btn btn-outline-primary btn-sm" [routerLink]="['/employees', employee.id, 'edit']" i18n="@@edit">  
  51.                   Edit  
  52.                 </button>  
  53.               </td>  
  54.               <td>  
  55.                   <button class="btn btn-outline-warning btn-sm" (click)="deleteEmployee(employee.id,employee.name);" i18n="@@delete">  
  56.                     Delete  
  57.                   </button>  
  58.                 </td>  
  59.             </tr>  
  60.           </tbody>  
  61.         </table>  
  62.       </div>  
  63.     
  64.     </div>  
  65.   </div>  
  66.     
  67.   <div *ngIf="errorMessage" class="alert alert-danger">  
  68.     Error: {{ errorMessage }}  
  69.   </div>  
employee-list.component.css
  1. thead {  
  2.     color#337AB7;  
  3. }  
We can create employee edit component in the same way.
 
ng g c employee\EmployeeEdit
 
Copy the below code and paste to component class and HTML files.
 
employee-edit.component.ts
  1. import { Component, OnInit, AfterViewInit, OnDestroy, ElementRef, ViewChildren } from '@angular/core';  
  2. import { FormControlName, FormGroup, FormBuilder, Validators } from '@angular/forms';  
  3. import { Subscription, Observable, fromEvent, merge } from 'rxjs';  
  4. import { Employee } from '../employee';  
  5. import { EmployeeService } from '../employee.service';  
  6. import { ActivatedRoute, Router } from '@angular/router';  
  7. import { debounceTime } from 'rxjs/operators';  
  8. import { GenericValidator } from 'src/app/shared/generic-validator';  
  9.   
  10. @Component({  
  11.   selector: 'app-employee-edit',  
  12.   templateUrl: './employee-edit.component.html',  
  13.   styleUrls: ['./employee-edit.component.css']  
  14. })  
  15. export class EmployeeEditComponent implements OnInit, AfterViewInit, OnDestroy {  
  16.   @ViewChildren(FormControlName, { read: ElementRef }) formInputElements: ElementRef[];  
  17.   pageTitle = 'Employee Edit';  
  18.   errorMessage: string;  
  19.   employeeForm: FormGroup;  
  20.   
  21.   employee: Employee;  
  22.   private sub: Subscription;  
  23.   
  24.   displayMessage: { [key: string]: string } = {};  
  25.   private validationMessages: { [key: string]: { [key: string]: string } };  
  26.   private genericValidator: GenericValidator;  
  27.   
  28.   
  29.   constructor(private fb: FormBuilder,  
  30.     private route: ActivatedRoute,  
  31.     private router: Router,  
  32.     private employeeService: EmployeeService) {  
  33.   
  34.     this.validationMessages = {  
  35.       name: {  
  36.         required: 'Employee name is required.',  
  37.         minlength: 'Employee name must be at least three characters.',  
  38.         maxlength: 'Employee name cannot exceed 50 characters.'  
  39.       },  
  40.       address: {  
  41.         required: 'Employee address is required.',  
  42.       }  
  43.     };  
  44.   
  45.     this.genericValidator = new GenericValidator(this.validationMessages);  
  46.   }  
  47.   
  48.   ngOnInit() {  
  49.     this.employeeForm = this.fb.group({  
  50.       name: ['', [Validators.required  
  51.     ]],  
  52.       address: ['', [Validators.required]],  
  53.       gender: '',  
  54.       company: '',  
  55.       designation: ''  
  56.     });  
  57.   
  58.     this.sub = this.route.paramMap.subscribe(  
  59.       params => {  
  60.         const id = params.get('id');  
  61.         if (id == '0') {  
  62.           const employee: Employee = { id: "0", name: "", address: "", gender: "", company: "", designation: "" };  
  63.           this.displayEmployee(employee);  
  64.         }  
  65.         else {  
  66.           this.getEmployee(id);  
  67.         }  
  68.       }  
  69.     );  
  70.   }  
  71.   
  72.   ngOnDestroy(): void {  
  73.     this.sub.unsubscribe();  
  74.   }  
  75.   
  76.   ngAfterViewInit(): void {  
  77.     // Watch for the blur event from any input element on the form.  
  78.     const controlBlurs: Observable<any>[] = this.formInputElements  
  79.       .map((formControl: ElementRef) => fromEvent(formControl.nativeElement, 'blur'));  
  80.   
  81.     // Merge the blur event observable with the valueChanges observable  
  82.     merge(this.employeeForm.valueChanges, ...controlBlurs).pipe(  
  83.       debounceTime(800)  
  84.     ).subscribe(value => {  
  85.       this.displayMessage = this.genericValidator.processMessages(this.employeeForm);  
  86.     });  
  87.   }  
  88.   
  89.   
  90.   getEmployee(id: string): void {  
  91.     this.employeeService.getEmployee(id)  
  92.       .subscribe(  
  93.         (employee: Employee) => this.displayEmployee(employee),  
  94.         (error: any) => this.errorMessage = <any>error  
  95.       );  
  96.   }  
  97.   
  98.   displayEmployee(employee: Employee): void {  
  99.     if (this.employeeForm) {  
  100.       this.employeeForm.reset();  
  101.     }  
  102.     this.employee = employee;  
  103.   
  104.     if (this.employee.id == '0') {  
  105.       this.pageTitle = 'Add Employee';  
  106.     } else {  
  107.       this.pageTitle = `Edit Employee: ${this.employee.name}`;  
  108.     }  
  109.   
  110.     // Update the data on the form  
  111.     this.employeeForm.patchValue({  
  112.       name: this.employee.name,  
  113.       address: this.employee.address,  
  114.       gender: this.employee.gender,  
  115.       company: this.employee.company,  
  116.       designation: this.employee.designation  
  117.     });  
  118.   }  
  119.   
  120.   deleteEmployee(): void {  
  121.     if (this.employee.id == '0') {  
  122.       // Don't delete, it was never saved.  
  123.       this.onSaveComplete();  
  124.     } else {  
  125.       if (confirm(`Are you sure want to delete this Employee: ${this.employee.name}?`)) {  
  126.         this.employeeService.deleteEmployee(this.employee.id)  
  127.           .subscribe(  
  128.             () => this.onSaveComplete(),  
  129.             (error: any) => this.errorMessage = <any>error  
  130.           );  
  131.       }  
  132.     }  
  133.   }  
  134.   
  135.   saveEmployee(): void {  
  136.     if (this.employeeForm.valid) {  
  137.       if (this.employeeForm.dirty) {  
  138.         const p = { ...this.employee, ...this.employeeForm.value };  
  139.         if (p.id === '0') {  
  140.           this.employeeService.createEmployee(p)  
  141.             .subscribe(  
  142.               () => this.onSaveComplete(),  
  143.               (error: any) => this.errorMessage = <any>error  
  144.             );  
  145.         } else {  
  146.           this.employeeService.updateEmployee(p)  
  147.             .subscribe(  
  148.               () => this.onSaveComplete(),  
  149.               (error: any) => this.errorMessage = <any>error  
  150.             );  
  151.         }  
  152.       } else {  
  153.         this.onSaveComplete();  
  154.       }  
  155.     } else {  
  156.       this.errorMessage = 'Please correct the validation errors.';  
  157.     }  
  158.   }  
  159.   
  160.   
  161.   onSaveComplete(): void {  
  162.     // Reset the form to clear the flags  
  163.     this.employeeForm.reset();  
  164.     this.router.navigate(['/employees']);  
  165.   }  
  166. }  
employee-edit.component.html
  1. <div class="card">  
  2.     <div class="card-header">  
  3.       {{pageTitle}}  
  4.     </div>  
  5.     
  6.     <div class="card-body">  
  7.       <form novalidate  
  8.             (ngSubmit)="saveEmployee()"  
  9.             [formGroup]="employeeForm">  
  10.     
  11.         <div class="form-group row mb-2">  
  12.           <label class="col-md-2 col-form-label"  
  13.                  for="employeeNameId" i18n="@@empName">Name</label>  
  14.           <div class="col-md-8">  
  15.             <input class="form-control"  
  16.                    id="employeeNameId"  
  17.                    type="text"  
  18.                    placeholder="Name (required)"  
  19.                    formControlName="name"  
  20.                    [ngClass]="{'is-invalid': displayMessage.name }" />  
  21.             <span class="invalid-feedback">  
  22.               {{displayMessage.name}}  
  23.             </span>  
  24.           </div>  
  25.         </div>  
  26.     
  27.         <div class="form-group row mb-2">  
  28.           <label class="col-md-2 col-form-label"  
  29.                  for="addressId"  i18n="@@empAddress">Address</label>  
  30.           <div class="col-md-8">  
  31.             <input class="form-control"  
  32.                    id="addressId"  
  33.                    type="text"  
  34.                    placeholder="Address"  
  35.                    formControlName="address"  
  36.                    [ngClass]="{'is-invalid': displayMessage.address}" />  
  37.             <span class="invalid-feedback">  
  38.               {{displayMessage.address}}  
  39.             </span>  
  40.           </div>  
  41.         </div>  
  42.     
  43.         <div class="form-group row mb-2">  
  44.           <label class="col-md-2 col-form-label"  
  45.                  for="genderId"  i18n="@@empGender">Gender</label>  
  46.           <div class="col-md-8">  
  47.             <input class="form-control"  
  48.                    id="genderId"  
  49.                    type="text"  
  50.                    placeholder="Gender"  
  51.                    formControlName="gender"  
  52.                    [ngClass]="{'is-invalid': displayMessage.gender}" />  
  53.             <span class="invalid-feedback">  
  54.               {{displayMessage.gender}}  
  55.             </span>  
  56.           </div>  
  57.         </div>  
  58.     
  59.         <div class="form-group row mb-2">  
  60.           <label class="col-md-2 col-form-label"  
  61.                  for="companyId"  i18n="@@empCompany">Company</label>  
  62.           <div class="col-md-8">  
  63.             <input class="form-control"  
  64.                    id="companyId"  
  65.                    type="text"  
  66.                    placeholder="Company"  
  67.                    formControlName="company"  
  68.                    [ngClass]="{'is-invalid': displayMessage.gender}" />  
  69.             <span class="invalid-feedback">  
  70.               {{displayMessage.company}}  
  71.             </span>  
  72.           </div>  
  73.         </div>  
  74.     
  75.         <div class="form-group row mb-2">  
  76.           <label class="col-md-2 col-form-label"  
  77.                  for="designationId"  i18n="@@empDesignation">Designation</label>  
  78.           <div class="col-md-8">  
  79.             <input class="form-control"  
  80.                    id="designationId"  
  81.                    type="text"  
  82.                    placeholder="Designation"  
  83.                    formControlName="designation"  
  84.                    [ngClass]="{'is-invalid': displayMessage.designation}" />  
  85.             <span class="invalid-feedback">  
  86.               {{displayMessage.designation}}  
  87.             </span>  
  88.           </div>  
  89.         </div>  
  90.     
  91.         <div class="form-group row mb-2">  
  92.           <div class="offset-md-2 col-md-6">  
  93.             <button class="btn btn-primary mr-3"  
  94.                     style="width:100px;"  
  95.                     type="submit"  
  96.                     [title]="employeeForm.valid ? 'Save your entered data' : 'Disabled until the form data is valid'"  
  97.                     [disabled]="!employeeForm.valid"  i18n="@@save">  
  98.               Save  
  99.             </button>  
  100.             <button class="btn btn-outline-secondary mr-3"  
  101.                     style="width:140px;"  
  102.                     type="button"  
  103.                     title="Cancel your edits"  
  104.                     [routerLink]="['/employees']"  i18n="@@cancel">  
  105.               Cancel  
  106.             </button>  
  107.             <button class="btn btn-outline-warning"  *ngIf="employee.id != '0'"  
  108.                     style="width:100px"  
  109.                     type="button"  
  110.                     title="Delete this product"  
  111.                     (click)="deleteEmployee()"  i18n="@@delete">  
  112.               Delete  
  113.             </button>  
  114.           </div>  
  115.         </div>  
  116.       </form>  
  117.     </div>  
  118.     
  119.     <div class="alert alert-danger"  
  120.          *ngIf="errorMessage">{{errorMessage}}  
  121.     </div>  
  122.   </div>  
We can create a guard to prevent data loss before saving the data.
 
ng g guard employee\EmployeeEdit
 
Copy the below code and paste to guard class file.
 
employee-edit.guard.ts
  1. import { Injectable } from '@angular/core';  
  2. import { CanDeactivate } from '@angular/router';  
  3. import { Observable } from 'rxjs';  
  4. import { EmployeeEditComponent } from './employee-edit/employee-edit.component';  
  5.   
  6.   
  7. @Injectable({  
  8.   providedIn: 'root'  
  9. })  
  10. export class EmployeeEditGuard implements CanDeactivate<EmployeeEditComponent> {  
  11.   canDeactivate(component: EmployeeEditComponent): Observable<boolean> | Promise<boolean> | boolean {  
  12.     if (component.employeeForm.dirty) {  
  13.       const name = component.employeeForm.get('name').value || 'New Employee';  
  14.       return confirm(`Navigate away and lose all changes to ${name}?`);  
  15.     }  
  16.     return true;  
  17.   }  
  18. }   
We can create employee detail component.
 
ng g c employee\EmployeeDetail
 
Copy the below code and paste to component class and HTML files.
 
employee-detail.component.ts
  1. import { Component, OnInit } from '@angular/core';  
  2. import { Employee } from '../employee';  
  3. import { ActivatedRoute, Router } from '@angular/router';  
  4. import { EmployeeService } from '../employee.service';  
  5.   
  6. @Component({  
  7.   selector: 'app-employee-detail',  
  8.   templateUrl: './employee-detail.component.html',  
  9.   styleUrls: ['./employee-detail.component.css']  
  10. })  
  11. export class EmployeeDetailComponent implements OnInit {  
  12.   pageTitle = 'Employee Detail';  
  13.   errorMessage = '';  
  14.   employee: Employee | undefined;  
  15.   
  16.   constructor(private route: ActivatedRoute,  
  17.     private router: Router,  
  18.     private employeeService: EmployeeService) { }  
  19.   
  20.   ngOnInit() {  
  21.     const param = this.route.snapshot.paramMap.get('id');  
  22.     if (param) {  
  23.       const id = param;  
  24.       this.getEmployee(id);  
  25.     }  
  26.   }  
  27.   
  28.   getEmployee(id: string) {  
  29.     this.employeeService.getEmployee(id).subscribe(  
  30.       employee => this.employee = employee,  
  31.       error => this.errorMessage = <any>error);  
  32.   }  
  33.   
  34.   onBack(): void {  
  35.     this.router.navigate(['/employees']);  
  36.   }  
  37. }  
employee-detail.component.html
  1. <div class="card">  
  2.     <div class="card-header"  
  3.          *ngIf="employee">  
  4.       {{pageTitle + ": " + employee.name}}  
  5.     </div>  
  6.     
  7.     <div class="card-body"  
  8.          *ngIf="employee">  
  9.     
  10.       <div class="row">  
  11.     
  12.         <div class="col-md-8">  
  13.           <div class="row">  
  14.             <div class="col-md-3" i18n="@@empName">Name:</div>  
  15.             <div class="col-md-6">{{employee.name}}</div>  
  16.           </div>  
  17.           <div class="row">  
  18.             <div class="col-md-3" i18n="@@empAddress">Address:</div>  
  19.             <div class="col-md-6">{{employee.address}}</div>  
  20.           </div>  
  21.           <div class="row">  
  22.             <div class="col-md-3" i18n="@@empGender">Gender:</div>  
  23.             <div class="col-md-6">{{employee.gender}}</div>  
  24.           </div>  
  25.           <div class="row">  
  26.             <div class="col-md-3" i18n="@@empCompany">Company:</div>  
  27.             <div class="col-md-6">{{employee.company}}</div>  
  28.           </div>  
  29.           <div class="row">  
  30.             <div class="col-md-3" i18n="@@empDesignation">Designation:</div>  
  31.             <div class="col-md-6">{{employee.designation}}</div>  
  32.           </div>  
  33.         </div>  
  34.     
  35.       </div>  
  36.     
  37.       <div class="row mt-4">  
  38.         <div class="col-md-6">  
  39.           <button class="btn btn-outline-secondary mr-3"  
  40.                   style="width:100px"  
  41.                   (click)="onBack()" i18n="@@back">  
  42.             <i class="fa fa-chevron-left"></i> Back  
  43.           </button>  
  44.           <button class="btn btn-outline-primary"  
  45.                   style="width:100px"  
  46.                   [routerLink]="['/employees', employee.id, 'edit']" i18n="@@edit">  
  47.             Edit  
  48.           </button>  
  49.         </div>  
  50.       </div>  
  51.     
  52.     </div>  
  53.     
  54.     <div class="alert alert-danger"  
  55.          *ngIf="errorMessage">{{errorMessage}}  
  56.     </div>  
  57.   </div>  
We have created all components. We can create a route config file now.
 
ng g class RouterConfig
 
Copy the below code and paste inside the router constant file.
 
router-config.ts
  1. import { Routes } from '@angular/router';  
  2. import { HomeComponent } from './home/home.component';  
  3. import { EmployeeListComponent } from './employee/employee-list/employee-list.component';  
  4. import { EmployeeEditComponent } from './employee/employee-edit/employee-edit.component';  
  5. import { EmployeeEditGuard } from './employee/employee-edit.guard';  
  6. import { EmployeeDetailComponent } from './employee/employee-detail/employee-detail.component';  
  7.   
  8. export const routes: Routes = [  
  9.     {  
  10.         path: 'home',  
  11.         component: HomeComponent  
  12.     },  
  13.     {  
  14.         path: 'employees',  
  15.         component: EmployeeListComponent  
  16.     },  
  17.     {  
  18.         path: 'employees/:id',  
  19.         component: EmployeeDetailComponent  
  20.     },  
  21.     {  
  22.         path: 'employees/:id/edit',  
  23.         canDeactivate: [EmployeeEditGuard],  
  24.         component: EmployeeEditComponent  
  25.     },  
  26.     {  
  27.         path: '',  
  28.         redirectTo: 'home',  
  29.         pathMatch: 'full'  
  30.     },  
  31.     {  
  32.         path: '**',  
  33.         redirectTo: 'home',  
  34.         pathMatch: 'full'  
  35.     }  
  36. ];  
We can modify the app.module.ts file.
 
app.module.ts
  1. import { BrowserModule } from '@angular/platform-browser';  
  2. import { NgModule } from '@angular/core';  
  3.   
  4. import { AppComponent } from './app.component';  
  5. import { HomeComponent } from './home/home.component';  
  6. import { HeaderComponent } from './ui/header/header.component';  
  7. import { FooterComponent } from './ui/footer/footer.component';  
  8. import { LayoutComponent } from './ui/layout/layout.component';  
  9. import { EmployeeListComponent } from './employee/employee-list/employee-list.component';  
  10. import { EmployeeEditComponent } from './employee/employee-edit/employee-edit.component';  
  11. import { EmployeeDetailComponent } from './employee/employee-detail/employee-detail.component';  
  12. import { ReactiveFormsModule, FormsModule } from '@angular/forms';  
  13. import { RouterModule } from '@angular/router';  
  14. import { HttpClientModule } from '@angular/common/http';  
  15. import { routes } from './router-config';  
  16. import { InMemoryWebApiModule } from 'angular-in-memory-web-api';  
  17. import { EmployeeData } from './employee/employee-data';  
  18.   
  19. @NgModule({  
  20.   declarations: [  
  21.     AppComponent,  
  22.     HomeComponent,  
  23.     HeaderComponent,  
  24.     FooterComponent,  
  25.     LayoutComponent,  
  26.     EmployeeListComponent,  
  27.     EmployeeEditComponent,  
  28.     EmployeeDetailComponent  
  29.   ],  
  30.   imports: [  
  31.     BrowserModule,  
  32.     ReactiveFormsModule,  
  33.     FormsModule,  
  34.     RouterModule,  
  35.     HttpClientModule,  
  36.     RouterModule.forRoot(routes),  
  37.     InMemoryWebApiModule.forRoot(EmployeeData),   
  38.   ],  
  39.   providers: [],  
  40.   bootstrap: [AppComponent]  
  41. })  
  42. export class AppModule { }  
We can import bootstrap and font-awesome classes inside the style.css file to use these libraries globally without further references.
 
style.css
  1. /* You can add global styles to this file, and also import other style files */  
  2. @import "~bootstrap/dist/css/bootstrap.css";  
  3. @import "~font-awesome/css/font-awesome.css";  
  4.   
  5. div.card-header {  
  6.     font-sizelarge;  
  7. }  
  8.      
  9. div.card {  
  10.     margin-top10px  
  11. }  
  12.   
  13. .table {  
  14.     margin-top10px  
  15. }  
We can modify app.component.html file
 
app.component.html
  1. <app-layout>    
  2.   <router-outlet></router-outlet>    
  3. </app-layout>    
We have added app layout component inside this file.
 
We have completed the entire coding part except for the translation content creation.
 
We can create a translation source file using the below command.
 
ng xi18n --output-path src\app\translate
 
It will create a folder “translate” and create a messages.xlf file inside it. Open the file and you can observe the following XML code inside it.
 
messages.xlf
  1. <?xml version="1.0" encoding="UTF-8" ?>    
  2. <xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">    
  3.   <file source-language="en" datatype="plaintext" original="ng2.template">    
  4.     <body>    
  5.       <trans-unit id="welcome" datatype="html">    
  6.         <source>    
  7.       Welcome to Employee Application!    
  8.     </source>    
  9.         <context-group purpose="location">    
  10.           <context context-type="sourcefile">src/app/home/home.component.html</context>    
  11.           <context context-type="linenumber">2</context>    
  12.         </context-group>    
  13.       </trans-unit>    
  14.       <trans-unit id="homemessage" datatype="html">    
  15.         <source>    
  16.         Localization in Angular 8 using i18n    
  17.       </source>    
  18.         <context-group purpose="location">    
  19.           <context context-type="sourcefile">src/app/home/home.component.html</context>    
  20.           <context context-type="linenumber">9</context>    
  21.         </context-group>    
  22.       </trans-unit>    
  23.       <trans-unit id="home" datatype="html">    
  24.         <source>Home</source>    
  25.         <context-group purpose="location">    
  26.           <context context-type="sourcefile">src/app/ui/header/header.component.html</context>    
  27.           <context context-type="linenumber">5</context>    
  28.         </context-group>    
  29.       </trans-unit>    
  30.       <trans-unit id="app" datatype="html">    
  31.         <source>Employee</source>    
  32.         <context-group purpose="location">    
  33.           <context context-type="sourcefile">src/app/ui/header/header.component.html</context>    
  34.           <context context-type="linenumber">6</context>    
  35.         </context-group>    
  36.       </trans-unit>    
  37.       <trans-unit id="developer" datatype="html">    
  38.         <source>    
  39.       Developed with <x id="START_ITALIC_TEXT" ctype="x-i" equiv-text="<i>"/><x id="CLOSE_ITALIC_TEXT" ctype="x-i" equiv-text="</i>"/> by <x id="START_LINK" ctype="x-a" equiv-text="<a>"/>Sarathlal<x id="CLOSE_LINK" ctype="x-a" equiv-text="</a>"/>    
  40.     </source>    
  41.         <context-group purpose="location">    
  42.           <context context-type="sourcefile">src/app/ui/footer/footer.component.html</context>    
  43.           <context context-type="linenumber">2</context>    
  44.         </context-group>    
  45.       </trans-unit>    
  46.       <trans-unit id="filter" datatype="html">    
  47.         <source>Filter by:</source>    
  48.         <context-group purpose="location">    
  49.           <context context-type="sourcefile">src/app/employee/employee-list/employee-list.component.html</context>    
  50.           <context context-type="linenumber">8</context>    
  51.         </context-group>    
  52.       </trans-unit>    
  53.       <trans-unit id="newemployee" datatype="html">    
  54.         <source>    
  55.             New Employee    
  56.           </source>    
  57.         <context-group purpose="location">    
  58.           <context context-type="sourcefile">src/app/employee/employee-list/employee-list.component.html</context>    
  59.           <context context-type="linenumber">14</context>    
  60.         </context-group>    
  61.       </trans-unit>    
  62.       <trans-unit id="filteredBy" datatype="html">    
  63.         <source>Filtered by: <x id="INTERPOLATION" equiv-text="{{listFilter}}"/></source>    
  64.         <context-group purpose="location">    
  65.           <context context-type="sourcefile">src/app/employee/employee-list/employee-list.component.html</context>    
  66.           <context context-type="linenumber">21</context>    
  67.         </context-group>    
  68.       </trans-unit>    
  69.       <trans-unit id="empName" datatype="html">    
  70.         <source>Name</source>    
  71.         <context-group purpose="location">    
  72.           <context context-type="sourcefile">src/app/employee/employee-list/employee-list.component.html</context>    
  73.           <context context-type="linenumber">29</context>    
  74.         </context-group>    
  75.         <context-group purpose="location">    
  76.           <context context-type="sourcefile">src/app/employee/employee-edit/employee-edit.component.html</context>    
  77.           <context context-type="linenumber">13</context>    
  78.         </context-group>    
  79.         <context-group purpose="location">    
  80.           <context context-type="sourcefile">src/app/employee/employee-detail/employee-detail.component.html</context>    
  81.           <context context-type="linenumber">14</context>    
  82.         </context-group>    
  83.       </trans-unit>    
  84.       <trans-unit id="empAddress" datatype="html">    
  85.         <source>Address</source>    
  86.         <context-group purpose="location">    
  87.           <context context-type="sourcefile">src/app/employee/employee-list/employee-list.component.html</context>    
  88.           <context context-type="linenumber">30</context>    
  89.         </context-group>    
  90.         <context-group purpose="location">    
  91.           <context context-type="sourcefile">src/app/employee/employee-edit/employee-edit.component.html</context>    
  92.           <context context-type="linenumber">29</context>    
  93.         </context-group>    
  94.         <context-group purpose="location">    
  95.           <context context-type="sourcefile">src/app/employee/employee-detail/employee-detail.component.html</context>    
  96.           <context context-type="linenumber">18</context>    
  97.         </context-group>    
  98.       </trans-unit>    
  99.       <trans-unit id="empGender" datatype="html">    
  100.         <source>Gender</source>    
  101.         <context-group purpose="location">    
  102.           <context context-type="sourcefile">src/app/employee/employee-list/employee-list.component.html</context>    
  103.           <context context-type="linenumber">31</context>    
  104.         </context-group>    
  105.         <context-group purpose="location">    
  106.           <context context-type="sourcefile">src/app/employee/employee-edit/employee-edit.component.html</context>    
  107.           <context context-type="linenumber">45</context>    
  108.         </context-group>    
  109.         <context-group purpose="location">    
  110.           <context context-type="sourcefile">src/app/employee/employee-detail/employee-detail.component.html</context>    
  111.           <context context-type="linenumber">22</context>    
  112.         </context-group>    
  113.       </trans-unit>    
  114.       <trans-unit id="empCompany" datatype="html">    
  115.         <source>Company</source>    
  116.         <context-group purpose="location">    
  117.           <context context-type="sourcefile">src/app/employee/employee-list/employee-list.component.html</context>    
  118.           <context context-type="linenumber">32</context>    
  119.         </context-group>    
  120.         <context-group purpose="location">    
  121.           <context context-type="sourcefile">src/app/employee/employee-edit/employee-edit.component.html</context>    
  122.           <context context-type="linenumber">61</context>    
  123.         </context-group>    
  124.         <context-group purpose="location">    
  125.           <context context-type="sourcefile">src/app/employee/employee-detail/employee-detail.component.html</context>    
  126.           <context context-type="linenumber">26</context>    
  127.         </context-group>    
  128.       </trans-unit>    
  129.       <trans-unit id="empDesignation" datatype="html">    
  130.         <source>Designation</source>    
  131.         <context-group purpose="location">    
  132.           <context context-type="sourcefile">src/app/employee/employee-list/employee-list.component.html</context>    
  133.           <context context-type="linenumber">33</context>    
  134.         </context-group>    
  135.         <context-group purpose="location">    
  136.           <context context-type="sourcefile">src/app/employee/employee-edit/employee-edit.component.html</context>    
  137.           <context context-type="linenumber">77</context>    
  138.         </context-group>    
  139.         <context-group purpose="location">    
  140.           <context context-type="sourcefile">src/app/employee/employee-detail/employee-detail.component.html</context>    
  141.           <context context-type="linenumber">30</context>    
  142.         </context-group>    
  143.       </trans-unit>    
  144.       <trans-unit id="edit" datatype="html">    
  145.         <source>    
  146.                   Edit    
  147.                 </source>    
  148.         <context-group purpose="location">    
  149.           <context context-type="sourcefile">src/app/employee/employee-list/employee-list.component.html</context>    
  150.           <context context-type="linenumber">50</context>    
  151.         </context-group>    
  152.         <context-group purpose="location">    
  153.           <context context-type="sourcefile">src/app/employee/employee-detail/employee-detail.component.html</context>    
  154.           <context context-type="linenumber">46</context>    
  155.         </context-group>    
  156.       </trans-unit>    
  157.       <trans-unit id="delete" datatype="html">    
  158.         <source>    
  159.                     Delete    
  160.                   </source>    
  161.         <context-group purpose="location">    
  162.           <context context-type="sourcefile">src/app/employee/employee-list/employee-list.component.html</context>    
  163.           <context context-type="linenumber">55</context>    
  164.         </context-group>    
  165.         <context-group purpose="location">    
  166.           <context context-type="sourcefile">src/app/employee/employee-edit/employee-edit.component.html</context>    
  167.           <context context-type="linenumber">111</context>    
  168.         </context-group>    
  169.       </trans-unit>    
  170.       <trans-unit id="save" datatype="html">    
  171.         <source>    
  172.               Save    
  173.             </source>    
  174.         <context-group purpose="location">    
  175.           <context context-type="sourcefile">src/app/employee/employee-edit/employee-edit.component.html</context>    
  176.           <context context-type="linenumber">97</context>    
  177.         </context-group>    
  178.       </trans-unit>    
  179.       <trans-unit id="cancel" datatype="html">    
  180.         <source>    
  181.               Cancel    
  182.             </source>    
  183.         <context-group purpose="location">    
  184.           <context context-type="sourcefile">src/app/employee/employee-edit/employee-edit.component.html</context>    
  185.           <context context-type="linenumber">104</context>    
  186.         </context-group>    
  187.       </trans-unit>    
  188.       <trans-unit id="back" datatype="html">    
  189.         <source>    
  190.             <x id="START_ITALIC_TEXT" ctype="x-i" equiv-text="<i>"/><x id="CLOSE_ITALIC_TEXT" ctype="x-i" equiv-text="</i>"/> Back    
  191.           </source>    
  192.         <context-group purpose="location">    
  193.           <context context-type="sourcefile">src/app/employee/employee-detail/employee-detail.component.html</context>    
  194.           <context context-type="linenumber">41</context>    
  195.         </context-group>    
  196.       </trans-unit>    
  197.     </body>    
  198.   </file>    
  199. </xliff>  
This file contains a list of <trans-unit> tags. These tags have all the content that was marked for translation using i18n attribute. You can also observe that each <trans-unit> tag has an “id” property associated with it.
 
Also, observe that each tag has “source” property associated. We can translate our application content to two new languages, Hindi and Malayalam.
 
Copy the content of messages.xlf to messages.hi.xlf and messages.ml.xlf. We can add a “target” property to each “source” property and add corresponding translation contents for each language.
 
messages.hi.xlf
  1. <?xml version="1.0" encoding="UTF-8" ?>    
  2. <xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">    
  3.   <file source-language="en" datatype="plaintext" original="ng2.template">    
  4.     <body>    
  5.       <trans-unit id="welcome" datatype="html">    
  6.         <source>Welcome to Employee App!</source>    
  7.         <target>एम्प्लोयी आप्प्लिकशन में आपका स्वागत है</target>    
  8.         <context-group purpose="location">    
  9.           <context context-type="sourcefile">src/app/home/home.component.html</context>    
  10.           <context context-type="linenumber">2</context>    
  11.         </context-group>    
  12.       </trans-unit>    
  13.       <trans-unit id="homemessage" datatype="html">    
  14.         <source>Localization in Angular using i18n</source>    
  15.         <target>i18n का उपयोग कर अंगुलार में स्थानीयकरण</target>    
  16.         <context-group purpose="location">    
  17.           <context context-type="sourcefile">src/app/home/home.component.html</context>    
  18.           <context context-type="linenumber">9</context>    
  19.         </context-group>    
  20.       </trans-unit>    
  21.       <trans-unit id="app" datatype="html">    
  22.         <source>Employee</source>    
  23.          <target>एम्प्लोयी</target>    
  24.         <context-group purpose="location">    
  25.           <context context-type="sourcefile">src/app/ui/header/header.component.html</context>    
  26.           <context context-type="linenumber">6</context>    
  27.         </context-group>    
  28.       </trans-unit>    
  29.       <trans-unit id="developer" datatype="html">    
  30.         <source>Developed with <x id="START_ITALIC_TEXT" ctype="x-i" equiv-text="<i>"/><x id="CLOSE_ITALIC_TEXT" ctype="x-i" equiv-text="</i>"/> by <x id="START_LINK" ctype="x-a" equiv-text="<a>"/>Sarathlal<x id="CLOSE_LINK" ctype="x-a" equiv-text="</a>"/></source>    
  31.         <target>के साथ विकसित <x id="START_ITALIC_TEXT" ctype="x-i" equiv-text="<i>"/><x id="CLOSE_ITALIC_TEXT" ctype="x-i" equiv-text="</i>"/> द्वारा <x id="START_LINK" ctype="x-a" equiv-text="<a>"/>सरथलाल<x id="CLOSE_LINK" ctype="x-a" equiv-text="</a>"/></target>    
  32.         <context-group purpose="location">    
  33.           <context context-type="sourcefile">src/app/ui/footer/footer.component.html</context>    
  34.           <context context-type="linenumber">2</context>    
  35.         </context-group>    
  36.       </trans-unit>    
  37.        <trans-unit id="home" datatype="html">    
  38.         <source>Home</source>    
  39.          <target>होम</target>    
  40.         <context-group purpose="location">    
  41.           <context context-type="sourcefile">src/app/ui/header/header.component.html</context>    
  42.           <context context-type="linenumber">5</context>    
  43.         </context-group>    
  44.       </trans-unit>    
  45.     
  46.        <trans-unit id="filter" datatype="html">    
  47.         <source>Filter by:</source>    
  48.         <target>के द्वारा छनित:</target>    
  49.         <context-group purpose="location">    
  50.           <context context-type="sourcefile">src/app/employee/employee-list/employee-list.component.html</context>    
  51.           <context context-type="linenumber">8</context>    
  52.         </context-group>    
  53.       </trans-unit>    
  54.       <trans-unit id="newemployee" datatype="html">    
  55.         <source>    
  56.             New Employee    
  57.           </source>    
  58.           <target>    
  59.             नए एम्प्लोयी    
  60.           </target>    
  61.         <context-group purpose="location">    
  62.           <context context-type="sourcefile">src/app/employee/employee-list/employee-list.component.html</context>    
  63.           <context context-type="linenumber">14</context>    
  64.         </context-group>    
  65.       </trans-unit>    
  66.       <trans-unit id="filteredBy" datatype="html">    
  67.         <source>Filtered by: <x id="INTERPOLATION" equiv-text="{{listFilter}}"/></source>    
  68.         <target>द्वारा फ़िल्टर किया गया : <x id="INTERPOLATION" equiv-text="{{listFilter}}"/></target>    
  69.         <context-group purpose="location">    
  70.           <context context-type="sourcefile">src/app/employee/employee-list/employee-list.component.html</context>    
  71.           <context context-type="linenumber">21</context>    
  72.         </context-group>    
  73.       </trans-unit>    
  74.       <trans-unit id="empName" datatype="html">    
  75.         <source>Name</source>    
  76.          <target>नाम</target>    
  77.         <context-group purpose="location">    
  78.           <context context-type="sourcefile">src/app/employee/employee-list/employee-list.component.html</context>    
  79.           <context context-type="linenumber">29</context>    
  80.         </context-group>    
  81.         <context-group purpose="location">    
  82.           <context context-type="sourcefile">src/app/employee/employee-edit/employee-edit.component.html</context>    
  83.           <context context-type="linenumber">13</context>    
  84.         </context-group>    
  85.       </trans-unit>    
  86.       <trans-unit id="empAddress" datatype="html">    
  87.         <source>Address</source>    
  88.         <target>पता</target>    
  89.         <context-group purpose="location">    
  90.           <context context-type="sourcefile">src/app/employee/employee-list/employee-list.component.html</context>    
  91.           <context context-type="linenumber">30</context>    
  92.         </context-group>    
  93.       </trans-unit>    
  94.       <trans-unit id="empGender" datatype="html">    
  95.         <source>Gender</source>    
  96.         <target>लिंग</target>    
  97.         <context-group purpose="location">    
  98.           <context context-type="sourcefile">src/app/employee/employee-list/employee-list.component.html</context>    
  99.           <context context-type="linenumber">31</context>    
  100.         </context-group>    
  101.       </trans-unit>    
  102.       <trans-unit id="empCompany" datatype="html">    
  103.         <source>Company</source>    
  104.         <target>कंपनी</target>    
  105.         <context-group purpose="location">    
  106.           <context context-type="sourcefile">src/app/employee/employee-list/employee-list.component.html</context>    
  107.           <context context-type="linenumber">32</context>    
  108.         </context-group>    
  109.       </trans-unit>    
  110.       <trans-unit id="empDesignation" datatype="html">    
  111.         <source>Designation</source>    
  112.          <target>पद</target>    
  113.         <context-group purpose="location">    
  114.           <context context-type="sourcefile">src/app/employee/employee-list/employee-list.component.html</context>    
  115.           <context context-type="linenumber">33</context>    
  116.         </context-group>    
  117.       </trans-unit>    
  118.       <trans-unit id="edit" datatype="html">    
  119.         <source>Edit</source>    
  120.          <target>एडिट</target>    
  121.         <context-group purpose="location">    
  122.           <context context-type="sourcefile">src/app/employee/employee-list/employee-list.component.html</context>    
  123.           <context context-type="linenumber">50</context>    
  124.         </context-group>    
  125.       </trans-unit>    
  126.       <trans-unit id="delete" datatype="html">    
  127.         <source>Delete</source>    
  128.         <target>डिलीट</target>    
  129.         <context-group purpose="location">    
  130.           <context context-type="sourcefile">src/app/employee/employee-list/employee-list.component.html</context>    
  131.           <context context-type="linenumber">55</context>    
  132.         </context-group>    
  133.       </trans-unit>    
  134.       <trans-unit id="save" datatype="html">    
  135.         <source>Save</source>    
  136.         <target>सेव</target>    
  137.         <context-group purpose="location">    
  138.           <context context-type="sourcefile">src/app/employee/employee-edit/employee-edit.component.html</context>    
  139.           <context context-type="linenumber">97</context>    
  140.         </context-group>    
  141.       </trans-unit>    
  142.       <trans-unit id="cancel" datatype="html">    
  143.         <source>Cancel</source>    
  144.         <target>कैंसल</target>    
  145.         <context-group purpose="location">    
  146.           <context context-type="sourcefile">src/app/employee/employee-edit/employee-edit.component.html</context>    
  147.           <context context-type="linenumber">104</context>    
  148.         </context-group>    
  149.       </trans-unit>    
  150.       <trans-unit id="back" datatype="html">    
  151.         <source>    
  152.             <x id="START_ITALIC_TEXT" ctype="x-i" equiv-text="<i>"/><x id="CLOSE_ITALIC_TEXT" ctype="x-i" equiv-text="</i>"/> Back    
  153.           </source>    
  154.            <target>    
  155.             <x id="START_ITALIC_TEXT" ctype="x-i" equiv-text="<i>"/><x id="CLOSE_ITALIC_TEXT" ctype="x-i" equiv-text="</i>"/> बैक    
  156.           </target>    
  157.         <context-group purpose="location">    
  158.           <context context-type="sourcefile">src/app/employee/employee-detail/employee-detail.component.html</context>    
  159.           <context context-type="linenumber">41</context>    
  160.         </context-group>    
  161.       </trans-unit>    
  162.     </body>    
  163.   </file>    
  164. </xliff>    
messages.ml.xlf
  1. <?xml version="1.0" encoding="UTF-8" ?>    
  2. <xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">    
  3.   <file source-language="en" datatype="plaintext" original="ng2.template">    
  4.     <body>    
  5.       <trans-unit id="welcome" datatype="html">    
  6.         <source>Welcome to Employee App!</source>    
  7.         <target>എംപ്ലോയീ അപ്പ്ലിക്കേഷനില്ലെക്ക് സ്വഗതം</target>    
  8.         <context-group purpose="location">    
  9.           <context context-type="sourcefile">src/app/home/home.component.html</context>    
  10.           <context context-type="linenumber">2</context>    
  11.         </context-group>    
  12.       </trans-unit>    
  13.       <trans-unit id="homemessage" datatype="html">    
  14.         <source>Localization in Angular using i18n</source>    
  15.         <target>ആംഗുലരിൽ i18n ഉപയോഗിച്ച് ലോക്കലൈസഷൻ</target>    
  16.         <context-group purpose="location">    
  17.           <context context-type="sourcefile">src/app/home/home.component.html</context>    
  18.           <context context-type="linenumber">9</context>    
  19.         </context-group>    
  20.       </trans-unit>    
  21.       <trans-unit id="app" datatype="html">    
  22.         <source>Employee</source>    
  23.          <target>എംപ്ലോയീ</target>    
  24.         <context-group purpose="location">    
  25.           <context context-type="sourcefile">src/app/ui/header/header.component.html</context>    
  26.           <context context-type="linenumber">6</context>    
  27.         </context-group>    
  28.       </trans-unit>    
  29.       <trans-unit id="developer" datatype="html">    
  30.         <source>Developed with <x id="START_ITALIC_TEXT" ctype="x-i" equiv-text="<i>"/><x id="CLOSE_ITALIC_TEXT" ctype="x-i" equiv-text="</i>"/> by <x id="START_LINK" ctype="x-a" equiv-text="<a>"/>Sarathlal<x id="CLOSE_LINK" ctype="x-a" equiv-text="</a>"/></source>    
  31.         <target>ഡെവലപ്പേഡ് വിത്ത് <x id="START_ITALIC_TEXT" ctype="x-i" equiv-text="<i>"/><x id="CLOSE_ITALIC_TEXT" ctype="x-i" equiv-text="</i>"/> ബൈ <x id="START_LINK" ctype="x-a" equiv-text="<a>"/>ശരത്ലാൽ<x id="CLOSE_LINK" ctype="x-a" equiv-text="</a>"/></target>    
  32.         <context-group purpose="location">    
  33.           <context context-type="sourcefile">src/app/ui/footer/footer.component.html</context>    
  34.           <context context-type="linenumber">2</context>    
  35.         </context-group>    
  36.       </trans-unit>    
  37.        <trans-unit id="home" datatype="html">    
  38.         <source>Home</source>    
  39.          <target>ഹോം</target>    
  40.         <context-group purpose="location">    
  41.           <context context-type="sourcefile">src/app/ui/header/header.component.html</context>    
  42.           <context context-type="linenumber">5</context>    
  43.         </context-group>    
  44.       </trans-unit>    
  45.     
  46.        <trans-unit id="filter" datatype="html">    
  47.         <source>Filter by:</source>    
  48.         <target>ഫിൽട്ടർ ബൈ:</target>    
  49.         <context-group purpose="location">    
  50.           <context context-type="sourcefile">src/app/employee/employee-list/employee-list.component.html</context>    
  51.           <context context-type="linenumber">8</context>    
  52.         </context-group>    
  53.       </trans-unit>    
  54.       <trans-unit id="newemployee" datatype="html">    
  55.         <source>    
  56.             New Employee    
  57.           </source>    
  58.           <target>    
  59.             പുതിയ എംപ്ലോയീ    
  60.           </target>    
  61.         <context-group purpose="location">    
  62.           <context context-type="sourcefile">src/app/employee/employee-list/employee-list.component.html</context>    
  63.           <context context-type="linenumber">14</context>    
  64.         </context-group>    
  65.       </trans-unit>    
  66.       <trans-unit id="filteredBy" datatype="html">    
  67.         <source>Filtered by: <x id="INTERPOLATION" equiv-text="{{listFilter}}"/></source>    
  68.         <target>ഫിൽട്ടർ ചെയ്‌തു : <x id="INTERPOLATION" equiv-text="{{listFilter}}"/></target>    
  69.         <context-group purpose="location">    
  70.           <context context-type="sourcefile">src/app/employee/employee-list/employee-list.component.html</context>    
  71.           <context context-type="linenumber">21</context>    
  72.         </context-group>    
  73.       </trans-unit>    
  74.       <trans-unit id="empName" datatype="html">    
  75.         <source>Name</source>    
  76.          <target>പേര്</target>    
  77.         <context-group purpose="location">    
  78.           <context context-type="sourcefile">src/app/employee/employee-list/employee-list.component.html</context>    
  79.           <context context-type="linenumber">29</context>    
  80.         </context-group>    
  81.         <context-group purpose="location">    
  82.           <context context-type="sourcefile">src/app/employee/employee-edit/employee-edit.component.html</context>    
  83.           <context context-type="linenumber">13</context>    
  84.         </context-group>    
  85.       </trans-unit>    
  86.       <trans-unit id="empAddress" datatype="html">    
  87.         <source>Address</source>    
  88.         <target>വിലാസം</target>    
  89.         <context-group purpose="location">    
  90.           <context context-type="sourcefile">src/app/employee/employee-list/employee-list.component.html</context>    
  91.           <context context-type="linenumber">30</context>    
  92.         </context-group>    
  93.       </trans-unit>    
  94.       <trans-unit id="empGender" datatype="html">    
  95.         <source>Gender</source>    
  96.         <target>ജൻഡർ</target>    
  97.         <context-group purpose="location">    
  98.           <context context-type="sourcefile">src/app/employee/employee-list/employee-list.component.html</context>    
  99.           <context context-type="linenumber">31</context>    
  100.         </context-group>    
  101.       </trans-unit>    
  102.       <trans-unit id="empCompany" datatype="html">    
  103.         <source>Company</source>    
  104.         <target>കമ്പനി</target>    
  105.         <context-group purpose="location">    
  106.           <context context-type="sourcefile">src/app/employee/employee-list/employee-list.component.html</context>    
  107.           <context context-type="linenumber">32</context>    
  108.         </context-group>    
  109.       </trans-unit>    
  110.       <trans-unit id="empDesignation" datatype="html">    
  111.         <source>Designation</source>    
  112.          <target>പദവി</target>    
  113.         <context-group purpose="location">    
  114.           <context context-type="sourcefile">src/app/employee/employee-list/employee-list.component.html</context>    
  115.           <context context-type="linenumber">33</context>    
  116.         </context-group>    
  117.       </trans-unit>    
  118.       <trans-unit id="edit" datatype="html">    
  119.         <source>Edit</source>    
  120.          <target>എഡിറ്റ്</target>    
  121.         <context-group purpose="location">    
  122.           <context context-type="sourcefile">src/app/employee/employee-list/employee-list.component.html</context>    
  123.           <context context-type="linenumber">50</context>    
  124.         </context-group>    
  125.       </trans-unit>    
  126.       <trans-unit id="delete" datatype="html">    
  127.         <source>Delete</source>    
  128.         <target>ഡിലീറ്റ്</target>    
  129.         <context-group purpose="location">    
  130.           <context context-type="sourcefile">src/app/employee/employee-list/employee-list.component.html</context>    
  131.           <context context-type="linenumber">55</context>    
  132.         </context-group>    
  133.       </trans-unit>    
  134.       <trans-unit id="save" datatype="html">    
  135.         <source>Save</source>    
  136.         <target>സേവ്</target>    
  137.         <context-group purpose="location">    
  138.           <context context-type="sourcefile">src/app/employee/employee-edit/employee-edit.component.html</context>    
  139.           <context context-type="linenumber">97</context>    
  140.         </context-group>    
  141.       </trans-unit>    
  142.       <trans-unit id="cancel" datatype="html">    
  143.         <source>Cancel</source>    
  144.         <target>ക്യാൻസൽ</target>    
  145.         <context-group purpose="location">    
  146.           <context context-type="sourcefile">src/app/employee/employee-edit/employee-edit.component.html</context>    
  147.           <context context-type="linenumber">104</context>    
  148.         </context-group>    
  149.       </trans-unit>    
  150.       <trans-unit id="back" datatype="html">    
  151.         <source>    
  152.             <x id="START_ITALIC_TEXT" ctype="x-i" equiv-text="<i>"/><x id="CLOSE_ITALIC_TEXT" ctype="x-i" equiv-text="</i>"/> Back    
  153.           </source>    
  154.            <target>    
  155.             <x id="START_ITALIC_TEXT" ctype="x-i" equiv-text="<i>"/><x id="CLOSE_ITALIC_TEXT" ctype="x-i" equiv-text="</i>"/> ബാക്ക്    
  156.           </target>    
  157.         <context-group purpose="location">    
  158.           <context context-type="sourcefile">src/app/employee/employee-detail/employee-detail.component.html</context>    
  159.           <context context-type="linenumber">41</context>    
  160.         </context-group>    
  161.       </trans-unit>    
  162.     </body>    
  163.   </file>    
  164. </xliff>    
We can modify the angular.json file to serve in multiple languages.
 
Localization In Angular 8 Employee App
 
angular.json
  1. {  
  2.   "$schema""./node_modules/@angular/cli/lib/config/schema.json",  
  3.   "version": 1,  
  4.   "newProjectRoot""projects",  
  5.   "projects": {  
  6.     "employeelocalization": {  
  7.       "projectType""application",  
  8.       "schematics": {},  
  9.       "root""",  
  10.       "sourceRoot""src",  
  11.       "prefix""app",  
  12.       "architect": {  
  13.         "build": {  
  14.           "builder""@angular-devkit/build-angular:browser",  
  15.           "options": {  
  16.             "outputPath""dist/employeelocalization",  
  17.             "index""src/index.html",  
  18.             "main""src/main.ts",  
  19.             "polyfills""src/polyfills.ts",  
  20.             "tsConfig""tsconfig.app.json",  
  21.             "aot"false,  
  22.             "assets": [  
  23.               "src/favicon.ico",  
  24.               "src/assets"  
  25.             ],  
  26.             "styles": [  
  27.               "src/styles.css"  
  28.             ],  
  29.             "scripts": []  
  30.           },  
  31.           "configurations": {  
  32.             "production": {  
  33.               "fileReplacements": [  
  34.                 {  
  35.                   "replace""src/environments/environment.ts",  
  36.                   "with""src/environments/environment.prod.ts"  
  37.                 }  
  38.               ],  
  39.               "optimization"true,  
  40.               "outputHashing""all",  
  41.               "sourceMap"false,  
  42.               "extractCss"true,  
  43.               "namedChunks"false,  
  44.               "aot"true,  
  45.               "extractLicenses"true,  
  46.               "vendorChunk"false,  
  47.               "buildOptimizer"true,  
  48.               "budgets": [  
  49.                 {  
  50.                   "type""initial",  
  51.                   "maximumWarning""2mb",  
  52.                   "maximumError""5mb"  
  53.                 }  
  54.               ]  
  55.             },  
  56.             "hi": {  
  57.               "aot"true,  
  58.               "i18nFile""src/app/translate/messages.hi.xlf",  
  59.               "i18nFormat""xlf",  
  60.               "i18nLocale""hi",  
  61.               "i18nMissingTranslation""error"  
  62.             },  
  63.             "ml": {  
  64.               "aot"true,  
  65.               "i18nFile""src/app/translate/messages.ml.xlf",  
  66.               "i18nFormat""xlf",  
  67.               "i18nLocale""ml",  
  68.               "i18nMissingTranslation""error"  
  69.             }  
  70.           }  
  71.         },  
  72.         "serve": {  
  73.           "builder""@angular-devkit/build-angular:dev-server",  
  74.           "options": {  
  75.             "browserTarget""employeelocalization:build"  
  76.           },  
  77.           "configurations": {  
  78.             "production": {  
  79.               "browserTarget""employeelocalization:build:production"  
  80.             },  
  81.             "hi": {  
  82.               "browserTarget""employeelocalization:build:hi"  
  83.             },  
  84.             "ml": {  
  85.               "browserTarget""employeelocalization:build:ml"  
  86.             }  
  87.           }  
  88.         },  
  89.         "extract-i18n": {  
  90.           "builder""@angular-devkit/build-angular:extract-i18n",  
  91.           "options": {  
  92.             "browserTarget""employeelocalization:build"  
  93.           }  
  94.         },  
  95.         "test": {  
  96.           "builder""@angular-devkit/build-angular:karma",  
  97.           "options": {  
  98.             "main""src/test.ts",  
  99.             "polyfills""src/polyfills.ts",  
  100.             "tsConfig""tsconfig.spec.json",  
  101.             "karmaConfig""karma.conf.js",  
  102.             "assets": [  
  103.               "src/favicon.ico",  
  104.               "src/assets"  
  105.             ],  
  106.             "styles": [  
  107.               "src/styles.css"  
  108.             ],  
  109.             "scripts": []  
  110.           }  
  111.         },  
  112.         "lint": {  
  113.           "builder""@angular-devkit/build-angular:tslint",  
  114.           "options": {  
  115.             "tsConfig": [  
  116.               "tsconfig.app.json",  
  117.               "tsconfig.spec.json",  
  118.               "e2e/tsconfig.json"  
  119.             ],  
  120.             "exclude": [  
  121.               "**/node_modules/**"  
  122.             ]  
  123.           }  
  124.         },  
  125.         "e2e": {  
  126.           "builder""@angular-devkit/build-angular:protractor",  
  127.           "options": {  
  128.             "protractorConfig""e2e/protractor.conf.js",  
  129.             "devServerTarget""employeelocalization:serve"  
  130.           },  
  131.           "configurations": {  
  132.             "production": {  
  133.               "devServerTarget""employeelocalization:serve:production"  
  134.             }  
  135.           }  
  136.         }  
  137.       }  
  138.     }},  
  139.   "defaultProject""employeelocalization"  
  140. }  
Use the below command to run the application in Hindi.
 
ng serve --configuration=hi
 
By default, application will run on 4200 port.
 
Localization In Angular 8 Employee App
 
If you click the employee menu, you will get the below screen.
 
Localization In Angular 8 Employee App
 
You can add a new employee by clicking the corresponding button.
 
Localization In Angular 8 Employee App
 
You can also click any employee name and display employee information as read-only.
 
Localization In Angular 8 Employee App
 
You can click the Edit button to edit the employee data.
 
Localization In Angular 8 Employee App
 
We can open one more command prompt and run the application in a different port with the Malayalam language also.
 
ng serve --configuration=ml --port 4201
 
Localization In Angular 8 Employee App

Conclusion

 
In this post, we have seen how to create an Employee application using Angular CLI commands and we have created translation source files and content files for different languages using i18n and localization. We have run the application in two different ports with two languages, Hindi and Malayalam.


Similar Articles