Attribut DisplayName des ressources?

160

J'ai une application localisée, et je me demande s'il est possible d'avoir la DisplayNamepropriété pour un certain modèle définie à partir d'une ressource.

J'aimerais faire quelque chose comme ça:

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

Mais je ne peux pas le faire, comme le dit le compilateur: "Un argument d'attribut doit être une expression constante, une expression de type ou une expression de création de tableau d'un type de paramètre d'attribut" :(

Existe-t-il des solutions de contournement? Je produis des étiquettes manuellement, mais j'en ai besoin pour la sortie du validateur!

Palantir
la source

Réponses:

112

Que diriez-vous d'écrire un attribut personnalisé:

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
    }
}

qui pourrait être utilisé comme ceci:

public class MyModel 
{
    [Required]
    [LocalizedDisplayName("labelForName")]
    public string Name { get; set; }
}
Darin Dimitrov
la source
Oui, j'ai travaillé sur une solution similaire ce matin et c'est faisable. Puis j'ai trouvé ce post qui a la même approche: adamyan.blogspot.com/2010/02/…
Palantir
23
@Gunder son message, en dessous de celui-ci (avec le plus de votes), est une solution beaucoup plus agréable. Juste pour les personnes qui lisent uniquement les articles acceptés
321X
1
En fait, cela ne fonctionne PAS pour accéder à différentes traductions car il renverra la même valeur pour tous les utilisateurs, peu importe quoi. Stocker l'ID de ressource dans une variable locale et remplacer DisplayName à la place
Fischer
5
Suggestion pour TODO: retournez Resources.Language.ResourceManager.GetString (resourceId);
Leonel Sanches da Silva
4
Vous devez utiliser: [Display (Name = "labelForName", ResourceType = typeof (Resources.Resources))] comme décrit ci-dessous ...
Frank Boucher
376

Si vous utilisez MVC 3 et .NET 4, vous pouvez utiliser le nouvel Displayattribut dans l' System.ComponentModel.DataAnnotationsespace de noms. Cet attribut remplace l' DisplayNameattribut et fournit beaucoup plus de fonctionnalités, y compris la prise en charge de la localisation.

Dans votre cas, vous l'utiliseriez comme ceci:

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

En remarque, cet attribut ne fonctionnera pas avec les ressources à l'intérieur App_GlobalResourcesou App_LocalResources. Cela a à voir avec l'outil personnalisé ( GlobalResourceProxyGenerator) que ces ressources utilisent. Assurez-vous plutôt que votre fichier de ressources est défini sur «Ressource intégrée» et utilisez l'outil personnalisé «ResXFileCodeGenerator».

(En guise de remarque supplémentaire, vous ne devriez pas utiliser App_GlobalResourcesou App_LocalResourcesavec MVC. Vous pouvez en savoir plus sur les raisons pour lesquelles c'est le cas ici )

René
la source
C'est bien pour l'exemple spécifique utilisé ici, mais cela ne fonctionnera pas pour la majorité des setters de propriétés dynamiques qui veulent des chaînes.
kingdango
1
C'est bien lorsque nous avons tous nos paramètres régionaux avec nous avant le déploiement en production. Mais si je veux appeler db pour les chaînes de base de données, ce type d'approche n'est pas approprié. L'inconvénient du fichier de ressources est également qu'il nécessite une nouvelle compilation pour effectuer les modifications, ce qui signifie que vous devez publier une autre version pour le client. Je ne dis pas cela comme une mauvaise approche, s'il vous plaît ne vous sentez pas comme ça
kbvishnu
Juste un problème: nous devons connaître le type de ressource dans le modèle. J'ai une DLL modèle et un site Web dans deux projets distincts. Je voudrais pouvoir définir des noms d'affichage dans le modèle et définir le type de ressource dans le site Web ...
Kek
1
Obtenez Resharper et créez le fichier de ressources dans votre projet, et il vous rappellera automatiquement et vous aidera à déplacer le nom dans le fichier de ressources.
anIBMer
14
Avec C # 6, au lieu de Name = "labelForName"vous pouvez également utiliser Name = nameof(Resources.Resources.labelForName).
Uwe Keim
35

Si vous ouvrez votre fichier de ressources et changez le modificateur d'accès en public ou en interne, il générera une classe à partir de votre fichier de ressources qui vous permettra de créer des références de ressources fortement typées.

Option de génération de code de fichier de ressources

Ce qui signifie que vous pouvez faire quelque chose comme ça à la place (en utilisant C # 6.0). Ensuite, vous n'avez pas à vous rappeler si le prénom était en minuscules ou en camelcas. Et vous pouvez voir si d'autres propriétés utilisent la même valeur de ressource avec une recherche de toutes les références.

[Display(Name = nameof(PropertyNames.FirstName), ResourceType = typeof(PropertyNames))]
public string FirstName { get; set; }
Tikall
la source
cela fonctionnera-t-il avec Winforms? J'ai une classe que j'ai ajoutée Afficher l'annotation à partir des ressources et j'ai utilisé la méthode GetAttributeFrom du lien pour obtenir le nom de la propriété, mais elle ne montre pas celle localisée!
Dark_Knight
2
Utilisez-vous attribute.Name ou attribute.GetName () pour obtenir le texte localisé? La documentation pour .Name indique "N'utilisez pas cette propriété pour obtenir la valeur de la propriété Name. Utilisez plutôt la méthode GetName." msdn.microsoft.com/en-us/library/…
Tikall
Oui, j'ai compris que je devais utiliser GetName (). Merci
Dark_Knight
20

Mettre à jour:

Je sais qu'il est trop tard mais j'aimerais ajouter cette mise à jour:

J'utilise le fournisseur de métadonnées de modèle conventionnel qui, présenté par Phil Haacked , est plus puissant et facile à appliquer, regardez-le: ConventionalModelMetadataProvider


Ancienne réponse

Ici, si vous souhaitez prendre en charge de nombreux types de ressources:

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);
        }
    }
}

Ensuite, utilisez-le comme ceci:

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

Pour le didacticiel de localisation complet, consultez cette page .

Wahid Bitar
la source
1
+1. La solution de Haack est certainement la plus élégante par rapport aux autres ici. Il s'intègre très bien dans le style de programmation basé sur les conventions dans ASP.NET MVC et est facilement implémenté via une seule commande nuget et une seule ligne de code dans Global.asax.cs.
Nilzor le
11

J'ai obtenu la réponse de Gunders en travaillant avec mon App_GlobalResources en choisissant les propriétés des ressources et en basculant «Outil personnalisé» en «PublicResXFileCodeGenerator» et en construisant l'action en «Ressource intégrée». Veuillez observer le commentaire de Gunders ci-dessous.

entrez la description de l'image ici

Fonctionne comme un charme :)

Magnus Karlsson
la source
Il en résulte un fichier de ressources qui sera compilé comme si vous aviez ajouté un fichier de ressources en dehors de App_GlobalResources. Ainsi, votre fichier de ressources ne se comportera plus comme un fichier de ressources "App_GlobalResources", ce qui est absolument parfait. Mais vous devez juste en être conscient. Vous n'avez donc plus les "avantages" de mettre le fichier de ressources dans App_GlobalResources. Vous auriez tout aussi bien pu le mettre ailleurs.
René
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. Définissez ResourceType de l'attribut pour qu'il recherche une ressource
  2. Définissez le nom de l'attribut utilisé pour la clé de la ressource, après C # 6.0, vous pouvez l'utiliser nameofpour une prise en charge forte au lieu de coder en dur la clé.

  3. Définissez la culture du thread actuel dans le contrôleur.

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

  1. Définir l'accessibilité de la ressource au public

  2. Affichez l'étiquette dans cshtml comme ceci

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

code4j
la source