相关文章推荐
失恋的茶壶  ·  python 3 typeerror ...·  11 月前    · 
玉树临风的电梯  ·  TemplateDoesNotExist ...·  1 年前    · 
茫然的香槟  ·  c# change display ...·  1 年前    · 
  • Download sample - 5 MB
  • https://github.com/LazZiya/ExpressLocalizationSampleCore3 (Core 3.x Razor Pages & Mvc)
  • 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 . By default ASP.NET Core provides the below request culture providers:

  • QueryStringRequestCultureProvider
  • CookieRequestCultureProvider
  • AcceptLanguageHeaderRequestCultureProvider
  • In order to have route value localization, we will 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, e.g., www.example.com/en-US/

  • Setup DataAnnotations localization

    Localization of data annotations like Display names, Required, StringLength, ...etc.
    DataAnnotations are defined in System.ComponentModel.DataAnnotations namespace.
    In addition to localizing the default DataAnnotation s, custom attributes also must be localized using the same logic.

    For more details, see DataAnnotations.

  • Setup ModelBinding error messages localization

    Validation of input types at server side after submit, e.g., ValueMustBeANumberAccessor , AttemptedValueIsInvalidAccessor , etc.

    For more details, see DefaultModelBindingMessageProvider .

  • Setup IdentityDescriber error messages

    Localization of all user and role related messages like " User already in role ", " User name already exists ", ...etc.

    For more details, see IdentityErrorDescriber .

  • Setup OnRedirectTo... Events

    These events are fired automatically when the user is being automatically redirected to Login, Logout or AccessDenied pages. The auto redirection is not handling the culture value so it must be configured manually.

    For more details, see https://github.com/LazZiya/ExpressLocalization/issues/6

  • Setup view localization

    Localize text/html in razor pages.

    For more details, see ViewLocalization .

  • Setup client side validation scripts

    Localizing of client validation 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 use period and other cultures use 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.)

    For more details, see Client side validation and localization related issue in GitHub .

  • Create localized resource files for each culture

    Views, DataAnnotations, ModelBinding and IdentityErrors all require 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 web application (I'm using VS2019):

  • Create a new project by selecting ASP.NET Core Web Application:
  • Click Next , give the project a friendly name and click Create :
  • Select Web Application , set target framework to any of ASP.NET Core versions (3, 2 or 1) and make sure you change the authentication to Individual User Accounts .
  • 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

  • In the solution explorer under the project name, right click on Dependencies and select " Manage Nuget Packages ".
  • Go to Browse tab and search for " LazZiya ", select " LazZiya.ExpressLocalization " and click " Install ", select the latest version and install it:

    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.

    Notice: In this sample, I will use two resource files for localization strings, but you are free to work with one, two or more localization resource files for all your locailzation strings. Switch between the different variants of AddExpressLocalization to see the available possibilities. Read more in GitHub.

    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 required namespace:

    using LazZiya.ExpressLocalization;

    then inside ConfigureServices method, define supported cultures and add localization setup as below:

    var cultures = new[]
        new CultureInfo("tr"),
        new CultureInfo("ar"),
        new CultureInfo("hi"),
        new CultureInfo("en"),
    services.AddRazorPages()
        .AddExpressLocalization<ExpressLocalizationResource, ViewLocalizationResource>(
        ops =>
            ops.ResourcesPath = "LocalizationResources";
            ops.RequestLocalizationOptions = o =>
                o.SupportedCultures = cultures;
                o.SupportedUICultures = cultures;
                o.DefaultRequestCulture = new RequestCulture("en");
    

    Notice: Depending on the project pattern (MVC or Razor pages) and version, the services implementation may be changed to services.AddMvc() or services.AddControllersWithViews() or services.AddRazorPages().

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

    app.UseRequestLocalization();

    Setup in Different .NET Core Versions and Project Types

    Different versions of .NET Core (1, 2, 3) may have different services setup steps, ExpressLocalization works with all versions and pattern variants:

    services.AddMvc().AddExpressLocalization(...);
    services.AddRazorPages().AddExpressLocalization(...);
    services.AddControllersWithViews().AddExpressLocalization(...);

    If you have routes defined in startup, you need to add the culture parameter to the route table:

    app.UseRequestLocalization();
    app.UseEndpoints(endpoints =>
        endpoints.MapControllerRoute(
            name: "default",
            pattern: "{culture=en}/{controller=Home}/{action=Index}/{id?}");
    

    Or if you are using route attributes on controllers, add the culture route parameter to the route attribute:

    ["{culture}/Home"]
    public HomeController : Controller
    

    Adding Language Navigation

    This step requires another nuget package LazZiya.TagHelpers that can be installed from nuget as well.

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

    @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></language-nav>

    Setup Culture Cookie

    ExpressLocalization uses all culture providers by below order:

  • RouteSegmentCultureProvider
  • QueryStringRequestCultureProvider
  • CookieRequestCultureProvider
  • AcceptedLanguageHeaderRequestCultureProvider
  • Use default request culture from startup settings
  • For Cookie value to be set, we need to create a handler in IndexPage or the home page that will set the cookie culture value for the app.

    public class IndexModel : PageModel
        // ...
        public IActionResult OnGetSetCultureCookie(string cltr, string returnUrl)
            Response.Cookies.Append(
                CookieRequestCultureProvider.DefaultCookieName,
                CookieRequestCultureProvider.MakeCookieValue(new RequestCulture(cltr)),
                new CookieOptions { Expires = DateTimeOffset.UtcNow.AddYears(1) }
            return LocalRedirect(returnUrl)        
    

    Then we can configure our language navigation as below:

    <language-nav cookie-handler-url="@Url.Page("/Index", "SetCultureCookie", 
    new { area="", cltr="{0}", returnUrl="{1}" })">
    </language-nav>

    The place holders "{0}" and "{1}" will be replaced by the language taghelper with reference to each listed culture.

    After adding the 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.

    Visit LanguageTagHelper wiki for more details.

    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

    Here, we will use LocalizeTagHelper that ships with LazZiya.ExpressLocalization, it is offering a simple and clear HTML tag for localizing views.

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

    @addTagHelper *, LazZiya.ExpressLocalization

    Using the LocalizeTagHelper is pretty easy, just add "localize-contents" attribute to any html tag, or surround any html content with "<localize>" tag.

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

    @page
    @model IndexModel
        ViewData["Title"] = "Home page";
        var locArgs = new[] { "https://docs.microsoft.com/aspnet/core" };
    <div class="text-center">
        <h1 class="display-4" localize-content>Welcome</h1>
        <p localize-args="@locArgs">Learn about
        <a href="{0}">building Web apps with ASP.NET Core</a>.</p>
    

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

    See more details in live demo page and LocalizeTagHelper wiki.

    Option 2

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

    @using LazZiya.ExpressLocalization
    @inject ISharedCultureLocalizer _loc

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

    @page
    @model IndexModel
        ViewData["Title"] = _loc.GetLocalizedString("Home page");
    <div class="text-center">
        <h1 class="display-4">@_loc.GetLocalizedString("Welcome")</h1>
        <p>@_loc.GetLocalizedHtmlString("Learn about
             <a href='{0}'> building Web apps with ASP.NET Core</a>",
             new[]{"https://docs.microsoft.com/aspnet/core"}).</p>
    

    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 overridden 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
    @addTagHelper *, LazZiya.TagHelpers
    @addTagHelper *, LazZiya.ExpressLocalization

    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 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 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>
                <button type="submit" class="btn btn-primary" localize-content>Register</button>
            </form>
    @section Scripts {
        <partial name="_ValidationScriptsPartial" />
    

    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.DataAnnotations" at the top of the page; it offers express attributes that already produce localized error messages:

    @using LazZiya.ExpressLocalization.DataAnnotations;

    Then modify the Input model to be like below:

    public class InputModel
        [ExRequired]
        [EmailAddress]
        [Display(Name = "Email")]
        public string Email { get; set; }
        [ExRequired]
        [ExStringLength(100, MinimumLength = 6)]
        [DataType(DataType.Password)]
        [Display(Name = "Password")]
        public string Password { get; set; }
        [DataType(DataType.Password)]
        [Display(Name = "Confirm password")]
        [ExCompare("Password"]
        public string ConfirmPassword { get; set; }
    

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

    Localizing Custom Backend Messages

    Any custom messages coming from the backend can be localized using the ISharedCultureLocalizer that comes in LazZiya.ExpressLocalization. To localize any custom backend message, inject the ISharedCultureLocalizer and call the localizing method as below (the process is same in MVC and RazorPages):

    public IndexModel : PageModel
        private readonly ISharedCultureLocalizer _loc;
        public IndexModel(ISharedCultureLocalizer loc)
            _loc = loc;
        public void OnGet()
            //...
            var msg = _loc.GetLocalizedString("This is a custom backend message.");
            // ...
    

    The localized texts for backend messages must be defined in the same resource file that contain the localized texts of the views.

    ISharedCultureLocalizer has other variants that allows you to specify the target culture and target resource file as well. Browse and test the function to see other possibilities.

    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 LocalizationValidatonScripts that comes with LazZiya.TagHelpers.

    Register the taghelper in startup:

    services.AddTransient<ITagHelperComponent, LocalizationValidationScriptsTagHelperComponent>();

    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 with localized input will be validated with reference to the current culture as the decimal input validation with comma or period.

    Sample Project

    You can download a sample project with more than 19 cultures included from GitHub:

  • Download sample - 5 MB
  • https://github.com/LazZiya/ExpressLocalizationSampleCore3 (Core 3.x Razor Pages & Mvc)
  • References

    Read more details about the used nuget packages here:

  • LazZiya.TagHelpers
  • LazZiya.ExpressLocalization
  • Github repo: ExpressLocalization Sample Core 2
  • Github repo: ExpressLocalization Sample Core 3
  • Manually installing client side localization validation scripts
  • Manually creating language drop down navigation
  • Detailed tutorial for localizing ASP.NET Core 2.1
  • Even more can be found on my website http://ziyad.info
  • History

  • 3rd June, 2019
  • Initial version
  • 17 th June, 2019
  • Updated version using LazZiya.TagHelpers.Localization
  • 22 nd June, 2019
  • Updated sample, hotfix for some issues in localizing ModelBinding and Identity messages (Update LazZiya.ExpressLocalization to v2.0.1)
  • 2 nd October, 2019
  • DotNet Core 3.0 support
  • MVC and Razor Pages setup
  • Configuring Authentication cookie redirect paths
  • Added two download samples (.NET Core 3.0 and .NET Core 2.2)
  • Updated nuget packages to latest versions
  • 3 rd October, 2019
  • LazZiya.TagHelpers ViewContext update notice
  • 22 nd February, 2020
  • Update to latest versions of ExpressLocalization and TagHelpers
  • Cookie handler for culture value
  • 22 th February, 2020
  • Download sample updated
  • This article was originally posted at https://ziyad.info/en/articles/36-Develop_Multi_Cultural_Web_Application_Using_ExpressLocalization

    License

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

    computer engineer, asp.net developer, 3Ds designer, regional training expert
    http://ziyad.info
    http://demo.ziyad.info
    https://docs.ziyad.info
    http://github.com/lazziya Hi. Thank You for Your work. As my project targets Razor Pages with Asp.NET Core 3.1 You have saved me a lot of effort and time (I think localization - because of routing - is a bit harder to set on Razor Page).
    Could You please show me an example of how to add culture prefix to
    return RedirectToPage( " ./AnyOtherPageHere" );method? I`m running out of ideas Smile | :)

    modified 20-Mar-21 19:16pm.

    Sign In · View Thread var currentCulture = CultureInfo.CurrentCulture.Name; return new LocalRedirectResult( " ~/" + currentCulture + " /TheRestOfPath/" );
    Sign In · View Thread Hi @paul_newman
    Sorry for the late reply, but somehow I didn't receive notification for your comment.
    You can add the culture param to the route as below:
    If the pages are in the same folder on the same level below code should work fine:
    return RedirectToPage( " ./Index" )
    If you are redirecting to a page in another area folder would prefer the hard coded version to guarantee that the culture param exists in the route:
    return RedirectToPage( " /Account/Login" , new { area= " Identity" , culture = CultureInfo.CurrentCulture.Name })
    Let me know if you need any further help Smile | :)
    Sign In · View Thread btw, if you are just starting a new project I highly recommend to switch to XLocalizer :
    - GitHub repo
    - Article on code project
    - Docs
    It has a lot more useful features like built-in online translation, auto resource creating and it supports different resource types like (RESX, XML, DB, custom...) .
    Sign In · View Thread can you point me in the direction of create custom cultures.
    So I need to create a custom culture for each state in the united states. So en-US-TX or en-US-IN
    Language changes based on the state the user is working in.
    I spent alot of time researching I found the .net framework way. But it doesn't work with .net core 3.1 and I can't seem to find anywhere were I can create a customer culture like you can whit CultureAndRegionInfoBuilder in .net framework.
    Thanks for any help you can give to me....
    Angela
    Sign In · View Thread var enTx = new CultureInfo( " en-US-TX" )
    Please let me know if you need any further help.
    Sign In · View Thread Sorry for the late reply.
    I've developed another package that supports DB, XML or any custom storage type for localization. It also supports online translation and auto adding missed keys, check the docs in the below link.
    XLocalizer - DOCS.Ziyad.info [ ^ ].
    Hope it helps Smile | :)
    Sign In · View Thread Wonderful code!! Very easy and workinng...but I have a question, how could I add cultures from other languages ​​that are not on the list? For example in Spain there is Catalan, Valencia...etc etc...My app run about Core 3.1. Thanks for the help! Smile | :)
    Sign In · View Thread Thanks! Just add the culture to the cultures list in startup, and create relevant resources under LocalizationResources folder, thats all Smile | :)
    Sign In · View Thread Thank you very much for this project, I was looking for this solution since long time
    and now i am happy to find the solution.
    Again thank you soooooo much.
    Sign In · View Thread The title clearly mentions Core 2.x Smile | :)
    What is more, Core 3 is still in preview mode and it is not supported yet by these packages, but I will add Core 3 support whenever it goes to a full release.
    Sign In · View Thread Web03 2.8:2023-08-14:1