Angular 6 Internationalization (with examples)

Angular is one of the most widely used front-end framework for modern web applications. The component-based design of Angular is suitable for modern web applications because each component can have its own UI, DataBinding, Events, etc.

We can use Angular with its object model to design and develop a web application's front-end. Since there could be several users requesting the web application from different parts of the globe, it is important for us to manage the rendering of the UI in a localized format.

Angular 6 Internationalization (i18n)

Angular framework has provided an Internationalization (i18n) tool. This tool helps developers to make application available in multiple languages.

The demo project discussed for this article is implemented using Angular-CLI. If you are new to Angular-CLI then please visit this link. Visual Studio Code, a free IDE tool, is used for this application. It can be downloaded from this link.

The i18n tools helps to simplify the following aspects of Internationalization:

1. Currencies, dates, numbers
2. Translating static text in the HTML template of the Angular component
3. Handling plural forms of words

Step 1: Open Node.js command prompt as Administrator. Navigate to the System Drive (on Windows 10) e.g. c:\ or any other drive. Run the following command from the command prompt:
npm install -g @angular/cli
This will install Angular CLI in the global scope. To create a new Angular application run the following command from the command prompt
ng new ng-internationalization-app
This will create a ng-internationalization-app folder and application of name  ng-internationalization-app in it.

Step 2: Open the VSCode IDE and open the ng-internationalization-app folder in it.  We will create model classes and components using following commands.

To create model classes run following two commands

ng g class Person
ng g class PersonLogic


These commands will add person.ts and person-logic.ts files in the app folder.

Add the following code in person.ts
export class PersonInfo {
  constructor(
      public PersonId: number,
      public PersonName: string,
      public Address: string,
      public City: string,
      public State: string,
      public Occupation:  string
  ){}
}

export const States: Array = [
  'Maharashtra',
  'Telengana',
  'Karnataka',
  'Gujarat',
  'Rajasthan'
];
export const Occupations:Array = [
  'Employeed','Self-Employeed','Un-Employeed'
];
Listing 1: The Person Class and Constants


The Person class contains public members for personal data. This file also contains constant arrays for States and Occupations. Add the following code in person-logic.ts
import { PersonInfo} from './person';
export class PersonLogic {
  person: PersonInfo;
  persons: Array;
  constructor() {
    this.person = new PersonInfo(0, '', '', '', '', '');
    this.persons = new Array();
    this.persons.push(
      new PersonInfo(
        101,
        'Mahesh',
        'Bavdhan',
        'Pune',
        'Maharashtra',
        'Self-Employeed'
      )
    );
    this.persons.push(
      new PersonInfo(
        102,
        'Sachin',
        'Kothrud',
        'Pune',
        'Maharashtra',
        'Employeed'
      )
    );
  }
  getPersonsInfo(): Array {
    return this.persons;
  }
  savePersonInfo(per: PersonInfo): Array {
    this.persons.push(per);
    return this.persons;
  }
}
Listing 2: The PersonLogic class


The above class defines methods for reading Person information and adding a new person.

Step 3: To create a new component in the project, run the following command from the command prompt
ng g component PersonInfo
This command will add a new folder of name person-info in the app sub-folder of src folder. The person-info folder will contains following files:
  • person-info.component.css
  • person-info.component.html
  • person-info.component.spec.ts
  • person-info.component.ts

Add the following code in PersonInfoComponent class:
import { Component, OnInit } from '@angular/core';
import { PersonInfo, States, Occupations } from './../person';
import { PersonLogic } from './../person-logic';
@Component({
    selector: 'app-personinfo-component',
    templateUrl: './person-info.component.html',
})
export class PersonInfoComponent implements OnInit {
    person: PersonInfo;
    persons: Array;
    logic: PersonLogic;
    states = States;
    occupations = Occupations;
    tableColumns: Array;
    constructor() {
        this.person = new PersonInfo(0,'','','','','');
        this.persons = new Array();
        this.logic = new PersonLogic();
        this.tableColumns = new Array();
    }
    ngOnInit(): void {
        for(let p in this.person) {
            this.tableColumns.push(p);
        }
        this.persons = this.logic.getPersonsInfo();
     }
    clear():void {
        this.person = new PersonInfo(0,'','','','','');
    }
    save():void {
        this.logic.savePersonInfo(this.person);
    }
    getSelectedData(p:PersonInfo):void {
       this.person = Object.assign({},p);
    }
}
Listing 3: The PersonComponent class


The above component class uses Person and PersonLogic classes for performing read/write operations on Person object. The ngOnInit() method iterate over the Person object to read its members. These members will be pushed in the tableColumns array. This array will be used to generate table headers of the HTML table which will be used to display Person details.

The PersonInfoComponent accepts person-info.component.html as templateUrl for displaying View. We will add Html in this view in forthcoming steps.

Understanding i18n Attribute

The Html template will contain static text for showing Person information e.g. PersonId, Person Name, etc. To display this static text in the localize form, we need to define i18n attribute for Html element. This attribute is available using an i18n template translation tool. This tool allows extracting localize file where we can define localized  translation for static text which will be rendered on Html document. The i18n attribute has a following process for implementing transformation
  • Marking the static text in the component template for translation
  • The Angular i18n tool extract the marked text in a separate translation source file
  • The translator edits the file by translating the extracted text into the target language
  • The Angular compiler will import this translated file and replaces original static text as per the translated text. This will render the app in the specified target translated language.

The translation file has the naming standard as <filename>.<language-symbol>.xlf. e.g. source.fr.xlf. These XML localization Interchange File Format files are generated using an  i18n tool. The project generated using Angular CLI by default provides locales for various cultures as shown in the following image

locales
Figure 1: The locale folder structure


Step 4: In the src folder add a new folder and name it as locale. We will use this folder to store all xlf files. To generate the xlf file run following commands from the command prompt

ng xi18n --output-path locale --out-file source.fr.xlf --i18n-locale fr


This will generate file which we will be used for French transformation.

ng xi18n --output-path locale --out-file source.de.xlf --i18n-locale de


This will generate file which we will be used for German transformation.

Step 5: We need to define a translation for the specific language  using a translator, for this article I have used google translator. Lets modify the source.fr.xlf file for French translation as shown in the following Xml code
<?xml version="1.0" encoding="UTF-8" ?>
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
  <file source-language="fr" datatype="plaintext" original="ng2.template">
    <body>
      <trans-unit id="pageH1Tag" datatype="html">
        <source>Personal Information System</source>
        <target>Système d'information personnel</target>
        <note priority="1" from="description">A header</note>
        <note priority="1" from="meaning">Screen notification</note>
      </trans-unit>
      <trans-unit id="label1" datatype="html">
        <source>Person Id</source>
        <target>Identifiant de la personne</target>
      </trans-unit>
      <trans-unit id="label2" datatype="html">
        <source>Person Name</source>
        <target>Nom d'une personne</target>
      </trans-unit>
      <trans-unit id="label3" datatype="html">
        <source>Person Address</source>
        <target>Adresse de la personne</target>
      </trans-unit>
      <trans-unit id="label4" datatype="html">
        <source>Person City</source>
        <target>Personne Ville</target>
      </trans-unit>
      <trans-unit id="label5" datatype="html">
        <source>Person State</source>
        <target>État personne</target>
      </trans-unit>
      <trans-unit id="label6" datatype="html">
        <source>Person Occupation</source>
        <target>Personne Occupation</target>
      </trans-unit>
      <trans-unit id="label7" datatype="html">
        <source>New</source>
        <target>Nouveau</target>
      </trans-unit>
      <trans-unit id="label8" datatype="html">
        <source>Save</source>
        <target>sauvegarder</target>
      </trans-unit>
      <trans-unit id="columnHeaderCaptions" datatype="html">
        <source>{VAR_SELECT, select, c,{c}}</source>
        <target>{VAR_SELECT, select, PersonId{PersonId} 
PersonName{Nom d'une personne} Address{Adresse} City{Ville} 
State{Etat} Occupation{Occupation}}</target>
      </trans-unit>
    </body>
  </file>
</xliff>
Listing 4: The source.fr.xlf file


The above file defines trans-unit with an id attribute. This attribute will be used for i18n attribute which will be defined in Html template of which static text needs to be translated. The trans-unit has the source and the target children which contains the source static text and the expected translated text. This file contains French translations for all static texts. The file contains trans-unit with id as columnHeaderCaptions. We will use this translation for the dynamically generated Html e.g. Table headers. We need to use select expressions. These expressions help to define the translation for dynamically generated texts on Html document. Here we have the expression {c}, this will be used to on Html template to generate table headers using *ngFor Angular Directive. We will see this in Html document in forthcoming steps.

Similar to French source file we will also add translation in source.de.xlf. This file is based on source.fr.xlf file, the only difference that its contains target as German text.

Step 6: The most important step of internationalization is that, we need to merge these translation files in AOT compiler so that the build will be generated used these translated files. Open the angular.json file and modify it to use production configurations as shown in the following code
"configurations": {
            …,
            "production-fr": {
              "fileReplacements": [
                {
                  "replace": "src/environments/environment.ts",
                  "with": "src/environments/environment.prod.ts"
                }
              ],
              "optimization": true,
              "outputHashing": "all",
              "sourceMap": false,
              "extractCss": true,
              "namedChunks": false,
              "aot": true,
              "extractLicenses": true,
              "vendorChunk": false,
              "buildOptimizer": true,
              "outputPath": "dist/my-project-fr/",
              "i18nFile": "src/locale/source.fr.xlf",
              "i18nFormat": "xlf",
              "i18nLocale": "fr",
              "i18nMissingTranslation": "error"
            },
            "fr": {
              "aot": true,
              "outputPath": "dist/my-project-fr/",
              "i18nFile": "src/locale/source.fr.xlf",
              "i18nFormat": "xlf",
              "i18nLocale": "fr",
              "i18nMissingTranslation": "error"
            },
            "production-de": {
              "fileReplacements": [
                {
                  "replace": "src/environments/environment.ts",
                  "with": "src/environments/environment.prod.ts"
                }
              ],
              "optimization": true,
              "outputHashing": "all",
              "sourceMap": false,
              "extractCss": true,
              "namedChunks": false,
              "aot": true,
              "extractLicenses": true,
              "vendorChunk": false,
              "buildOptimizer": true,
              "outputPath": "dist/my-project-de/",
              "i18nFile": "src/locale/source.de.xlf",
              "i18nFormat": "xlf",
              "i18nLocale": "de",
              "i18nMissingTranslation": "error"
            },
            "de": {
              "aot": true,
              "outputPath": "dist/my-project-de/",
              "i18nFile": "src/locale/source.de.xlf",
              "i18nFormat": "xlf",
              "i18nLocale": "de",
              "i18nMissingTranslation": "error"
            }
          } ,
        "serve": {
          "builder": "@angular-devkit/build-angular:dev-server",
          "options": {
            "browserTarget": "ng-internationalization-app:build"
          },
          "configurations": {
           …
            "fr": {
              "browserTarget": "ng-internationalization-app:build:fr"
            },
            "de": {
              "browserTarget": "ng-internationalization-app:build:de"
            }
          }
Listing 5: The angular.json file modifications


We have defined following properties for a production configuration
  • i18nFile: This is the path of the translation file
  • i18nFormat: This is the format of the file
  • i18nLcalte: The locale id. This is the id for the file which is to be used while translation

The angular.json modification will make sure that the corresponding translation file will be used by JIT compiler to create corresponding translation and then bootstrap the application.

Step 7: Modify the app.module.ts file as shown in the following code to bootstrap PersonInfoComponent.
import { BrowserModule } from '@angular/platform-browser';
import {NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { PersonInfoComponent } from './person-info/person-info.component';

@NgModule({
  declarations: [
  PersonInfoComponent],
  imports: [
    BrowserModule, FormsModule
  ],
  providers: [  ],
  bootstrap: [PersonInfoComponent]
})
export class AppModule { }
Listing 6: The AppModule with bootstrapper
Step 8: Modify the personinfo-logic.component.html as shown in the following Html markup
<h1 i18n="@@pageH1Tag" style="text-align: center">
Personal Information System
</h1>
<div class="container">
  <table class="table table-bordered table-striped">
    <tr>
      <td i18n="@@label1">
        Person Id
      </td>
      <td>
        <input type="text" class="form-control" 
                           [(ngModel)]="person.PersonId">
      </td>
    </tr>
    <tr>
      <td i18n="@@label2">
        Person Name
      </td>
      <td>
        <input type="text" class="form-control" 
                          [(ngModel)]="person.PersonName">
      </td>
    </tr>
    <tr>
      <td i18n="@@label3">
        Person Address
      </td>
      <td>
        <input type="text" class="form-control" 
                          [(ngModel)]="person.Address">
      </td>
    </tr>
    <tr>
      <td i18n="@@label4">
        Person City
      </td>
      <td>
        <input type="text" class="form-control" 
                          [(ngModel)]="person.City">
      </td>
    </tr>
    <tr>
      <td i18n="@@label5">
        Person State
      </td>
      <td>
        <select class="form-control" [(ngModel)]="person.State">
          <option *ngFor="let s of states" value="{{s}}">
           {{s}}
          </option>
        </select>
      </td>
    </tr>
    <tr>
      <td i18n="@@label6">
        Person Occupation
      </td>
      <td>
        <select class="form-control" [(ngModel)]="person.Occupation">
          <option *ngFor="let o of occupations" value="{{o}}">
            {{o}}
          </option>
        </select>
      </td>
    </tr>
    <tr>
      <td>
        <button i18n="@@label7" type="button" (click)="clear()" 
                           class="btn btn-default">New</button>
      </td>
      <td>
        <button i18n="@@label8" type="button" (click)="save()" 
                          class="btn btn-success">Save</button>
      </td>
    </tr>
  </table>
</div>
<div class="container">
  <table class="table table-bordered table-striped">
    <thead>
      <tr>
          <th i18n="@@columnHeaderCaptions" 
                *ngFor="let c of tableColumns">{c,select,c{c}}</th>
      </tr>
    </thead>
    <tbody>
      <tr *ngFor="let per of persons" (click)="getSelectedData(per)">
        <td *ngFor="let c of tableColumns">{{per[c]}}</td>
      </tr>
    </tbody>
  </table>
</div>
Listing 7: The person-logic.component.html file with i18n attribute 
Take a look at the Html markup, we have applied i18n attribute for Html tag and we have passed the id of trans-unit from .xlf file to the matching static text containing Html element. This will make sure that the rendering of a static test of the Html element will be done based on the translation. To Run the application, enter the following command from the command prompt

ng serve --configuration=fr

The configuration=fr switch will use the source.fr.xlf file for a dynamic translation of the Html rendering in French language. Open the chrome browser and enter the following address in address-bar
http://localhost:4200
frence-rendering

Figure 2: The French Text rendering


Run the following command for German language

ng serve --configuration=de


The result will be as follows:

german-rendering
Figure 3: The German Text rendering

Conclusion: Internationalization in Angular with i18n provides an easy way for a dynamic implementation of localization.

File Upload in ASP.NET Core

In one of the .NET Core trainings I was conducting recently, one of my students asked about uploading files using .NET Core application. Here's a solution. When we use ASP.NET MVC to upload a file, we use the HttpPostedFileBase class. This class is used to access the files uploaded by the client in an MVC application. In .NET Core, the IFromFile interface is used to represent a file that is sent with the HttpRequest. In this article we will use the IFromFile interface to upload the file. We will use Visual Studio 2017 and .NET Core 2.1. Information about the IFromFile interface can be read from this link.

Step 1: Open Visual Studio 2017 and create a new ASP.NET Core Web Application. Name this application as Core_FileUpload. Select the MVC project with .NET Core and ASP.NET Core 2.1 version for the project as shown in the following image:


mvc-app[8]

Step 2: In the project, add a new folder and name this folder as UploadedFiles. This folder will be used to store uploaded files. Step 3: In the Models folder, add a new class file. Name this file as FileManager.cs. In this file, add the following code

using Microsoft.AspNetCore.Http;
using System;
using System.IO;
using System.Threading.Tasks;

namespace Core_FileUpload.Models
{
public class FileManager
{
    public async Task UploadFile(IFormFile file)
    {
        try
        {
            bool isCopied = false;
            //1 check if the file length is greater than 0 bytes 
            if (file.Length > 0)
            {
                string fileName = file.FileName;
                //2 Get the extension of the file
                string extension = Path.GetExtension(fileName);
                //3 check the file extension as png
                if (extension == ".png" || extension == ".jpg")
                {
                    //4 set the path where file will be copied
                    string filePath = Path.GetFullPath(
                        Path.Combine(Directory.GetCurrentDirectory(), 
                                                    "UploadedFiles"));
                    //5 copy the file to the path
                    using (var fileStream = new FileStream(
                        Path.Combine(filePath, fileName), 
                                       FileMode.Create))
                    {
                        await file.CopyToAsync(fileStream);
                        isCopied = true;
                    }
                }
                else
                {
                    throw new Exception("File must be either .png or .JPG");
                }
            }
            return isCopied;
        }
        catch (Exception ex)
        {
            throw ex;
        }
    }
}
}


The above code has following specifications. The UploadFile method accepts IFromFile interface. Note that the numbering below matches with the number in the comments applied on the code above.

  1. Check the file length if it is greater than 0. If the file size is greater than 0 then only the uploaded file will be further processed.
  2. Since we are restricting the file upload for .png and .jpg files, this step reads an extension of the file using Path.GetExtension() method. This method accepts the file name of which is read using FileName property of IFromFile interface.
  3. If the file extension is .png or .jpg, only  then will the file  used for uploading else the code will throw an exception.
  4. This code set the path for the UploadedFiles folder, to copy uploaded files in this folder.
  5. This code uploads the file in the UploadedFiles folder using CopyToAsync() method of the IFromFile interface.
Step 4: Since we will perform error handling in our application, lets change ErrorViewModel class from the Models class by adding the following properties:

public string ErrorMessage { get; set; }
public string Controller { get; set; }
public string Action { get; set; }
Step 5: Change the Error.cshtml file from the Shared sub-folder of Views folder as shown in the following markup:

<h2>@Model.ErrorMessage</h2>
<hr />
<div>
    <a asp-action="@Model.Action" asp-controller="@Model.Controller">
        Go Back
    </a>
</div>
Step 6: In the Controllers folder add a new empty MVC controller. Name this controller as FileUploadController. Add the following code in this controller:

using Core_FileUpload.Models;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Threading.Tasks;
namespace Core_FileUpload.Controllers
{
    public class FileUploadController : Controller
    {
        FileManager fileManager;
        public FileUploadController()
        {
            fileManager = new FileManager();
        }
        public IActionResult Index()
        {
            return View("Index");
        }

        [HttpPost]
        public async  Task UploadFile(IFormFile file)
        {
            try
            {
                bool uploadSuccess = await fileManager.UploadFile(file);
                ViewBag.Message = "File Uploaded Successfully";
                return View("Index");
            }
            catch (Exception ex)
            {
                return View("Error", new ErrorViewModel() 
                                    {ErrorMessage= ex.Message });
            }
        }
    }
}

The controller class uses FileManager class created in Step 3. The Index action method of the controller class returns Index view. The UploadFile() action method accepts the IFromFile interface as a parameter and pass it to the UploadFile() method of the FileManager class. The UploadFile() method of the controller is executed for each HttpPost request. Step 7: Scaffold an Index view from the Index action method by right-clicking in the method and selecting Add View option. Select View template as empty view without model as shown in the following image:  index-view[7] 

Add the following markup in the Index.cshtml

@{
    ViewData["Title"] = "Index";
}

<h2>Uploading File in ASP.NET Core using 
    <strong>IFormFile</strong> Interface
</h2>
<form method="post" enctype="multipart/form-data" 
      asp-controller="FileUpload" asp-action="UploadFile">
    <div class="form-group">
        <div class="col-md-10">
            <p>Upload Image File (.png):</p>
            <input type="file" name="file"/>
        </div>
    </div>
    <hr />
    <div class="form-group">
        <div class="col-md-10">
            <input type="submit" value="Upload File" />
        </div>
    </div>
    <hr />
    <div class="form-group">
        <span>@ViewBag.Message</span>
    </div>
</form>
The above markup uses Html <form> tag. The form will be posted to UploadFile action method of the FileUpload controller. The enctype value multipart/form-data means the file will be send using POST request. Step 8: Modify the Configure() method of the StartUp.cs file for setting default route for FileUploadController as shown in the following code

app.UseMvc(routes =>
    {
        routes.MapRoute(
            name: "default",
            template: "{controller=FileUpload}/{action=Index}/{id?}");
    });

Run the application, and it will display the following result:

 run-page[6]

Click on the Choose File button, this will open the File Open window. Select .png or .Jpg file from the windows. Once the file is selected, click on the Upload File button, the file will be uploaded in the UploadedFiles folder as shown in the following image: uploaded-files[3]

If you select a file other than .png or .jpg, an error page will be displayed as shown in the following image :

 error[4]

Conclusion: The IFromFile interface can be used to read and access uploaded files using HttpRequest in an ASP.NET Core application.

Using CSS Selection Pseudo-element to change the Default Selection Style of your WebPage

CSS Pseduo-elements allow you to style certain parts of a document. In this article, we will see how to change the default selection style of your web page contents using ::Selection PseudoElement.

Although you can use any webeditor of your choice, I am using Visual Studio 2013 for this demonstration. Let’s start by creating a new ASP.NET Web application. Start Visual Studio and click on New Project. Choose the Web Category and choose ASP.NET Web Application as shown below –

crtwebapp

HTML5 Picture Element for Responsive Images

In this article, we will look at the <picture> element and how it helps in responsive design to render images of different sizes, based on the physical device accessing the content.

To display an image in our HTML pages, we have so far made use of the <img/> element with the source [src] attribute. Here’s an example:

<img src="Images/default.png" alt="Default Logo" />

With varying screen sizes and pixels, handling this image becomes a challenge. Let's create a small example for demonstrating this. I am using the free Visual Studio Community Edition although you can use any other web editor of your choice. Create an empty web application and add a HTML page with the name TestPicture.html in our project. Add the following code:

Login failed for user IIS APPPOOL\AppPool4.5 or APPPOOL\ASP.NET

The error ‘Login failed for user 'IIS APPPOOL\AppPool4.5’ usually occurs when you configure a new website in IIS or move an existing website to a newer version of IIS.

A simple solution to the error is to add a login to SQL Server for IIS APPPOOL\ASP.NET v4.5 and grant appropriate permission to the database.

Open SQL Server Management Studio > Right click ‘Security’ > New > Login

iis-apppool-login

Action Filters in ASP.NET MVC

Continuing with our MVC 101 series today we look at an important MVC feature, Filters. Filters in ASP.NET MVC are a way to apply cross-cutting logic at the controller level. Some examples of cross-cutting logic is Security and Logging.

Security is a cross cutting concern because, once enabled, we need to apply it for all incoming requests in the web Application. Imagine a world where you don’t have filters, in such case for every request that comes in, you Action method in controller will have to check if the user was Authorized to perform the action and view its result.

This not only violates single responsibility principle of the Action Method (it’s doing two things, evaluating security and computing the Action’s output) but is also an extremely verbose and repetitive work that we’ve to do irrespective of what the Action is supposed to do. Moreover, writing Authentication Code in action method cannot be guaranteed. There is no certainty that a developer may or may not miss out on implementing the code!

C# (Csharp) Switch Case Statement with Example

A C# switch case statement defines multiple groups of statements, and then executes one of these statement; depending on the value of a constant or test expression.

Let us understand this concept with an example.

string siteName = "DevCurry";
switch (siteName) {
    case "DotNetCurry":
    Console.WriteLine("This website is DotNetCurry.com");
    break;

    case "DevCurry":
    Console.WriteLine("This website is DevCurry.com");
    break;

    default:
    Console.WriteLine("Default Website");
}

As you can see, we start by defining a constant at the top:

Remove Vertical Scrollbars in SyntaxHighlighter

SyntaxHighlighter created by Alex Gorbatchev is a code syntax highlighter developed in JavaScript. It basically makes your code snippets beautiful.

Many a times even when not required, a vertical scroll gets added to your code as shown here:

syntax-highlighter-scroll

The scroll appears in some browsers (especially Chrome) as the default style set in shCoreDefault.css is overflow: auto. If you do not want a vertical scrollbar, you can always do this: