I have struggled a bit and finally succeeded in compiling a working solution to this question. Thanks to @Szymon Sasin for his answer, although it is not working against the latest version and his configuration is partial, it helped me build this solution.
First, configure localization at Startup.cs:
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
//...
services.AddLocalization(options => options.ResourcesPath = "Resources");
services
.AddMvc(mvcOptions =>
{
IServiceProvider provider = services.BuildServiceProvider();
IStringLocalizer localizer = provider.GetService<IStringLocalizer<DisplayResources>>();
mvcOptions.ModelMetadataDetailsProviders.Add(new DisplayAttributeLocalizationProvider(localizer));
});
//...
}
}
Second, verify your folder structure against the configured ResourcePath. The important thing here is that the path to the custom resource type and the path to its resx files should be relative. Example:
<root_proj_dir>/Resources/Resources_Common/DisplayResources.en.resx
<root_proj_dir>/Resources/Resources_Common/DisplayResources.bg.resx
<root_proj_dir>/Resources_Common/DisplayResources.cs
Third, define your custom metadata provider:
public sealed class DisplayAttributeLocalizationProvider : IDisplayMetadataProvider
{
private IStringLocalizer _localizer;
public DisplayAttributeLocalizationProvider(IStringLocalizer localizer)
{
_localizer = localizer;
}
public void CreateDisplayMetadata(DisplayMetadataProviderContext context)
{
context.PropertyAttributes?
.Where(attribute => attribute is DisplayAttribute)
.Cast<DisplayAttribute>().ToList().ForEach(display =>
{
display.Name = _localizer[display.Name].Value;
});
}
}
Fourth, use all this in your view model just like this:
public class SomeViewModel
{
[Display(Name = "Email")]
public string Email { get; set; }
}
The "Email" value will be the key to look-up for in the DisplayResources.xx.resx files.
Hope that many others will find this info helpful!