リソースのDisplayName属性?


160

ローカライズされたアプリケーションがありDisplayName、Resourceから特定のモデルプロパティを設定できるかどうか疑問に思っています。

私はこのようなことをしたいと思います:

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の提案:Resources.Language.ResourceManager.GetString(resourceId);を返します。
Leonel Sanches da Silva 2013

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これらのリソースが使用するカスタムツール()に関係しています。代わりに、リソースファイルが「Embedded resource」に設定されていることを確認し、「ResXFileCodeGenerator」カスタムツールを使用してください。

(補足として、MVC を使用しApp_GlobalResourcesたりApp_LocalResources、使用したりしないでください。これが当てはまる理由については、こちらをご覧ください


これは、ここで使用する特定の例には適していますが、文字列を必要とする大部分の動的プロパティセッターでは機能しません。
kingdango

1
これは、実稼働環境にデプロイする前にすべてのロケールを持っている場合に適しています。しかし、db文字列に対してdbを呼び出したい場合、この種のアプローチは適切ではありません。また、リソースファイルの欠点は、変更を有効にするために再度コンパイルする必要があることです。つまり、別のバージョンをクライアントにリリースする必要があります。私はこれを悪いアプローチとして言っているのではありません、そのように感じないでください
kbvishnu

ただ問題です。モデルのリソースタイプを知る必要があります。2つの異なるプロジェクトにモデルDLLとWebサイトがあります。モデルに表示名を設定し、Webサイトにリソースタイプを設定できるようにしたい...
Kek

1
Resharperを入手し、プロジェクトにリソースファイルを作成すると、自動的に通知され、名前をリソースファイルに移動するのに役立ちます。
IBMer 2013年

14
C#6では、代わりにName = "labelForName"も使用できますName = nameof(Resources.Resources.labelForName)
Uwe Keim、2016年

35

リソースファイルを開いてアクセス修飾子をpublicまたはinternalに変更すると、リソースファイルからクラスが生成され、厳密に型指定されたリソース参照を作成できます。

リソースファイルコード生成のオプション

つまり、代わりに(C#6.0を使用して)このようなことを行うことができます。次に、名が小文字かキャメルケースかを覚えておく必要はありません。また、他のプロパティがすべての参照を検索して同じリソース値を使用しているかどうかを確認できます。

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

これはWinformsで動作しますか?リソースからDisplayアノテーションを追加したクラスがあり、リンクからGetAttributeFromメソッドを使用してプロパティの名前を取得しましたが、ローカライズされた名前は表示されません!
Dark_Knight 2016

2
ローカライズされたテキストを取得するために、attribute.Nameまたはattribute.GetName()を使用していますか?.Nameのドキュメントには、「このプロパティを使用してNameプロパティの値を取得しないでください。代わりにGetNameメソッドを使用してください。」と書かれています。 msdn.microsoft.com/en-us/library/...
Tikall

はい、GetName()を使用する必要があることがわかりました。ありがとう
Dark_Knight 2016

20

更新:

手遅れであることがわかっていますが、この更新を追加したいと思います。

私が使用している従来のモデルメタデータプロバイダによって提示されたフィル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。Haackのソリューションは、他のソリューションに比べて間違いなく最もエレガントなソリューションです。ASP.NET MVCのコンベンションベースのプログラミングスタイルに非常によく適合し、単一のnugetコマンドとGlobal.asax.csの1行のコードで簡単に実装できます。
Nilzor

11

リソースのプロパティを選択し、「カスタムツール」を「PublicResXFileCodeGenerator」に切り替えて、アクションを「Embedded Resource」に切り替えることで、GundersがApp_GlobalResourcesで動作するように回答しました。以下の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)

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.