DataAnnotationsLocalization for the type located in another assembly in ASP.NET Core MVC 2.1
I'm trying to use resource localization in ASP.NET Core MVC 2.1 project to configure validation error messages as described in this article.
I want to separate assemblies in my project in accordance with the onion architecture, to models were in a separate Assembly. Accordingly, the model namespace is different from the host application namespace.
As described here, I call my resource file by the full type name.
However, the messages of the model attributes are not replaced in accordance with the resource files.
What am I doing wrong?
Details:
Model:
using System.ComponentModel.DataAnnotations;
namespace LocalizedAspNetCoreApp.DomainModels
{
public class TestModel
{
[Display(Name = "Value")]
[Required(ErrorMessage = "ValueRequired")]
public string Value { get; set; }
}
}
Sturtup.cs:
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
namespace LocalizedAspNetCoreApp.Host
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc()
.AddDataAnnotationsLocalization();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
app.UseMvcWithDefaultRoute();
}
}
}
View:
@model TestModel;
@{
ViewData["Title"] = "About";
}
<h2>@ViewData["Title"]</h2>
<h3>@ViewData["Message"]</h3>
<form method="post" asp-controller="Home" asp-action="About">
<div asp-validation-summary="ModelOnly"></div>
<div>
<label asp-for="Value"></label>
<input asp-for="Value" />
<span asp-validation-for="Value"></span>
</div>
<div>
<input type="submit" value="Save" />
</div>
</form>
Resources
Project structure
Result
asp.net-core data-annotations resx
add a comment |
I'm trying to use resource localization in ASP.NET Core MVC 2.1 project to configure validation error messages as described in this article.
I want to separate assemblies in my project in accordance with the onion architecture, to models were in a separate Assembly. Accordingly, the model namespace is different from the host application namespace.
As described here, I call my resource file by the full type name.
However, the messages of the model attributes are not replaced in accordance with the resource files.
What am I doing wrong?
Details:
Model:
using System.ComponentModel.DataAnnotations;
namespace LocalizedAspNetCoreApp.DomainModels
{
public class TestModel
{
[Display(Name = "Value")]
[Required(ErrorMessage = "ValueRequired")]
public string Value { get; set; }
}
}
Sturtup.cs:
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
namespace LocalizedAspNetCoreApp.Host
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc()
.AddDataAnnotationsLocalization();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
app.UseMvcWithDefaultRoute();
}
}
}
View:
@model TestModel;
@{
ViewData["Title"] = "About";
}
<h2>@ViewData["Title"]</h2>
<h3>@ViewData["Message"]</h3>
<form method="post" asp-controller="Home" asp-action="About">
<div asp-validation-summary="ModelOnly"></div>
<div>
<label asp-for="Value"></label>
<input asp-for="Value" />
<span asp-validation-for="Value"></span>
</div>
<div>
<input type="submit" value="Save" />
</div>
</form>
Resources
Project structure
Result
asp.net-core data-annotations resx
add a comment |
I'm trying to use resource localization in ASP.NET Core MVC 2.1 project to configure validation error messages as described in this article.
I want to separate assemblies in my project in accordance with the onion architecture, to models were in a separate Assembly. Accordingly, the model namespace is different from the host application namespace.
As described here, I call my resource file by the full type name.
However, the messages of the model attributes are not replaced in accordance with the resource files.
What am I doing wrong?
Details:
Model:
using System.ComponentModel.DataAnnotations;
namespace LocalizedAspNetCoreApp.DomainModels
{
public class TestModel
{
[Display(Name = "Value")]
[Required(ErrorMessage = "ValueRequired")]
public string Value { get; set; }
}
}
Sturtup.cs:
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
namespace LocalizedAspNetCoreApp.Host
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc()
.AddDataAnnotationsLocalization();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
app.UseMvcWithDefaultRoute();
}
}
}
View:
@model TestModel;
@{
ViewData["Title"] = "About";
}
<h2>@ViewData["Title"]</h2>
<h3>@ViewData["Message"]</h3>
<form method="post" asp-controller="Home" asp-action="About">
<div asp-validation-summary="ModelOnly"></div>
<div>
<label asp-for="Value"></label>
<input asp-for="Value" />
<span asp-validation-for="Value"></span>
</div>
<div>
<input type="submit" value="Save" />
</div>
</form>
Resources
Project structure
Result
asp.net-core data-annotations resx
I'm trying to use resource localization in ASP.NET Core MVC 2.1 project to configure validation error messages as described in this article.
I want to separate assemblies in my project in accordance with the onion architecture, to models were in a separate Assembly. Accordingly, the model namespace is different from the host application namespace.
As described here, I call my resource file by the full type name.
However, the messages of the model attributes are not replaced in accordance with the resource files.
What am I doing wrong?
Details:
Model:
using System.ComponentModel.DataAnnotations;
namespace LocalizedAspNetCoreApp.DomainModels
{
public class TestModel
{
[Display(Name = "Value")]
[Required(ErrorMessage = "ValueRequired")]
public string Value { get; set; }
}
}
Sturtup.cs:
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
namespace LocalizedAspNetCoreApp.Host
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc()
.AddDataAnnotationsLocalization();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
app.UseMvcWithDefaultRoute();
}
}
}
View:
@model TestModel;
@{
ViewData["Title"] = "About";
}
<h2>@ViewData["Title"]</h2>
<h3>@ViewData["Message"]</h3>
<form method="post" asp-controller="Home" asp-action="About">
<div asp-validation-summary="ModelOnly"></div>
<div>
<label asp-for="Value"></label>
<input asp-for="Value" />
<span asp-validation-for="Value"></span>
</div>
<div>
<input type="submit" value="Save" />
</div>
</form>
Resources
Project structure
Result
asp.net-core data-annotations resx
asp.net-core data-annotations resx
asked Nov 26 '18 at 13:04
W. AndrewW. Andrew
233
233
add a comment |
add a comment |
1 Answer
1
active
oldest
votes
First of all:
The path to the resources is determined according to the following template: {RootNamespace}.{ResourceLocation}.{FullTypeName - AssemblyName}
,
where{RootNamespace}
is, in most cases, the name of the Assembly of the type for which the resource is searched,{ResourceLocation}
- this is usually "Resources"
,{FullTypeName - AssemblyName}
is the remainder of the type FullName.
You can verify this by injecting the type IStringLocalazer<TestModel>
into your controller and accessing the indexer with the key of an existing resource (for example, Value in your case).
The ResourceNotFound
property will be set to true
and SearchedLocation
will reflect the path used to find the resource.
Therefore, model resource files should be placed in the same Assembly where the model is described by the same path pattern:LocalizedAspNetCoreApp.DomainModels/Resources/TestModel.resx
If the namespace of the type it would be longer than the name of the Assembly, the remaining part should be added to the file name of the resource. For example:LocalizedAspNetCoreApp.DomainModels/Resources/Models.TestModel.resx
Secondly:
{ResourceLocation} is initialized by default with an empty string.
To initialize this value, install the package Microsoft.Extensions.Localization
:
install-package Microsoft.Extensions.Localization
and change ConfigureServices method in Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
services
.AddLocalization(opts => opts.ResourcesPath = "Resources")
.AddMvc()
.AddDataAnnotationsLocalization();
}
Yes, it worked. Thanks!
– W. Andrew
Nov 26 '18 at 13:29
Don't mention it. Just accept the answer is right.
– Vladimir Shiyanov
Nov 26 '18 at 13:31
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53481759%2fdataannotationslocalization-for-the-type-located-in-another-assembly-in-asp-net%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
First of all:
The path to the resources is determined according to the following template: {RootNamespace}.{ResourceLocation}.{FullTypeName - AssemblyName}
,
where{RootNamespace}
is, in most cases, the name of the Assembly of the type for which the resource is searched,{ResourceLocation}
- this is usually "Resources"
,{FullTypeName - AssemblyName}
is the remainder of the type FullName.
You can verify this by injecting the type IStringLocalazer<TestModel>
into your controller and accessing the indexer with the key of an existing resource (for example, Value in your case).
The ResourceNotFound
property will be set to true
and SearchedLocation
will reflect the path used to find the resource.
Therefore, model resource files should be placed in the same Assembly where the model is described by the same path pattern:LocalizedAspNetCoreApp.DomainModels/Resources/TestModel.resx
If the namespace of the type it would be longer than the name of the Assembly, the remaining part should be added to the file name of the resource. For example:LocalizedAspNetCoreApp.DomainModels/Resources/Models.TestModel.resx
Secondly:
{ResourceLocation} is initialized by default with an empty string.
To initialize this value, install the package Microsoft.Extensions.Localization
:
install-package Microsoft.Extensions.Localization
and change ConfigureServices method in Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
services
.AddLocalization(opts => opts.ResourcesPath = "Resources")
.AddMvc()
.AddDataAnnotationsLocalization();
}
Yes, it worked. Thanks!
– W. Andrew
Nov 26 '18 at 13:29
Don't mention it. Just accept the answer is right.
– Vladimir Shiyanov
Nov 26 '18 at 13:31
add a comment |
First of all:
The path to the resources is determined according to the following template: {RootNamespace}.{ResourceLocation}.{FullTypeName - AssemblyName}
,
where{RootNamespace}
is, in most cases, the name of the Assembly of the type for which the resource is searched,{ResourceLocation}
- this is usually "Resources"
,{FullTypeName - AssemblyName}
is the remainder of the type FullName.
You can verify this by injecting the type IStringLocalazer<TestModel>
into your controller and accessing the indexer with the key of an existing resource (for example, Value in your case).
The ResourceNotFound
property will be set to true
and SearchedLocation
will reflect the path used to find the resource.
Therefore, model resource files should be placed in the same Assembly where the model is described by the same path pattern:LocalizedAspNetCoreApp.DomainModels/Resources/TestModel.resx
If the namespace of the type it would be longer than the name of the Assembly, the remaining part should be added to the file name of the resource. For example:LocalizedAspNetCoreApp.DomainModels/Resources/Models.TestModel.resx
Secondly:
{ResourceLocation} is initialized by default with an empty string.
To initialize this value, install the package Microsoft.Extensions.Localization
:
install-package Microsoft.Extensions.Localization
and change ConfigureServices method in Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
services
.AddLocalization(opts => opts.ResourcesPath = "Resources")
.AddMvc()
.AddDataAnnotationsLocalization();
}
Yes, it worked. Thanks!
– W. Andrew
Nov 26 '18 at 13:29
Don't mention it. Just accept the answer is right.
– Vladimir Shiyanov
Nov 26 '18 at 13:31
add a comment |
First of all:
The path to the resources is determined according to the following template: {RootNamespace}.{ResourceLocation}.{FullTypeName - AssemblyName}
,
where{RootNamespace}
is, in most cases, the name of the Assembly of the type for which the resource is searched,{ResourceLocation}
- this is usually "Resources"
,{FullTypeName - AssemblyName}
is the remainder of the type FullName.
You can verify this by injecting the type IStringLocalazer<TestModel>
into your controller and accessing the indexer with the key of an existing resource (for example, Value in your case).
The ResourceNotFound
property will be set to true
and SearchedLocation
will reflect the path used to find the resource.
Therefore, model resource files should be placed in the same Assembly where the model is described by the same path pattern:LocalizedAspNetCoreApp.DomainModels/Resources/TestModel.resx
If the namespace of the type it would be longer than the name of the Assembly, the remaining part should be added to the file name of the resource. For example:LocalizedAspNetCoreApp.DomainModels/Resources/Models.TestModel.resx
Secondly:
{ResourceLocation} is initialized by default with an empty string.
To initialize this value, install the package Microsoft.Extensions.Localization
:
install-package Microsoft.Extensions.Localization
and change ConfigureServices method in Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
services
.AddLocalization(opts => opts.ResourcesPath = "Resources")
.AddMvc()
.AddDataAnnotationsLocalization();
}
First of all:
The path to the resources is determined according to the following template: {RootNamespace}.{ResourceLocation}.{FullTypeName - AssemblyName}
,
where{RootNamespace}
is, in most cases, the name of the Assembly of the type for which the resource is searched,{ResourceLocation}
- this is usually "Resources"
,{FullTypeName - AssemblyName}
is the remainder of the type FullName.
You can verify this by injecting the type IStringLocalazer<TestModel>
into your controller and accessing the indexer with the key of an existing resource (for example, Value in your case).
The ResourceNotFound
property will be set to true
and SearchedLocation
will reflect the path used to find the resource.
Therefore, model resource files should be placed in the same Assembly where the model is described by the same path pattern:LocalizedAspNetCoreApp.DomainModels/Resources/TestModel.resx
If the namespace of the type it would be longer than the name of the Assembly, the remaining part should be added to the file name of the resource. For example:LocalizedAspNetCoreApp.DomainModels/Resources/Models.TestModel.resx
Secondly:
{ResourceLocation} is initialized by default with an empty string.
To initialize this value, install the package Microsoft.Extensions.Localization
:
install-package Microsoft.Extensions.Localization
and change ConfigureServices method in Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
services
.AddLocalization(opts => opts.ResourcesPath = "Resources")
.AddMvc()
.AddDataAnnotationsLocalization();
}
answered Nov 26 '18 at 13:14
Vladimir ShiyanovVladimir Shiyanov
881712
881712
Yes, it worked. Thanks!
– W. Andrew
Nov 26 '18 at 13:29
Don't mention it. Just accept the answer is right.
– Vladimir Shiyanov
Nov 26 '18 at 13:31
add a comment |
Yes, it worked. Thanks!
– W. Andrew
Nov 26 '18 at 13:29
Don't mention it. Just accept the answer is right.
– Vladimir Shiyanov
Nov 26 '18 at 13:31
Yes, it worked. Thanks!
– W. Andrew
Nov 26 '18 at 13:29
Yes, it worked. Thanks!
– W. Andrew
Nov 26 '18 at 13:29
Don't mention it. Just accept the answer is right.
– Vladimir Shiyanov
Nov 26 '18 at 13:31
Don't mention it. Just accept the answer is right.
– Vladimir Shiyanov
Nov 26 '18 at 13:31
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53481759%2fdataannotationslocalization-for-the-type-located-in-another-assembly-in-asp-net%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown