Blazor Web Server中IStringLocalizer返回键而非值,无法找到资源文件
Blazor Web Server本地化功能故障排查
问题现象
- 注入
IStringLocalizer<SharedResources>后,始终返回键名而非对应本地化文本 - 调用
Localizer.GetAllStrings()时抛出错误:"No manifests exist for the current culture." - 多次调整资源文件命名后问题依旧
相关代码
TodoList页面代码
@using BlazorPractice.Data @using BlazorPractice.Services @using Microsoft.Extensions.Localization @inject TodoService TodoService @inject IStringLocalizer<SharedResources> Localizer @page "/todolist" <h1>@Localizer["Title"]</h1> <h3>@(string.Join(" ", Localizer.GetAllStrings().Select(v => v.ToString())))</h3> <ul class="my-2" id="notes-list"> @foreach (var todoItem in _todoNotes) { <TodoListItem TodoItem="@todoItem" OnDeleteNote="() => FetchNotesAsync()" OnToggleState="() => FetchNotesAsync()"></TodoListItem> } </ul> <EditForm class="my-2" Model="@_newNote" OnSubmit="@AddTodoItem"> <div class="mb-3"> <p class="form-label">@Localizer["Title"]</p> <InputText @bind-Value="_newNote.Title"></InputText> </div> <div class="mb-3"> <p class="form-label">@Localizer["Description"]</p> <InputTextArea @bind-Value="_newNote.Description"></InputTextArea> </div> <div class="mb-3 form-label"> <InputCheckbox class="d-inline-block" @bind-Value="_newNote.IsCompleted"></InputCheckbox> <p class="form-label d-inline-block">@Localizer["IsCompleted"]</p> </div> <button class="btn btn-primary" type="submit">@Localizer["CreateNewNote"]</button> <div class="text-danger my-2">@ErrorMessage</div> </EditForm> @code { TodoNote[] _todoNotes = { }; TodoNote _newNote = new(); private string ErrorMessage { get; set; } = string.Empty; protected override async void OnInitialized() { _todoNotes = await TodoService.GetTodoNotesAsync(); } async Task AddTodoItem() { if (!IsValidInput(_newNote.Title, Localizer["TitleRequired"])) return; if (!IsValidInput(_newNote.Description, Localizer["DescriptionRequired"])) return; await TodoService.AddTodoNoteAsync(_newNote); _todoNotes = await TodoService.GetTodoNotesAsync(); _newNote = new TodoNote(); StateHasChanged(); } private async void FetchNotesAsync() { _todoNotes = await TodoService.GetTodoNotesAsync(); StateHasChanged(); } private bool IsValidInput(string input, string errorMessage) { if (!string.IsNullOrWhiteSpace(input)) return true; ShowErrorMessage(errorMessage); return false; } private async void ShowErrorMessage(string message) { ErrorMessage = message; StateHasChanged(); await Task.Delay(2000); ErrorMessage = string.Empty; } }
Program.cs代码
using Microsoft.AspNetCore.Components.Authorization; using Microsoft.AspNetCore.Identity; using Microsoft.EntityFrameworkCore; using BlazorPractice.Areas.Identity; using BlazorPractice.Data; using BlazorPractice.Services; using Microsoft.AspNetCore.Localization; var builder = WebApplication.CreateBuilder(args); // Add services to the container. var connectionString = builder.Configuration.GetConnectionString("DefaultConnection"); builder.Services.AddDbContext<ApplicationDbContext>(options => options.UseSqlite(connectionString)); builder.Services.AddDatabaseDeveloperPageExceptionFilter(); builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true) .AddEntityFrameworkStores<ApplicationDbContext>(); builder.Services.AddHsts(options => { options.Preload = true; options.IncludeSubDomains = true; options.MaxAge = TimeSpan.FromDays(1); }); builder.Services.AddRazorPages(); builder.Services.AddServerSideBlazor(); builder.Services .AddScoped<AuthenticationStateProvider, RevalidatingIdentityAuthenticationStateProvider<IdentityUser>>(); builder.Services.AddSingleton<WeatherForecastService>(); builder.Services.AddScoped<TodoService>(); builder.Services.AddLocalization(options => { options.ResourcesPath = "Resources"; }); var supportedCultures = new[] { "en", "it" }; var localizationOptions = new RequestLocalizationOptions() .SetDefaultCulture(supportedCultures[0]) .AddSupportedCultures(supportedCultures) .AddSupportedUICultures(supportedCultures); builder.Services.Configure<RequestLocalizationOptions>(options => { options.DefaultRequestCulture = new RequestCulture(supportedCultures[0]); options.SupportedCultures = localizationOptions.SupportedCultures; options.SupportedUICultures = localizationOptions.SupportedUICultures; }); var app = builder.Build(); app.UseRequestLocalization(localizationOptions); // Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) { app.UseMigrationsEndPoint(); } else { app.UseExceptionHandler("/Error"); app.UseHsts(); } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); app.UseAuthentication(); app.UseAuthorization(); app.MapControllers(); app.MapBlazorHub(); app.MapFallbackToPage("/_Host"); app.Run();
解决方案步骤
1. 匹配资源文件与SharedResources类
因为你注入的是IStringLocalizer<SharedResources>,需要确保资源文件和对应类的命名、结构一致:
- 在项目根目录的
Resources文件夹下创建空类SharedResources.cs,命名空间与项目根命名空间一致:
namespace BlazorPractice; public class SharedResources { }
- 资源文件命名为
SharedResources.en.resx(英文)、SharedResources.it.resx(意大利语),直接放在Resources文件夹下即可,无需额外嵌套子目录
2. 检查资源文件属性
右键资源文件 → 打开属性面板:
- 确保生成操作设置为
嵌入的资源 - 自定义工具设置为
ResXFileCodeGenerator
3. 简化本地化配置
Program.cs中重复配置了RequestLocalizationOptions,可以调整为更简洁的写法:
builder.Services.AddLocalization(options => options.ResourcesPath = "Resources"); var supportedCultures = new[] { "en", "it" }; builder.Services.Configure<RequestLocalizationOptions>(options => { options.DefaultRequestCulture = new RequestCulture("en"); options.SupportedCultures = supportedCultures.Select(c => new CultureInfo(c)).ToList(); options.SupportedUICultures = supportedCultures.Select(c => new CultureInfo(c)).ToList(); }); // 使用依赖注入获取配置好的选项 app.UseRequestLocalization(app.Services.GetRequiredService<IOptions<RequestLocalizationOptions>>().Value);
4. 验证当前文化设置
在TodoList页面添加调试代码,确认当前请求的文化是否正确:
<p>当前文化:@System.Globalization.CultureInfo.CurrentCulture.Name</p>
5. 清理缓存并重新生成
- 右键项目 → 选择「清理」
- 删除项目目录下的
bin和obj文件夹 - 重新生成项目,避免旧缓存干扰
内容的提问来源于stack exchange,提问作者Thomas Youssef
相关产品推荐
相关产品推荐

