Core 6.0 Web API 本地化 IStringLocalizer 选择不正确的文化

Core 6.0 Web API Localization IStringLocalizer selecting incorrect culture

我正在使用带有本地化功能的 Core 6.0 开发 Web API 项目。

我使用了一些在线指南,主要是 this one

我正在为资源使用外部库。

Program.cs:

builder.Services.AddLocalization();

builder.Services.Configure<RequestLocalizationOptions>(options =>
{
    options.DefaultRequestCulture = new RequestCulture(culture: "en-US");
    options.SupportedCultures = new List<CultureInfo>
    {
        new CultureInfo("en-US"),
        new CultureInfo("he-IL"),
        new CultureInfo("ru-RU")
    };
    options.RequestCultureProviders = new[] { new RouteDataRequestCultureProvider { IndexOfCulture = 3 } };
});

builder.Services.Configure<RouteOptions>(options => 
{
    options.ConstraintMap.Add("lang", typeof(LanguageRouteConstraint));
});

WebApplication app = builder.Build();

// Configure the HTTP request pipeline.

app.UseHttpsRedirection();

IOptions<RequestLocalizationOptions>? localizationOptions = app.Services.GetService<IOptions<RequestLocalizationOptions>>();

if (localizationOptions != null)
    app.UseRequestLocalization(localizationOptions.Value);

app.UseRouting();

app.UseAuthorization();

app.MapControllers();

app.Run();

扩展方法:

public class LanguageRouteConstraint : IRouteConstraint
{
    public bool Match(HttpContext? httpContext, IRouter? route, string routeKey, RouteValueDictionary values, RouteDirection routeDirection)
    {
        if (!values.ContainsKey("lang"))
            return false;

        string? culture = values["lang"]?.ToString();
        return culture == "en-US" || culture == "he-IL" || culture == "ru-RU";
    }
}


public class RouteDataRequestCultureProvider : RequestCultureProvider
{
    public int IndexOfCulture;

    public override Task<ProviderCultureResult?> DetermineProviderCultureResult(HttpContext httpContext)
    {
        if (httpContext == null)
            throw new ArgumentNullException(nameof(httpContext));

        string? culture = httpContext.Request.Path.Value is not null && httpContext.Request.Path.Value.Split('/').Length > IndexOfCulture ? httpContext.Request.Path.Value?.Split('/')[IndexOfCulture]?.ToString() : null;

        ProviderCultureResult? providerResultCulture = culture is null ? null : new(culture);

        return Task.FromResult(providerResultCulture);
    }
}

控制器:

[Route("[controller]/[action]/{lang:lang?}")]
[ApiController]
public class AccountController : BaseController
{
    public AccountController(IStringLocalizer<LangRes.App> localizer) : base(localizer)
    {
    }

    public async Task<IActionResult> Test()
    {
        return Ok(GetErrorMessage("TEST"));
    }
}

和BaseController:

[Route("")]
[ApiController]
public class BaseController : ControllerBase
{
    private readonly IStringLocalizer<LangRes.App> localizer;

    public BaseController(IStringLocalizer<LangRes.App> localizer)
    {
        this.localizer = localizer;
    }

    public string GetErrorMessage(string result)
    {
        return localizer.GetString(result);
    }
}

外部库LangRes有空Appclass和

App.en-US.resxTEST 键作为 This is english 作为值

App.he-IL.resxTEST 键作为 This is hebrew 作为值

App.ru-RU.resxTEST 作为 This is russian 作为值

当使用options.DefaultRequestCulture = new RequestCulture(culture: "en-US")时,account/Test/he-ILaccount/Test/ru-RU都不匹配,定位器使用回退en-US,所以响应是This is english .

当使用options.DefaultRequestCulture = new RequestCulture(culture: "he-IL")时:

account/Test/ru-RU 不匹配,定位器使用回退 he-IL

然而 account/Test/en-US 确实匹配正确,结果是 This is english!

当使用options.DefaultRequestCulture = new RequestCulture(culture: "ru-RU")时:

account/Test/he-IL 不匹配,定位器使用回退 ru-RU

然而 account/Test/en-US 确实匹配正确,结果是 This is english!

调试显示 RouteDataRequestCultureProvider 正在为每个请求返回正确的 ProviderCultureResult 文化。

我在这里错过了什么?这似乎不是预期的行为。

Web API IStringLocalizer 的控制器依赖注入依赖于 UiCulture,而不是 Culture

所以这种不稳定的行为是由于我没有在 RequestLocalizationOptions 的选项中定义 SupportedUICultures 造成的。

Program.cs 中完成更改:

List<CultureInfo> supportedCultures = new()
{
    new CultureInfo("en-us"),
    new CultureInfo("he-il"),
    new CultureInfo("ru")
};

builder.Services.Configure<RequestLocalizationOptions>(options =>
{
    options.DefaultRequestCulture = new RequestCulture(culture: "ru");
    options.SupportedCultures = supportedCultures;
    options.SupportedUICultures = supportedCultures; // important bit
    options.RequestCultureProviders = new[] { new RouteDataRequestCultureProvider { IndexOfCulture = 3 } };
});