8

System.ComponentModel.DataAnnotations我想为一个 ASP.NET 项目覆盖一个字符串。我是否需要进行卫星组装、搞乱自定义构建任务al.exe等?即使是,我也找不到如何转换.resx为. 如果没有,在哪里放置以及如何命名?.resourcesal.exe.resx.

UPD:为了清楚起见:我想使用自定义资源字符串而不是来自程序集的默认资源的字符串。我不想在每个使用该字符串的地方进行更改。毕竟,资源的存在只是为了覆盖它们。

4

5 回答 5

5

Phil Haack 有一篇很棒的文章Localizing ASP.Net MVC Validation,它专门指导您覆盖字符串。DataAnnotations这篇文章比它更适用ASP.net MVC。因此,这将有助于您使用DataAnnotattions

下面我列出了在 Visual Studio 中添加本地化资源的最简单步骤。

  1. 打开Project Properties对话框。
  2. 选择Resources选项卡。
  3. 单击以创建新的默认资源文件
  4. 这将在您的文件夹中创建两个文件Properties
    • 资源.resx
    • 资源.Designer.cs
  5. Resources.resx打开后,将其更改Access ModifierPublic.
  6. 添加你的字符串。

要为特定文化添加额外的资源文件,您需要这样做。

  1. 右键单击您ProjectSolution Explorer.
  2. 选择添加->新项目->资源 文件。
  3. 命名它Resources.en-us.resx。(用适当的代码替换“en-us”)
  4. 点击添加
  5. 将其拖到Properties文件夹中。
  6. 打开Resources.en-us.resx并将其更改Access ModifierPublic.
  7. 添加你的字符串。
  8. 对您需要支持的每种文化重复此操作。

在构建过程中,VS 会将.resx文件转换为.resource文件并为您构建包装类。然后您可以通过命名空间访问YourAssembly.Properties.Resources

有了这个 using 语句。

using YourAssembly.Properties;

您可以使用如下属性进行装饰:

[Required(ErrorMessageResourceType = typeof(Resources), ErrorMessageResourceName = "MyStringName")]

注意:为了保持一致性,我使用了Properties文件夹。要使用 App_GlobalResources 将您的.resx文件移到那里并更改您的 using 语句以匹配目录名称。像这样:

using YourAssembly.App_GlobalResources;

编辑:最接近强类型资源名称的方法是执行以下操作:

public class ResourceNames
{
    public const string EmailRequired = "EmailRequired";
}

然后,您可以使用这样的属性进行装饰。

[Required(ErrorMessageResourceType = typeof(Resources), ErrorMessageResourceName = ResourceNames.EmailRequired)]

要启用自动客户端区域性检测,请将​​全球化部分添加到web.config文件中。

<configuration>
    <system.web>
        <globalization enableClientBasedCulture="true" culture="auto:en-us" uiCulture="auto:en-us"/>
    </system.web>
<configuration>

在这里,我启用了基于客户端的文化,并将文化uiculture设置为“ auto ”,默认为“ en-us ”。


创建单独的卫星程序集:

MSDN创建附属程序集文章也会有所帮助。如果您不熟悉附属程序集,请确保您阅读了打包和部署资源

在过去创建附属程序集时,我发现使用 VS 构建事件很有用。这些是我将采取的步骤。

  1. Class Library在我的解决方案中创建一个单独的项目。
  2. 创建或添加我的.resx文件到这个项目。
  3. Post-Build EventProject Properties对话框中添加一个。(比如下面那张)

示例 VS 构建后脚本:

set RESGEN="C:\Program Files\Microsoft SDKs\Windows\v6.0A\bin\resgen.exe"
set LINKER="C:\Program Files\Microsoft SDKs\Windows\v6.0A\bin\al.exe"
set ASSEMBLY=$(TargetName)
set SOURCEDIR=$(ProjectDir)
Set OUTDIR=$(TargetDir)

REM Build Default Culture Resources (en)
%RESGEN% %SOURCEDIR%en\%ASSEMBLY%.en.resx  %SOURCEDIR%en\%ASSEMBLY%.resources

REM Embed Default Culture
%LINKER% /t:lib /embed:%SOURCEDIR%en\%ASSEMBLY%.resources /culture:en /out:%OUTDIR%%ASSEMBLY%.resources.dll
REM Embed English Culture
IF NOT EXIST %OUTDIR%en\ MKDIR $%OUTDIR%en\
%LINKER% /t:lib /embed:%SOURCEDIR%en\%ASSEMBLY%.resources /culture:en /out:%OUTDIR%en\%ASSEMBLY%.resources.dll


REM These are just a byproduct of using the project build event to run the resource build script
IF EXIST %OUTDIR%%ASSEMBLY%.dll DEL %OUTDIR%%ASSEMBLY%.dll
IF EXIST %OUTDIR%%ASSEMBLY%.pdb DEL %OUTDIR%%ASSEMBLY%.pdb

如果你不想使用ResGen.exe来转换你的.resx文件,你可以这样做。

using System;
using System.Collections;
using System.IO;
using System.Resources;

namespace ResXConverter
{
    public class ResxToResource
    {
        public void Convert(string resxPath, string resourcePath)
        {
            using (ResXResourceReader resxReader = new ResXResourceReader(resxPath))
            using (IResourceWriter resWriter = new ResourceWriter(
                    new FileStream(resourcePath, FileMode.Create, FileAccess.Write)))
            {
                foreach (DictionaryEntry entry in resxReader)
                {
                    resWriter.AddResource(entry.Key.ToString(), entry.Value);
                }
                resWriter.Generate();
                resWriter.Close();
            }
        }
    }
}

以这种方式进行转换的潜在缺点之一是需要引用System.Windows.Forms.dll. 您仍然需要使用Assembly Linker

编辑:正如 wRAR 提醒我们的那样,如果您正在签署您的程序集,您的密钥必须匹配

于 2010-04-05T01:07:35.417 回答
2

虽然这很奇怪,特别是对于熟悉开源本地化技术的人来说,不能为任何系统组件构建卫星组件,甚至不能由第 3 方签署:

如果您的主程序集使用强命名,则必须使用与主程序集相同的私钥对附属程序集进行签名。如果主程序集和附属程序集之间的公钥/私钥对不匹配,则不会加载您的资源。

尽管我对此表示怀疑,但是否可以自动进行相同的操作,但没有卫星组件,这是未知的。

于 2010-04-07T13:45:10.150 回答
2

如果服务器没有安装 .NET 语言包,那么无论 CurrentUICulture 设置为什么,您将始终在 DataAnnotations 验证消息中获得英语。这个史诗般的黑客对我们有用。

  • 转到“Microsoft .NET Framework 4.6.1 语言包”下载页面 https://www.microsoft.com/en-us/download/details.aspx?id=49977
  • 选择语言并下载
  • 使用 7-Zip 解压 NDP461-KB3102436-x86-x64-AllOS-{LANG}.exe
  • 使用 7-Zip 提取 CAB 文件 x64-Windows10.0-KB3102502-x64.cab
  • 找到“msil_system.componentmod..notations.resources_....”
  • ...您将在其中找到“system.componentmodel.dataannotations.resources.dll”
  • 使用 ILSpy 打开 .resources.dll,找到 Resources 并单击 String Table 上方的 Save 按钮以另存为 System.ComponentModel.DataAnnotations.Resources.DataAnnotationsResources.{LANGUAGE}.resources
  • 在“资源”下添加到您的项目中
  • 确保资源文件的文件构建操作属性设置为“嵌入式资源”

然后在项目的 PreStart 方法中,用项目中的静态字段覆盖System.ComponentModel.DataAnnotations.Resources.DataAnnotationsResources.resourceMan私有静态字段(告诉你这是一个 hack)。

using System;
using System.Linq;
using System.Reflection;
using System.Resources;

[assembly: WebActivator.PreApplicationStartMethod(typeof(ResourceManagerUtil), nameof(ResourceManagerUtil.PreStart))]

class ResourceManagerUtil
{
    public static void PreStart()
    {
        initDataAnnotationsResourceManager();
    }

    /// <summary>
    /// If the server doesn't have .NET language packs installed then no matter what CurrentUICulture is set to, you'll always get English in 
    /// DataAnnotations validation messages. Here we override DataAnnotationsResources to use a ResourceManager that uses language .resources 
    /// files embedded in this assembly.
    /// </summary>
    static void initDataAnnotationsResourceManager()
    {
        var embeddedResourceNamespace = "<YourProjectDefaultNamespace>.<FolderYouSavedResourcesFilesIn>";
        var dataAnnotationsResourcesName = "System.ComponentModel.DataAnnotations.Resources.DataAnnotationsResources";
        var thisAssembly = typeof(ResourceManagerUtil).Assembly;
        var dataAnnotationsAssembly = typeof(System.ComponentModel.DataAnnotations.ValidationAttribute).Assembly;

        var resourceManager = new ResourceManager(embeddedResourceNamespace + "." + dataAnnotationsResourcesName, thisAssembly);

        // Set internal field `DataAnnotationsResources.resourceMan`
        var dataAnnotationsResourcesType = dataAnnotationsAssembly.GetType(dataAnnotationsResourcesName);
        var resmanProp = dataAnnotationsResourcesType.GetField("resourceMan", BindingFlags.NonPublic | BindingFlags.Static);
        resmanProp.SetValue(null, resourceManager);
    }
}
于 2017-10-06T13:49:27.127 回答
1

假设您要覆盖验证属性中的默认错误消息字符串,您可以通过设置ErrorMessageResourceNameErrorMessageResourceType属性来做到这一点,如下所示:

[Required(ErrorMessageResourceName = "Required_Username", ErrorMessageResourceType = typeof(MyResourceFile)]
public string Username { get; set; }

您可以创建一个名为 MyResourceFile.resx 的资源文件,其中包含Required_Username所需的错误消息。

希望这可以帮助。

于 2010-04-03T05:59:04.693 回答
1

我想提供与 Duncan Smart 相同想法的答案,但针对 .NET Core 2.2 而不是 .NET Framework 4.x。

这里是。

using System;
using System.Linq;
using System.Reflection;
using System.Resources;

public static class ResourceManagerHack
{
    /// <summary>
    /// If the server doesn't have .NET language packs installed then no matter what CurrentUICulture is set to, you'll always get English in 
    /// DataAnnotations validation messages. Here we override DataAnnotationsResources to use a ResourceManager that uses language .resources 
    /// files embedded in this assembly.
    /// </summary>
    public static void OverrideComponentModelAnnotationsResourceManager()
    {
        EnsureAssemblyIsLoaded();

        FieldInfo resourceManagerFieldInfo = GetResourceManagerFieldInfo();
        ResourceManager resourceManager = GetNewResourceManager();
        resourceManagerFieldInfo.SetValue(null, resourceManager);
    }

    private static FieldInfo GetResourceManagerFieldInfo()
    {
        var srAssembly = AppDomain.CurrentDomain
                                  .GetAssemblies()
                                  .First(assembly => assembly.FullName.StartsWith("System.ComponentModel.Annotations,"));
        var srType = srAssembly.GetType("System.SR");
        return srType.GetField("s_resourceManager", BindingFlags.Static | BindingFlags.NonPublic);
    }
    internal static ResourceManager GetNewResourceManager()
    {
        return new ResourceManager($"{typeof(<YourResource>).Namespace}.Strings", typeof(<YourResource>).Assembly);
    }
    private static void EnsureAssemblyIsLoaded()
    {
        var _ = typeof(System.ComponentModel.DataAnnotations.RequiredAttribute);
    }
}

我这样称呼它:

public static void Main(string[] args)
{
    ResourceManagerHack.OverrideComponentModelAnnotationsResourceManager();
    CreateWebHostBuilder(args).Build().Run();
}

此外,我创建了一个文件并用默认值~/Resources/<YourResource>.resx填充它并随意更改它们。最后我创建了一个公共空类。<YourResource>

于 2019-08-09T10:38:04.650 回答