You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 05:09:51