.net core 3.0 通过资源文件 实现多语言支持——资源文件无法获取的问题
https://github.com/dotnet/aspnetcore/issues/17733
https://github.com/dotnet/aspnetcore/issues/17729
https://github.com/dotnet/aspnetcore/issues/18026
Describe the bug
Resources referenced through SharedResources via@inject IStringLocalizer<SharedResources> SharedResources
does not work using .net core sdk version 3.1.100
When changing to sdk 3.1.100 (or removing global.json) with installed .net core sdk 3.1, values from SharedResources is not displayed correctly ("ValueFromSharedResources" is displayed instead).
Describe the bug
Blazor server localization is not working in Visual Studio 16.4 + .NET Core 3.1 SDK.
To Reproduce
Please see
https://github.com/mttrkm/BlazorLocalizationIssueWithCore3_1SDK
Localization works with Visual Studio 16.3.10 + .NET Core 3.0 SDK.
But that does not work by upgrading Visual Studio 16.4 + .NET Core 3.1 SDK.
Further technical details
-
ASP.NET Core version
3.0(Target) but SDK is 3.1
I have setup globalization as per the documents here but something must be wrong cause I am only getting the value of the key not the value of the resource.
The following is my code which i have more or less copied from the document link above.
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddLocalization(opts => { opts.ResourcesPath = "Resources"; });
services.Configure<RequestLocalizationOptions>(opts =>
{
var supportedCultures = new List<CultureInfo> { new CultureInfo("en-GB"), new CultureInfo("fr-FR"), };
opts.DefaultRequestCulture = new RequestCulture("en-GB");
// Formatting numbers, dates, etc.
opts.SupportedCultures = supportedCultures;
// UI strings that we have localized.
opts.SupportedUICultures = supportedCultures;
});
// Add framework services.
services.AddControllersWithViews()
.SetCompatibilityVersion(CompatibilityVersion.Version_3_0)
// Maintain property names during serialization. See:
// https://github.com/aspnet/Announcements/issues/194
.AddNewtonsoftJson(options =>
options.SerializerSettings.ContractResolver = new DefaultContractResolver())
.AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix,
opts => { opts.ResourcesPath = "Resources"; })
.AddDataAnnotationsLocalization();
services.Configure<ConnectionStringConfig>(Configuration);
//lets inject the connection string to the data layer
//but we should be using the api layer for any heavy lifting.
services.AddHttpClient("externalservice", c =>
{
// Assume this is an "external" service which requires an API KEY
c.BaseAddress = new Uri("https://localhost:5001/");
});
// Add Kendo UI services to the services container
services.AddKendo();
}
// 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();
}
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();
}
SetUpLocalization(app);
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
var options = app.ApplicationServices.GetService<IOptions<RequestLocalizationOptions>>();
app.UseRequestLocalization(options.Value);
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(name: "default", pattern: "{controller=Home}/{action=Index}/{id?}");
});
}
private static void SetUpLocalization(IApplicationBuilder app)
{
var supportedCultures = new[]
{
new CultureInfo("en-US"),
new CultureInfo("en-GB"),
new CultureInfo("pl")
};
var options = new RequestLocalizationOptions
{
DefaultRequestCulture = new RequestCulture("en-GB", "en-GB"),
SupportedCultures = supportedCultures,
SupportedUICultures = supportedCultures,
};
options.AddInitialRequestCultureProvider(new CustomRequestCultureProvider(async context =>
{
// My custom request culture logic
return new ProviderCultureResult("en");
}));
// Find the cookie provider with LINQ
var cookieProvider = options.RequestCultureProviders
.OfType<CookieRequestCultureProvider>()
.First();
// Set the new cookie name
cookieProvider.CookieName = "UserCulture";
// Configure the Localization middleware
app.UseRequestLocalization(options);
}
This is my controller where I am accessing the resouce file key
public class StockController : Controller
{
private readonly IStringLocalizer<StockController> _localizer;
RoundTableAPIClient apiClient = new RoundTableAPIClient();
// GET
public StockController(IStringLocalizer<StockController> localizer)
{
_localizer = localizer;
}
public IActionResult Index()
{
var test = _localizer[ResourceKeys.StockPageTitle].Value;
ViewBag.Title = _localizer["StockPageTitle"];
return View();
}
}
But I am not getting the value that is in my resource file I am just getting the text StockPageTItle which is incorrect also how does one give the en-GB as default in asp.net core routes that is not explained in the docs. enter code here
Please note also this screenshot saying the following. I presume it's not finding the resource even though I have the naming correct my culture is en-GB by the way.
why:
After debugging, found the reason.
If class SharedResource.cs
and SharedResource.*.resx
in same folder, the namespace will be error in compiled dll xxx.lang.dll
.
//here is the incorrect namesapce.
.mresource public IdentityHub.PosSharedResource.zh.resources
{
// Offset: 0x00000000 Length: 0x000009D0
}
.mresource public IdentityHub.Resources.Views.FcAccount.LoggedOut.zh.resources
{
// Offset: 0x000009D8 Length: 0x00000238
}
.mresource public IdentityHub.Resources.Views.FcAccount.Shared.Welcome.zh.resources
{
// Offset: 0x00000C18 Length: 0x0000022C
}
As part of the 3.1 release, the .NET SDK changed how namespaces for resource files are calculated. Let's look at the sample shared by @emedbo:
Views.Home.Index.nb-NO.resx
gets embedded as $AssemblyName.Resources.Views.Home.Index
. However, SharedResources.nb-NO.resx
is treated as a DependentUpon
item on SharedResources
and picks it's namespace which is WebApplication
. This obviously doesn't work with the options.ResourcesPath = "Resources"
setting.
this seems to be an intentional change in how the 3.1 SDK generates namespaces for resources. The workaround you have seems to be an appropriate workaround. I'll follow up on the issue you linked to.
HOW:
1.
There's few options here:
a) Move the SharedResources file as noted #17733 (comment)
b) Change the namespace of SharedResources
to match the expected namespace for the resource. In this case, it would be WebApplication.Resources
.
c) You can configure the SDK to use pre-3.1 behavior by setting <EmbeddedResourceUseDependentUponConvention>false</EmbeddedResourceUseDependentUponConvention>
in your project file.
2.
also the accessability of being able to do Resources.ResourceName to get the value from the default resource appears to be broken as well.
3.
Solved this It ended being I had to add the following I re did my Project.
var supportedCultures = new string[] { "en-GB", "fr-FR" };
app.UseRequestLocalization(options =>
options
.AddSupportedCultures(supportedCultures)
.AddSupportedUICultures(supportedCultures)
.SetDefaultCulture("en-gb")
.RequestCultureProviders.Insert(0, new CustomRequestCultureProvider(context =>
{
return Task.FromResult(new ProviderCultureResult("en-gb"));
}))
);
Also, I don't believe the above step was in the documentation i believe it needs to be adjusted.
4.
1 2 3 4 5 6 7 8 9 | services.AddDataAnnotationsLocalization(options => { options.DataAnnotationLocalizerProvider = (type, factory) => { var sl = factory.Create( "SharedResource" , System.Reflection.Assembly.GetExecutingAssembly().GetName().Name); return sl; }; }).AddMvcLocalization() .AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix); |
1 2 3 4 5 6 7 | services.AddSingleton<IStringLocalizer>((sp) => { var factory = sp.GetRequiredService<IStringLocalizerFactory>(); var sl = factory.Create( "SharedResource" , System.Reflection.Assembly.GetExecutingAssembly().GetName().Name); return sl; }); |
删除
取代:
1 2 3 4 5 6 7 8 9 | services.AddDataAnnotationsLocalization(options => { options.DataAnnotationLocalizerProvider = (type, factory) => { var sl = factory.Create( "SharedResource" , System.Reflection.Assembly.GetExecutingAssembly().GetName().Name); return sl; }; }).AddMvcLocalization() .AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix); |
1 2 3 4 5 | services.AddSingleton<IStringLocalizer>((sp) => { var sharedLocalizer = sp.GetRequiredService<IStringLocalizer<SharedResource>>(); return sharedLocalizer; }); |
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· .NET Core 托管堆内存泄露/CPU异常的常见思路
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· DeepSeek R1 简明指南:架构、训练、本地部署及硬件要求
· 没有源码,如何修改代码逻辑?
· NetPad:一个.NET开源、跨平台的C#编辑器
· 面试官:你是如何进行SQL调优的?