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

.NET Core中不使用RESX文件实现作用域本地化的方案咨询

针对CMS多语言表单模型注解的解决方案

以下是几个适配你场景的可行方案,兼顾RCL模块的独立性和数据库动态翻译的需求:

1. 实现动态作用域的模型元数据提供器

放弃继承DefaultModelMetadataProvider(它会缓存元数据,导致无法动态更新),直接实现IModelMetadataProvider,在每次请求时根据当前区域性从数据库获取翻译:

public class ScopedModelMetadataProvider : IModelMetadataProvider
{
    private readonly ITranslationService _translationService;
    private readonly IHttpContextAccessor _httpContextAccessor;

    public ScopedModelMetadataProvider(ITranslationService translationService, IHttpContextAccessor httpContextAccessor)
    {
        _translationService = translationService;
        _httpContextAccessor = httpContextAccessor;
    }

    public ModelMetadata GetMetadataForType(Type modelType)
    {
        var culture = GetCurrentCulture();
        var metadata = new ModelMetadata(
            new ModelMetadataIdentity(modelType),
            null,
            () => _translationService.GetTranslation($"DisplayName_{modelType.Name}", culture)
        );
        // 补充其他元数据属性(如描述、验证规则等)
        return metadata;
    }

    public ModelMetadata GetMetadataForProperty(Type containerType, PropertyInfo property)
    {
        var culture = GetCurrentCulture();
        var displayNameKey = $"DisplayName_{containerType.Name}_{property.Name}";
        var displayName = _translationService.GetTranslation(displayNameKey, culture);
        
        var metadata = new ModelMetadata(
            new ModelMetadataIdentity(containerType, property),
            null,
            () => displayName
        );
        // 可在此处设置验证消息相关元数据,比如ErrorMessage
        return metadata;
    }

    private string GetCurrentCulture()
    {
        // 从HttpContext获取当前页面设置的区域性(比如路由、Cookie或CMS上下文)
        return _httpContextAccessor.HttpContext?.Items["PageCulture"]?.ToString() ?? "en-US";
    }
}

然后在Program.cs中注册为Scoped服务:

services.AddScoped<IModelMetadataProvider, ScopedModelMetadataProvider>();

这个方案的核心是每次请求都重新解析元数据,确保使用当前页面的区域性,同时支持从数据库动态获取翻译,无需依赖RESX文件。

2. 自定义本地化验证属性

对于自定义验证注解,避免硬编码错误消息,而是在验证时动态从数据库获取翻译。由于ValidationAttribute是单例,需要通过ValidationContext获取Scoped的翻译服务:

public class LocalizedRequiredAttribute : RequiredAttribute
{
    private readonly string _translationKey;

    public LocalizedRequiredAttribute(string translationKey)
    {
        _translationKey = translationKey;
    }

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        var translationService = validationContext.GetService<ITranslationService>();
        var cultureProvider = validationContext.GetService<ICmsCultureProvider>();
        var currentCulture = cultureProvider.GetCurrentPageCulture();

        ErrorMessage = translationService.GetTranslation(_translationKey, currentCulture);
        return base.IsValid(value, validationContext);
    }
}

使用时直接指定翻译键:

public class ContactFormModel
{
    [LocalizedRequired("Validation_Required_Name")]
    public string Name { get; set; }
}

同时,对于框架自带的验证消息(比如必填、格式错误),可以通过配置ModelBindingMessageProvider动态替换:

services.AddMvc(options =>
{
    var serviceProvider = services.BuildServiceProvider();
    var translationService = serviceProvider.GetRequiredService<ITranslationService>();
    var cultureProvider = serviceProvider.GetRequiredService<ICmsCultureProvider>();

    options.ModelBindingMessageProvider.SetValueMustNotBeNullAccessor(
        _ => translationService.GetTranslation("Validation_ValueCannotBeNull", cultureProvider.GetCurrentPageCulture())
    );
    // 其他默认验证消息同理配置
});

3. 自定义Tag Helper处理表单标签与验证消息

如果不想修改元数据或验证属性,可以通过Tag Helper动态替换表单元素的显示名和验证消息,完全绕过传统的元数据机制:

本地化Input标签的显示名

[HtmlTargetElement("input", Attributes = "asp-for")]
public class LocalizedInputTagHelper : InputTagHelper
{
    private readonly ITranslationService _translationService;
    private readonly ICmsCultureProvider _cultureProvider;

    public LocalizedInputTagHelper(IHtmlGenerator generator, ITranslationService translationService, ICmsCultureProvider cultureProvider) 
        : base(generator)
    {
        _translationService = translationService;
        _cultureProvider = cultureProvider;
    }

    public override void Process(TagHelperContext context, TagHelperOutput output)
    {
        var culture = _cultureProvider.GetCurrentPageCulture();
        var propertyKey = $"DisplayName_{For.Metadata.ContainerType.Name}_{For.Metadata.PropertyName}";
        var displayName = _translationService.GetTranslation(propertyKey, culture);

        // 替换placeholder或关联的label文本
        if (!string.IsNullOrEmpty(displayName))
        {
            output.Attributes.SetAttribute("placeholder", displayName);
            // 如果页面中有对应的label标签,也可以通过context查找并修改
        }

        base.Process(context, output);
    }
}

本地化验证消息

[HtmlTargetElement("span", Attributes = "asp-validation-for")]
public class LocalizedValidationMessageTagHelper : ValidationMessageTagHelper
{
    private readonly ITranslationService _translationService;
    private readonly ICmsCultureProvider _cultureProvider;

    public LocalizedValidationMessageTagHelper(IHtmlGenerator generator, ITranslationService translationService, ICmsCultureProvider cultureProvider) 
        : base(generator)
    {
        _translationService = translationService;
        _cultureProvider = cultureProvider;
    }

    public override void Process(TagHelperContext context, TagHelperOutput output)
    {
        base.Process(context, output);
        
        var culture = _cultureProvider.GetCurrentPageCulture();
        var validationKey = $"Validation_{For.Metadata.PropertyName}";
        var validationMessage = _translationService.GetTranslation(validationKey, culture);

        if (!string.IsNullOrEmpty(validationMessage))
        {
            output.Content.SetContent(validationMessage);
        }
    }
}

注册Tag Helper后,在RCL的视图中直接使用标准的asp-for和asp-validation-for标签,就能自动获取数据库中的翻译。

方案选择建议

  • 如果需要全局统一处理所有模型元数据,优先选择动态模型元数据提供器;
  • 如果只需要处理验证消息,自定义验证属性+ModelBindingMessageProvider更轻量;
  • 如果希望最小化对现有代码的修改,自定义Tag Helper是最佳选择,无需修改模型或元数据配置。

内容的提问来源于stack exchange,提问作者user470249

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 14:42:19