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ç
相关产品推荐
相关产品推荐

