来自资源的DisplayName属性?


160

我有一个本地化的应用程序,我想知道是否可以DisplayName从Resource中为特定模型属性设置。

我想做这样的事情:

public class MyModel {
  [Required]
  [DisplayName(Resources.Resources.labelForName)]
  public string name{ get; set; }
}

但是我不能接受,正如编译器所说:“属性参数必须是属性参数类型的常量表达式,typeof表达式或数组创建表达式” :(

有什么解决方法吗?我正在手动输出标签,但是验证器输出需要这些标签!

Answers:


112

如何编写自定义属性:

public class LocalizedDisplayNameAttribute: DisplayNameAttribute
{
    public LocalizedDisplayNameAttribute(string resourceId) 
        : base(GetMessageFromResource(resourceId))
    { }

    private static string GetMessageFromResource(string resourceId)
    {
        // TODO: Return the string from the resource file
    }
}

可以这样使用:

public class MyModel 
{
    [Required]
    [LocalizedDisplayName("labelForName")]
    public string Name { get; set; }
}

是的,今天早上我研究了类似的解决方案,而且可行。然后,我发现此帖子具有相同的方法:adamyan.blogspot.com/2010/02/…–
Palantir

23
@Gunder在他的帖子下(该投票最多)是一个更好的解决方案。只是谁的人只读了接受职位
子321x

1
实际上,这对于访问不同的翻译不起作用,因为它将为所有用户返回相同的值,而不管是什么。将resourceid存储在本地变量中,并改写DisplayName
Fischer

5
对TODO的建议:返回Resources.Language.ResourceManager.GetString(resourceId);
Leonel Sanches da Silva

4
您应该使用:[Display(Name =“ labelForName”,ResourceType = typeof(Resources.Resources))]如下所述……
Frank Boucher

375

如果使用MVC 3和.NET 4,则可以DisplaySystem.ComponentModel.DataAnnotations名称空间中使用new 属性。该属性替换该DisplayName属性,并提供更多功能,包括本地化支持。

在您的情况下,可以这样使用它:

public class MyModel
{
    [Required]
    [Display(Name = "labelForName", ResourceType = typeof(Resources.Resources))]
    public string name{ get; set; }
}

另外请注意,此属性不适用于App_GlobalResources或中的资源App_LocalResources。这与GlobalResourceProxyGenerator这些资源使用的自定义工具()有关。相反,请确保将资源文件设置为“嵌入式资源”,并使用“ ResXFileCodeGenerator”自定义工具。

(作为补充说明,您不应该使用MVC App_GlobalResources或将其App_LocalResources与MVC 一起使用。您可以在此处阅读有关为何如此的更多信息)


这对于此处使用的特定示例很有用,但不适用于大多数需要字符串的动态属性设置器。
kingdango 2011年

1
当我们在部署到生产之前拥有所有语言环境时,这很好。但是,如果我想为数据库字符串调用db,则这种方法是不合适的。资源文件的另一个缺点是,它需要再次编译才能使更改生效,这意味着您需要向客户端发布另一个版本。我并不是说这不是一个坏方法,请不要那样想
kbvishnu 2012年

只是一个问题:我们必须知道模型中的资源类型。我在两个不同的项目中都有一个模型DLL和一个网站。我希望能够在模型中设置显示名称并在网站中设置资源类型...
Kek 2012年

1
获取Resharper,并在项目中创建资源文件,它将自动提醒您,然后帮助您将Name移到资源文件中。
an IBMer

14
使用C#6时,Name = "labelForName"也可以使用代替Name = nameof(Resources.Resources.labelForName)
Uwe Keim

35

如果打开资源文件并将access修饰符更改为public或internal,它将从您的资源文件生成一个类,该类使您可以创建强类型的资源引用。

资源文件代码生成的选项

这意味着您可以改为执行以下操作(使用C#6.0)。然后,您不必记住名字是小写还是驼峰。您可以通过查找所有引用来查看其他属性是否使用相同的资源值。

[Display(Name = nameof(PropertyNames.FirstName), ResourceType = typeof(PropertyNames))]
public string FirstName { get; set; }

可以与Winforms一起使用吗?我有一个类,我从资源中添加了Display批注,并使用了链接中的 GetAttributeFrom方法来获取属性的名称,但是它不显示本地化的属性!
Dark_Knight

2
您是否正在使用attribute.Name或attribute.GetName()来获取本地化的文本?.Name的文档说:“不要使用此属性获取Name属性的值。请改用GetName方法。” msdn.microsoft.com/en-us/library/...
Tikall

是的,我发现必须使用GetName()。谢谢
Dark_Knight '16

20

更新:

我知道为时已晚,但我想添加此更新:

我正在使用Phil Haack提出的ConventionalModel Model Metadata Provider,它更强大,更容易应用,请看一下: ConventionalModelMetadataProvider


旧答案

如果您想支持多种类型的资源,请在这里:

public class LocalizedDisplayNameAttribute : DisplayNameAttribute
{
    private readonly PropertyInfo nameProperty;

    public LocalizedDisplayNameAttribute(string displayNameKey, Type resourceType = null)
        : base(displayNameKey)
    {
        if (resourceType != null)
        {
            nameProperty = resourceType.GetProperty(base.DisplayName,
                                           BindingFlags.Static | BindingFlags.Public);
        }
    }

    public override string DisplayName
    {
        get
        {
            if (nameProperty == null)
            {
                return base.DisplayName;
            }
            return (string)nameProperty.GetValue(nameProperty.DeclaringType, null);
        }
    }
}

然后像这样使用它:

    [LocalizedDisplayName("Password", typeof(Res.Model.Shared.ModelProperties))]
    public string Password { get; set; }

有关完整的本地化教程,请参见此页面


1
+1。与这里的其他解决方案相比,Haack的解决方案绝对是最优雅的解决方案。它非常适合ASP.NET MVC中基于约定的编程风格,可以通过Global.asax.cs中的单个nuget命令和一行代码轻松实现。
Nilzor 2014年

11

通过选择资源属性,将“自定义工具”切换为“ PublicResXFileCodeGenerator”,并将操作构建为“嵌入式资源”,我得到了Gunders使用我的App_GlobalResources的答案。请注意下面的Gunders评论。

在此处输入图片说明

奇迹般有效 :)


这将导致将编译资源文件,就像您在App_GlobalResources之外添加资源文件一样。因此,您的资源文件将不再充当“ App_GlobalResources”资源文件,这绝对可以。但是您应该只是意识到这一点。因此,您不再具有将资源文件放入App_GlobalResources的“好处”。您也可以将其放在其他位置。
勒内

5
public class Person
{
    // Before C# 6.0
    [Display(Name = "Age", ResourceType = typeof(Testi18n.Resource))]
    public string Age { get; set; }

    // After C# 6.0
    // [Display(Name = nameof(Resource.Age), ResourceType = typeof(Resource))]
}
  1. 定义属性的ResourceType,以便它查找资源
  2. 定义用于资源密钥的属性的名称,在C#6.0之后,您可以使用nameof强类型支持而不是对密钥进行硬编码。

  3. 在控制器中设置当前线程的区域性。

Resource.Culture = CultureInfo.GetCultureInfo("zh-CN");

  1. 将资源的可访问性设置为公共

  2. 像这样在cshtml中显示标签

@Html.DisplayNameFor(model => model.Age)

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.