Git Product home page Git Product logo

expresslocalization's Introduction

ATTENTION: Due to several major breaking changes in the planned v5, the next version has been moved to a new project! ExpressLocalization will continue to get support and hotfixes. To continue with the next vesion please see:

New project repository: XLocalizer

New project docs: DOCS.Ziyad.info

Sample repository: XLocalizer.Samples

What is ExpressLocalization?

A nuget package to simplify the localization setup of any Asp.Net Core web application.

How to use

Install from nuget :

Install-Package LazZiya.ExpressLocalization

Add ExpressLocalization to startup.cs:

using LazZiya.ExpressLocalization;

public void ConfigureServices(IServiceCollection services)
{    
    var cultures = new CultureInfo[]
    {
        new CultureInfo("en"),
        new CultureInfo("tr"),
        new CultureInfo("ar")
    };

    services.AddRazorPages()
        .AddExpressLocalization<LocalizationResource>(
            ops =>
            {
                ops.ResourcesPath = "LocalizationResources";
                ops.RequestLocalizationOptions = o =>
                {
                    o.SupportedCultures = cultures;
                    o.SupportedUICultures = cultures;
                    o.DefaultRequestCulture = new RequestCulture("en");
                };
            });
}

Then configure the app to use RequestLocalization middleware :

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    // Other codes...
    
    // Add localization middleware to the app
    app.UseRequestLocalization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapRazorPages();
    });
}

Setup and options

For all details goto wiki pages

Step by step tutorial

Sample projects

License

MIT

expresslocalization's People

Contributors

dependabot[bot] avatar lazziya avatar mmsaffari avatar rrtischler avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

expresslocalization's Issues

Case sensitivity mismatch

Let's say I have this:

@_loc.GetLocalizedString("Privacy Policy")

And in my ViewLocalizationResource.cy.resx, I have an entry for 'Privacy Policy':

pp

When I toggle between English and Welsh, I see the string is 'translated' as expected--awesome!

However, somewhere else in my app, I may want to add this (casing exaggerated):

@_loc.GetLocalizedString("pRiVaCy PoLiCy")

Now if I toggle between English and Welsh, the string doesn't get translated--it remains as 'pRiVaCy PoLiCy' in both English and Welsh localised pages. So, I go to my ViewLocalizationResource.cy.resx to enter an entry for this odd casing, but the .resx file doesn't let me enter it:

pp2

Please correct me if I'm wrong, but it seems the resource file is case insensitive, but the GetLocalizedString method is case sensitive, meaning I can't provide a translation for this casing?

Is there a way around this? Not sure if this is 'as designed' or a bug.

Versions:

    <PackageReference Include="LazZiya.ExpressLocalization" Version="3.2.0" />
    <PackageReference Include="LazZiya.TagHelpers.Localization" Version="1.3.0" />

Thanks
Morgan

Null Reference Error

Hi Laz, I hope you are keeping safe and well.

I'm struggling with a null reference error for localized values when I scaffold in the ForgotPassword page in .Net 5.0. I have been trying to follow implementation from the ExpressLocalization .Net Core example project, and have reviewed the prior issues here on GitHub, but am still having the same problem. The confusing thing is that it has not happened on other pages I have scaffolded in.

Exception:

An unhandled exception occurred while processing the request.
ArgumentNullException: Value cannot be null. (Parameter 'name')
Microsoft.AspNetCore.Mvc.Localization.HtmlLocalizer.get_Item(string name, object[] arguments)

Stack Query Cookies Headers Routing
ArgumentNullException: Value cannot be null. (Parameter 'name')
Microsoft.AspNetCore.Mvc.Localization.HtmlLocalizer.get_Item(string name, object[] arguments)
LazZiya.ExpressLocalization.SharedCultureLocalizer.GetLocalizedString(string key, object[] args)
LazZiya.ExpressLocalization.SharedCultureLocalizer.get_Item(string key)
AspNetCore.Areas_Identity_Pages_Account_ForgotPassword.ExecuteAsync() in ForgotPassword.cshtml
+
    ViewData["Title"] = _loc[Model.PageTabTitle];
Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderPageCoreAsync(IRazorPage page, ViewContext context)
Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderPageAsync(IRazorPage page, ViewContext context, bool invokeViewStarts)
Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderAsync(ViewContext context)
Microsoft.AspNetCore.Mvc.ViewFeatures.ViewExecutor.ExecuteAsync(ViewContext viewContext, string contentType, 

When I replace ViewData["Title"] = _loc[Model.PageTabTitle]; with ViewData["Title"] = "Test"; in my .cshtml file, the error simply appears for the next localized value down the page. Plus using _loc[Model.PageTabTitle] works fine on the other pages, so its not an issue with my string referencing.

.cshtml

@page
@model ForgotPasswordModel
@using LazZiya.ExpressLocalization
@using System.Globalization
@inject ISharedCultureLocalizer _loc
@{
    ViewData["Title"] = _loc[Model.PageTabTitle];
    var culture = System.Globalization.CultureInfo.CurrentCulture.Name;
}

Link to ForgotPassword page from Login.cshtml:

<a class="link-style" asp-page="./ForgotPassword" asp-route-culture="@CultureInfo.CurrentCulture.Name" id="LoginForgotPasswordLink">@_loc[Model.ForgotPassword]</a>

Model/Controller .cshtml.cs file

I have the parameter specified in my model and referenced in the OnPostAsync method:

public string PageTabTitle { get; set; }

and

PageTabTitle = _locSourceForgotPasswordPageNameReferenceLibrary.GetLocSourcePageTabTitleNameReferenceForForgotPasswordPage();

There are no build errors and the URL appears to be correct:

https://localhost:44305/en/Identity/Account/ForgotPassword

I have compared to my other scaffolded pages which are working fine, I cant figure out why this one is different. Any ideas would be appreciated?

DataAnnotation messages not being translated

Thanks for your work here!

I've added a new razor page (named Create) to the ExpressLocalizationSampleCore31 project.

@page
@model ExpressLocalizationSampleCore31.Pages.CreateModel
@{
    ViewData["Title"] = "Create";
}

<h1>Create</h1>

<div class="row">
    <div class="col-md-4">
        <form asp-route-returnUrl="UserIndex" method="post">
            <hr />
            <input type="hidden" asp-for="UserVM.Id" />
            <div asp-validation-summary="All" class="text-danger"></div>
            <div class="form-group">
                <label asp-for="UserVM.Email"></label>
                <input asp-for="UserVM.Email" class="form-control" />
                <span asp-validation-for="UserVM.Email" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="UserVM.Password"></label>
                <input asp-for="UserVM.Password" class="form-control" autocomplete="new-password" />
                <span asp-validation-for="UserVM.Password" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="UserVM.ConfirmPassword"></label>
                <input asp-for="UserVM.ConfirmPassword" class="form-control" />
                <span asp-validation-for="UserVM.ConfirmPassword" class="text-danger"></span>
            </div>
            <button type="submit" class="btn btn-primary">Guardar</button>
        </form>
    </div>
</div>

With this model for the page:

using ExpressLocalizationSampleCore31.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using System.Diagnostics;
using System.Threading.Tasks;

namespace ExpressLocalizationSampleCore31.Pages
{
    public class CreateModel : PageModel
    {
        [BindProperty]
        public UserVM UserVM { get; set; }

        public void OnGet()
        {
            UserVM = new UserVM()
            {
                Id = "1",
                Email = "[email protected]",
                Password = string.Empty
            };
        }

        public async Task<IActionResult> OnPostAsync()
        {
            if (ModelState.IsValid)
            {
                Debug.WriteLine(UserVM.Id);
                return RedirectToPage("Index");
            }

            // If we got this far, something failed, redisplay form
            return Page();
        }
    }
}

And this viewmodel class:

public class UserVM
    {
        public string Id { get; set; }
        [Required]
        [EmailAddress]
        [Display(Name = "Correo")]
        public string Email { get; set; }

        [Required]
        [DataType(DataType.Password)]
        [Display(Name = "Contraseña")]
        public string Password { get; set; }

        [DataType(DataType.Password)]
        [Display(Name = "Confirmar contraseña")]
        [Compare("Password")]
        public string ConfirmPassword { get; set; }
    }

When I leave one of the fields blank, i'm getting an error message like: "The Correo field is required."
However, i've selected German in the upper right list, and I see a row in resx file with the comment DataAnnotations: RequiredAttribute_ValidationError with that exact message ("The {0} field is required.").

Do you know why isn't this message being translated?

Culture is not supported

Hi,
Sorry for reaching out to you with another issue, I am a big fan of your expresslocalization package, but I'm having a terrible time trying to get localization working for my Identity Registration when I click submit to register a new user.

I have it setup as per the ExpressLocalizationSampleCore31 but I keep getting the below error after clicking submit, when it tries to direct the user to the RegisterConfirmation page to tell them to check their email.

NOTE: If I change the mailbody property in Register.cshtml.cs from
var mailBody = _loc.GetLocalizedString("Please confirm your account by clicking here.", HtmlEncoder.Default.Encode(callbackUrl));
to
var mailBody = "Any plain string";
then it works fine. But the email has no clickable link for the recipient.

image
image
image

Problem with ExpressValidationAttributeAdapterProvider

I need to create custom DataAnnotations to work both on client side and server side.
I have tried to implement them as indicated in the Microsoft docs:
https://docs.microsoft.com/en-us/aspnet/core/mvc/models/validation?view=aspnetcore-3.1#custom-attributes
https://docs.microsoft.com/en-us/aspnet/core/mvc/models/validation?view=aspnetcore-3.1#client-side-validation

But when adding my own provider in dependency injection, I have overwritten the one used by this system and the 'Ex' DataAnnotations (ExRequiredAttribute, etc) on the client side have stopped working (no longer painting the data-val-* attributes in the inputs).

The problem is that the operation of ExpressValidationAttributeAdapterProvider cannot be extended since it is internal, and neither can you create your own provider since you cannot add the adapters of the "Ex" DataAnnotations because they are also internal.

How can I add new adapters for custom validation attributes without affecting the "Ex" DataAnnotations functionality?

Can we create custom DataAnnotations to avoid re-typing ErrorMessage in every attribute

DataAnnotations requires an error message to be provided in order to be localized:

[Required(ErrorMessage = "The {0} field is required")]

Can we create custom DataAnnotations that already has predefined ErrorMessage inside its body to avoid manually adding message to every annotation? e.g.

// This will provide localized error message
[ExRequired]

DataAnnotations source code :

SO questions :

Language Selection Dropdown not Working

Hi. This was working fine until today, and I have walked through the setup again, compared all files against the downloaded repo, but I can't get it working.

I restructured my Razor pages to add additional @RenderSection 's and the Localize dropdown does nothing when clicked anymore.

Any thoughts are appreciated...its a great package, but I have no idea what had caused the dropdown to stop working. I was only refactoring header code out of the Index page and into _Layout.cshtml.

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

Index.cshtml

@page
@model MyApp.Pages.IndexModel
@using LazZiya.ExpressLocalization
@{
    ViewData["Title"] = "MyApp";
}
<body>
    @section header_image{
        <div class="bg-img">
    }
    @section header_content{
        <div class="container-title">
            <div class="block-title block-title1">
                <language-nav language-label="NativeName" cookie-handler-url="@Url.Page("/Index", "SetCultureCookie", new { area="", cltr="{0}", returnUrl="{1}" })"></language-nav>
                <br>
                <a button type="button" class="btn btn-outline-success button-title" asp-page="Register">@_loc["Make A Difference"]</a>
                <h1 class="block-title-text" localize-content>@Model.TitleText</h1>
                <h2 class="block-subtitle-text" localize-content>@Model.SubTitleText1</h2>
                <h2 class="block-subtitle-text" localize-content>@Model.SubTitleText2</h2>
            </div>
            <div class="block-title block-title2 d-none d-md-block d-lg-block d-xl-block"><img src="/image/title_image.png" class="img-fluid"></div>
        </div>
    }
</div>
<main>
    <div class="row_primary">
    </div>
</main>
</body>

_ViewImports.cshtml

@using Microsoft.AspNetCore.Identity
@using MyApp
@using MyApp.Data
@using LazZiya.ExpressLocalization
@inject ISharedCultureLocalizer _loc
@namespace MyApp.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@using LazZiya.TagHelpers
@addTagHelper *, LazZiya.TagHelpers
@addTagHelper *, LazZiya.ExpressLocalization

Startup.cs
Has everything as per the instructions.

Index.cshtml.cs

        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);
        }

_Layout.cshtml

@using LazZiya.ExpressLocalization
@inject ISharedCultureLocalizer _loc
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>@ViewData["Title"] | @_loc["Subtitle"]</title>
    <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
    <link rel="stylesheet" href="~/css/style.css" />
</head>

<body>
    <header>
        @RenderSection("header_image", required: false)
        <nav class="navbar navbar-expand-lg navbar-light">
            <div class="container">
                <a class="navbar-brand mt-20" asp-page="Index">
                    <img src="~/image/circle_logo.png" width="40" height="40" style="margin:0px 20px;" alt="Logo Image">
                </a>
                <button class="navbar-toggler" type="button" data-toggle="collapse"
                        data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
                    <span class="navbar-toggler-icon"></span>
                </button>
                <div class="collapse navbar-collapse" id="navbarSupportedContent">
                    <ul class="navbar-nav mr-auto">
                        <li class="nav-item active">
                            <a class="nav-link" style="color: rgba(255, 140, 0, 0.781)" asp-page="Index">@_loc["Home"]</a>
                        </li>
                        <li class="nav-item active">
                            <a class="nav-link" style="color: green" asp-page="Features">@_loc["Features"]</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link" style="color: green" asp-page="About">@_loc["About Us"]</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link" style="color: green" asp-page="HelpCenter">@_loc["Help Center"]</a>
                        </li>
                        <partial name="_LoginPartial" />
                    </ul>
                </div>
            </div>
        </nav>
        @RenderSection("header_content", required:false)
    </header>
    <main role="main">
        @RenderBody()
    </main>

Improve RedirectToLogin to handle culture

Considering the issue here LazZiya/ExpressLocalizationSampleCore3#1

Add the relevant cookie configuration to the ExpressLocalization nugget package... So when in-authenticated user clicks on link, the culture parameter will not be lost;

services.ConfigureApplicationCookie(options =>
{
    options.Events = new CookieAuthenticationEvents
    {
        OnRedirectToLogin = ctx =>
        {
            var culture = ctx.Request.RouteValues["culture"];
            var requestPath = ctx.Request.Path;

            if (culture == null)
            {
                culture = "en";
                requestPath = $"/{culture}{requestPath}";
            }

            ctx.Response.Redirect($"/{culture}/Identity/Account/Login/?ReturnUrl={requestPath}{ctx.Request.QueryString}");
            return Task.CompletedTask;
        }
    };
});

Missing ExRequiredAttributeAdapter

Hi,
nice work with the library.
I think you've missed the ExRequiredAttributeAdapter that could be easy written as there is already base code available: https://github.com/aspnet/Mvc/blob/master/src/Microsoft.AspNetCore.Mvc.DataAnnotations/RequiredAttributeAdapter.cs

I checked the ExpressValidationAttributeAdapterProvider and it seems that it uses the base RequiredAttributeAdapter. In my case this adapter does not emit the data-val-* atrributes on the client side.

cheers,
Kamil

Change culture along with Area and extra parameter

Hello,

I am facing a strange issue when trying to change the culture when I am on the following page:
http://localhost:xxxxx/en/Admin/GlobalSettings/Edit?Id=1
where:
1- Admin is an area.
2- GlobalSettings is a controller.
3- Edit is an action
4- is a parameter.
** Note: this page is working perfectly when I navigate from the index page by passing these tag helpers to the tag:
<a asp-action="Edit" asp-area="Admin" asp-route-culture="@CultureInfo.CurrentCulture.Name" asp-route-Id="1" class="btn btn-primary btn-modern"><localize>Update</localize></a>

but when I try to change the culture when I am on this page it gave me the following
No webpage was found for the web address: http://localhost:xxxxx/en/GlobalSettings?area=Admin
HTTP ERROR 404

In _Layout:
@{var routeData = ViewContext.RouteData.Values;
routeData["culture"] = "{0}"; }

Retrieve Value from .resx file

Is it possible to retrieve a value from one of your localization.resx files using the primary key - directly from a C# class?

I have been trying to do this, so I can unit test the value, but because the Express Localization file structure embeds the .resx file under the LocSource.cs file I cant seem to retrieve values directly. If this is possible, it might be useful for other people also to add it to your documentation. I looked but cant find it there, apologies if I missed it. Thanks.

Tested idea. Do not block others сulture providers

For other providers to work when a segment is not defined, it would be nice to replace the return
Task.FromResult(new ProviderCultureResult(DefaultCulture))
on
Task.FromResult<ProviderCultureResult>(null)
in the DetermineProviderCultureResult method of RouteValueRequestCultureProvider class.

Thus, the problem of using the current culture from cookies on pages where a culture segment cannot be applied, for example, in the extensions: HangFire Dashboard, SignalR hubs and others.

The name RouteSegmentCultureProvider more accurate than RouteValueRequestCultureProvider

ExRange unexpected behavior as Required

I just changed the .net attributes to the Ex ones and got the fields with ExRange validating as if they were Required as well.
I think it's because of this:

MergeAttribute(context.Attributes, "data-val-required", GetRequiredErrorMessage(context));

In my opinion the ExRange shouldn't validate as required.
For example, I'm using to validate a nullable number. If I needed to validate as required, I'd include that attribute as well.

Is there a workaround for the ExRange that doesn't validate as required?

For now, I'm using as below, but I would rather use ExRange or other way that doesn't need to set the ErrorMessage every time.
[Range(1, 999999, ErrorMessage = DataAnnotationsErrorMessages.RangeAttribute_ValidationError)]

Thanks!

ExpressLocalizationCore3 Field Validation Issue

Hi, I'm running into a strange defect with the implementation of ExpressLocalizationCore3. I have spent hours cross-checking against the sample application for Core3 and also against other pages I have working ok, but I cannot figure out what's going on here.

When My NewEmail field is empty the validation does not work, but when it has an invalid entry it does work.

Empty Field validation text is not translated other than the embedded string for "New email" (This should be German):

EmptyFieldValidation

Invalid Field validation text is translated (This is Ok):
InvalidFieldValidation

**Input Model:
InputModel
**

LocSource.de.resx file
LocSourcederesx
image

cshtml:
html

Any pointers would be much appreciated?

Set culture based off URL domain/tld instead of URL path

Let's say I want to support to support two cultures: English and Welsh (Cymru).

I have a domain setup called mywebsite.wales and another one called mywebsite.cymru. Their DNS settings both point to the same server. Is it possible to configure ExpressLocalization to set the current culture based off the domain name?

Cheers
Morgan

DefaultRequestCulture not working when change to non-en culture in 3.1.3

I have change the DefaultRequestCulture to "de" but it still display in en culture when start up.

        services.AddRazorPages()
            .AddExpressLocalization<ExpressLocalizationResource, ViewLocalizationResource>(ops =>
            {
                ops.ResourcesPath = "LocalizationResources";
                ops.RequestLocalizationOptions = o =>
                {
                    o.SupportedCultures = cultures;
                    o.SupportedUICultures = cultures;
                    o.DefaultRequestCulture = new RequestCulture("de");
                };
            });

Breaking change in MS.NET.Test.Sdk

Hi,

I updated the Microsoft.NET.Test.Sdk package from 16.11.0 to 17.0.0 and when I run my unit tests they fail with the error:

System.InvalidOperationException : Different CurrentCulture and CurrentUICulture culture is not supported.

I reverted the package and they run fine.

For info.

Roadmap

Do you plan any roadmap for your library?

RoutePatternException

I have an ASP.Net Core 3.0 MVC project in which I implemented Express Localization for MVC as per the github ExpressLocalizationSampleCore3MVC project . This works ok.

I then scaffolded identity into the project. This works ok.

At this point when I clicked my 'Register' link I was losing the culture cookie i.e the app was navigating to page https://localhost:44305/Identity/Account/Register. If I typed in the localisation url manually, for example https://localhost:44305/de/Identity/Account/Register it worked.

So I decided to try and implement the identity pages as per the ExpressLocalizationSampleCore3 sample project. Unfortunately I am now getting an error:

An unhandled exception occurred while processing the request.
RoutePatternException: The route parameter name 'culture' appears more than one time in the route template.
Microsoft.AspNetCore.Routing.Patterns.RoutePatternParser.Parse(string pattern)

So I have 2 questions please:

1) Do you have a localization example for an MVC project with scaffolded entity framework identity pages?

2) If not, what am I doing wrong below?

Thank you.

Project File Structure:

Areas

Identity

Pages

_ViewImports.cshtml
_ViewStart.cshtml
_ValidationScriptsPartial.cshtml
Account

_ViewImports.cshtml
Login.cshtml
Register.cshtml

Controllers

LocalizationResources

LocSource.cs

LocSource.de.resx
LocSource.en.resx

LocSourceData
LocSourceViews

Models
Views

_ViewImports.cshtml
_ViewStart.cshtml
SetCultureCookie.cshtml

Shared

_CookieConsentPartial.cshtml
_Layout.cshtml
_LoginPartial.cshtml
_ValidationScriptsPartial.cshtml
Error.cshtml

Program.cs
Startup.cs

Identity > Pages > Account > _ViewImports.cshtml

@using MyApp
@using MyApp.Areas.Identity.Pages.Account

Identity > Pages > Account > Register.cshtml

@page
@model RegisterModel
@using LazZiya.ExpressLocalization
@inject ISharedCultureLocalizer _loc
@{
    ViewData["Title"] = _loc[Model.PageTabTitle];
    var culture = System.Globalization.CultureInfo.CurrentCulture.Name;
}
@section header_image{
    <div class="bg-img-non-home" id="FeaturesPageBanner">
}
@section header_content{
    <div class="container-title">
        <div class="block-title">
            <br>
            <h1 class="block-subtitle-text" id="FeaturesPageSubtitle">@_loc[Model.SubTitle]</h1>
            <h2 class="block-title-text-non-home" id="FeaturesPageTitle">@_loc[Model.Title]</h2>
        </div>
    </div>
}

Identity > Pages > Account > Register.cshtml.cs

namespace MyApp.Areas.Identity.Pages.Account
{
    [AllowAnonymous]
    public class RegisterModel : PageModel
    {
        private readonly SignInManager<ApplicationUser> _signInManager;
        private readonly UserManager<ApplicationUser> _userManager;
        private readonly ILogger<RegisterModel> _logger;
        private readonly IEmailSender _emailSender;
        RegisterPageLocSourceNames _locSourceRegisterPageNameReferenceLibrary = new RegisterPageLocSourceNames();
        SharedCrossPageLocSourceNames _locSourceSharedCrossPageNameReferenceLibrary = new SharedCrossPageLocSourceNames();

        public RegisterModel(
            UserManager<ApplicationUser> userManager,
            SignInManager<ApplicationUser> signInManager,
            ILogger<RegisterModel> logger,
            IEmailSender emailSender)
        {
            _userManager = userManager;
            _signInManager = signInManager;
            _logger = logger;
            _emailSender = emailSender;
        }

        [BindProperty]
        public InputModel Input { get; set; }
        public string ReturnUrl { get; set; }
        public string PageTabTitle { get; set; }
        public string Title { get; set; }
        public string SubTitle { get; set; }

        public IList<AuthenticationScheme> ExternalLogins { get; set; }

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

            [Required]
            [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)]
            [DataType(DataType.Password)]
            [Display(Name = "Password")]
            public string Password { get; set; }

            [DataType(DataType.Password)]
            [Display(Name = "Confirm Password")]
            [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
            public string ConfirmPassword { get; set; }
        }

        public async Task OnGetAsync(string returnUrl = null)
        {
            PageTabTitle = _locSourceRegisterPageNameReferenceLibrary.GetLocSourcePageTabTitleNameReferenceForRegisterPage();
            Title = _locSourceRegisterPageNameReferenceLibrary.GetLocSourceTitleNameReferenceForRegisterPage();
            SubTitle = _locSourceRegisterPageNameReferenceLibrary.GetLocSourceSubtitleNameReferenceForRegisterPage();

            ReturnUrl = returnUrl;
            ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();
        }

        public async Task<IActionResult> OnPostAsync(string returnUrl = null)
        {
            returnUrl ??= Url.Content("~/");
            ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();
            if (ModelState.IsValid)
            {
                var user = new ApplicationUser { UserName = Input.Email, Email = Input.Email };
                var result = await _userManager.CreateAsync(user, Input.Password);
                if (result.Succeeded)
                {
                    _logger.LogInformation("User created a new account with password.");

                    var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
                    code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
                    var callbackUrl = Url.Page(
                        "/Account/ConfirmEmail",
                        pageHandler: null,
                        values: new { area = "Identity", userId = user.Id, code },
                        protocol: Request.Scheme);

                    await _emailSender.SendEmailAsync(Input.Email, "Confirm your email",
                        $"Please confirm your account by <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>.");

                    if (_userManager.Options.SignIn.RequireConfirmedAccount)
                    {
                        return RedirectToPage("RegisterConfirmation", new { email = Input.Email });
                    }
                    else
                    {
                        await _signInManager.SignInAsync(user, isPersistent: false);
                        return LocalRedirect(returnUrl);
                    }
                }
                foreach (var error in result.Errors)
                {
                    ModelState.AddModelError(string.Empty, error.Description);
                }
            }

            // If we got this far, something failed, redisplay form
            return Page();
        }
        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);
        }
    }
}

IdentityHostingStartup.cs

[assembly: HostingStartup(typeof(MyApp.Areas.Identity.IdentityHostingStartup))]
namespace MyApp.Areas.Identity
{
    public class IdentityHostingStartup : IHostingStartup
    {
        public void Configure(IWebHostBuilder builder)
        {
            builder.ConfigureServices((context, services) => {
                services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(context.Configuration.GetConnectionString("DefaultConnection")));
            });
        }
    }
}

Views > _ViewImports.cshtml

@using MyApp
@using MyApp.Models
@using MyApp.Data
@using LazZiya.ExpressLocalization
@namespace MyApp.Views
@inject ISharedCultureLocalizer _loc
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@addTagHelper *, LazZiya.TagHelpers
@addTagHelper *, LazZiya.ExpressLocalization

Views > SetCultureCookie.cshtml

@page
@model MyApp.Views.SetCultureCookieModel
@{
    ViewData["Title"] = "Redirect to";
}

<h1>Redirect to</h1>

~> Startup.cs

using LazZiya.ExpressLocalization;
using Microsoft.AspNetCore.Localization;
using MyApp.LocalizationResources;

namespace MyApp
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer( Configuration.GetConnectionString("DefaultConnection")));
            services.AddDefaultIdentity<ApplicationUser>( options => { options.SignIn.RequireConfirmedAccount = true; }).AddEntityFrameworkStores<ApplicationDbContext>();
            services.AddControllersWithViews();
            services.AddRazorPages();

            var cultures = new []
            {
                new CultureInfo("de"),
                new CultureInfo("en"),
            };

            services.AddRazorPages()
                .AddExpressLocalization<LocSource>(ops =>
                {
                                // When using all the culture providers, the localization process will
                                // check all available culture providers in order to detect the request culture.
                                // If the request culture is found it will stop checking and do localization accordingly.
                                // If the request culture is not found it will check the next provider by order.
                                // If no culture is detected the default culture will be used.

                                // Checking order for request culture:
                                // 1) RouteSegmentCultureProvider
                                //      e.g. http://localhost:1234/tr
                                // 2) QueryStringCultureProvider
                                //      e.g. http://localhost:1234/?culture=tr
                                // 3) CookieCultureProvider
                                //      Determines the culture information for a request via the value of a cookie.
                                // 4) AcceptedLanguageHeaderRequestCultureProvider
                                //      Determines the culture information for a request via the value of the Accept-Language header.
                                //      See the browsers language settings

                                // Uncomment and set to true to use only route culture provider
                                //ops.UseAllCultureProviders = false;
                                ops.ResourcesPath = "LocalizationResources";
                    ops.RequestLocalizationOptions = o =>
                    {
                        o.SupportedCultures = cultures;
                        o.SupportedUICultures = cultures;
                        o.DefaultRequestCulture = new RequestCulture("en");
                    };
                });

            services.AddControllersWithViews()
                .AddExpressLocalization<LocSource>(ops =>
                    {
                        ops.UseAllCultureProviders = false;
                        ops.ResourcesPath = "LocalizationResources";
                        ops.RequestLocalizationOptions = o =>
                        {
                            o.SupportedCultures = cultures;
                            o.SupportedUICultures = cultures;
                            o.DefaultRequestCulture = new RequestCulture("en");
                        };
                    });
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
                app.UseDatabaseErrorPage();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");

                // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
                app.UseHsts();
            }
            app.UseHttpsRedirection();
            app.UseStaticFiles();

            app.UseRouting();

            app.UseAuthentication();
            app.UseAuthorization();
            app.UseRequestLocalization();

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

~> Program.cs

namespace MyApp
{
    public class Program
    {
        public static void Main(string[] args)
        {
            CreateHostBuilder(args).Build().Run();
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                });
    }
}

Localization with query string

Hello,

I would like to reopen the question, please.
One of the cases I faced regarding parameters is like this:


   [Route("{culture}/Properties/Details/{propRefNo?}")]
        public async Task<IActionResult> PropertyDetails(string propRefNo)

as you can see the parameter is of type string. whenever I try to change the language the parameters are gone.
Please refer to the below image marked with red.
image
@LazZiya

Originally posted by @faresayyad in #20 (comment)

NullReferenceException when RazorPages is not enabled for MVC project with .NET CORE 3.1

services.AddExpressLocalization<>() throws a NullReferenceException when called in a Startup.cs of an MVC project with .NET Core 3.1.
The exception can be traced back to ExAddRouteValueRequestCultureProvider() under LazZiya.ExpressLocalization.ExpressLocalizationExtensions.

I'll send a PR to change

builder.AddRazorPagesOptions(x =>
{
    x.Conventions.Add(new RouteTemplateModelConvention());
});

into

builder.AddRazorPagesOptions(x =>
{
    if (x.Conventions != null)
    {
        x.Conventions.Add(new RouteTemplateModelConvention());
    }
});

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.