System.ComponentModel.DataAnnotations
我想为一个 ASP.NET 项目覆盖一个字符串。我是否需要进行卫星组装、搞乱自定义构建任务al.exe
等?即使是,我也找不到如何转换.resx
为. 如果没有,在哪里放置以及如何命名?.resources
al.exe
.resx.
UPD:为了清楚起见:我想使用自定义资源字符串而不是来自程序集的默认资源的字符串。我不想在每个使用该字符串的地方进行更改。毕竟,资源的存在只是为了覆盖它们。
System.ComponentModel.DataAnnotations
我想为一个 ASP.NET 项目覆盖一个字符串。我是否需要进行卫星组装、搞乱自定义构建任务al.exe
等?即使是,我也找不到如何转换.resx
为. 如果没有,在哪里放置以及如何命名?.resources
al.exe
.resx.
UPD:为了清楚起见:我想使用自定义资源字符串而不是来自程序集的默认资源的字符串。我不想在每个使用该字符串的地方进行更改。毕竟,资源的存在只是为了覆盖它们。
Phil Haack 有一篇很棒的文章Localizing ASP.Net MVC Validation,它专门指导您覆盖字符串。DataAnnotations
这篇文章比它更适用ASP.net MVC
。因此,这将有助于您使用DataAnnotattions。
下面我列出了在 Visual Studio 中添加本地化资源的最简单步骤。
Project Properties
对话框。Resources
选项卡。Properties
。
Access Modifier
为Public
.要为特定文化添加额外的资源文件,您需要这样做。
Project
的
Solution Explorer
.Resources.en-us.resx
。(用适当的代码替换“en-us”)Properties
文件夹中。Access Modifier
为Public
.在构建过程中,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 构建事件很有用。这些是我将采取的步骤。
Class Library
在我的解决方案中创建一个单独的项目。.resx
文件到这个项目。Post-Build Event
在Project 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 提醒我们的那样,如果您正在签署您的程序集,您的密钥必须匹配。
虽然这很奇怪,特别是对于熟悉开源本地化技术的人来说,不能为任何系统组件构建卫星组件,甚至不能由第 3 方签署:
如果您的主程序集使用强命名,则必须使用与主程序集相同的私钥对附属程序集进行签名。如果主程序集和附属程序集之间的公钥/私钥对不匹配,则不会加载您的资源。
尽管我对此表示怀疑,但是否可以自动进行相同的操作,但没有卫星组件,这是未知的。
如果服务器没有安装 .NET 语言包,那么无论 CurrentUICulture 设置为什么,您将始终在 DataAnnotations 验证消息中获得英语。这个史诗般的黑客对我们有用。
然后在项目的 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);
}
}
假设您要覆盖验证属性中的默认错误消息字符串,您可以通过设置ErrorMessageResourceName
和ErrorMessageResourceType
属性来做到这一点,如下所示:
[Required(ErrorMessageResourceName = "Required_Username", ErrorMessageResourceType = typeof(MyResourceFile)]
public string Username { get; set; }
您可以创建一个名为 MyResourceFile.resx 的资源文件,其中包含Required_Username
所需的错误消息。
希望这可以帮助。
我想提供与 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>