I wasted a lot of time sifting through incomplete explanations of how to localize Razor Pages, often discovering that answers were about MVC. So let me save you all that time, and just give you the simple steps that worked for me. I don't pretend to know the details of why anything is needed, just that it worked.
Add the Microsoft.Extensions.Localization nuGet package to the project.
To _ViewImports.cshtml, add:
#using Microsoft.AspNetCore.Mvc.Localization
#inject IViewLocalizer Localizer
In Startup.cs, change
services.AddRazorPages();
to
services.AddLocalization( options => options.ResourcesPath = "Resources" );
services.AddRazorPages()
.AddViewLocalization();
Add the supported languages in Startup.cs, by inserting something like the following before app.UseHttpsRedirection():
var supportedCultures = new List<CultureInfo>
{
new CultureInfo( "en-US" ),
new CultureInfo( "fr" )
};
var options = new RequestLocalizationOptions
{
DefaultRequestCulture = new RequestCulture( "en-US" ),
SupportedCultures = supportedCultures,
SupportedUICultures = supportedCultures
};
app.UseRequestLocalization( options );
Add localization to your pages, like:
<h2>#Localizer["Hello"]</h2>
To the web project, add a folder named Resources, with a subdirectory named Pages. In Resources/Pages, add resource files matching the names of the pages to localize. For example, Index.fr.resx:
<?xml version="1.0" encoding="utf-8"?>
<root>
<data name="Hello" xml:space="preserve">
<value>Bonjour</value>
</data>
</root>
Test your localization by adding a query string, such as during debugging changing your URL to https://localhost:5001/?culture=fr
Related
I am trying to force my asp.net core 3.1 application to use the locale "en-US", no matter the default locale of the Windows server it runs on.
My main goal is to have ModelBinding correctly parse doubles entered by users. So far, I did not have any luck. The comma is interpreted as the decimal separator, whereas the point is interpreted as the thousands separator, which is consistent with the Windows default locale.
So my code looks (simplified) like this:
public class Model
{
public double Percentage {get; set;}
}
[HttpPost]
public ActionResult Index(Model model)
{
Debug.WriteLine(model.Percentage);
}
This results in data entered and posted as "12.34" to be cast to double 1234, etc.
Previously, in ASP.Net MVC 5, adding this to the web.config file solved this issue:
<configuration>
<system.web>
<globalization culture="en-US" uiCulture="en-US" />
I have tried various methods described, which I will list below, but to no avail. These seem to be revolving around localization of the mark-up, but do not affect model binding.
Apply the same settings as listed above.
Adding this to the Startup's ConfigureServices() method:
System.Threading.Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US");
System.Threading.Thread.CurrentThread.CurrentUICulture = new CultureInfo("en-US");
Adding this to the same method:
services.Configure<RequestLocalizationOptions>(
options =>
{
options.DefaultRequestCulture = new RequestCulture(culture: "en-US", uiCulture: "en-US");
options.SetDefaultCulture("en-US");
});
The latter attempts being merely trial-and-error.
I tried running on IIS and on IIS Express.
I do realize I could write a custom model binder, but I think this adds unneeded complexity. Also, I could skip model binding and parse the post data manually, but the actual models are extensive and contain a lot of data. I do not want to go that way.
Addendum
I could reproduce this behavior with a small demo application. Steps to reproduce:
Create a new web application in Visual Studio .Net 2019 with:
ASP.Net Core Web Application
Web Application (Model-View-Controller)
Add a view model:
public class InputModel
{
[Range(0, 1)]
public double Fraction { get; set; }
}
Replace the index.cshtml with:
#model InputModel
#using (Html.BeginForm("Index", "Home", FormMethod.Post))
{
#Html.LabelFor(m=>m.Fraction)
#Html.TextBoxFor(m=>m.Fraction)
#Html.ValidationMessageFor(m => m.Fraction)
<input type="submit" value="Submit" />
}
Add an action to the conntroller:
[HttpPost]
public IActionResult Index(InputModel input)
{
if (ModelState.IsValid)
{
return Content($"Received fraction: {input.Fraction:E3}");
}
return View(input);
}
On my development Pc:
input 0.5 is invalid and
input 0,5 is valid.
input 0,1 is valid, but the action will output:
Received fraction: 1,000E+000
Adding this to ConfigureServices in Startup.cs:
services.Configure<RequestLocalizationOptions>(
options =>
{
options.SupportedCultures = new List<CultureInfo> { new CultureInfo("en-US") };
options.SupportedUICultures = new List<CultureInfo> { new CultureInfo("en-US") };
options.SetDefaultCulture("en-US");
});
does not make a difference.
Only when I set change settings through Control Panel - Clock and Region - Change date time or number formats, can I make my web application accept the dot as the decimal separator. I do not like having to rely on that.
I am having difficulty using a custom index.html and other assets with swashbuckle. Swashbuckle/Swagger do not seem to recognizing or using them at all. I do have app.UseDefaultFiles() and app.UseStaticFiles() set. I am trying to understand what I am doing incorrectly.
I have attempted to set up my configuration somewhat similar to what is defined on the Microsoft article without success. (https://learn.microsoft.com/en-us/aspnet/core/tutorials/web-api-help-pages-using-swagger?tabs=visual-studio)
I am presently using the files from the dist folder referenced in the article (https://github.com/swagger-api/swagger-ui/tree/2.x/dist) along with the custom css file provided.
My index.html file is located under /wwwroot/swagger/ui
The custom css file is located under /wwwroot/swagger/ui/css (as custom.css)
Here is my Startup.cs class.
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.AddMvc()
.AddJsonOptions(options =>
{
// Swagger - Format JSON
options.SerializerSettings.Formatting = Formatting.Indented;
});
// Register the Swagger generator, defining one or more Swagger documents
services.AddSwaggerGen(c =>
{
c.DescribeAllEnumsAsStrings();
c.DescribeStringEnumsInCamelCase();
// c.DescribeAllParametersInCamelCase();
c.SwaggerDoc("v1",
new Info
{
Title = "My Web API - v1",
Version = "v1",
Description = "New and improved version. A simple example ASP.NET Core Web API. "
}
);
c.SwaggerDoc("v2",
new Info
{
Title = "My Web API - v2",
Version = "v2",
Description = "New and improved version. A simple example ASP.NET Core Web API. "
}
);
// Set the comments path for the Swagger JSON and UI.
var basePath = AppContext.BaseDirectory;
var xmlPath = Path.Combine(basePath, "ApiTest.xml");
c.IncludeXmlComments(xmlPath);
});
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
string swaggerUIFilesPath = env.WebRootPath + "\\swagger\\ui";
if (!string.IsNullOrEmpty(swaggerUIFilesPath))
{
app.UseDefaultFiles();
app.UseStaticFiles(new StaticFileOptions
{
FileProvider = new PhysicalFileProvider(swaggerUIFilesPath),
RequestPath = new PathString("/api-docs"),
});
}
// Enable middleware to serve generated Swagger as a JSON endpoint.
app.UseSwagger(c =>
{
c.RouteTemplate = "api-docs/{documentName}/swagger.json";
});
// Enable middleware to serve swagger-ui (HTML, JS, CSS, etc.), specifying the Swagger JSON endpoint.
app.UseSwaggerUI(c =>
{
//c.ShowJsonEditor();
c.RoutePrefix = "api-docs";
c.SwaggerEndpoint("/api-docs/v1/swagger.json", "My Web API - V1 ");
c.SwaggerEndpoint("/api-docs/v2/swagger.json", "My Web API - V2 ");
c.DocumentTitle("My Web API");
});
app.UseMvc();
}
}
My ultimate objective is to be able to use something like the slate style theme available here (https://github.com/omnifone/slate-swagger-ui). For right now, I am just trying to get Swashbuckle/Swagger to use the customized files referenced in the Microsoft documentation before trying to make the other files work.
I really do NOT want to try and convert my assets to embedded resources--since there will many of them. I just want to reference a normal index.html file and be able to use all of its referenced files.
What am I doing wrong?
Relevant Software Versions
.Net Core Version: 2.0.3
Swashbuckle.AspNetCore: 1.2.0
Windows 10 Enterprise Build 1703
Visual Studio 2017 Enterprise 15.5.2
Here is the minimum action I found to be necessary to replace SwashBuckle's index.html in a .NET Core project:
Get a copy of the original index.html from here: https://github.com/domaindrivendev/Swashbuckle.AspNetCore/blob/master/src/Swashbuckle.AspNetCore.SwaggerUI/index.html
Place that copy in some sub-folder of your project.
The file may have a different name, I chose:
\Resources\Swagger_Custom_index.html
Right-click that file in Solution Explorer, select 'Properties', select 'Configuration Properties' in left pane. Under 'Advanced' in right pane find entry 'Build Action' and set it to 'Embedded resource'. Click Ok.
In Startup.cs add the following line to your app.UseSwaggerUI() call:
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
//...
app.UseSwaggerUI(c =>
{
c.IndexStream = () => GetType().GetTypeInfo().Assembly.GetManifestResourceStream("Your.Default.Namespace.Resources.Swagger_Custom_index.html");
});
//...
}
The identifier for the file resource in the above GetManifestResourceStream method is composed of:
your default namespace (i.e. 'Your.Default.Namespace')
the sub-path of your resource (i.e. 'Resources')
the filename of your resource (i.e. 'Swagger_Custom_index.html')
All three parts are concatenated using dots (NO slashes or backslashes here).
If you don't use a sub-path but have your resource in root, just omit part 2.
For people who separate ApplicationBuilder config methods on ASP.NET Core:
If the separated method/class is static, it is not possible to call GetType() because an object reference is required.
In that case, switch GetType() to MethodBase.GetCurrentMethod().DeclaringType
c.IndexStream = () => MethodBase.GetCurrentMethod().DeclaringType.Assembly.GetManifestResourceStream("xxx.index.html");
I'm working on Polish operating system:
In my Statup.csclass I have following code
// Configure the localization options
var supportedCultures = new[]
{
new CultureInfo("en-GB")
};
app.UseRequestLocalization(
new RequestLocalizationOptions
{
DefaultRequestCulture = new RequestCulture("en-GB"),
SupportedCultures = supportedCultures,
SupportedUICultures = supportedCultures,
FallBackToParentCultures = true,
FallBackToParentUICultures = true,
RequestCultureProviders = null
});
The full options are for reference only to be sure that nothing is set behind. In my _Layout.cshtml I have following code:
<div>Current Culture: #CultureInfo.CurrentCulture.DisplayName</div>
<div>Current UI Culture: #CultureInfo.CurrentUICulture.DisplayName</div>
The only supported and available culture should be en-GB, however on web site it is always showing:
Current Culture: Polski (Polska)
Current UI Culture: Polski (Polska)
I've tried to add Microsoft.AspNet.Localization package, but it makes no difference. Based on code in localization middleware, all should work as expected. I'm running latest version of ASP.NET Core 1.0.0.
There is one important thing, not mentioned in documentation. UseRequestLocalization has to be placed before UseMvc, mine was below.
We creating a windows azure website in MVC4 and we are using dataannotation to set the display-name and also to validate the input fields. It is a multilanguage page and thus we are unsing Resource-Files to translate.
[Display(ResourceType = typeof(GlobalResource), Name = "LitZip")]
[Required(ErrorMessageResourceType = typeof(GlobalResource), ErrorMessageResourceName = "ErrRequiredZip")]
public string ZIP { get; set; }
Local all works perfect. Uploaded on windows azure all shows fine too but the errors after validation are not translated. When I'm returning the CurrentCulture, it's correctly set to german. We are using the custom tool "PublicResXFileCodeGenerator" to generate the resource files.
Hope anybody can help us with this issue.
thanks in advance!
kind reagrds
Edit:
The culture is set by a filterattribute as follows:
var culture = new CultureInfo("de-de");
Thread.CurrentThread.CurrentCulture = culture;
Thread.CurrentThread.CurrentUICulture = culture;
Edit 2013-05-02:
I'm currently setting the culture in the ActionFilterAttribute. When I set the culture in the web.config-File it all works.
<globalization culture="de-DE" uiCulture="de-DE" />
Nevertheless, I need to be able to change the culture on runtime individual for the users. Maybe the ActionFilterAttribute is the wrong position here? I need to access cookie data..
Add this line to Web.Config file.
<globalization uiCulture="auto:ru-RU" culture="auto:ru-RU" requestEncoding="utf-8" responseEncoding="utf-8"/>
For my MVC project (Image Server Application), I cannot do caching using imageresizer. I can access my images like this and the image source could be either FileSystem/Database (Dependency injeciton) :
localhost/images/123.jpg?width=500
localhost/images/123?width=500
I have an MVC 3 project with routes like
routes.RouteExistingFiles = true;
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.IgnoreRoute("favicon.ico");
routes.MapRoute(
"ImagesWithExtension", // Route name
"images/{imageName}.{extension}/", // URL with parameters
new { controller = "Home", action = "ViewImageWithExtension", imageName = "", extension = "", id = UrlParameter.Optional } // Parameter defaults
);
routes.MapRoute(
"Images", // Route name
"images/{imageName}/", // URL with parameters
new { controller = "Home", action = "ViewImage", imageName = "", id = UrlParameter.Optional } // Parameter defaults
);
I have two controllers to deal with images
public ActionResult ViewImageWithExtension(string imageName, string extension) {}
public ActionResult ViewImage(string imageName) {}
The caching is done when the URL is like :
localhost/images/123.jpg?width=500 and the image source is FileSystem
localhost/images/123?width=500 Cache not working image source is Filesystem
localhost/images/123.jpg?width=500 Cache not working, image source DB
localhost/images/123?width=500 Cache not working , image source DB
My web config is like this:
<configSections>
<section name="resizer" type="ImageResizer.ResizerSection" /> </configSections>
<resizer>
<!-- Unless you (a) use Integrated mode, or (b) map all reqeusts to ASP.NET,
you'll need to add .ashx to your image URLs: image.jpg.ashx?width=200&height=20
Optional - this is the default setting -->
<diagnostics enableFor="AllHosts" />
<pipeline fakeExtensions=".ashx" />
<DiskCache dir="~/MyCachedImages" autoClean="false" hashModifiedDate="true" enabled="true" subfolders="32" cacheAccessTimeout="15000" asyncWrites="true" asyncBufferSize="10485760" />
<cleanupStrategy startupDelay="00:05" minDelay="00:00:20" maxDelay="00:05" optimalWorkSegmentLength="00:00:04" targetItemsPerFolder="400" maximumItemsPerFolder="1000" avoidRemovalIfCreatedWithin="24:00" avoidRemovalIfUsedWithin="4.00:00" prohibitRemovalIfUsedWithin="00:05" prohibitRemovalIfCreatedWithin="00:10" />
<plugins>
<add name="DiskCache" />
</plugins> </resizer>
Am I doing something wrong or Imageresizer doesnt support this scenario ? If not any good plugin to use disk based image cahce ?
Thanks in advance.
As I explained in the other public forums which you simultaneously posted this question, the ImageResizer supports dependency injection from the ground up. You're trying to wrap dependency injection with more dependency injection, but backwards.
A) ASP.NET MVC 3 and 4 prevent efficient disk caching, by design. You need to work with the ImageResizer HttpModule, not against it, to get good performance. That means using the URL API, not the Managed API wrapped by your own MVC ActionResults. Listen to my podcast with Scott Hanselman for more info.
B) SqlReader, S3Reader, MongoReader, VirtualFolder, and AzureReader all support dynamic injection, and can (with a tiny bit of configuration), all use the same path syntax. The ImageResizer is designed to allow easy migration between data stores.
C) You can use the Config.Current.Pipeline.Rewrite event to make the URL API use any syntax you want. This is much more flexible than MVC routes (and less buggy).
D) If you want to add another layer of dependency injection, implement IPlugin, and dynamically pick the appropriate data store and configure it from the Install method.