如何本地化 C# 中的属性描述?

发布于 2024-12-04 19:08:19 字数 714 浏览 1 评论 0原文

我正在开发一个课程,该课程将被来自其他国家的一些人使用。我必须本地化每条消息,警告 ec,以便他们能够理解我们的意思。在很多情况下我都实现了我的目标。但是像描述这样的属性属性实在是太麻烦了。

这就是我现在所拥有的:

[Category("Editable Values"), Description("Sets the minimum select...")]
    public Ampere Simin
    {
        get
        {...}
        set
        {...}
    }

[Category("Editable Values"), Description(Localisation.Simin)] // "Localisation" here is the internal resource file that i wrote for messages, warnings, exceptions and -unfortunately- descriptions
        public Ampere Simin
        {
            get
            {...}
            set
            {...}
        }

就是我正在努力做的事情。但不可能以这种方式使用本地化。关于我可以用来代替它的东西有什么建议吗?

I'm working on a class that is going to be used by some people from another countries. I have to localize every message, warning e.c. so that they can understand what we mean. In many cases i achieved my goal. But these property attributes like descriptions are such a pain in the ass.

Here`s what I have right now:

[Category("Editable Values"), Description("Sets the minimum select...")]
    public Ampere Simin
    {
        get
        {...}
        set
        {...}
    }

and

[Category("Editable Values"), Description(Localisation.Simin)] // "Localisation" here is the internal resource file that i wrote for messages, warnings, exceptions and -unfortunately- descriptions
        public Ampere Simin
        {
            get
            {...}
            set
            {...}
        }

That's what I'm trying to do. But it's not possible to use Localisations this way. Any Suggestions about something that I can use instead of it?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

花心好男孩 2024-12-11 19:08:19

子类:

[STAThread]
static void Main()
{   // just some example code to show it working in winforms, but
    // anything using System.ComponentModel should see the change
    Application.EnableVisualStyles();        
    Application.Run(new Form {Controls = {new PropertyGrid {Dock = DockStyle.Fill, SelectedObject = new Foo()}}});
}

class Foo
{   // assume the following literals are keys, for example to a RESX
    [LocalizedCategory("cat")]
    [LocalizedDescription("desc")]
    [LocalizedDisplayName("disp name")]
    public string Bar { get; set; }
}
[AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Module | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Interface | AttributeTargets.Parameter | AttributeTargets.Delegate | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter)]
class LocalizedDescriptionAttribute : DescriptionAttribute
{
    static string Localize(string key)
    {
        // TODO: lookup from resx, perhaps with cache etc
        return "Something for " + key;
    }
    public LocalizedDescriptionAttribute(string key)
        : base(Localize(key))
    {
    }
}
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Event)]
class LocalizedDisplayNameAttribute : DisplayNameAttribute
{
    static string Localize(string key)
    {
        // TODO: lookup from resx, perhaps with cache etc
        return "Something for " + key;
    }
    public LocalizedDisplayNameAttribute(string key)
        : base(Localize(key))
    {
    }
}
[AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Module | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Interface | AttributeTargets.Parameter | AttributeTargets.Delegate | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter)]
class LocalizedCategoryAttribute : CategoryAttribute
{
    public LocalizedCategoryAttribute(string key) : base(key) { }
    protected override string  GetLocalizedString(string value)
    {
            // TODO: lookup from resx, perhaps with cache etc
        return "Something for " + value;
    }
}

Subclasses:

[STAThread]
static void Main()
{   // just some example code to show it working in winforms, but
    // anything using System.ComponentModel should see the change
    Application.EnableVisualStyles();        
    Application.Run(new Form {Controls = {new PropertyGrid {Dock = DockStyle.Fill, SelectedObject = new Foo()}}});
}

class Foo
{   // assume the following literals are keys, for example to a RESX
    [LocalizedCategory("cat")]
    [LocalizedDescription("desc")]
    [LocalizedDisplayName("disp name")]
    public string Bar { get; set; }
}
[AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Module | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Interface | AttributeTargets.Parameter | AttributeTargets.Delegate | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter)]
class LocalizedDescriptionAttribute : DescriptionAttribute
{
    static string Localize(string key)
    {
        // TODO: lookup from resx, perhaps with cache etc
        return "Something for " + key;
    }
    public LocalizedDescriptionAttribute(string key)
        : base(Localize(key))
    {
    }
}
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Event)]
class LocalizedDisplayNameAttribute : DisplayNameAttribute
{
    static string Localize(string key)
    {
        // TODO: lookup from resx, perhaps with cache etc
        return "Something for " + key;
    }
    public LocalizedDisplayNameAttribute(string key)
        : base(Localize(key))
    {
    }
}
[AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Module | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Interface | AttributeTargets.Parameter | AttributeTargets.Delegate | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter)]
class LocalizedCategoryAttribute : CategoryAttribute
{
    public LocalizedCategoryAttribute(string key) : base(key) { }
    protected override string  GetLocalizedString(string value)
    {
            // TODO: lookup from resx, perhaps with cache etc
        return "Something for " + value;
    }
}
久夏青 2024-12-11 19:08:19

我结合了 Marc Gravelldsmith的回答:

首先:创建一个名为LocalizedDescriptionAttribute的Attribute类,

public class LocalizedDescriptionAttribute : DescriptionAttribute
{
    static string Localize(string key)
    {
        return YourResourceClassName.ResourceManager.GetString(key);
    }

    public LocalizedDescriptionAttribute(string key)
        : base(Localize(key))
    {
    }
}

将其用作LocalizedDescription(“Attribute”一词不是必需的)

public class Foo
{   
    // myString is a key that exists in your Resources file
    [LocalizedDescription("myString")]
    public string Bar { get; set; }
}

I've combined Marc Gravell and dsmith's answers:

First: create an Attribute class named as LocalizedDescriptionAttribute

public class LocalizedDescriptionAttribute : DescriptionAttribute
{
    static string Localize(string key)
    {
        return YourResourceClassName.ResourceManager.GetString(key);
    }

    public LocalizedDescriptionAttribute(string key)
        : base(Localize(key))
    {
    }
}

Use it as LocalizedDescription (the word "Attribute" is not necessary)

public class Foo
{   
    // myString is a key that exists in your Resources file
    [LocalizedDescription("myString")]
    public string Bar { get; set; }
}
倒带 2024-12-11 19:08:19
[Required(ErrorMessageResourceName = "LogOnModel_UserName_Required",     
      ErrorMessageResourceType = typeof(Resources.Global))]    
[Display(Name = "LogOnModel_UserName_Required",resourceType = typeof(Resources.Global))]  
public string UserName { get; set; }

请参阅:http://geekswithblogs.net/shaunxu/archive/2010/05/06/localization-in-asp.net-mvc-ndash-3-days-investigation-1-day.aspx

[Required(ErrorMessageResourceName = "LogOnModel_UserName_Required",     
      ErrorMessageResourceType = typeof(Resources.Global))]    
[Display(Name = "LogOnModel_UserName_Required",resourceType = typeof(Resources.Global))]  
public string UserName { get; set; }

see: http://geekswithblogs.net/shaunxu/archive/2010/05/06/localization-in-asp.net-mvc-ndash-3-days-investigation-1-day.aspx

爱的那么颓废 2024-12-11 19:08:19

让我们看一下这个示例类:

// ------------------------------------------------------------------------
public class Customer
{
    // ----------------------------------------------------------------------
    [Category( "Editable Values" ), LocDescription( "FirstName", "Sets the first name..." )]
    public string FirstName { get; set; }

    // ----------------------------------------------------------------------
    [Category( "Editable Values" ), LocDescription(  Key = "LastName", DefaultDescription = "Sets the last name..." )]
    public string LastName { get; set; }
} // class Customer

现在您可以实现自定义属性类:

// ------------------------------------------------------------------------
public class LocDescriptionAttribute : DescriptionAttribute
{
    // ----------------------------------------------------------------------
    public LocDescriptionAttribute()
    {
    } // LocDescriptionAttribute

    // ----------------------------------------------------------------------
    public LocDescriptionAttribute( string key, string defaultDescription ) :
        base( defaultDescription )
    {
        Key = key;
        DefaultDescription = defaultDescription;
    } // LocDescriptionAttribute

    // ----------------------------------------------------------------------
    public string Key { get; set; }

    // ----------------------------------------------------------------------
    public string DefaultDescription { get; set; }

    // ----------------------------------------------------------------------
    public override string Description
    {
        get
        {
            // load from resx
            string description = Strings.GetString( Key );
            if ( string.IsNullOrEmpty( description ) )
            {
                description = DefaultDescription;
            }
            return description;
        }
    } // Description
} // class LocDescriptionAttribute

现在您有了本地化的描述:

AttributeCollection attributes = TypeDescriptor.GetProperties( customer )[ "FirstName" ].Attributes;
DescriptionAttribute myAttribute = (DescriptionAttribute)attributes[ typeof( DescriptionAttribute ) ];
ConsoleWiterLine( myAttribute.Description );

Let's take this sample class:

// ------------------------------------------------------------------------
public class Customer
{
    // ----------------------------------------------------------------------
    [Category( "Editable Values" ), LocDescription( "FirstName", "Sets the first name..." )]
    public string FirstName { get; set; }

    // ----------------------------------------------------------------------
    [Category( "Editable Values" ), LocDescription(  Key = "LastName", DefaultDescription = "Sets the last name..." )]
    public string LastName { get; set; }
} // class Customer

Now you can implement a custom attribute class:

// ------------------------------------------------------------------------
public class LocDescriptionAttribute : DescriptionAttribute
{
    // ----------------------------------------------------------------------
    public LocDescriptionAttribute()
    {
    } // LocDescriptionAttribute

    // ----------------------------------------------------------------------
    public LocDescriptionAttribute( string key, string defaultDescription ) :
        base( defaultDescription )
    {
        Key = key;
        DefaultDescription = defaultDescription;
    } // LocDescriptionAttribute

    // ----------------------------------------------------------------------
    public string Key { get; set; }

    // ----------------------------------------------------------------------
    public string DefaultDescription { get; set; }

    // ----------------------------------------------------------------------
    public override string Description
    {
        get
        {
            // load from resx
            string description = Strings.GetString( Key );
            if ( string.IsNullOrEmpty( description ) )
            {
                description = DefaultDescription;
            }
            return description;
        }
    } // Description
} // class LocDescriptionAttribute

Now you have the localized description:

AttributeCollection attributes = TypeDescriptor.GetProperties( customer )[ "FirstName" ].Attributes;
DescriptionAttribute myAttribute = (DescriptionAttribute)attributes[ typeof( DescriptionAttribute ) ];
ConsoleWiterLine( myAttribute.Description );
娇纵 2024-12-11 19:08:19

稍微扩展贾尼的答案(这对我来说效果很好):

string description = Strings.GetString( Key );

可以更清楚地完成,因为

ResourceManager rm = YourResourceClassName.ResourceManager;
string description = rm.GetString(Key);

它含糊地暗示,但不清楚原始代码中缺少什么。资源类的 .ResourceManager 属性是自动生成的。

然后,要访问它,请使用 Glennular 在此描述的扩展方法: StackOverflow 而不是在字段/类名称中进行硬编码(只需使用 LocDescriptionAttribute 而不是 DescriptionAttribute)。

我所做的唯一的其他调整是基于 this IComparable enum 使其静态,以允许它被任何枚举使用,而不是被锁定到特定类型;这是我能做的最好的事情,因为我无法应用接口将其限制为仅包含我添加了描述的枚举。

Slight extension to Jani's answer (which has worked well for me):

string description = Strings.GetString( Key );

can be more clearly done as

ResourceManager rm = YourResourceClassName.ResourceManager;
string description = rm.GetString(Key);

It was vaguely implied, but wasn't clear what was missing from the original code. The .ResourceManager property of the resource class is auto-generated.

Then, to access it, use the extension method described by Glennular here: StackOverflow instead of hardcoding in field/class names (just use LocDescriptionAttribute instead of DescriptionAttribute).

The only other adjustment I made was to make it static based on this IComparable enum, to allow it to be used by any enum instead of being locked to a particular type; it was the best I could do given that I can't apply an interface to limit it to just the enums I've added descriptions to.

故乡的云 2024-12-11 19:08:19

似乎 CategoryAttribute 具有根据内部资源查找本地化字符串的代码,但 DescriptionAttribute 没有本地化。

来自 Reflector:

public string Category
{
    get
    {
        if (!this.localized)
        {
            this.localized = true;
            string localizedString = this.GetLocalizedString(this.categoryValue);
            if (localizedString != null)
            {
                this.categoryValue = localizedString;
            }
        }
        return this.categoryValue;
     }
}

我认为您可以扩展属性并创建自己的实现,传入 ResourceType。像这样的东西,模糊地基于 DataAnnotation 逻辑。显然需要更多的清理工作。

public class LocaleDescriptionAttribute : DescriptionAttribute
{
    ...

    public LocaleDescriptionAttribute(string resourceKey, Type resourceType)
        : base(resourceKey)
    {
        this.resourceType = resourceType;
    }

    public override string  Description
    {
        get 
        { 
            var description = base.Description;
            PropertyInfo property = resourceType.GetProperty(
                       description, BindingFlags.Public | BindingFlags.Static);
            if (property == null)
            {
                return description;
            }
            return property.GetValue(null, null) as string;
        }
    }    
}

It seems the CategoryAttribute has code to look for an localized string based on internal resources, butDescriptionAttribute has no localization.

From Reflector:

public string Category
{
    get
    {
        if (!this.localized)
        {
            this.localized = true;
            string localizedString = this.GetLocalizedString(this.categoryValue);
            if (localizedString != null)
            {
                this.categoryValue = localizedString;
            }
        }
        return this.categoryValue;
     }
}

I think you would to extend the attributes and create your own implementation, passing in the ResourceType. Something like this, based vaguely on DataAnnotation logic. Obviously needs a bit more clean up.

public class LocaleDescriptionAttribute : DescriptionAttribute
{
    ...

    public LocaleDescriptionAttribute(string resourceKey, Type resourceType)
        : base(resourceKey)
    {
        this.resourceType = resourceType;
    }

    public override string  Description
    {
        get 
        { 
            var description = base.Description;
            PropertyInfo property = resourceType.GetProperty(
                       description, BindingFlags.Public | BindingFlags.Static);
            if (property == null)
            {
                return description;
            }
            return property.GetValue(null, null) as string;
        }
    }    
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文