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

160

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

Я хотел бы сделать что-то вроде этого:

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

Но я не могу этого сделать, поскольку компилятор говорит: «Аргументом атрибута должно быть константное выражение, выражение typeof или выражение создания массива типа параметра атрибута» :(

Есть ли обходные пути? Я выводю метки вручную, но они мне нужны для вывода валидатора!

Palantir
источник

Ответы:

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
@Подробнее его пост, ниже этого (с наибольшим количеством голосов), гораздо более удачное решение. Просто для людей, которые только читают Принятые посты
321X
1
Это на самом деле не работает для доступа к различным переводам, так как он будет возвращать одно и то же значение для всех пользователей, независимо от того, что. Сохраните resourceid в локальной переменной и переопределите DisplayName вместо этого
Fischer
5
Предложение для TODO: return Resources.Language.ResourceManager.GetString (resourceId);
Леонель Санчес да Силва
4
Вы должны использовать: [Display (Name = "labelForName", ResourceType = typeof (Resources.Resources))], как описано ниже ...
Франк Баучер
376

Если вы используете 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_LocalResourcesс MVC. Вы можете прочитать больше о том, почему это так, здесь )

Рене
источник
Это хорошо для конкретного примера, используемого здесь, но не будет работать для большинства динамических установщиков свойств, которые хотят строки.
кингданго
1
Это хорошо, когда у нас есть все наши локали до развертывания в производство. Но если я хочу вызвать db для строк db, такой подход не подходит. Также недостатком файла ресурсов является то, что он требует повторной компиляции, чтобы внести изменения, это означает, что вам нужно выпустить другую версию для клиента. Я не говорю, что это плохой подход, пожалуйста, не
думайте так
Просто проблема: мы должны знать тип ресурса в модели. У меня есть модель DLL и веб-сайт в двух разных проектах. Я хотел бы иметь возможность установить отображаемые имена в модели и установить тип ресурса на веб-сайте ...
Kek
1
Получите Resharper и создайте файл ресурсов в своем проекте, и он автоматически напомнит, а затем поможет вам переместить Имя в файл ресурсов.
13:
14
С C # 6 вместо Name = "labelForName"вас также можно использовать Name = nameof(Resources.Resources.labelForName).
Уве Кейм
35

Если вы откроете свой файл ресурсов и измените модификатор доступа на public или internal, он сгенерирует класс из вашего файла ресурсов, который позволит вам создавать строго типизированные ссылки на ресурсы.

Опция для генерации кода файла ресурса

Это означает, что вы можете сделать что-то вроде этого (используя C # 6.0). Тогда вам не нужно помнить, было ли имя в нижнем или верблюжьем корпусе. И вы можете увидеть, используют ли другие свойства то же значение ресурса, что и найти все ссылки.

[Display(Name = nameof(PropertyNames.FirstName), ResourceType = typeof(PropertyNames))]
public string FirstName { get; set; }
Tikall
источник
это будет работать с Winforms? У меня есть класс, который я добавил Показать аннотации из ресурсов, и я использовал метод GetAttributeFrom из ссылки, чтобы получить имя свойства, но он не показывает локализованный!
Dark_Knight
2
Вы используете attribute.Name или attribute.GetName () для получения локализованного текста? Документация для .Name гласит: «Не используйте это свойство для получения значения свойства Name. Вместо этого используйте метод GetName». msdn.microsoft.com/en-us/library/…
Тикалл
Да, я понял, что мне нужно использовать GetName (). Спасибо
Dark_Knight
20

Обновить:

Я знаю, что уже слишком поздно, но я бы хотел добавить это обновление:

Я использую поставщика метаданных обычной модели, представленного Филом Хаакедом, он более мощный и простой в применении, посмотрите на него: 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; }

Полное руководство по локализации смотрите на этой странице .

Wahid Bitar
источник
1
+1. Решение Haack определенно самое элегантное по сравнению с другими здесь. Он очень хорошо вписывается в основанный на соглашениях стиль программирования в ASP.NET MVC и легко реализуется с помощью одной команды nuget и одной строки кода в Global.asax.cs.
Нильзор
11

Я получил ответ Gunders, работая с моими App_GlobalResources, выбрав свойства ресурсов и переключив «Custom Tool» на «PublicResXFileCodeGenerator» и собрав действие «Embedded Resource». Пожалуйста, обратите внимание на комментарий 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)

code4j
источник