DotNetCurry Logo

Implementing Internationalization in ASP.NET MVC and AngularJS

Posted by: Mahesh Sabnis , on 3/4/2016, in Category ASP.NET MVC
Views: 15439
Abstract: Using Angular.js to implement Internationalization in ASP.NET MVC applications.

The marketplace is global. There is a huge market beyond your local base and it is reachable electronically. Web applications are one way using which we can reach out to a large set of audiences. If an organization wants to develop commercial applications (eg: online shopping, service offerings, publications etc.) and wants it to be used by various people across the globe, then they should strive to create applications that can easily be adapted to local languages and cultures. This is done using Internationalization.

 

Web applications now-a-days use technologies supporting patterns and best practices on client and server side. While we have technologies like ASP.NET MVC on server side; to compliment it, we have modular AngularJS on the client side. Angular is a client-side MVC framework and provides module segregation using Services, Controllers, Factory, Directives, etc.

Internationalization in ASP.NET MVC 

In this application, we will work on implementing Internationalization in ASP.NET MVC and Angular.js application. This application is created using the Free Community edition of Visual Studio 2015, although Visual Studio 2013 can also be used here.

Step 1: Open Visual Studio and create a new ASP.NET MVC application of name NG_Int_App as shown in the following image

create-mvc-project

We have selected ASP.NET MVC and Web API checkboxes so that we can use the MVC project structure and Web API services for making data available to Angular.js.

Step 2: In the App_Data folder, add a new Sql Server database of name ApplicationDB.mdf. In this database, add a new table as provided in the following script

CREATE TABLE [dbo].[EmployeeInfo] (
    [EmpNo]       INT          IDENTITY (1, 1) NOT NULL,
    [EmpName]     VARCHAR (50) NOT NULL,
    [Salary]      DECIMAL (18) NOT NULL,
    [DeptName]    VARCHAR (50) NOT NULL,
    [Designation] VARCHAR (50) NOT NULL,
    PRIMARY KEY CLUSTERED ([EmpNo] ASC)
);

Add the following Test Data in this table

INSERT INTO [dbo].[EmployeeInfo] ([EmpNo], [EmpName], [Salary], [DeptName], [Designation]) VALUES (1, N'MS', CAST(78000 AS Decimal(18, 0)), N'IT', N'Manager')
INSERT INTO [dbo].[EmployeeInfo] ([EmpNo], [EmpName], [Salary], [DeptName], [Designation]) VALUES (2, N'VB', CAST(52000 AS Decimal(18, 0)), N'HR', N'LEAD')

Step 3: We need to create a Data Access Layer, so we will use ADO.NET Entity Framework for data access. In the Models folder, add a new ADO.NET Entity Data Model of name ApplicationDS. Follow the wizard by selecting database name as ApplicationDB.mdf and Table as EmployeeInfo from it. After completion of the wizard, the EmployeeInfo class will be created with properties mapping to the EmployeeInfo table. The wizard also generates classes for performing CRUD operations with the EmployeeInfo table.

Step 4: In the Controllers folder, add a new Web API controller with actions using entity framework. Name this controller as EmployeeInfoAPIController. Create the controller with Model class, Data Context class and controller name as shown in the following image

web-api-controller

This will generate methods for performing Http based operations for EmployeeInfo.

Build the application.

Step 5: In the project, add AngularJS framework scripts using NuGet Package Manager. Right-click on the project and from the Manage NuGet Packager, search for AngularJS as shown in the following image

ng-package

This will add a Scripts folder in the project with Angular Script references. In this folder, we are provided with i18n subfolder which contains internationalization scripts. These are used for localizing products as per the local language and culture. In the project, add Bootstrap framework using Manage NuGet Package with same way as explained for Angular.

Step 6: In the project, add a folder of name MyScript. In this folder, we will add a script with logic for defining Angular Module, Service and Controller. In this folder add a new JavaScript file of name logic.js. Add the following code in it:

 //1.
var app = angular.module('app', []);
//2.
app.service('serv', function ($http) {
    this.getData = function () {
        var response = $http.get('http://localhost:54733/api/EmployeeInfoAPI');
        return response;
    };
    this.post = function (emp) {
        var response = $http({
            url: 'http://localhost:54733/api/EmployeeInfoAPI',
            method: 'post',
            data: emp,
            datatype:'json',
            contenttype:'application/json;utf-8'
        });
        return response;
    };
});

//3.
app.controller('ctrl', function ($scope, serv) {
    $scope.Employee = {
        EmpNo: 0,
        EmpName: '',
        Salary: 0,
        DeptName: '',
        Designation:''
    };

    $scope.Employees = [];
    $scope.Message = '';

    load();
    function load() {
        var promise = serv.getData();
        promise.then(function (resp) {
            $scope.Employees = resp.data;
            $scope.Message = 'Call is successful...';

        }, function (err) {
            $scope.Message = 'Call failed...' + err.status;
        });
    };

    $scope.clear = function () {
        $scope.Employee.EmpNo = 0;
        $scope.Employee.EmpName = '';
        $scope.Employee.Salary = 0;
        $scope.Employee.DeptName = '';
        $scope.Employee.Designation = '';
    };

    $scope.save = function () {
        var promise = crudserv.post($scope.Employee);
        promise.then(function (resp) {
            $scope.Employee.EmpNo = resp.data.EmpNo;
            $scope.Message = 'Call Completed Succesfully';
            loadData();
        }, function (err) {
            $scope.Message = 'Call Fail ' + err.status;
        });
    };
});

The above code does the following:

(Note: Following line numbers match with comments applied in JavaScript code.)

1. Define the angular module of name appmodule.

2. The Angular service of name serv is defined with $http as dependency. This is used to make calls to Web API. This service contains getData() function to make GET call to Web API and receive Employees data. The post() function accepts emp parameter and posts it to Web API using http POST call. These functions returns promise object.

3. An Angular controller of name ctrl is defined with $scope and serv as dependency object. This defines Employee scope object. This will be used for databinding with the View. The load() function calls the getData() function of the service and receives EmployeeInfo array. The save() function calls the post() function of the service and passes the employee object to it. The clear() function reset values of Employee object.

Step 7: In the Controllers folder, add a new Empty MVC controller of the name EmployeeInfoMVCController. This will have an Index action method. Scaffold an empty view from this action Index.cshtml. Add the following HTML markup in the view with Angular Databinding

<link href="~/Content/bootstrap.min.css" rel="stylesheet" />
http://~/Scripts/angular.min.js
http://~/MyScript/logic.js

<table class="table table-striped table-bordered table-condensed" ng-app="appmodule"
       ng-controller="ctrl">
   <tr>
       <td>
           <table class="table table-striped table-bordered table-condensed">
               <tr>
                   <td>EmpNo</td>
                   <td>
                      <input type="text" ng-model="Employee.EmpNo"/> 
                   </td>
               </tr>
               <tr>
                   <td>EmpName</td>
                   <td>
                       <input type="text" ng-model="Employee.EmpName"/>
                   </td>
               </tr>
               <tr>
                   <td>Salary</td>
                   <td>
                       <input type="text" ng-model="Employee.Salary"/>
                   </td>
               </tr>
               <tr>
                   <td>DeptName</td>
                   <td>
                       <input type="text" ng-model="Employee.DeptName"/>
                   </td>
               </tr>
               <tr>
                   <td>Designation</td>
                   <td>
                       <input type="text" ng-model="Employee.Designation"/>
                   </td>
               </tr>
               <tr>
                   <td>
                       <input type="button" value="Clear" ng-click="clear()"/>
                   </td>
                   <td>
                       <input type="button" value="Save" ng-click="save()"/>
                   </td>
               </tr>
           </table>
       </td>
   </tr>
   <tr>
       <td>
           <table class="table table-striped table-bordered table-condensed">
               <thead>
                   <tr>
                       <td>EmpNo</td>
                       <td>EmpName</td>
                       <td>Salary</td>
                       <td>DeptName</td>
                       <td>Designation</td>
                   </tr>
               </thead>
               <tbody>
                   <tr ng-repeat="Emp in Employees">
                       <td>{{Emp.EmpNo}}</td>
                       <td>{{Emp.EmpName}}</td>
                       <td>{{Emp.Salary}}</td>
                       <td>{{Emp.DeptName}}</td>
                       <td>{{Emp.Designation}}</td>
                   </tr>
               </tbody>
           </table>
</td>
   </tr> 
</table>

Step 8: In the RouteConfig.cs add the default routing for EmployeeInfoMVC controller as shown as following code

routes.MapRoute(
    name: "Default",
    url: "{controller}/{action}/{id}",
    defaults: new { controller = "EmployeeInfoMVC", action = "Index", id = UrlParameter.Optional }
);

Run the application.

employee-info

Implementing Internationalization for the our MVC Application using Browser Settings

To implement internationalization, we need to create resource files in the application. We will show the result on the Web Page using English and German forms.

Step 9: In the MVC project, add a new ASP.NET Folder App_LocalResources. In this folder add a new Resource File of name MVCInternational.resx. Change the Access Modifier of this file to Public as shown in the following image

resource-file-public

Add the following Name/Value Pairs in the MVCInternational.resx file.

resource-file-english

Copy-Paste the MVCInternational.resx in the same folder and rename it as MVCInternational.de.resx. Change the Access Modifier for the file to Public. We will use this file for German strings. Use Google Translator for finding the German Values for Name labels. The file with German values will be displayed as shown in the following image:

resource-file-german

We need to embed these resource in the assembly. Right click on each Resource file and set the Build Action as Embedded Resource.

Build the project.

 

Step 10: In the Controllers folder, add a new empty Web API Controller of name ResourceAPIController. In this file, we will add code to read data from the resource files.

public class ResourceAPIController : ApiController
{
    [HttpGet]
    [Route("api/accessresources")]
    public IHttpActionResult GetResourceStringsFromResources()
    {
        //1.   
        ResourceSet resources = MVCInternattional.ResourceManager.GetResourceSet(CultureInfo.CurrentUICulture, true, true);
        Dictionary<string, string> resDictionary = new Dictionary<string, string>();
        //2.
        foreach (DictionaryEntry resource in resources)
        {
            resDictionary.Add(resource.Key.ToString(), resource.Value.ToString());
        }
        //3.
        return Ok(resDictionary);
    }
}

The above code does the following. The following Line numbers matches with comments marked in the code

1. Load Resources using the MVCInternational class for the current culture.

2. Iterate through resources to read its Key/Value pairs and store them in Dictionary.

3. This dictionary will be returned.

Run the application and in the browser (article uses Chrome), enter the following URL : http://localhost:<port>.api/accessresurces . The following response will be displayed:

en-use-response-json

Now to View the German result, use the Quick Language Switcher extension for Chrome. (Use the necessary plug-in for various browser or you can change browser languages manually as well). Via the switcher, make sure that the German language is added through the Chrome settings. Switch to the German language and the following response will be displayed

german-json

Step 11: To access the JSON data with culture settings from the Web API, add the following function in the serv Angular service of logic.js

this.getResources = function () {
    var response = $http.get('http://localhost:54733/api/accessresources');
    return response;
};

Step 12: Make the following changes in the Angular controller of logic.js

app.controller('ctrl', function ($scope, serv) {
    $scope.Employee = {
        EmpNo: 0,
        EmpName: '',
        Salary: 0,
        DeptName: '',
        Designation:''
    };

    $scope.resourcesData = {};

    $scope.Employees = [];
    $scope.Message = '';

    load();
    function load() {
        var promise = serv.getData();
        promise.then(function (resp) {
            $scope.Employees = resp.data;
            $scope.Message = 'Call is successful...';
            getResources();

        }, function (err) {
            $scope.Message = 'Call failed...' + err.status;
        });
    };

    function getResources() {
        var promise = serv.getResources();
        promise.then(function (resp) {
            $scope.resourcesData = resp.data;
        }, function (err) {
            $scope.Message = 'Call failed...' + err.status;
        });
    };


    $scope.clear = function () {
        $scope.Employee.EmpNo = 0;
        $scope.Employee.EmpName = '';
        $scope.Employee.Salary = 0;
        $scope.Employee.DeptName = '';
        $scope.Employee.Designation = '';
    };

    $scope.save = function () {
        var promise = crudserv.post($scope.Employee);
        promise.then(function (resp) {
            $scope.Employee.EmpNo = resp.data.EmpNo;
            $scope.Message = 'Call Completed Succesfully';
            loadData();
        }, function (err) {
            $scope.Message = 'Call Fail ' + err.status;
        });
    };
});

The highlighted code is added in the controller. The controller has a new scope object declared with the name resourcesData. This is used to store JSON data of the culture received from the server. The getResources() function calls the getResources() function of the Angular service, which receives the culture based JSON data depending on the request made by the browser. This is called on success callback of the load() function of the controller.

Step 13: To display the page in the culture specific format, we need to make changes in Html markup of the Index.cshtml. Make the following changes in it.

@{
ViewBag.Title = "Index";
}
<link href="~/Content/bootstrap.min.css" rel="stylesheet" />
http://~/Scripts/angular.min.js
http://~/MyScript/logic.js
<body ng-app="appmodule">
<h2>{{resourcesData.EmployeeInformationLabel}}</h2>


<table class="table table-striped table-bordered table-condensed" 
   ng-controller="ctrl">
<tr>
    <td>
        <table class="table table-striped table-bordered table-condensed">
            <tr>
                <td>{{resourcesData.EmpNoLabel}}</td>
                <td>
                    <input type="text" ng-model="Employee.EmpNo" />
                </td>
            </tr>
            <tr>
                <td>{{resourcesData.EmpNameLabel}}</td>
                <td>
                    <input type="text" ng-model="Employee.EmpName" />
                </td>
            </tr>
            <tr>
                <td>{{resourcesData.SalaryLabel}}</td>
                <td>
                    <input type="text" ng-model="Employee.Salary" />
                </td>
            </tr>
            <tr>
                <td>{{resourcesData.DeptNameLabel}}</td>
                <td>
                    <input type="text" ng-model="Employee.DeptName" />
                </td>
            </tr>
            <tr>
                <td>{{resourcesData.DesignationLabel}}</td>
                <td>
                    <input type="text" ng-model="Employee.Designation" />
                </td>
            </tr>
            <tr>
                <td>
                    <input type="button" value="{{resourcesData.ClearLabel}}" ng-click="clear()" />
                </td>
                <td>
                    <input type="button" value="{{resourcesData.SaveLabel}}" ng-click="save()" />
                </td>
            </tr>
        </table>
    </td>
</tr>
<tr>
    <td>
        <table class="table table-striped table-bordered table-condensed">
            <thead>
                <tr>
                    <td>{{resourcesData.EmpNoLabel}}</td>
                    <td>{{resourcesData.EmpNameLabel}}</td>
                    <td>{{resourcesData.SalaryLabel}}</td>
                    <td>{{resourcesData.DeptNameLabel}}</td>
                    <td>{{resourcesData.DesignationLabel}}</td>
                </tr>
            </thead>
            <tbody>
                <tr ng-repeat="Emp in Employees">
                    <td>{{Emp.EmpNo}}</td>
                    <td>{{Emp.EmpName}}</td>
                    <td>{{Emp.Salary}}</td>
                    <td>{{Emp.DeptName}}</td>
                    <td>{{Emp.Designation}}</td>
                </tr>
            </tbody>
        </table>
    </td>
</tr>
</table>

</body>

In the above markup, the highlighted code represents databinding of markup using Angular Expressions based on the data stored in resourcesData scope object.

Step 14: In web.config file define settings for globalization under <system.web> tab as shown in the following code

<globalization culture="auto" uiCulture="auto" />

Step 15: Run the application and the default English labels will be displayed as shown in the following image

englishp-result

We have our Quick Language Switcher extension added in the browser. Click on it and select Language as German. This will make a new Http call with Accept-Language as German in Http Header and the page will show all labels in German language as shown in the following image

german-result

This is how we can set the culture settings for the page.

Note: Internationalization can also be implemented using AngularTranslate which is a third party module created by the Angular Community.

Using Angular i18n Scripts

When we install Angular package in our application, we are provided with i18n subfolder which contains scripts for culture local. We can load these scripts as per the culture selection from the browser using Quick Language Switcher plugin.

Step 16: Open the _layout.cshtml and add the following code in it

@{
    var culture = HttpContext.Current.Request.UserLanguages[0].ToString();
}

The will receive the language selected by the user in the browser. The language will be passed to server in http requests’ Accept-Language.

Add the following line after all script references of the _layout.cshtml

http://@Url.Content(

This will load the specific culture angular script in the page based on the language received in Http Accept-Language.

Run the application, the page will be loaded with default language as English. Open Chrome developer’s tool, you will see that the angular-locale_en.js is loaded and the page shows the English culture as shown in the following image:

english-locale-chrome

The Salary currency is in $.

Change the language using the Quick Language Switcher extension to German. The following result will be displayed with Currency Symbol change to Euro and angular-locale_de.js is loaded.

german-locale-chrome

That’s it. Likewise based on the Accept-Language header, any supported culture can be loaded.

Conclusion:

The Angular.js framework provides a seamless way to implement Internationalization in ASP.NET MVC applications.

Download the entire source code of this article (Github)

  • Please Share this article if you think it was worth reading. Thanks!
Further Reading - Articles You May Like!
Author
Mahesh Sabnis is a Microsoft MVP having 14 years of experience in IT education and development. He is a Microsoft Certified Trainer (MCT) since 2005 and has conducted various Corporate Training programs for .NET Technologies (all versions). Follow him on twitter @maheshdotnet


Page copy protected against web site content infringement by Copyscape




Feedback - Leave us some adulation, criticism and everything in between!
comments powered by Disqus