แอตทริบิวต์ DisplayName จากทรัพยากร?


160

ฉันมีแอปพลิเคชั่นที่แปลเป็นภาษาท้องถิ่นและฉันสงสัยว่าเป็นไปได้หรือไม่ที่จะมีDisplayNameชุดคุณสมบัติของโมเดลจากทรัพยากร

ฉันต้องการทำสิ่งนี้:

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

แต่ฉันทำไม่ได้ในขณะที่คอมไพเลอร์พูดว่า: "อาร์กิวเมนต์ของแอตทริบิวต์ต้องเป็นนิพจน์คงที่, นิพจน์ typeof หรือการสร้างอาเรย์ของประเภทพารามิเตอร์คุณลักษณะ" :(

มีวิธีแก้ไขปัญหาหรือไม่? ฉันกำลังแสดงฉลากด้วยตนเอง แต่ฉันต้องการสิ่งเหล่านี้สำหรับเอาท์พุท validator!

คำตอบ:


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 โพสต์ของเขาด้านล่างนี้ (มีคะแนนมากที่สุด) เป็นทางออกที่ดีกว่ามาก สำหรับคนที่เพียงแค่อ่านโพสต์ที่ได้รับการยอมรับ
321X2

1
สิ่งนี้ใช้ไม่ได้กับการเข้าถึงการแปลที่แตกต่างกันเพราะมันจะคืนค่าที่เหมือนกันสำหรับผู้ใช้ทุกคนโดยไม่มีอะไรเกิดขึ้น เก็บ resourceid ในตัวแปรโลคัลและแทนที่ DisplayName แทน
Fischer

5
คำแนะนำสำหรับสิ่งที่ต้องทำ: return Resources.Language.ResourceManager.GetString (resourceId);
Leonel Sanches da Silva

4
คุณควรใช้: [Display (Name = "labelForName", ResourceType = typeof (Resources.Resources))] ตามที่อธิบายไว้ด้านล่าง ...
Frank Boucher

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. คุณสามารถอ่านข้อมูลเพิ่มเติมเกี่ยวกับสาเหตุที่เป็นกรณีนี้ที่นี่ )


นี่เป็นสิ่งที่ดีสำหรับตัวอย่างเฉพาะที่ใช้ที่นี่ แต่มันจะไม่ทำงานสำหรับผู้ตั้งค่าคุณสมบัติแบบไดนามิกส่วนใหญ่ที่ต้องการสตริง
kingdango

1
นี่เป็นสิ่งที่ดีเมื่อเรามีโลแคลทั้งหมดของเรากับเราก่อนที่จะปรับใช้กับการผลิต แต่ถ้าฉันต้องการเรียก db สำหรับสตริง db วิธีการแบบนี้ไม่เหมาะสม ข้อเสียของไฟล์ทรัพยากรก็คือมันต้องมีการรวบรวมอีกครั้งเพื่อให้เกิดการเปลี่ยนแปลงนั่นหมายความว่าคุณต้องปล่อยเวอร์ชันอื่นไปยังไคลเอนต์ ฉันไม่ได้พูดแบบนี้ว่าเป็นวิธีการที่ไม่ดีโปรดอย่ารู้สึกอย่างนั้น
kbvishnu

แค่มีปัญหา: เราต้องรู้ประเภททรัพยากรในตัวแบบ ฉันมีรุ่น DLL และเว็บไซต์ในโครงการที่แตกต่างกันสองโครงการ ฉันต้องการที่จะสามารถแสดงชื่อชุดในรูปแบบและการตั้งค่าชนิดของทรัพยากรในเว็บไซต์ ...
Kek

1
รับ Resharper และสร้างไฟล์ทรัพยากรในโครงการของคุณและมันจะเตือนโดยอัตโนมัติแล้วช่วยให้คุณย้ายชื่อลงในไฟล์ทรัพยากร
anIBMer

14
ด้วย C # 6 Name = "labelForName"คุณสามารถใช้Name = nameof(Resources.Resources.labelForName)แทนได้
Uwe Keim

35

หากคุณเปิดไฟล์ทรัพยากรของคุณและเปลี่ยนตัวแก้ไขการเข้าถึงเป็นสาธารณะหรือภายในมันจะสร้างคลาสจากไฟล์ทรัพยากรของคุณซึ่งช่วยให้คุณสร้างการอ้างอิงทรัพยากรที่พิมพ์ได้อย่างมาก

ตัวเลือกสำหรับการสร้างรหัสไฟล์ทรัพยากร

ซึ่งหมายความว่าคุณสามารถทำสิ่งนี้แทน (ใช้ C # 6.0) จากนั้นคุณไม่จำเป็นต้องจำไว้ว่าชื่อแรกถูกลดขนาดหรือมีคาเมลอยู่ และคุณสามารถดูว่าคุณสมบัติอื่นใช้ค่าทรัพยากรเดียวกันกับการค้นหาทั้งหมดหรือไม่

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

จะทำงานกับ Winforms นี้ได้หรือไม่ ฉันมีคลาสที่ฉันเพิ่มหมายเหตุประกอบ Display จากทรัพยากรและฉันใช้เมธอด GetAttributeFrom จากลิงก์เพื่อรับชื่อของคุณสมบัติ แต่จะไม่แสดงภาษาที่แปลแล้ว!
Dark_Knight

2
คุณใช้ attribute.Name หรือ attribute.GetName () เพื่อรับข้อความที่แปลแล้วหรือไม่? เอกสารสำหรับ. ชื่อบอกว่า "อย่าใช้คุณสมบัตินี้เพื่อรับค่าของคุณสมบัติชื่อใช้วิธี GetName แทน" msdn.microsoft.com/en-us/library/…
Tikall

ใช่ฉันคิดว่าฉันต้องใช้ GetName () ขอบคุณ
Dark_Knight

20

ปรับปรุง:

ฉันรู้ว่ามันสายเกินไป แต่ฉันต้องการเพิ่มอัปเดตนี้:

ฉันใช้ผู้ให้บริการเมทาดาทารุ่นธรรมดาซึ่งนำเสนอโดยPhil 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
Nilzor

11

ฉันได้รับคำตอบจาก Gunders ที่ทำงานกับ App_GlobalResources ของฉันโดยเลือกคุณสมบัติของทรัพยากรและสลับ "เครื่องมือที่กำหนดเอง" เป็น "PublicResXFileCodeGenerator" และสร้างการกระทำเป็น "ทรัพยากรที่ฝังตัว" โปรดสังเกตความคิดเห็นของ Gunders ด้านล่าง

ป้อนคำอธิบายรูปภาพที่นี่

ทำงานเหมือนมีเสน่ห์ :)


ซึ่งส่งผลให้ไฟล์ทรัพยากรที่จะรวบรวมเช่นเดียวกับถ้าคุณได้เพิ่มไฟล์ทรัพยากรนอก App_GlobalResources ดังนั้นไฟล์ทรัพยากรของคุณจะไม่ทำงานเป็นไฟล์ทรัพยากร "App_GlobalResources" อีกต่อไปซึ่งถือว่าใช้ได้จริง แต่คุณควรตระหนักถึงมัน ดังนั้นคุณจะไม่มี "ประโยชน์" ในการวางไฟล์ทรัพยากรใน App_GlobalResources อีกต่อไป คุณสามารถวางมันไว้ที่อื่นได้เช่นกัน
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. กำหนด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.