Atributo DisplayName de Recursos?

160

Eu tenho um aplicativo localizado e estou me perguntando se é possível ter a DisplayNamepropriedade de um determinado modelo definida em um Recurso.

Eu gostaria de fazer algo assim:

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

Mas não posso, pois o compilador diz: "Um argumento de atributo deve ser uma expressão constante, tipo de expressão ou expressão de criação de matriz de um tipo de parâmetro de atributo" :(

Existem soluções alternativas? Estou produzindo etiquetas manualmente, mas preciso delas para a saída do validador!

Palantir
fonte

Respostas:

112

Que tal escrever um atributo personalizado:

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

que poderia ser usado assim:

public class MyModel 
{
    [Required]
    [LocalizedDisplayName("labelForName")]
    public string Name { get; set; }
}
Darin Dimitrov
fonte
Sim, trabalhei em uma solução semelhante esta manhã e é possível. Então eu encontrei este post que tem a mesma abordagem: adamyan.blogspot.com/2010/02/…
Palantir
23
@Gunder, seu post, abaixo deste (com mais votos), é uma solução muito melhor. Apenas para pessoas que leem apenas as postagens aceitas
321X
1
Na verdade, isso NÃO funciona para acessar traduções diferentes, pois retornará o mesmo valor para todos os usuários, não importa o quê. Armazene resourceid em uma variável local e, em vez disso, substitua DisplayName
Fischer
5
Sugestão para TODO: retornar Resources.Language.ResourceManager.GetString (resourceId);
Leonel Sanches da Silva
4
Você deve usar: [Display (Name = "labelForName", ResourceType = typeof (Resources.Resources))] como descrevemos abaixo ...
Frank Boucher
375

Se você usar o MVC 3 e o .NET 4, poderá usar o novo Displayatributo no System.ComponentModel.DataAnnotationsespaço para nome. Este atributo substitui o DisplayNameatributo e fornece muito mais funcionalidade, incluindo suporte à localização.

No seu caso, você usaria assim:

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

Como uma observação lateral, este atributo não funcionará com recursos internos App_GlobalResourcesou App_LocalResources. Isso tem a ver com a ferramenta personalizada ( GlobalResourceProxyGenerator) usada por esses recursos. Em vez disso, verifique se o arquivo de recurso está definido como 'Recurso incorporado' e use a ferramenta personalizada 'ResXFileCodeGenerator'.

(Como uma observação adicional, você não deve estar usando App_GlobalResourcesou App_LocalResourcescom o MVC. Você pode ler mais sobre por que esse é o caso aqui )

René
fonte
Isso é bom para o exemplo específico usado aqui, mas não funcionará para a maioria dos criadores de propriedades dinâmicos que desejam seqüências de caracteres.
kingdango 6/12/11
1
Isso é bom quando temos todo o nosso local conosco antes de implantarmos na produção. Mas se eu quiser chamar db para db strings, esse tipo de abordagem não é adequada. Também a desvantagem do arquivo de recurso é que ele requer compilação novamente para efetuar as alterações, o que significa que você precisa liberar outra versão para o cliente. Não estou dizendo isso como uma má abordagem, por favor, não me sinto assim
kbvishnu
Apenas um problema: precisamos saber o tipo de recurso no modelo. Eu tenho uma DLL modelo e um site em dois projetos distintos. Eu gostaria de ser capaz de nomes de exibição definidas em modelo e conjunto de tipo de recurso no website ...
Kek
1
Obtenha o novo compartilhador e crie o arquivo de recurso em seu projeto, e ele lembrará automaticamente e ajudará você a mover o Nome para o arquivo de recurso.
AnIBMer 22/05
14
Com o C # 6, em vez de Name = "labelForName"você também pode usar Name = nameof(Resources.Resources.labelForName).
Uwe Keim 31/05
35

Se você abrir o arquivo de recursos e alterar o modificador de acesso para público ou interno, ele gerará uma classe a partir do arquivo de recursos, o que permitirá criar referências de recursos fortemente tipadas.

Opção para geração de código do arquivo de recurso

O que significa que você pode fazer algo assim (usando o C # 6.0). Então você não precisa se lembrar se o primeiro nome estava em minúsculas ou em camelcased. E você pode ver se outras propriedades usam o mesmo valor de recurso com uma localização de todas as referências.

[Display(Name = nameof(PropertyNames.FirstName), ResourceType = typeof(PropertyNames))]
public string FirstName { get; set; }
Tikall
fonte
isso funcionará com o Winforms? Eu tenho uma classe que eu adicionei a anotação Display dos recursos e usei o método GetAttributeFrom do link para obter o nome da propriedade, mas ela não mostra a localizada!
31916 Dark_Knight
2
Você está usando attribute.Name ou attribute.GetName () para obter o texto localizado? A documentação para .Name diz "Não use esta propriedade para obter o valor da propriedade Name. Use o método GetName." msdn.microsoft.com/en-us/library/…
Tikall
Sim, eu descobri que eu tinha que usar GetName (). Graças
Dark_Knight
20

Atualizar:

Sei que é tarde demais, mas gostaria de adicionar esta atualização:

Estou usando o Provedor de Metadados do Modelo Convencional, apresentado por Phil Haacked, que é mais poderoso e fácil de aplicar. Confira: ConventionalModelMetadataProvider


Resposta antiga

Aqui, se você deseja oferecer suporte a muitos tipos de recursos:

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

Em seguida, use-o assim:

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

Para o tutorial completo de localização, consulte esta página .

Wahid Bitar
fonte
1
+1. A solução da Haack é definitivamente a mais elegante em comparação com as outras aqui. Ele se encaixa muito bem no estilo de programação baseado em convenções no ASP.NET MVC e é facilmente implementado através de um único comando nuget e uma única linha de código no Global.asax.cs.
Nilzor
11

Eu recebi a resposta do Gunders trabalhando com meu App_GlobalResources escolhendo as propriedades dos recursos e alterne "Ferramenta Personalizada" para "PublicResXFileCodeGenerator" e crie uma ação para "Recurso Incorporado". Observe o comentário do Gunders abaixo.

insira a descrição da imagem aqui

Funciona como um encanto :)

Magnus Karlsson
fonte
Isso resulta em um arquivo de recurso que será compilado como se você tivesse adicionado um arquivo de recurso fora do App_GlobalResources. Portanto, seu arquivo de recurso não funcionará mais como um arquivo de recurso "App_GlobalResources", o que é absolutamente aceitável. Mas você deve apenas estar ciente disso. Portanto, você não tem mais os "benefícios" de colocar o arquivo de recurso no App_GlobalResources. Você poderia colocá-lo em outro lugar.
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. Defina ResourceType do atributo para procurar um recurso
  2. Defina o Nome do atributo que é usado para a chave do recurso, após o C # 6.0, você pode usar nameofpara suporte de tipo forte em vez de codificar a chave.

  3. Defina a cultura do segmento atual no controlador.

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

  1. Defina a acessibilidade do recurso para público

  2. Exiba o rótulo em cshtml como este

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

code4j
fonte