从ASP.NET中的标准程序集覆盖资源

时间:2010-03-29 12:52:46

标签: asp.net localization satellite-assembly

我想覆盖ASP.NET项目的System.ComponentModel.DataAnnotations字符串。我是否需要制作附属程序集,搞乱自定义构建任务,al.exe等?即使是,我也找不到如何将.resx转换为.resources以将其提供给al.exe。如果不是,在哪里放.resx.以及如何命名?

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

5 个答案:

答案 0 :(得分:4)

菲尔·哈克(Phil Haack)有一篇优秀的文章 Localizing ASP.Net MVC Validation ,它专门指导你完成你的字符串。本文适用于 DataAnnotations ,而不是 ASP.net MVC 。因此,这将有助于您使用 DataAnnotattions

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

  1. 打开 Project Properties 对话框。
  2. 选择 Resources 标签。
  3. 点击以创建新的默认值 资源文件
  4. 这将在Properties文件夹中创建两个文件。
    • Resources.resx
    • Resources.Designer.cs
  5. Resources.resx 时有 打开,更改 Access Modifier Public
  6. 添加字符串。
  7. 要为特定文化添加其他资源文件,您需要这样做。

    1. 右键点击中的 Project Solution Explorer
    2. 选择添加 - > 新商品 - > 资源 文件。
    3. 将其命名为 Resources.en-us.resx 。 (用适当的替换'en-us' 代码)
    4. 点击添加
    5. 将其拖至Properties文件夹。
    6. 打开 Resources.en-us.resx 并更改 Access Modifier Public
    7. 添加字符串。
    8. 重复您需要的每种文化 支持。
    9. 在构建期间,VS会将 .resx 文件转换为 .resource 文件,并为您构建包装类。然后,您可以通过命名空间 YourAssembly.Properties.Resources

      进行访问

      使用此声明。

      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)]
      

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

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

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


      创建单独的卫星装配:

      MSDN Creating Satellite Assemblies 文章也会有所帮助。 如果您不熟悉附属程序集,请务必阅读 Packaging and Deploying Resources

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

      1. 在我的解决方案中创建单独的 Class Library 项目。
      2. 创建或添加 .resx 文件到此项目。
      3. Post-Build Event 添加到 Project Properties 对话框中。 (如下所示)
      4. 示例VS Post-Build脚本:

        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提醒我们,如果您要为您的程序集签署密钥must match

答案 1 :(得分:2)

虽然这很奇怪,特别是对于熟悉开源本地化技术的人来说,无法为任何系统程序集甚至是第三方签名的程序集构建附属程序集:

  

If your main assembly uses strong naming, satellite assemblies must be signed with the same private key as the main assembly. If the public/private key pair does not match between the main and satellite assemblies, your resources will not be loaded.

是否可以自动进行,但没有卫星装配,是不可知的,但我对此表示怀疑。

答案 2 :(得分:1)

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

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

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

希望这有帮助。

答案 3 :(得分:0)

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

  • 转到&#34; Microsoft .NET Framework 4.6.1语言包&#34;下载页面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
  • 找到&#34; msil_system.componentmod..notations.resources _....&#34;
  • ...您将在其中找到&#34; system.componentmodel.dataannotations.resources.dll&#34;
  • 使用ILSpy打开.resources.dll,找到Resources并单击String Table上方的Save按钮保存为System.ComponentModel.DataAnnotations.Resources.DataAnnotationsResources。{LANGUAGE} .resources
  • 根据&#34;资源&#34;
  • 添加到您的项目中
  • 确保资源文件的Build Action属性设置为&#34; Embedded Resource&#34;

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

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);
    }
}

答案 4 :(得分:0)

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

在这里。

function myEventFunction(event) {
  if (event.target.name == "select2") {
    event.preventDefault();
  } else {
    console.log('changed');
  }
}

我这样称呼它:

<form onchange="myEventFunction(event)">
  <select name="select1">
    <option>1</option>
    <option>2</option>
    <option>3</option>
    <option>4</option>
  </select>
  <select name="select2">
    <option>1</option>
    <option>2</option>
    <option>3</option>
  </select>
  <select name="select3">
    <option>1</option>
    <option>2</option>
    <option>3</option>
    <option>4</option>
  </select>
</form>

此外,我创建了一个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); } } 文件,并在其中填充了the default values,并随意进行了更改。最后,我创建了一个公共的空类public static void Main(string[] args) { ResourceManagerHack.OverrideComponentModelAnnotationsResourceManager(); CreateWebHostBuilder(args).Build().Run(); }