Click here to Skip to main content
14,209,828 members
Click here to Skip to main content
Article
Posted 3 Jun 2019

Stats

5.9K views
83 downloads
8 bookmarked

Developing Multicultural ASP.NET Core 2.x Project Using LazZiya.ExpressLocalization

,
Rate this:
3.97 (15 votes)
Please Sign up or sign in to vote.
3.97 (15 votes)
17 Jun 2019     CPOL    
Create multi-cultural ASP.NET Core 2.x web application with simple steps

Introduction

Developing multicultural ASP.NET Core 2.x web application requires a lot of infrastructure setup and it consumes time and effort. In this article, we will use LazZiya.ExpressLocalization nuget package for localizing our web application with one step.

Background

Most of the web apps use URL based localization, so we can see the selected culture in the URL, e.g., http://www.example.com/en/Contact. Unfortunately, ASP.NET Core provides only below request culture providers:

  • QueryStringRequestCultureProvider
  • CookieRequestCultureProvider
  • AcceptLanguageHeaderRequestCultureProvider

In order to have route value localization, we need to build custom localization provider and define a global route template, so basically, we need to complete the below localization steps to have fully localized web application:

  • Build route value request culture provider :
    For request localization according to {culture} route value.
     
  • Define global route template for culture parameter :
    Add {culture} parameter to url, eg. www.example.com/en-US/
     
  • Setup DataAnnotations localization :
    Localizaton of data annotations like; Display names, Required, StringLength, ...etc.
     
  • Setup ModelBinding error messages localization :
    Validation of input types at server side after submit, e.g. ValueMustBeANumberAccessor, AttemptedValueIsInvalidAccessor, etc.
     
  • Setup IdentityDescriber error messsages :
    Locaization of all user and role related messsages like "User already in role", "User name already exists", ...etc.
     
  • Setup view localization :
    Localize text/html in razor pages
     
  • Setup client side validation scripts :
    Localizing of client validaton messages is essential, forms will be validated before submit. The client side validation scripts must be localized so the user will see localized messages like: The field is required, Passwords must match, ..etc.

    One more important thing is validating localized decimal numbers, some cultures uses period and other cultures uses comma in decimal numbers like (1,2) and (1.2), this case should be handled carefully in client side validation.

    What is more: Some cultures may use totally different numbering systems; e.g. Arabic cultures are using numbers like "٠١٢٣٤٥٦٧٨٩" and latin cultures are using "0123456789". If the numbering system setup is not correct a validation error will rise. (see this article to learn how to change numbering system for client side validation).
     
  • Create localized resource files for each culture :
    Views, DataAnnotations, ModelBinding and IdentityErrors all requires localized resources. This step consumes a lot of time and effort!

All these steps require a lot of work and consume too much time. So, here comes the benefit of LazZiya.ExpressLocalization nuget package that eliminates the localization setup time and effort with simple lines of code.

Creating the Project

Let's start by creating a basic ASP.NET Core 2.2 web application (I'm using VS2019):

  1. Create a new project by selecting ASP.NET Core Web Application:

  2. Click Next, give the project a friendly name and click Create:

  3. Select Web Application and make sure you change the authentication to Individual User Accounts.

  4. Click Create and wait till the solution creates the basic template, once it is done, you can do a test run by selecting the project name in the solution explorer, then pressing (Ctrl + Shift + B) to build the project, then (Ctrl + Shift + W) to run without debugging in the browser.

Installing LazZiya.ExpressLocalization

  1. In the solution explorer under the project name, right click on Dependencies and select "Manage Nuget Packages".

  2. Go to Browse tab and search for "LazZiya", select "LazZiya.ExpressLocalization" and click "Install", select the latest version ( updated to v2.0.0 in 17 June 2019):

  3. It will ask to install two packages, click "Ok" and wait till installation is done:
    1. LazZiya.ExpressLocalization (required for all localization setup)
    2. LazZiya.TagHelpers (required for client side localization validation and language dropdown)
  4. UPDATE : Manually install LazZiya.TagHelpers.Localization package, required for easily localizing text/html in razor pages.

Creating Localized Resources

I've already prepared localized resources for the project, so you don't have to waste your time on creating localized resources.

Under the project root, create a new folder and name it "LocalizationResources":

Under LocalizationResources folder, create new public class and name it "ViewLocalizationResource", this class will be used to group resource files for view localization:

namespace ExpressLocalizationSample.LocalizationResources
{
    public class ViewLocalizationResource
    {
    }
}

Under LocalizationResources folder, create new public class and name it "ExpressLocalizationResource", this class will be used to group resource files for identity, model binding and data annotations.

namespace ExpressLocalizationSample.LocalizationResources
{
    public class ExpressLocalizationResource
    {
    }
}

We will use these two classes to pass resource type to the express localization method.

Finally, download relevant cultures resources from this repository folder. Please notice that you need to download two files for each culture, e.g., (ExpressLocalizationResource.tr.resx and ViewLocalizationResource.tr.resx). Copy the downloaded files to "LocalizationResources" folder.

Using the Code

Finally, we are ready for the localization setup. :)

Open startup.cs file and add the needed cultures list, then add one step localization setup as below:

var cultures = new[]
{
    new CultureInfo("tr"),
    new CultureInfo("ar"),
    new CultureInfo("hi"),
    new CultureInfo("en"),
};

services.AddMvc()
    .AddExpressLocalization<ExpressLocalizationResource, ViewLocalizationResource>(
    ops =>
    {
        ops.ResourcesPath = "LocalizationResources";
        ops.RequestLocalizationOptions = o =>
        {
            o.SupportedCultures = cultures;
            o.SupportedUICultures = cultures;
            o.DefaultRequestCulture = new RequestCulture("en");
        };
    })
    .SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

Then under Configure method, configure the app to use request localization:

app.UseRequestLocalization();

Adding Language Navigation

Under Pages folder, open _ViewImports.cshtml file and add LazZiya.TagHelpers that will help in creating language navigation:

@using LazZiya.TagHelpers
@addTagHelper *, LazZiya.TagHelpers

Then open Pages/Shared/_Layout.cshtml file and add the language navigation tag helper under the _LoginPartial tag as below:

<partial name="_LoginPartial" />
<language-nav view-context="ViewContext"></language-nav>

LanguageNav has one required parameter "view-context", read more about LanguageNav tag helper.

We are ready for the first run:

So good, so far we have our navigation with supported cultures, but we still need to localize view texts to see the localized versions.

Localizing Views

The localized texts for the default project are already provided in "ViewLocalizationResource.xx.resx" in the downloaded files. If you need to add more custom texts for views, add them to the "ViewLocalizationResource.xx.resx" files.

Option 1

Open Pages/_ViewImports.cshtml file and add localization tag helper:

@using LazZiya.TagHelpers.Localization
@addTagHelper *, LazZiya.TagHelpers.Localization

Then open Pages/Index.cshtml and use localize tag helper to localize texts/html:

@page
@model IndexModel
@{
    ViewData["Title"] = "Home page";
}

<div class="text-center">
    <h1 class="display-4" localize-content>Welcome</h1>
    <p localize-content>Learn about <a href='https://docs.microsoft.com/aspnet/core'> building Web apps with ASP.NET Core</a>.</p>
</div>

Use the same process to localize all texts in other views as well.

See more details in live demo page and GitHub.

Option 2

Open Pages/_ViewImports.cshtml file and inject SharedCultureLocalizer that already comes with ExpressLocalization:

@using LazZiya.ExpressLocalization
@inject SharedCultureLocalizer _loc

Then open Pages/Index.cshtml and use localizer function for texts:

@page
@model IndexModel
@{
    ViewData["Title"] = _loc.Text("Home page");
}

<div class="text-center">
    <h1 class="display-4">@_loc.Text("Welcome")</h1>
    <p>@_loc.Text("Learn about <a href='https://docs.microsoft.com/aspnet/core'> building Web apps with ASP.NET Core</a>").</p>
</div>

Use the same process to localize all texts in other views as well.

Localizing URLs

While the page is in any culture other than the default, if you click on Privacy, Login or Register links, you will notice that we are losing the selected culture, that is because we didn't add the culture route value to the link.

Open Pages/_ViewImports.cshtml and add reference to System.Globalization:

@using System.Globalization

Then open Pages/_LoginPartial.cshtml and add to the top of the page a culture parameter as below:

@{
    var culture = CultureInfo.CurrentCulture.Name;
}

Use this parameter to provide culture route value to all links as below:

<a class="nav-link text-dark" 

   asp-area="Identity"

   asp-page="/Account/Register"

   asp-route-culture="@culture"

   localize-content>Register</a>

Do this to all views in the project.

Localizing Identity Views

Identity related like login, register and profile needs to be scaffolded in order to be modified.

Right click on the project name, select Add --> New Scaffolded Item...

Select Identity and click Add:

Select "Override all files" and select "ApplicationDbContext":

When you click Add, a new Areas folder will be created including all identity related views:

Identity area has three _ViewImports folders:

  • Areas/Identity/Pages/_ViewImports.cshtml
  • Areas/Identity/Pages/Account/_ViewImports.cshtml
  • Areas/Identity/Pages/Account/Manage/_ViewImports.cshtml

Add the below code to all of them as we did for Pages/_ViewImports.cshtml previously:

@using System.Globalization

@using LazZiya.TagHelpers
@addTagHelper *, LazZiya.TagHelpers

@using LazZiya.TagHelpers.Localization
@addTagHelper *, LazZiya.TagHelpers.Localization

Go over the views and use localization steps as we did before for localizing views and add culture route parameter as well. Below is the Register.cshtml page:

@page
@model RegisterModel
@{
    ViewData["Title"] = "Register";
    var culture = CultureInfo.CurrentCulture.Name;
}

<h1 localize-content>Register</h1>

<div class="row">
    <div class="col-md-4">
        <form asp-route-returnUrl="@Model.ReturnUrl" 

        method="post" asp-route-culture="@culture">
            <h4 localize-content>Create a new account.</h4>
            <hr />
            <div asp-validation-summary="All" class="text-danger"></div>
            <div class="form-group">
                <label asp-for="Input.Email"></label>
                <input asp-for="Input.Email" class="form-control" />
                <span asp-validation-for="Input.Email" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Input.Password"></label>
                <input asp-for="Input.Password" class="form-control" />
                <span asp-validation-for="Input.Password" 

                class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Input.ConfirmPassword"></label>
                <input asp-for="Input.ConfirmPassword" class="form-control" />
                <span asp-validation-for="Input.ConfirmPassword" class="text-danger"></span>
            </div>
            <button type="submit" class="btn btn-primary" localize-content>Register</button>
        </form>
    </div>
</div>

@section Scripts {
    <partial name="_ValidationScriptsPartial" />
}

Localizing DataAnnotations

If you run the page and do some invalid inputs, you will notice that the validation messages are in English, so we will need to localize the data annotations messages, e.g., Required, StringLength, etc..

Open Areas/Identity/Pages/Account/Register.cshtml.cs file and add reference to "LazZiya.ExpressLocalization.Messages" at the top of the page; it contains a predefined struct for all DataAnnotations error messages for easy use:

@using LazZiya.ExpressLocalization.Messages;

Then modify the Input model to be like below:

public class InputModel
{
    [Required(ErrorMessage = DataAnnotationsErrorMessages.RequiredAttribute_ValidationError)]
    [EmailAddress(ErrorMessage = DataAnnotationsErrorMessages.EmailAddressAttribute_Invalid)]
    [Display(Name = "Email")]
    public string Email { get; set; }

    [Required(ErrorMessage = DataAnnotationsErrorMessages.RequiredAttribute_ValidationError)]
    [StringLength(100, ErrorMessage = 
           DataAnnotationsErrorMessages.StringLengthAttribute_ValidationErrorIncludingMinimum,
           MinimumLength = 6)]
    [DataType(DataType.Password)]
    [Display(Name = "Password")]
    public string Password { get; set; }

    [DataType(DataType.Password)]
    [Display(Name = "Confirm password")]
    [Compare("Password", 
     ErrorMessage = DataAnnotationsErrorMessages.CompareAttribute_MustMatch)]
    public string ConfirmPassword { get; set; }
}

Compile and run the project, you will see localized data annotation error messages:

Client Side Validation

The server side validation is working well, but we still need to add client side validation as well, so the input fields will be validated on client side before submitting the form.

One major issue with client side validation is validating localized inputs like numbers, dates, etc. For example, if you are using a decimal input, you will see validation error for localized numbers like 1.3 is valid in English culture, but is invalid for Turkish because it should be 1,3 (comma instead of period).

Here, we will use another useful tag helper that comes with LazZiya.TagHelpers.

Open Register.cshtml page and add the tag helper under the default validation scripts partial:

@section Scripts {

    <partial name="_ValidationScriptsPartial" />
    <localization-validation-scripts></localization-validation-scripts>
}

That's all, now the fields will be validated before submitting the form with localized validation messages:

Sample Project

You can download a sample project with more than 19 cultures included from GitHub (not yet updated to use LocalizeTagHelper):

References

Read more details about the used nuget packages here:

History

  • 3rd June, 2019: Initial version
  • 17th June, 2019: Updated version using LazZiya.TagHelpers.Localization 

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

computer engineer, asp.net developer, 3Ds designer, regional training expert

Comments and Discussions

 
PraiseI didn't know I needed this till.. Pin
juan_van12-Jun-19 14:57
memberjuan_van12-Jun-19 14:57 
GeneralRe: I didn't know I needed this till.. Pin
Ziya Mollamahmut12-Jun-19 21:13
memberZiya Mollamahmut12-Jun-19 21:13 
GeneralMy vote of 5 Pin
Member 102409367-Jun-19 2:06
memberMember 102409367-Jun-19 2:06 
GeneralRe: My vote of 5 Pin
Ziya Mollamahmut7-Jun-19 5:23
memberZiya Mollamahmut7-Jun-19 5:23 
GeneralMy vote of 5 Pin
Stylianos Polychroniadis4-Jun-19 9:38
memberStylianos Polychroniadis4-Jun-19 9:38 
GeneralRe: My vote of 5 Pin
Ziya Mollamahmut4-Jun-19 23:10
memberZiya Mollamahmut4-Jun-19 23:10 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Cookies | Terms of Use | Mobile
Web05 | 2.8.190617.3 | Last Updated 17 Jun 2019
Article Copyright 2019 by Ziya Mollamahmut
Everything else Copyright © CodeProject, 1999-2019
Layout: fixed | fluid