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. <div class="jumbotron">
  8. <h5 i18n="@@homemessage">
  9. Localization in Angular 8 using i18n
  10. </h5>
  11. </div>
  12. </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. export class GenericValidator {
  3. constructor(private validationMessages: { [key: string]: { [key: string]: string } }) {
  4. }
  5. processMessages(container: FormGroup): { [key: string]: string } {
  6. const messages = {};
  7. for (const controlKey in container.controls) {
  8. if (container.controls.hasOwnProperty(controlKey)) {
  9. const c = container.controls[controlKey];
  10. // If it is a FormGroup, process its child controls.
  11. if (c instanceof FormGroup) {
  12. const childMessages = this.processMessages(c);
  13. Object.assign(messages, childMessages);
  14. } else {
  15. // Only validate if there are validation messages for the control
  16. if (this.validationMessages[controlKey]) {
  17. messages[controlKey] = '';
  18. if ((c.dirty || c.touched) && c.errors) {
  19. Object.keys(c.errors).map(messageKey => {
  20. if (this.validationMessages[controlKey][messageKey]) {
  21. messages[controlKey] += this.validationMessages[controlKey][messageKey] + ' ';
  22. }
  23. });
  24. }
  25. }
  26. }
  27. }
  28. }
  29. return messages;
  30. }
  31. getErrorCount(container: FormGroup): number {
  32. let errorCount = 0;
  33. for (const controlKey in container.controls) {
  34. if (container.controls.hasOwnProperty(controlKey)) {
  35. if (container.controls[controlKey].errors) {
  36. errorCount += Object.keys(container.controls[controlKey].errors).length;
  37. console.log(errorCount);
  38. }
  39. }
  40. }
  41. return errorCount;
  42. }
  43. }
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. export class EmployeeData implements InMemoryDbService {
  4. createDb() {
  5. const employees: Employee[] = [
  6. {
  7. 'id': '496f04a0-5c6d-400f-a0a8-0929088dc41e',
  8. 'name': 'Sarath Lal',
  9. 'address': 'Kakkanad',
  10. 'gender': 'Male',
  11. 'company': 'Orion Business Innovation',
  12. 'designation': 'Technical Lead'
  13. },
  14. {
  15. 'id': '03810397-381c-4b9d-85f6-d7c87622e518',
  16. 'name': 'Baiju Mathew',
  17. 'address': 'Kakkanad',
  18. 'gender': 'Male',
  19. 'company': 'Orion Business Innovation',
  20. 'designation': 'Project Manager'
  21. }
  22. ];
  23. return { employees };
  24. }
  25. }
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. @Injectable({
  8. providedIn: 'root'
  9. })
  10. export class EmployeeService {
  11. private employeesUrl = 'api/employees';
  12. constructor(private http: HttpClient) { }
  13. getEmployees(): Observable<Employee[]> {
  14. return this.http.get<Employee[]>(this.employeesUrl)
  15. .pipe(
  16. catchError(this.handleError)
  17. );
  18. }
  19. getEmployee(id: string): Observable<Employee> {
  20. if (id === '') {
  21. return of(this.initializeEmployee());
  22. }
  23. const url = `${this.employeesUrl}/${id}`;
  24. return this.http.get<Employee>(url)
  25. .pipe(
  26. catchError(this.handleError)
  27. );
  28. }
  29. createEmployee(employee: Employee): Observable<Employee> {
  30. const headers = new HttpHeaders({ 'Content-Type': 'application/json' });
  31. employee.id = Guid.newGuid();
  32. debugger;
  33. return this.http.post<Employee>(this.employeesUrl, employee, { headers: headers })
  34. .pipe(
  35. catchError(this.handleError)
  36. );
  37. }
  38. deleteEmployee(id: string): Observable<{}> {
  39. const headers = new HttpHeaders({ 'Content-Type': 'application/json' });
  40. const url = `${this.employeesUrl}/${id}`;
  41. return this.http.delete<Employee>(url, { headers: headers })
  42. .pipe(
  43. catchError(this.handleError)
  44. );
  45. }
  46. updateEmployee(employee: Employee): Observable<Employee> {
  47. const headers = new HttpHeaders({ 'Content-Type': 'application/json' });
  48. const url = `${this.employeesUrl}/${employee.id}`;
  49. return this.http.put<Employee>(url, employee, { headers: headers })
  50. .pipe(
  51. map(() => employee),
  52. catchError(this.handleError)
  53. );
  54. }
  55. private handleError(err) {
  56. let errorMessage: string;
  57. if (err.error instanceof ErrorEvent) {
  58. errorMessage = `An error occurred: ${err.error.message}`;
  59. } else {
  60. errorMessage = `Backend returned code ${err.status}: ${err.body.error}`;
  61. }
  62. console.error(err);
  63. return throwError(errorMessage);
  64. }
  65. private initializeEmployee(): Employee {
  66. return {
  67. id: null,
  68. name: null,
  69. address: null,
  70. gender: null,
  71. company: null,
  72. designation: null
  73. };
  74. }
  75. }
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. @Component({
  5. selector: 'app-employee-list',
  6. templateUrl: './employee-list.component.html',
  7. styleUrls: ['./employee-list.component.css']
  8. })
  9. export class EmployeeListComponent implements OnInit {
  10. pageTitle = 'Employee List';
  11. filteredEmployees: Employee[] = [];
  12. employees: Employee[] = [];
  13. errorMessage = '';
  14. _listFilter = '';
  15. get listFilter(): string {
  16. return this._listFilter;
  17. }
  18. set listFilter(value: string) {
  19. this._listFilter = value;
  20. this.filteredEmployees = this.listFilter ? this.performFilter(this.listFilter) : this.employees;
  21. }
  22. constructor(private employeeService: EmployeeService) { }
  23. performFilter(filterBy: string): Employee[] {
  24. filterBy = filterBy.toLocaleLowerCase();
  25. return this.employees.filter((employee: Employee) =>
  26. employee.name.toLocaleLowerCase().indexOf(filterBy) !== -1);
  27. }
  28. ngOnInit(): void {
  29. this.employeeService.getEmployees().subscribe(
  30. employees => {
  31. this.employees = employees;
  32. this.filteredEmployees = this.employees;
  33. },
  34. error => this.errorMessage = <any>error
  35. );
  36. }
  37. deleteEmployee(id: string, name: string): void {
  38. if (id === '') {
  39. // Don't delete, it was never saved.
  40. this.onSaveComplete();
  41. } else {
  42. if (confirm(`Are you sure want to delete this Employee: ${name}?`)) {
  43. this.employeeService.deleteEmployee(id)
  44. .subscribe(
  45. () => this.onSaveComplete(),
  46. (error: any) => this.errorMessage = <any>error
  47. );
  48. }
  49. }
  50. }
  51. onSaveComplete(): void {
  52. this.employeeService.getEmployees().subscribe(
  53. employees => {
  54. this.employees = employees;
  55. this.filteredEmployees = this.employees;
  56. },
  57. error => this.errorMessage = <any>error
  58. );
  59. }
  60. }
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. <div class="card-body">
  6. <div class="row">
  7. <div class="col-md-2" i18n="@@filter">Filter by:</div>
  8. <div class="col-md-4">
  9. <input type="text" [(ngModel)]="listFilter" />
  10. </div>
  11. <div class="col-md-3"></div>
  12. <div class="col-md-3">
  13. <button class="btn btn-primary mr-3" [routerLink]="['/employees/0/edit']" i18n="@@newemployee">
  14. New Employee
  15. </button>
  16. </div>
  17. </div>
  18. <div class="row" *ngIf="listFilter">
  19. <div class="col-md-6">
  20. <h4 i18n="@@filteredBy">Filtered by: {{listFilter}}</h4>
  21. </div>
  22. </div>
  23. <div class="table-responsive">
  24. <table class="table mb-0" *ngIf="employees && employees.length">
  25. <thead>
  26. <tr>
  27. <th i18n="@@empName">Name</th>
  28. <th i18n="@@empAddress">Address</th>
  29. <th i18n="@@empGender">Gender</th>
  30. <th i18n="@@empCompany">Company</th>
  31. <th i18n="@@empDesignation">Designation</th>
  32. <th></th>
  33. <th></th>
  34. </tr>
  35. </thead>
  36. <tbody>
  37. <tr *ngFor="let employee of filteredEmployees">
  38. <td>
  39. <a [routerLink]="['/employees', employee.id]">
  40. {{ employee.name }}
  41. </a>
  42. </td>
  43. <td>{{ employee.address }}</td>
  44. <td>{{ employee.gender }}</td>
  45. <td>{{ employee.company }}</td>
  46. <td>{{ employee.designation}} </td>
  47. <td>
  48. <button class="btn btn-outline-primary btn-sm" [routerLink]="['/employees', employee.id, 'edit']" i18n="@@edit">
  49. Edit
  50. </button>
  51. </td>
  52. <td>
  53. <button class="btn btn-outline-warning btn-sm" (click)="deleteEmployee(employee.id,employee.name);" i18n="@@delete">
  54. Delete
  55. </button>
  56. </td>
  57. </tr>
  58. </tbody>
  59. </table>
  60. </div>
  61. </div>
  62. </div>
  63. <div *ngIf="errorMessage" class="alert alert-danger">
  64. Error: {{ errorMessage }}
  65. </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. @Component({
  10. selector: 'app-employee-edit',
  11. templateUrl: './employee-edit.component.html',
  12. styleUrls: ['./employee-edit.component.css']
  13. })
  14. export class EmployeeEditComponent implements OnInit, AfterViewInit, OnDestroy {
  15. @ViewChildren(FormControlName, { read: ElementRef }) formInputElements: ElementRef[];
  16. pageTitle = 'Employee Edit';
  17. errorMessage: string;
  18. employeeForm: FormGroup;
  19. employee: Employee;
  20. private sub: Subscription;
  21. displayMessage: { [key: string]: string } = {};
  22. private validationMessages: { [key: string]: { [key: string]: string } };
  23. private genericValidator: GenericValidator;
  24. constructor(private fb: FormBuilder,
  25. private route: ActivatedRoute,
  26. private router: Router,
  27. private employeeService: EmployeeService) {
  28. this.validationMessages = {
  29. name: {
  30. required: 'Employee name is required.',
  31. minlength: 'Employee name must be at least three characters.',
  32. maxlength: 'Employee name cannot exceed 50 characters.'
  33. },
  34. address: {
  35. required: 'Employee address is required.',
  36. }
  37. };
  38. this.genericValidator = new GenericValidator(this.validationMessages);
  39. }
  40. ngOnInit() {
  41. this.employeeForm = this.fb.group({
  42. name: ['', [Validators.required
  43. ]],
  44. address: ['', [Validators.required]],
  45. gender: '',
  46. company: '',
  47. designation: ''
  48. });
  49. this.sub = this.route.paramMap.subscribe(
  50. params => {
  51. const id = params.get('id');
  52. if (id == '0') {
  53. const employee: Employee = { id: "0", name: "", address: "", gender: "", company: "", designation: "" };
  54. this.displayEmployee(employee);
  55. }
  56. else {
  57. this.getEmployee(id);
  58. }
  59. }
  60. );
  61. }
  62. ngOnDestroy(): void {
  63. this.sub.unsubscribe();
  64. }
  65. ngAfterViewInit(): void {
  66. // Watch for the blur event from any input element on the form.
  67. const controlBlurs: Observable<any>[] = this.formInputElements
  68. .map((formControl: ElementRef) => fromEvent(formControl.nativeElement, 'blur'));
  69. // Merge the blur event observable with the valueChanges observable
  70. merge(this.employeeForm.valueChanges, ...controlBlurs).pipe(
  71. debounceTime(800)
  72. ).subscribe(value => {
  73. this.displayMessage = this.genericValidator.processMessages(this.employeeForm);
  74. });
  75. }
  76. getEmployee(id: string): void {
  77. this.employeeService.getEmployee(id)
  78. .subscribe(
  79. (employee: Employee) => this.displayEmployee(employee),
  80. (error: any) => this.errorMessage = <any>error
  81. );
  82. }
  83. displayEmployee(employee: Employee): void {
  84. if (this.employeeForm) {
  85. this.employeeForm.reset();
  86. }
  87. this.employee = employee;
  88. if (this.employee.id == '0') {
  89. this.pageTitle = 'Add Employee';
  90. } else {
  91. this.pageTitle = `Edit Employee: ${this.employee.name}`;
  92. }
  93. // Update the data on the form
  94. this.employeeForm.patchValue({
  95. name: this.employee.name,
  96. address: this.employee.address,
  97. gender: this.employee.gender,
  98. company: this.employee.company,
  99. designation: this.employee.designation
  100. });
  101. }
  102. deleteEmployee(): void {
  103. if (this.employee.id == '0') {
  104. // Don't delete, it was never saved.
  105. this.onSaveComplete();
  106. } else {
  107. if (confirm(`Are you sure want to delete this Employee: ${this.employee.name}?`)) {
  108. this.employeeService.deleteEmployee(this.employee.id)
  109. .subscribe(
  110. () => this.onSaveComplete(),
  111. (error: any) => this.errorMessage = <any>error
  112. );
  113. }
  114. }
  115. }
  116. saveEmployee(): void {
  117. if (this.employeeForm.valid) {
  118. if (this.employeeForm.dirty) {
  119. const p = { ...this.employee, ...this.employeeForm.value };
  120. if (p.id === '0') {
  121. this.employeeService.createEmployee(p)
  122. .subscribe(
  123. () => this.onSaveComplete(),
  124. (error: any) => this.errorMessage = <any>error
  125. );
  126. } else {
  127. this.employeeService.updateEmployee(p)
  128. .subscribe(
  129. () => this.onSaveComplete(),
  130. (error: any) => this.errorMessage = <any>error
  131. );
  132. }
  133. } else {
  134. this.onSaveComplete();
  135. }
  136. } else {
  137. this.errorMessage = 'Please correct the validation errors.';
  138. }
  139. }
  140. onSaveComplete(): void {
  141. // Reset the form to clear the flags
  142. this.employeeForm.reset();
  143. this.router.navigate(['/employees']);
  144. }
  145. }
employee-edit.component.html
  1. <div class="card">
  2. <div class="card-header">
  3. {{pageTitle}}
  4. </div>
  5. <div class="card-body">
  6. <form novalidate
  7. (ngSubmit)="saveEmployee()"
  8. [formGroup]="employeeForm">
  9. <div class="form-group row mb-2">
  10. <label class="col-md-2 col-form-label"
  11. for="employeeNameId" i18n="@@empName">Name</label>
  12. <div class="col-md-8">
  13. <input class="form-control"
  14. id="employeeNameId"
  15. type="text"
  16. placeholder="Name (required)"
  17. formControlName="name"
  18. [ngClass]="{'is-invalid': displayMessage.name }" />
  19. <span class="invalid-feedback">
  20. {{displayMessage.name}}
  21. </span>
  22. </div>
  23. </div>
  24. <div class="form-group row mb-2">
  25. <label class="col-md-2 col-form-label"
  26. for="addressId" i18n="@@empAddress">Address</label>
  27. <div class="col-md-8">
  28. <input class="form-control"
  29. id="addressId"
  30. type="text"
  31. placeholder="Address"
  32. formControlName="address"
  33. [ngClass]="{'is-invalid': displayMessage.address}" />
  34. <span class="invalid-feedback">
  35. {{displayMessage.address}}
  36. </span>
  37. </div>
  38. </div>
  39. <div class="form-group row mb-2">
  40. <label class="col-md-2 col-form-label"
  41. for="genderId" i18n="@@empGender">Gender</label>
  42. <div class="col-md-8">
  43. <input class="form-control"
  44. id="genderId"
  45. type="text"
  46. placeholder="Gender"
  47. formControlName="gender"
  48. [ngClass]="{'is-invalid': displayMessage.gender}" />
  49. <span class="invalid-feedback">
  50. {{displayMessage.gender}}
  51. </span>
  52. </div>
  53. </div>
  54. <div class="form-group row mb-2">
  55. <label class="col-md-2 col-form-label"
  56. for="companyId" i18n="@@empCompany">Company</label>
  57. <div class="col-md-8">
  58. <input class="form-control"
  59. id="companyId"
  60. type="text"
  61. placeholder="Company"
  62. formControlName="company"
  63. [ngClass]="{'is-invalid': displayMessage.gender}" />
  64. <span class="invalid-feedback">
  65. {{displayMessage.company}}
  66. </span>
  67. </div>
  68. </div>
  69. <div class="form-group row mb-2">
  70. <label class="col-md-2 col-form-label"
  71. for="designationId" i18n="@@empDesignation">Designation</label>
  72. <div class="col-md-8">
  73. <input class="form-control"
  74. id="designationId"
  75. type="text"
  76. placeholder="Designation"
  77. formControlName="designation"
  78. [ngClass]="{'is-invalid': displayMessage.designation}" />
  79. <span class="invalid-feedback">
  80. {{displayMessage.designation}}
  81. </span>
  82. </div>
  83. </div>
  84. <div class="form-group row mb-2">
  85. <div class="offset-md-2 col-md-6">
  86. <button class="btn btn-primary mr-3"
  87. style="width:100px;"
  88. type="submit"
  89. [title]="employeeForm.valid ? 'Save your entered data' : 'Disabled until the form data is valid'"
  90. [disabled]="!employeeForm.valid" i18n="@@save">
  91. Save
  92. </button>
  93. <button class="btn btn-outline-secondary mr-3"
  94. style="width:140px;"
  95. type="button"
  96. title="Cancel your edits"
  97. [routerLink]="['/employees']" i18n="@@cancel">
  98. Cancel
  99. </button>
  100. <button class="btn btn-outline-warning" *ngIf="employee.id != '0'"
  101. style="width:100px"
  102. type="button"
  103. title="Delete this product"
  104. (click)="deleteEmployee()" i18n="@@delete">
  105. Delete
  106. </button>
  107. </div>
  108. </div>
  109. </form>
  110. </div>
  111. <div class="alert alert-danger"
  112. *ngIf="errorMessage">{{errorMessage}}
  113. </div>
  114. </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. @Injectable({
  6. providedIn: 'root'
  7. })
  8. export class EmployeeEditGuard implements CanDeactivate<EmployeeEditComponent> {
  9. canDeactivate(component: EmployeeEditComponent): Observable<boolean> | Promise<boolean> | boolean {
  10. if (component.employeeForm.dirty) {
  11. const name = component.employeeForm.get('name').value || 'New Employee';
  12. return confirm(`Navigate away and lose all changes to ${name}?`);
  13. }
  14. return true;
  15. }
  16. }
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. @Component({
  6. selector: 'app-employee-detail',
  7. templateUrl: './employee-detail.component.html',
  8. styleUrls: ['./employee-detail.component.css']
  9. })
  10. export class EmployeeDetailComponent implements OnInit {
  11. pageTitle = 'Employee Detail';
  12. errorMessage = '';
  13. employee: Employee | undefined;
  14. constructor(private route: ActivatedRoute,
  15. private router: Router,
  16. private employeeService: EmployeeService) { }
  17. ngOnInit() {
  18. const param = this.route.snapshot.paramMap.get('id');
  19. if (param) {
  20. const id = param;
  21. this.getEmployee(id);
  22. }
  23. }
  24. getEmployee(id: string) {
  25. this.employeeService.getEmployee(id).subscribe(
  26. employee => this.employee = employee,
  27. error => this.errorMessage = <any>error);
  28. }
  29. onBack(): void {
  30. this.router.navigate(['/employees']);
  31. }
  32. }
employee-detail.component.html
  1. <div class="card">
  2. <div class="card-header"
  3. *ngIf="employee">
  4. {{pageTitle + ": " + employee.name}}
  5. </div>
  6. <div class="card-body"
  7. *ngIf="employee">
  8. <div class="row">
  9. <div class="col-md-8">
  10. <div class="row">
  11. <div class="col-md-3" i18n="@@empName">Name:</div>
  12. <div class="col-md-6">{{employee.name}}</div>
  13. </div>
  14. <div class="row">
  15. <div class="col-md-3" i18n="@@empAddress">Address:</div>
  16. <div class="col-md-6">{{employee.address}}</div>
  17. </div>
  18. <div class="row">
  19. <div class="col-md-3" i18n="@@empGender">Gender:</div>
  20. <div class="col-md-6">{{employee.gender}}</div>
  21. </div>
  22. <div class="row">
  23. <div class="col-md-3" i18n="@@empCompany">Company:</div>
  24. <div class="col-md-6">{{employee.company}}</div>
  25. </div>
  26. <div class="row">
  27. <div class="col-md-3" i18n="@@empDesignation">Designation:</div>
  28. <div class="col-md-6">{{employee.designation}}</div>
  29. </div>
  30. </div>
  31. </div>
  32. <div class="row mt-4">
  33. <div class="col-md-6">
  34. <button class="btn btn-outline-secondary mr-3"
  35. style="width:100px"
  36. (click)="onBack()" i18n="@@back">
  37. <i class="fa fa-chevron-left"></i> Back
  38. </button>
  39. <button class="btn btn-outline-primary"
  40. style="width:100px"
  41. [routerLink]="['/employees', employee.id, 'edit']" i18n="@@edit">
  42. Edit
  43. </button>
  44. </div>
  45. </div>
  46. </div>
  47. <div class="alert alert-danger"
  48. *ngIf="errorMessage">{{errorMessage}}
  49. </div>
  50. </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. export const routes: Routes = [
  8. {
  9. path: 'home',
  10. component: HomeComponent
  11. },
  12. {
  13. path: 'employees',
  14. component: EmployeeListComponent
  15. },
  16. {
  17. path: 'employees/:id',
  18. component: EmployeeDetailComponent
  19. },
  20. {
  21. path: 'employees/:id/edit',
  22. canDeactivate: [EmployeeEditGuard],
  23. component: EmployeeEditComponent
  24. },
  25. {
  26. path: '',
  27. redirectTo: 'home',
  28. pathMatch: 'full'
  29. },
  30. {
  31. path: '**',
  32. redirectTo: 'home',
  33. pathMatch: 'full'
  34. }
  35. ];
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. import { AppComponent } from './app.component';
  4. import { HomeComponent } from './home/home.component';
  5. import { HeaderComponent } from './ui/header/header.component';
  6. import { FooterComponent } from './ui/footer/footer.component';
  7. import { LayoutComponent } from './ui/layout/layout.component';
  8. import { EmployeeListComponent } from './employee/employee-list/employee-list.component';
  9. import { EmployeeEditComponent } from './employee/employee-edit/employee-edit.component';
  10. import { EmployeeDetailComponent } from './employee/employee-detail/employee-detail.component';
  11. import { ReactiveFormsModule, FormsModule } from '@angular/forms';
  12. import { RouterModule } from '@angular/router';
  13. import { HttpClientModule } from '@angular/common/http';
  14. import { routes } from './router-config';
  15. import { InMemoryWebApiModule } from 'angular-in-memory-web-api';
  16. import { EmployeeData } from './employee/employee-data';
  17. @NgModule({
  18. declarations: [
  19. AppComponent,
  20. HomeComponent,
  21. HeaderComponent,
  22. FooterComponent,
  23. LayoutComponent,
  24. EmployeeListComponent,
  25. EmployeeEditComponent,
  26. EmployeeDetailComponent
  27. ],
  28. imports: [
  29. BrowserModule,
  30. ReactiveFormsModule,
  31. FormsModule,
  32. RouterModule,
  33. HttpClientModule,
  34. RouterModule.forRoot(routes),
  35. InMemoryWebApiModule.forRoot(EmployeeData),
  36. ],
  37. providers: [],
  38. bootstrap: [AppComponent]
  39. })
  40. 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. div.card-header {
  5. font-size: large;
  6. }
  7. div.card {
  8. margin-top: 10px
  9. }
  10. .table {
  11. margin-top: 10px
  12. }
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. <trans-unit id="filter" datatype="html">
  46. <source>Filter by:</source>
  47. <target>के द्वारा छनित:</target>
  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. <target>
  58. नए एम्प्लोयी
  59. </target>
  60. <context-group purpose="location">
  61. <context context-type="sourcefile">src/app/employee/employee-list/employee-list.component.html</context>
  62. <context context-type="linenumber">14</context>
  63. </context-group>
  64. </trans-unit>
  65. <trans-unit id="filteredBy" datatype="html">
  66. <source>Filtered by: <x id="INTERPOLATION" equiv-text="{{listFilter}}"/></source>
  67. <target>द्वारा फ़िल्टर किया गया : <x id="INTERPOLATION" equiv-text="{{listFilter}}"/></target>
  68. <context-group purpose="location">
  69. <context context-type="sourcefile">src/app/employee/employee-list/employee-list.component.html</context>
  70. <context context-type="linenumber">21</context>
  71. </context-group>
  72. </trans-unit>
  73. <trans-unit id="empName" datatype="html">
  74. <source>Name</source>
  75. <target>नाम</target>
  76. <context-group purpose="location">
  77. <context context-type="sourcefile">src/app/employee/employee-list/employee-list.component.html</context>
  78. <context context-type="linenumber">29</context>
  79. </context-group>
  80. <context-group purpose="location">
  81. <context context-type="sourcefile">src/app/employee/employee-edit/employee-edit.component.html</context>
  82. <context context-type="linenumber">13</context>
  83. </context-group>
  84. </trans-unit>
  85. <trans-unit id="empAddress" datatype="html">
  86. <source>Address</source>
  87. <target>पता</target>
  88. <context-group purpose="location">
  89. <context context-type="sourcefile">src/app/employee/employee-list/employee-list.component.html</context>
  90. <context context-type="linenumber">30</context>
  91. </context-group>
  92. </trans-unit>
  93. <trans-unit id="empGender" datatype="html">
  94. <source>Gender</source>
  95. <target>लिंग</target>
  96. <context-group purpose="location">
  97. <context context-type="sourcefile">src/app/employee/employee-list/employee-list.component.html</context>
  98. <context context-type="linenumber">31</context>
  99. </context-group>
  100. </trans-unit>
  101. <trans-unit id="empCompany" datatype="html">
  102. <source>Company</source>
  103. <target>कंपनी</target>
  104. <context-group purpose="location">
  105. <context context-type="sourcefile">src/app/employee/employee-list/employee-list.component.html</context>
  106. <context context-type="linenumber">32</context>
  107. </context-group>
  108. </trans-unit>
  109. <trans-unit id="empDesignation" datatype="html">
  110. <source>Designation</source>
  111. <target>पद</target>
  112. <context-group purpose="location">
  113. <context context-type="sourcefile">src/app/employee/employee-list/employee-list.component.html</context>
  114. <context context-type="linenumber">33</context>
  115. </context-group>
  116. </trans-unit>
  117. <trans-unit id="edit" datatype="html">
  118. <source>Edit</source>
  119. <target>एडिट</target>
  120. <context-group purpose="location">
  121. <context context-type="sourcefile">src/app/employee/employee-list/employee-list.component.html</context>
  122. <context context-type="linenumber">50</context>
  123. </context-group>
  124. </trans-unit>
  125. <trans-unit id="delete" datatype="html">
  126. <source>Delete</source>
  127. <target>डिलीट</target>
  128. <context-group purpose="location">
  129. <context context-type="sourcefile">src/app/employee/employee-list/employee-list.component.html</context>
  130. <context context-type="linenumber">55</context>
  131. </context-group>
  132. </trans-unit>
  133. <trans-unit id="save" datatype="html">
  134. <source>Save</source>
  135. <target>सेव</target>
  136. <context-group purpose="location">
  137. <context context-type="sourcefile">src/app/employee/employee-edit/employee-edit.component.html</context>
  138. <context context-type="linenumber">97</context>
  139. </context-group>
  140. </trans-unit>
  141. <trans-unit id="cancel" datatype="html">
  142. <source>Cancel</source>
  143. <target>कैंसल</target>
  144. <context-group purpose="location">
  145. <context context-type="sourcefile">src/app/employee/employee-edit/employee-edit.component.html</context>
  146. <context context-type="linenumber">104</context>
  147. </context-group>
  148. </trans-unit>
  149. <trans-unit id="back" datatype="html">
  150. <source>
  151. <x id="START_ITALIC_TEXT" ctype="x-i" equiv-text="<i>"/><x id="CLOSE_ITALIC_TEXT" ctype="x-i" equiv-text="</i>"/> Back
  152. </source>
  153. <target>
  154. <x id="START_ITALIC_TEXT" ctype="x-i" equiv-text="<i>"/><x id="CLOSE_ITALIC_TEXT" ctype="x-i" equiv-text="</i>"/> बैक
  155. </target>
  156. <context-group purpose="location">
  157. <context context-type="sourcefile">src/app/employee/employee-detail/employee-detail.component.html</context>
  158. <context context-type="linenumber">41</context>
  159. </context-group>
  160. </trans-unit>
  161. </body>
  162. </file>
  163. </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. <trans-unit id="filter" datatype="html">
  46. <source>Filter by:</source>
  47. <target>ഫിൽട്ടർ ബൈ:</target>
  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. <target>
  58. പുതിയ എംപ്ലോയീ
  59. </target>
  60. <context-group purpose="location">
  61. <context context-type="sourcefile">src/app/employee/employee-list/employee-list.component.html</context>
  62. <context context-type="linenumber">14</context>
  63. </context-group>
  64. </trans-unit>
  65. <trans-unit id="filteredBy" datatype="html">
  66. <source>Filtered by: <x id="INTERPOLATION" equiv-text="{{listFilter}}"/></source>
  67. <target>ഫിൽട്ടർ ചെയ്‌തു : <x id="INTERPOLATION" equiv-text="{{listFilter}}"/></target>
  68. <context-group purpose="location">
  69. <context context-type="sourcefile">src/app/employee/employee-list/employee-list.component.html</context>
  70. <context context-type="linenumber">21</context>
  71. </context-group>
  72. </trans-unit>
  73. <trans-unit id="empName" datatype="html">
  74. <source>Name</source>
  75. <target>പേര്</target>
  76. <context-group purpose="location">
  77. <context context-type="sourcefile">src/app/employee/employee-list/employee-list.component.html</context>
  78. <context context-type="linenumber">29</context>
  79. </context-group>
  80. <context-group purpose="location">
  81. <context context-type="sourcefile">src/app/employee/employee-edit/employee-edit.component.html</context>
  82. <context context-type="linenumber">13</context>
  83. </context-group>
  84. </trans-unit>
  85. <trans-unit id="empAddress" datatype="html">
  86. <source>Address</source>
  87. <target>വിലാസം</target>
  88. <context-group purpose="location">
  89. <context context-type="sourcefile">src/app/employee/employee-list/employee-list.component.html</context>
  90. <context context-type="linenumber">30</context>
  91. </context-group>
  92. </trans-unit>
  93. <trans-unit id="empGender" datatype="html">
  94. <source>Gender</source>
  95. <target>ജൻഡർ</target>
  96. <context-group purpose="location">
  97. <context context-type="sourcefile">src/app/employee/employee-list/employee-list.component.html</context>
  98. <context context-type="linenumber">31</context>
  99. </context-group>
  100. </trans-unit>
  101. <trans-unit id="empCompany" datatype="html">
  102. <source>Company</source>
  103. <target>കമ്പനി</target>
  104. <context-group purpose="location">
  105. <context context-type="sourcefile">src/app/employee/employee-list/employee-list.component.html</context>
  106. <context context-type="linenumber">32</context>
  107. </context-group>
  108. </trans-unit>
  109. <trans-unit id="empDesignation" datatype="html">
  110. <source>Designation</source>
  111. <target>പദവി</target>
  112. <context-group purpose="location">
  113. <context context-type="sourcefile">src/app/employee/employee-list/employee-list.component.html</context>
  114. <context context-type="linenumber">33</context>
  115. </context-group>
  116. </trans-unit>
  117. <trans-unit id="edit" datatype="html">
  118. <source>Edit</source>
  119. <target>എഡിറ്റ്</target>
  120. <context-group purpose="location">
  121. <context context-type="sourcefile">src/app/employee/employee-list/employee-list.component.html</context>
  122. <context context-type="linenumber">50</context>
  123. </context-group>
  124. </trans-unit>
  125. <trans-unit id="delete" datatype="html">
  126. <source>Delete</source>
  127. <target>ഡിലീറ്റ്</target>
  128. <context-group purpose="location">
  129. <context context-type="sourcefile">src/app/employee/employee-list/employee-list.component.html</context>
  130. <context context-type="linenumber">55</context>
  131. </context-group>
  132. </trans-unit>
  133. <trans-unit id="save" datatype="html">
  134. <source>Save</source>
  135. <target>സേവ്</target>
  136. <context-group purpose="location">
  137. <context context-type="sourcefile">src/app/employee/employee-edit/employee-edit.component.html</context>
  138. <context context-type="linenumber">97</context>
  139. </context-group>
  140. </trans-unit>
  141. <trans-unit id="cancel" datatype="html">
  142. <source>Cancel</source>
  143. <target>ക്യാൻസൽ</target>
  144. <context-group purpose="location">
  145. <context context-type="sourcefile">src/app/employee/employee-edit/employee-edit.component.html</context>
  146. <context context-type="linenumber">104</context>
  147. </context-group>
  148. </trans-unit>
  149. <trans-unit id="back" datatype="html">
  150. <source>
  151. <x id="START_ITALIC_TEXT" ctype="x-i" equiv-text="<i>"/><x id="CLOSE_ITALIC_TEXT" ctype="x-i" equiv-text="</i>"/> Back
  152. </source>
  153. <target>
  154. <x id="START_ITALIC_TEXT" ctype="x-i" equiv-text="<i>"/><x id="CLOSE_ITALIC_TEXT" ctype="x-i" equiv-text="</i>"/> ബാക്ക്
  155. </target>
  156. <context-group purpose="location">
  157. <context context-type="sourcefile">src/app/employee/employee-detail/employee-detail.component.html</context>
  158. <context context-type="linenumber">41</context>
  159. </context-group>
  160. </trans-unit>
  161. </body>
  162. </file>
  163. </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.