lazziya / expresslocalization Goto Github PK
View Code? Open in Web Editor NEWAll dirty localization settings in simple steps
Home Page: https://docs.ziyad.info
License: MIT License
All dirty localization settings in simple steps
Home Page: https://docs.ziyad.info
License: MIT License
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
Project based on asp.net core 5.0 web application
Hello, how to remove lang tag from routing? I want use localisation based on domains.
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?
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 :
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?
Do you plan any roadmap for your library?
Let's say I have this:
@_loc.GetLocalizedString("Privacy Policy")
And in my ViewLocalizationResource.cy.resx
, I have an entry for 'Privacy Policy':
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:
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
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?
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());
}
});
When validating fields, the field name is not passed to the message arguments.
e.g. when validating a price field named "Price", the message is:
The field true must be a number.
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;
}
};
});
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.
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");
};
});
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
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):
Invalid Field validation text is translated (This is Ok):
Any pointers would be much appreciated?
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.
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:
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!
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
After changing the language, model binding messages are still in the default culture.
Hi,
How can we redirect to default culture if it is not specified in the url?
At the same time, how can we auto redirect to user's previously selected culture value (we can get culture value from a cookie)?
Thanks.
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>();
});
}
}
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>
when validating identity fields, the field name is not passed to the message.
e.g.: validating new registered user email address message if already exist:
The user name '{0}' already exists.
github.com/LazZiya/ExpressLocalization/wiki/One-Resource-File
I have created the LocSource.cs file in my LocalizationResources folder and added the code below to the Startup.cs file.
I am getting a null pointer exception in the browser when I start the app.
services.AddRazorPages() .AddExpressLocalization<LocSource>(ops => { /* options */ });
Thanks.
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.
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}"; }
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.
@LazZiya
Originally posted by @faresayyad in #20 (comment)
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.