能否基于应用设置覆盖ASP.NET Core的本地化资源字符串
方案1:自定义扩展IStringLocalizer实现优先读取覆盖Resx
核心逻辑为扩展ASP.NET Core内置的本地化抽象层,无需修改底层ResourceManager,即可实现读取优先级:模式专属覆盖Resx -> 原生对应文化Resx,完全兼容原有IStringLocalizer注入的业务代码。
第一步:配置软件模式参数
在appsettings.json中定义模式配置:
{ "AppSettings": { "SoftwareMode": "DemoMode" // 可切换为Normal、Enterprise等自定义模式 } }
对应绑定的Options类:
public class AppSettings { public string SoftwareMode { get; set; } = "Normal"; }
在Program.cs中注册配置绑定:
builder.Services.Configure<AppSettings>(builder.Configuration.GetSection("AppSettings"));
第二步:准备覆盖用Resx文件
仅存放需要覆盖的键值对即可,无需复制全量原生Resx内容,命名规则和原生Resx对应,增加模式标识:
- 原生Resx示例:
Resources/Controllers.HomeController.zh-CN.resx - Demo模式覆盖Resx示例:
Resources/Controllers.HomeController.DemoMode.zh-CN.resx
注意所有Resx文件的生成操作需设置为「嵌入的资源」
第三步:实现优先读取覆盖资源的StringLocalizer
继承内置ResourceManagerStringLocalizer,重写索引器逻辑:
public class ModePrioritizedStringLocalizer : ResourceManagerStringLocalizer { private readonly ResourceManagerStringLocalizer _overrideLocalizer; private readonly bool _hasOverride; public ModePrioritizedStringLocalizer( ResourceManager resourceManager, Assembly resourceAssembly, string baseName, IResourceNamesCache resourceNamesCache, IOptions<AppSettings> appSettings, IStringLocalizerFactory factory) : base(resourceManager, resourceAssembly, baseName, resourceNamesCache, CultureInfo.CurrentUICulture) { var currentMode = appSettings.Value.SoftwareMode; if (currentMode != "Normal") { var overrideBaseName = $"{baseName}.{currentMode}"; _overrideLocalizer = (ResourceManagerStringLocalizer)factory.Create(overrideBaseName, resourceAssembly.FullName); _hasOverride = true; } } public override LocalizedString this[string name] { get { if (_hasOverride) { var overrideVal = _overrideLocalizer[name]; if (!overrideVal.ResourceNotFound) return overrideVal; } return base[name]; } } public override LocalizedString this[string name, params object[] arguments] { get { if (_hasOverride) { var overrideVal = _overrideLocalizer[name, arguments]; if (!overrideVal.ResourceNotFound) return overrideVal; } return base[name, arguments]; } } }
第四步:实现自定义Localizer工厂替换内置实现
public class ModePrioritizedStringLocalizerFactory : IStringLocalizerFactory { private readonly IOptions<LocalizationOptions> _localizationOptions; private readonly IOptions<AppSettings> _appSettings; private readonly IResourceNamesCache _resourceNamesCache; public ModePrioritizedStringLocalizerFactory( IOptions<LocalizationOptions> localizationOptions, IOptions<AppSettings> appSettings, IResourceNamesCache resourceNamesCache) { _localizationOptions = localizationOptions; _appSettings = appSettings; _resourceNamesCache = resourceNamesCache; } public IStringLocalizer Create(Type resourceSource) { var assembly = resourceSource.Assembly; var baseName = string.IsNullOrEmpty(_localizationOptions.Value.ResourcesPath) ? resourceSource.FullName : $"{assembly.GetName().Name}.{_localizationOptions.Value.ResourcesPath}.{resourceSource.Name}"; var resourceManager = new ResourceManager(baseName, assembly); return new ModePrioritizedStringLocalizer(resourceManager, assembly, baseName, _resourceNamesCache, _appSettings, this); } public IStringLocalizer Create(string baseName, string location) { var assembly = Assembly.Load(location); var resourceManager = new ResourceManager(baseName, assembly); return new ModePrioritizedStringLocalizer(resourceManager, assembly, baseName, _resourceNamesCache, _appSettings, this); } }
第五步:注册自定义服务
在Program.cs中替换内置的本地化工厂实现:
builder.Services.AddLocalization(options => options.ResourcesPath = "Resources"); builder.Services.AddSingleton<IStringLocalizerFactory, ModePrioritizedStringLocalizerFactory>();
方案2:轻量替代实现(无需额外Resx)
如果需要覆盖的本地化字符串数量较少,可以不用新增Resx文件,直接把覆盖配置存放在appsettings.json中,实现起来更便捷:
- 配置结构示例:
{ "LocalizationOverrides": { "DemoMode": { "zh-CN": { "HomePageTitle": "演示模式首页", "SubmitBtn": "演示提交" }, "en-US": { "HomePageTitle": "Demo Mode Home", "SubmitBtn": "Demo Submit" } } } }
- 自定义StringLocalizer时优先读取该配置节点的匹配值,无匹配时回退到原生Resx即可,无需调整Resx的发布逻辑,适合小范围覆盖的场景。
额外说明
- 如果需要运行时动态切换软件模式无需重启应用,将代码中的
IOptions替换为IOptionsSnapshot即可实现配置热更新。 - 多租户场景下也可以复用该逻辑,把软件模式替换为租户标识即可实现租户级别的本地化字符串自定义。
内容的提问来源于stack exchange,提问作者Steve Borman
相关产品推荐
相关产品推荐

