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

MVC .NET Core动态本地化咨询:数据库转Resx无冗余结构方案

解决方案:无路由前缀的动态本地化(数据库替代Resx)

一、移除路由前缀式语言区分,改用Cookie/查询字符串切换

放弃en/Index这类路由结构,通过查询字符串或Cookie识别当前语言,在Program.cs中配置本地化规则:

builder.Services.AddLocalization(options => options.ResourcesPath = "Resources");

builder.Services.Configure<RequestLocalizationOptions>(options =>
{
    var supportedCultures = new[]
    {
        new CultureInfo("en"),
        new CultureInfo("fr"),
        new CultureInfo("zh-CN")
    };

    options.DefaultRequestCulture = new RequestCulture("zh-CN");
    options.SupportedCultures = supportedCultures;
    options.SupportedUICultures = supportedCultures;

    // 优先用查询字符串(如 ?culture=en),其次用Cookie存储用户偏好
    options.RequestCultureProviders = new List<IRequestCultureProvider>
    {
        new QueryStringRequestCultureProvider(),
        new CookieRequestCultureProvider()
    };
});

// 启用本地化中间件
app.UseRequestLocalization();

你可以在页面添加语言切换按钮,通过设置Cookie或跳转带查询字符串的URL完成切换,无需修改路由结构。

二、实现数据库驱动的动态本地化(替代SharedResources.resx)

通过自定义IStringLocalizer和IStringLocalizerFactory,让本地化数据直接从数据库读取,同时加入缓存保证性能。

1. 定义数据库实体

创建存储本地化键值对的实体类:

public class LocalizationResource
{
    public int Id { get; set; }
    public string Key { get; set; } // 对应原Resx的键,如"WelcomeMessage"
    public string Culture { get; set; } // 语言标识,如"en"、"fr"
    public string Value { get; set; } // 对应语言的文本内容
}

通过EF Core将该实体映射到数据库表,初始化所需的多语言数据。

2. 自定义StringLocalizer(从数据库读取+缓存)

public class DbStringLocalizer : IStringLocalizer
{
    private readonly AppDbContext _dbContext;
    private readonly IMemoryCache _cache;
    private readonly string _currentCulture;

    public DbStringLocalizer(AppDbContext dbContext, IMemoryCache cache, IHttpContextAccessor httpContextAccessor)
    {
        _dbContext = dbContext;
        _cache = cache;
        _currentCulture = httpContextAccessor.HttpContext?.RequestCulture.Culture.Name ?? "zh-CN";
    }

    public LocalizedString this[string name]
    {
        get
        {
            var value = GetLocalizedValue(name);
            return new LocalizedString(name, value ?? name, resourceNotFound: value == null);
        }
    }

    public LocalizedString this[string name, params object[] arguments]
    {
        get
        {
            var value = GetLocalizedValue(name);
            return new LocalizedString(name, string.Format(value ?? name, arguments), resourceNotFound: value == null);
        }
    }

    public IEnumerable<LocalizedString> GetAllStrings(bool includeParentCultures)
    {
        var cacheKey = $"Localization_All_{_currentCulture}";
        return _cache.GetOrCreate(cacheKey, entry =>
        {
            // 缓存1小时,可根据业务调整过期时间
            entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(1);
            return _dbContext.LocalizationResources
                .Where(r => r.Culture == _currentCulture)
                .Select(r => new LocalizedString(r.Key, r.Value))
                .ToList();
        });
    }

    private string GetLocalizedValue(string key)
    {
        var cacheKey = $"Localization_{_currentCulture}_{key}";
        return _cache.GetOrCreate(cacheKey, entry =>
        {
            entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(1);
            return _dbContext.LocalizationResources
                .FirstOrDefault(r => r.Key == key && r.Culture == _currentCulture)?
                .Value;
        });
    }

    public IStringLocalizer WithCulture(CultureInfo culture)
    {
        // 返回指定语言的本地化实例
        return new DbStringLocalizer(_dbContext, _cache, new HttpContextAccessor 
        { 
            HttpContext = new DefaultHttpContext { RequestCulture = new RequestCulture(culture) } 
        });
    }
}

3. 实现StringLocalizerFactory

用于创建自定义本地化实例:

public class DbStringLocalizerFactory : IStringLocalizerFactory
{
    private readonly IServiceProvider _serviceProvider;

    public DbStringLocalizerFactory(IServiceProvider serviceProvider)
    {
        _serviceProvider = serviceProvider;
    }

    public IStringLocalizer Create(Type resourceSource)
    {
        return _serviceProvider.GetRequiredService<DbStringLocalizer>();
    }

    public IStringLocalizer Create(string baseName, string location)
    {
        return _serviceProvider.GetRequiredService<DbStringLocalizer>();
    }
}

4. 注册服务到DI容器

在Program.cs中添加:

// 注册数据库上下文
builder.Services.AddDbContext<AppDbContext>(options =>
    options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));

// 注册自定义本地化服务
builder.Services.AddSingleton<IStringLocalizerFactory, DbStringLocalizerFactory>();
builder.Services.AddScoped<IStringLocalizer, DbStringLocalizer>();
builder.Services.AddHttpContextAccessor();
builder.Services.AddMemoryCache();

三、使用方式(与原Resx完全兼容)

在控制器或视图中注入IStringLocalizer即可,用法和原SharedResources.resx完全一致:

控制器示例

public class HomeController : Controller
{
    private readonly IStringLocalizer _localizer;

    public HomeController(IStringLocalizer localizer)
    {
        _localizer = localizer;
    }

    public IActionResult Index()
    {
        ViewBag.Welcome = _localizer["WelcomeMessage"];
        return View();
    }
}

视图示例

<h1>@ViewBag.Welcome</h1>
<p>@_localizer["GreetingText"]</p>

四、性能保障说明

  • 用IMemoryCache缓存数据库查询结果,避免每次请求访问数据库,缓存过期时间可灵活调整;
  • 若需实时更新本地化内容,可添加缓存刷新机制(如后台定时任务、手动触发接口);
  • 数据库层面给Key和Culture字段加联合索引,提升查询效率。

内容的提问来源于stack exchange,提问作者Eray Genç

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 18:46:01