Attributo DisplayName da Risorse?


160

Ho un'applicazione localizzata e mi chiedo se sia possibile DisplayNameimpostare la proprietà per un determinato modello da una risorsa.

Mi piacerebbe fare qualcosa del genere:

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

Ma non posso, come dice il compilatore: "Un argomento di attributo deve essere un'espressione costante, un'espressione di tipo o un'espressione di creazione di array di un tipo di parametro di attributo" :(

Ci sono soluzioni alternative? Sto inviando le etichette manualmente, ma ho bisogno di queste per l'output del validatore!

Risposte:


112

Che ne dici di scrivere un attributo personalizzato:

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

che potrebbe essere usato in questo modo:

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

Sì, ho lavorato su una soluzione simile questa mattina ed è fattibile. Poi ho trovato questo post che ha lo stesso approccio: adamyan.blogspot.com/2010/02/…
Palantir

23
@Gunder il suo post, sotto questo (con il maggior numero di voti), è una soluzione molto più bella. Solo per le persone che leggono solo i post accettati
321X

1
Questo in realtà NON funziona per accedere a traduzioni diverse poiché restituirà lo stesso valore per tutti gli utenti, non importa cosa. Memorizza resourceid in una variabile locale e sostituisci invece DisplayName
Fischer il

5
Suggerimento per TODO: return Resources.Language.ResourceManager.GetString (resourceId);
Leonel Sanches da Silva,

4
Dovresti usare: [Display (Name = "labelForName", ResourceType = typeof (Resources.Resources))] come descritto di seguito ...
Frank Boucher,

376

Se si utilizzano MVC 3 e .NET 4, è possibile utilizzare il nuovo Displayattributo nello System.ComponentModel.DataAnnotationsspazio dei nomi. Questo attributo sostituisce l' DisplayNameattributo e fornisce molte più funzionalità, incluso il supporto per la localizzazione.

Nel tuo caso, lo useresti in questo modo:

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

Come nota a margine, questo attributo non funzionerà con risorse all'interno App_GlobalResourceso App_LocalResources. Ciò ha a che fare con lo strumento personalizzato ( GlobalResourceProxyGenerator) utilizzato da queste risorse. Assicurati invece che il tuo file di risorse sia impostato su "Risorse integrate" e utilizza lo strumento personalizzato "ResXFileCodeGenerator".

(Come ulteriore nota a margine, non dovresti usare App_GlobalResourceso App_LocalResourcescon MVC. Puoi leggere di più sul perché questo è il caso qui )


Questo è utile per l'esempio specifico utilizzato qui, ma non funzionerà per la maggior parte dei setter dinamici di proprietà che desiderano stringhe.
Kingdango,

1
Questo va bene quando abbiamo tutti i nostri locali con noi prima di distribuire alla produzione. Ma se voglio chiamare db per stringhe di db, questo tipo di approccio non è corretto. Inoltre lo svantaggio del file di risorse è che richiede di nuovo la compilazione per effettuare le modifiche, ciò significa che è necessario rilasciare un'altra versione sul client. Non lo sto dicendo come un cattivo approccio, per favore non sentirti così
kbvishnu,

Solo un problema: dobbiamo conoscere il tipo di risorsa nel modello. Ho un modello DLL e un sito Web in due progetti distinti. Mi piacerebbe essere in grado di impostare i nomi visualizzati nel modello e impostare il tipo di risorsa nel sito Web ...
Kek,

1
Ottieni Resharper e crea il file di risorse nel tuo progetto, che ricorderà automaticamente e ti aiuterà a spostare il Nome nel file di risorse.
anIBMer

14
Con C # 6, invece di Name = "labelForName"te puoi anche usare Name = nameof(Resources.Resources.labelForName).
Uwe Keim

35

Se si apre il file di risorse e si modifica il modificatore di accesso in pubblico o interno, verrà generata una classe dal file di risorse che consente di creare riferimenti di risorse fortemente tipizzati.

Opzione per la generazione del codice del file di risorse

Ciò significa che invece puoi fare qualcosa del genere (usando C # 6.0). Quindi non devi ricordare se il nome era in minuscolo o camelcas. E puoi vedere se altre proprietà usano lo stesso valore di risorsa con trova tutti i riferimenti.

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

funzionerà con Winforms? Ho una classe che ho aggiunto Visualizza annotazione dalle risorse e ho usato il metodo GetAttributeFrom dal link per ottenere il nome della proprietà ma non mostra quello localizzato!
Dark_Knight

2
Stai usando attributi.Name o attributo.GetName () per ottenere il testo localizzato? La documentazione per .Name dice "Non utilizzare questa proprietà per ottenere il valore della proprietà Name. Utilizzare invece il metodo GetName." msdn.microsoft.com/en-us/library/...
Tikall

Sì, ho capito che dovevo usare GetName (). Grazie
Dark_Knight

20

Aggiornare:

So che è troppo tardi, ma vorrei aggiungere questo aggiornamento:

Sto usando il fornitore di metadati del modello convenzionale, presentato da Phil Haacked , è più potente e facile da applicare, guardalo: ConvenzionaleModelloMetadataProvider


Vecchia risposta

Qui se vuoi supportare molti tipi di risorse:

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

Quindi usalo in questo modo:

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

Per l'esercitazione sulla localizzazione completa, consultare questa pagina .


1
+1. La soluzione di Haack è sicuramente la più elegante rispetto alle altre qui. Si adatta molto bene allo stile di programmazione basato su convenzione in ASP.NET MVC ed è facilmente implementabile attraverso un singolo comando nuget e una singola riga di codice in Global.asax.cs.
Nilzor,

11

Ho ottenuto la risposta di Gunders lavorando con il mio App_GlobalResources scegliendo le proprietà delle risorse e passando "Strumento personalizzato" su "PublicResXFileCodeGenerator" e creando un'azione su "Risorsa integrata". Si prega di osservare il commento di Gunders di seguito.

inserisci qui la descrizione dell'immagine

Funziona come un fascino :)


Ciò si traduce in un file di risorse che verrà compilato come se fosse stato aggiunto un file di risorse al di fuori di App_GlobalResources. Quindi il tuo file di risorse non si comporterà più come un file di risorse "App_GlobalResources", il che va assolutamente bene. Ma dovresti solo esserne consapevole. Quindi non hai più i "vantaggi" di inserire il file di risorse in App_GlobalResources. Potresti anche averlo messo altrove.
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. Definire ResourceType dell'attributo in modo che cerchi una risorsa
  2. Definire il nome dell'attributo utilizzato per la chiave della risorsa, dopo C # 6.0, è possibile utilizzare nameofper il supporto tipizzato forte invece di codificare a fondo la chiave.

  3. Imposta la cultura del thread corrente nel controller.

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

  1. Impostare l'accessibilità della risorsa su pubblico

  2. Visualizza l'etichetta in cshtml in questo modo

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

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.