Атрибут DisplayName з ресурсів?


160

У мене локалізований додаток, і мені цікаво, чи можна встановити DisplayNameдля ресурсу певну модель, задану з Ресурсу.

Я хотів би зробити щось подібне:

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

Але я не можу цього, як каже компілятор: "Аргумент атрибута повинен бути виразним константами, вираз typeof або вираз створення масиву типу параметра атрибута" :(

Чи є якісь обхідні шляхи? Я вивожу мітки вручну, але мені вони потрібні для виводу валідатора!

Відповіді:


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: return Resources.Language.ResourceManager.GetString (resourceId);
Leonel Sanches da Silva

4
Ви повинні використовувати: [Display (Name = "labelForName", ResourceType = typeof (Resources.Resources))], як описано нижче ...
Frank Boucher

375

Якщо ви використовуєте MVC 3 та .NET 4, ви можете використовувати новий Displayатрибут у System.ComponentModel.DataAnnotationsпросторі імен. Цей атрибут замінює DisplayNameатрибут та забезпечує набагато більше функціональних можливостей, включаючи підтримку локалізації.

У вашому випадку ви б використовували його так:

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

Як бічна примітка, цей атрибут не працюватиме з ресурсами всередині App_GlobalResourcesабо App_LocalResources. Це пов'язано зі спеціальним інструментом ( GlobalResourceProxyGenerator), який ці ресурси використовують. Замість цього переконайтесь, що для вашого ресурсного файла встановлено значення "Вбудований ресурс", і використовуйте спеціальний інструмент "ResXFileCodeGenerator".

(В якості додаткової сторони, ви не повинні використовувати App_GlobalResourcesабо використовувати App_LocalResourcesMVC. Докладніше про те, чому це так, ви можете прочитати тут )


Це добре для конкретного прикладу, який використовується тут, але він не працюватиме для більшості динамічних параметрів властивостей, які хочуть рядки.
kingdango

1
Це добре, коли у нас є вся наша локаль перед тим, як розгорнутися у виробництво. Але якщо я хочу викликати db для db рядків, то такий підхід не є правильним. Також недоліком файлу ресурсів є те, що він вимагає компіляції знову, щоб здійснити зміни, це означає, що вам потрібно випустити іншу версію для клієнта. Я не кажу про це як про поганий підхід, будь ласка, не відчувайте цього
kbvishnu

Просто проблема: ми маємо знати тип ресурсу в моделі. У мене є модель DLL та веб-сайт у двох різних проектах. Я хотів би мати можливість встановити відображувані імена в моделі та встановити тип ресурсу на веб-сайті ...
Kek

1
Отримайте Resharper та створіть файл ресурсу у вашому проекті, і він автоматично нагадає, а потім допоможе вам перемістити Ім'я у файл ресурсу.
anIBMer

14
З C # 6 замість Name = "labelForName"вас також можна використовувати Name = nameof(Resources.Resources.labelForName).
Уве Кеїм

35

Якщо ви відкриєте файл ресурсу та зміните модифікатор доступу на загальнодоступний або внутрішній, він створить клас із вашого ресурсного файлу, який дозволяє створювати сильно набрані посилання на ресурси.

Можливість створення коду файлу ресурсу

Це означає, що ви можете зробити щось подібне замість цього (використовуючи C # 6.0). Тоді вам не потрібно пам’ятати, чи було ім’я з нижнього розміру чи з камелі. І ви можете побачити, чи використовують інші властивості те саме значення ресурсу, знайдіть усі посилання.

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

це буде працювати з Winforms? У мене є клас, який я додав Відображення анотації з ресурсів, і я використав метод GetAttributeFrom за посиланням, щоб отримати ім’я властивості, але він не показує локалізований!
Dark_Knight

2
Ви використовуєте attribute.Name або attribute.GetName () для отримання локалізованого тексту? Документація для .Name говорить: "Не використовуйте цю властивість для отримання значення властивості Name. Замість цього використовуйте метод GetName." msdn.microsoft.com/en-us/library/…
Tikall

Так, я зрозумів, що мені потрібно використовувати GetName (). Спасибі
Dark_Knight

20

Оновлення:

Я знаю, що вже пізно, але я хотів би додати це оновлення:

Я використовую постачальника метаданих звичайної моделі, який представив Phil Haacked, це більш потужний і простий у застосуванні, подивіться на це: 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. Рішення Хаака, безумовно, найелегантніше в порівнянні з іншими тут. Він дуже добре вписується в конвеєрний стиль програмування в ASP.NET MVC і легко реалізується за допомогою єдиної команди nuget та одного рядка коду в Global.asax.cs.
Nilzor

11

Я отримав відповідь Gunders, що працює з моїми App_GlobalResources, вибравши властивості ресурсів і переключивши "Спеціальний інструмент" на "PublicResXFileCodeGenerator" та побудувавши дію на "Вбудований ресурс". Будь ласка, дивіться коментар 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)

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.