Локализация DisplayNameAttribute

119
задан serhio 11 June 2010 в 09:39
поделиться

6 ответов

Вот решение, с которым я закончил в отдельном собрании (названный «Распространенным» в моем случае):

   [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Event)]
   public class DisplayNameLocalizedAttribute : DisplayNameAttribute
   {
      public DisplayNameLocalizedAttribute(Type resourceManagerProvider, string resourceKey)
         : base(Utils.LookupResource(resourceManagerProvider, resourceKey))
      {
      }
   }

с кодексом, чтобы искать ресурс:

  internal static string LookupResource(Type resourceManagerProvider, string resourceKey)
  {
     foreach (PropertyInfo staticProperty in  resourceManagerProvider.GetProperties(BindingFlags.Static | BindingFlags.NonPublic))
     {
        if (staticProperty.PropertyType == typeof(System.Resources.ResourceManager))
        {
           System.Resources.ResourceManager resourceManager = (System.Resources.ResourceManager)staticProperty.GetValue(null, null);
           return resourceManager.GetString(resourceKey);
        }
     }

     return resourceKey; // Fallback with the key name
  }

Типичное использование было бы:

class Foo
{
      [Common.DisplayNameLocalized(typeof(Resources.Resource), "CreationDateDisplayName"),
      Common.DescriptionLocalized(typeof(Resources.Resource), "CreationDateDescription")]
      public DateTime CreationDate
      {
         get;
         set;
      }
}

, Что в значительной степени ужасно, поскольку я использую буквальные последовательности для ключа ресурса. Используя константу там означал бы изменять Ресурсы. Designer.cs, который является, вероятно, не хорошей идеей.

Заключение: Я не доволен этим, но я еще менее доволен Microsoft, которая не может обеспечить ничего полезного для такой общей задачи.

41
ответ дан PowerKiKi 24 November 2019 в 01:47
поделиться

Мы делаем это для многих атрибутов для поддержки нескольких язык. Мы проявили аналогичный подход к Microsoft, где они переопределяют свои основные атрибуты и передают имя ресурса, а не фактическую строку. Имя ресурса тогда используется для выполнения поиска в ресурсах DLL для фактической строки для возврата.

, Например:

class LocalizedDisplayNameAttribute : DisplayNameAttribute
{
    private readonly string resourceName;
    public LocalizedDisplayNameAttribute(string resourceName)
        : base()
    {
      this.resourceName = resourceName;
    }

    public override string DisplayName
    {
        get
        {
            return Resources.ResourceManager.GetString(this.resourceName);
        }
    }
}

можно взять это шаг вперед, на самом деле использование атрибута, и определяет имена ресурса как константы в статическом классе. Тем путем Вы получаете объявления как.

[LocalizedDisplayName(ResourceStrings.MyPropertyName)]
public string MyProperty
{
  get
  {
    ...
  }
}

Обновление
ResourceStrings посмотрело бы что-то как (примечание, каждая строка будет относиться к названию ресурса, который определяет фактическую строку):

public static class ResourceStrings
{
    public const string ForegroundColorDisplayName="ForegroundColorDisplayName";
    public const string FontSizeDisplayName="FontSizeDisplayName";
}
79
ответ дан serhio 24 November 2019 в 01:47
поделиться

Можно разделить DisplayNameAttribute на подклассы для обеспечения i18n путем переопределения одного из методов. Как так. редактирование: Вам, возможно, придется согласиться на использование константы для ключа.

using System;
using System.ComponentModel;
using System.Windows.Forms;

class Foo {
    [MyDisplayName("bar")] // perhaps use a constant: SomeType.SomeResName
    public string Bar {get; set; }
}

public class MyDisplayNameAttribute : DisplayNameAttribute {
    public MyDisplayNameAttribute(string key) : base(Lookup(key)) {}

    static string Lookup(string key) {
        try {
            // get from your resx or whatever
            return "le bar";
        } catch {
            return key; // fallback
        }
    }
}

class Program {
    [STAThread]
    static void Main() {
        Application.Run(new Form { Controls = {
            new PropertyGrid { SelectedObject =
                new Foo { Bar = "abc" } } } });
    }
}
6
ответ дан Marc Gravell 24 November 2019 в 01:47
поделиться

Ну, блок Microsoft.VisualStudio.Modeling.Sdk.dll. который идет с SDK Visual Studio (С Пакетом Интеграции Visual Studio).

, Но это использовалось бы в значительной степени тем же способом в качестве Вашего атрибута; нет никакого способа использовать сильно ресурсы типов в атрибутах просто, потому что они не являются постоянными.

1
ответ дан configurator 24 November 2019 в 01:47
поделиться

Я приношу извинения за код VB.NET, мой C# немного ржав... Но Вы получите идею, правильно?

, В первую очередь, создайте новый класс: LocalizedPropertyDescriptor, который наследовался PropertyDescriptor. Переопределите DisplayName свойство как это:

Public Overrides ReadOnly Property DisplayName() As String
         Get
            Dim BaseValue As String = MyBase.DisplayName
            Dim Translated As String = Some.ResourceManager.GetString(BaseValue)
            If String.IsNullOrEmpty(Translated) Then
               Return MyBase.DisplayName
            Else
               Return Translated
           End If
    End Get
End Property

Some.ResourceManager ResourceManager файла ресурсов, который содержит Ваши переводы.

Затем, реализация ICustomTypeDescriptor в классе с локализованными свойствами и переопределении GetProperties метод:

Public Function GetProperties() As PropertyDescriptorCollection Implements System.ComponentModel.ICustomTypeDescriptor.GetProperties
    Dim baseProps As PropertyDescriptorCollection = TypeDescriptor.GetProperties(Me, True)
    Dim LocalizedProps As PropertyDescriptorCollection = New PropertyDescriptorCollection(Nothing)

    Dim oProp As PropertyDescriptor
    For Each oProp In baseProps
        LocalizedProps.Add(New LocalizedPropertyDescriptor(oProp))
    Next
    Return LocalizedProps
End Function

можно теперь использовать атрибут 'DisplayName' для хранения ссылки на значение в файле ресурсов...

<DisplayName("prop_description")> _
Public Property Description() As String

prop_description ключ в файле ресурсов.

0
ответ дан Vincent Van Den Berghe 24 November 2019 в 01:47
поделиться

Вы можете использовать T4 для генерации констант. Я написал один:

<#@ template debug="false" hostspecific="true" language="C#" #>
<#@ output extension=".cs" #>
<#@ assembly name="System.Xml.dll" #>
<#@ import namespace="System.Xml" #>
<#@ import namespace="System.Xml.XPath" #>
using System;
using System.ComponentModel;


namespace Bear.Client
{
 /// <summary>
 /// Localized display name attribute
 /// </summary>
 public class LocalizedDisplayNameAttribute : DisplayNameAttribute
 {
  readonly string _resourceName;

  /// <summary>
  /// Initializes a new instance of the <see cref="LocalizedDisplayNameAttribute"/> class.
  /// </summary>
  /// <param name="resourceName">Name of the resource.</param>
  public LocalizedDisplayNameAttribute(string resourceName)
   : base()
  {
   _resourceName = resourceName;
  }

  /// <summary>
  /// Gets the display name for a property, event, or public void method that takes no arguments stored in this attribute.
  /// </summary>
  /// <value></value>
  /// <returns>
  /// The display name.
  /// </returns>
  public override String DisplayName
  {
   get
   {
    return Resources.ResourceManager.GetString(this._resourceName);
   }
  }
 }

 partial class Constants
 {
  public partial class Resources
  {
  <# 
   var reader = XmlReader.Create(Host.ResolvePath("resources.resx"));
   var document = new XPathDocument(reader);
   var navigator = document.CreateNavigator();
   var dataNav = navigator.Select("/root/data");
   foreach (XPathNavigator item in dataNav)
   {
    var name = item.GetAttribute("name", String.Empty);
  #>
   public const String <#= name#> = "<#= name#>";
  <# } #>
  }
 }
}
14
ответ дан 24 November 2019 в 01:47
поделиться
Другие вопросы по тегам:

Похожие вопросы: