複数のオプションが可能なListBoxFor
のenumをどのようにローカライズしますか?
たとえば、ロールを含むenum
は次のとおりです。
public enum RoleType
{
[Display(Description = "Administrator", ResourceType = typeof(Resource))]
Administrator = 1,
[Display(Description = "Moderator", ResourceType = typeof(Resource))]
Moderator = 2,
[Display(Description = "Webmaster", ResourceType = typeof(Resource))]
Webmaster = 3,
[Display(Description = "Guest", ResourceType = typeof(Resource))]
Guest = 4,
Etc.... = 5,
}
これはdropdownlist
/selectlists
で行われました。しかし、複数選択リストに対してこれを行う方法はありますか?
[編集]
これは私がそれをどのように使いたいかです、それは今それがどのように動作するかですが、異なる言語に翻訳されません:
var roles = from role r in Enum.GetValues(typeof(RoleType))
select new
{
Id = (int)Enum.Parse(typeof(RoleType), r.ToString()),
Name = r.ToString()
};
searchModel.roles = new MultiSelectList(roles, "Id", "Name");
注:列挙の名前をRoleからRoleTypeに変更しました。
説明属性を実装できます。
public class LocalizedDescriptionAttribute : DescriptionAttribute
{
private readonly string _resourceKey;
private readonly ResourceManager _resource;
public LocalizedDescriptionAttribute(string resourceKey, Type resourceType)
{
_resource = new ResourceManager(resourceType);
_resourceKey = resourceKey;
}
public override string Description
{
get
{
string displayName = _resource.GetString(_resourceKey);
return string.IsNullOrEmpty(displayName)
? string.Format("[[{0}]]", _resourceKey)
: displayName;
}
}
}
public static class EnumExtensions
{
public static string GetDescription(this Enum enumValue)
{
FieldInfo fi = enumValue.GetType().GetField(enumValue.ToString());
DescriptionAttribute[] attributes =
(DescriptionAttribute[])fi.GetCustomAttributes(
typeof(DescriptionAttribute),
false);
if (attributes != null &&
attributes.Length > 0)
return attributes[0].Description;
else
return enumValue.ToString();
}
}
次のように定義します。
public enum Roles
{
[LocalizedDescription("Administrator", typeof(Resource))]
Administrator,
...
}
そして、次のように使用します:
var roles = from RoleType role in Enum.GetValues(typeof(RoleType))
select new
{
Id = (int)role,
Name = role.GetDescription()
};
searchModel.roles = new MultiSelectList(roles, "Id", "Name");
ビューで使用するEnumExtensionを作成することで問題を解決しました。この拡張機能は、「EnumResources.resx」というリソースファイルを検索し、次の命名規則{Name of EnumType} _ {Value of enum Passed in}に従ってリソースを検索します。リソースキーがない場合、リソースの値が二重角括弧[[EnumValue]]内にカプセル化されて表示されます。これにより、ビューで「未翻訳」の列挙を簡単に見つけることができます。また、これは、名前の変更などの後にリソースファイルを更新するのを忘れた場合に思い出させるのに役立ちます。
public static class EnumExtensions
{
public static string GetDisplayName(this Enum e)
{
var rm = new ResourceManager(typeof (EnumResources));
var resourceDisplayName = rm.GetString(e.GetType().Name + "_" + e);
return string.IsNullOrWhiteSpace(resourceDisplayName) ? string.Format("[[{0}]]", e) : resourceDisplayName;
}
}
リソースファイルは次のようになります。
使用法:
<div>@ContractStatus.Created.GetDisplayName()</div>
属性を使用して、表示するときに列挙に使用する文字列を指定する方法がありますが、ローカライズを処理する必要がある場合、あまりにも手間がかかりすぎることがわかりました。
したがって、ローカライズが必要な列挙型に対して通常行うことは、翻訳された名前を取得するメソッドを提供する拡張クラスを作成することです。通常のリソースから文字列を返すスイッチを使用するだけです。そのようにして、他の文字列の場合と同様に、リソースを介して列挙型の翻訳された文字列を提供します。
例えば:
public enum Role
{
Administrator,
Moderator,
Webmaster,
Guest
}
public static class RoleExt
{
public static string AsDisplayString(this Role role)
{
switch (role)
{
case Role.Administrator: return Resources.RoleAdministrator;
case Role.Moderator: return Resources.RoleModerator;
case Role.Webmaster: return Resources.RoleWebmaster;
case Role.Guest: return Resources.RoleGuest;
default: throw new ArgumentOutOfRangeException("role");
}
}
}
次のように使用できます:
var role = Role.Administrator;
Console.WriteLine(role.AsDisplayString());
enum Role
実装の隣にRoleExt
クラスの実装を保持すると、Role
のインターフェースの一部になります。もちろん、このクラスにenumの他の便利な拡張機能を追加することもできます。
[編集]
複数のフラグ設定(「管理者ANDモデレーターANDウェブマスター」)を処理する場合は、少し異なる方法で処理する必要があります。
[Flags]
public enum Roles
{
None = 0,
Administrator = 1,
Moderator = 2,
Webmaster = 4,
Guest = 8
}
public static class RolesExt
{
public static string AsDisplayString(this Roles roles)
{
if (roles == 0)
return Resources.RoleNone;
var result = new StringBuilder();
if ((roles & Roles.Administrator) != 0)
result.Append(Resources.RoleAdministrator + " ");
if ((roles & Roles.Moderator) != 0)
result.Append(Resources.RoleModerator + " ");
if ((roles & Roles.Webmaster) != 0)
result.Append(Resources.RoleWebmaster + " ");
if ((roles & Roles.Guest) != 0)
result.Append(Resources.RoleGuest + " ");
return result.ToString().TrimEnd();
}
}
次のように使用できます:
Roles roles = Roles.Administrator | Roles.Guest | Roles.Moderator;
Console.WriteLine(roles.AsDisplayString());
リソースファイル
リソースファイルは、文字列を国際化する方法です。それらの使用方法の詳細については、こちらを参照してください。
http://msdn.Microsoft.com/en-us/library/vstudio/aa992030%28v=vs.100%29.aspxhttp://msdn.Microsoft.com/ en-us/library/vstudio/756hydy4%28v = vs.100%29.aspx
列挙値を使用して、必要な処理を実行できます。
public enum Roles
{
Administrator = 0,
Moderator = 1 ,
Webmaster = 2,
Guest = 3 ,
Etc.... = 4
}
リストボックスで選択された列挙型を取得する場合は、リストボックスアイテムを取得してから、関連する列挙型番号を取得します。
次に、これを次のような列挙型アイテムに変換します
Roles myrol = (Roles) i
(i
はこの例ではint valeに関連付けられています)
列挙型アイテムを整数に変換し、整数値を列挙型アイテムに戻す
Enum Item to Integer-----
int i = (int)Roles.Admin ;
Integer to enum Itenm
Roles r = (Roles)i ;
//Getting the name of the enum
string role = Role.Admin.ToString()
ハッシュテーブルに追加する場合は、この方法で行うことができます
Hashtable h = new Hashtable() ;
h.Items.Add((int)Roles.Admin , Roles.Admin.ToStrinng() ) ;
h.Items.Add((int)Roles.Local , Roles.Local.ToStrinng() ) ;
hashtableからアイテムを選択すると、それをEnumアイテムに変換し直して、必要な場所で使用します。同じ方法を使用して、Datatables/Comboboxes、ドロップダウンリストなどを作成できます。
列挙型の翻訳/ローカライズに関する問題の1つは、表示するために翻訳する必要があるだけでなく、翻訳を列挙値に解析し直す必要があることです。次のC#ファイルには、列挙型の双方向変換の問題をどのように克服したかが含まれています。過剰なコメントはご容赦ください。しかし、私は自分の作品についてかなり冗長になります。
//
// EnumExtensions.cs
//
using System;
using System.Collections.Generic;
namespace EnumExtensionsLibrary
{
/// <summary>
/// The functions in this class use the localized strings in the resources
/// to translate the enum value when output to the UI and reverse--
/// translate when receiving input from the UI to store as the actual
/// enum value.
/// </summary>
///
/// <Note>
/// Some of the exported functions assume that the ParserEnumLocalizationHolder
/// and ToStringEnumLocalizationHolder dictionaries (maps) may contain the enum
/// types since they callthe Initialize methods with the input type before executing.
/// </Note>
public static class EnumExtensions
{
#region Exported methods
/// <summary>
/// Save resource from calling project so that we can look up enums as needed.
/// </summary>
/// <param name="resourceManager">Where we fish the translated strings from</param>
/// <remarks>
/// We do not have access to all of the resources from the other projects directly,
/// so they must be loaded from the code from within the project.
/// </remarks>
public static void RegisterResource(System.Resources.ResourceManager resourceManager)
{
if (!MapOfResourceManagers.Contains(resourceManager))
MapOfResourceManagers.Add(resourceManager);
}
/// <summary>
/// Parses the localized string value of the enum by mapping it
/// to the saved enum value
/// </summary>
/// <remarks>
/// In some cases, string for enums in the applications may not be translated to the
/// localized version (usually when the program presets parameters). If the enumMap
/// doesn't contain the value string, we call to Enum.Parse() to handle the conversion
/// or throw an exception.
/// </remarks>
/// <typeparam name="T"></typeparam>
/// <param name="value"></param>
/// <exception cref="ArgumentNullException"> enumType or value is null.</exception>
/// <exception cref="ArgumentException"> enumType is not an Enum. value is either an
/// empty string or only contains white space, value is a name, but not one of the
/// named constants defined for the enumeration.</exception>
/// <exception cref="ArgumentNullException">enumType or value is null.</exception>
/// <returns>
/// The enum value that matched the input string if found. If not found, we call
/// Enum.Parse to handle the value.
/// </returns>
public static T ParseEnum<T>(this string value) where T : struct
{
ParserInitialize(typeof(T));
var enumMap = ParserEnumLocalizationHolder[typeof(T)];
if (enumMap.ContainsKey(value))
return (T) enumMap[value];
return (T)Enum.Parse(typeof(T), value);
}
/// <summary>
/// Parses the localized string value of the enum by mapping it
/// to the saved enum value.
/// </summary>
/// <remarks>
/// In some cases, string for enums in the applications may not be translated to the
/// localized version (usually when the program presets parameters). If the enumMap
/// doesn't contain the value string, we call to Enum.TryParse() to handle the
/// conversion. and return.
/// </remarks>
/// <typeparam name="T"></typeparam>
/// <param name="value"></param>
/// <param name="result"></param>
/// <returns>
/// Returns true if the enum mapping contains the localized string value and the data
/// in the returned result parameter will be a valid value of that enum type. if the
/// string value is not mapped, then calls Enum.TryParse to handle the conversion and
/// return result.
/// </returns>
public static bool TryParseEnum<T>(this string value, out T result) where T : struct
{
ParserInitialize(typeof(T));
var enumMap = ParserEnumLocalizationHolder[typeof(T)];
if (!enumMap.ContainsKey(value))
return Enum.TryParse(value, out result);
result = (T)enumMap[value];
return true;
}
/// <summary>
/// Converts the enum value to a localized string.
/// </summary>
/// <typeparam name="T">must be an enum to work</typeparam>
/// <param name="value">is an enum</param>
/// <returns>
/// The localized string equivalent of the input enum value
/// </returns>
public static string EnumToString<T>(this T value) where T : struct
{
ToStringInitialize(typeof(T));
var toStringMap = ToStringEnumLocalizationHolder[typeof(T)];
return toStringMap.ContainsKey(value) ? toStringMap[value] : value.ToString();
//return EnumDescription(value);
}
/// <summary>
/// Gathers all of the localized translations for each
/// value of the input enum type into an array
/// </summary>
/// <remarks>
/// The return array from Type.GetEnumValues(), the array elements are sorted by
/// the binary values (that is, the unsigned values) of the enumeration constants.
/// </remarks>
/// <param name="enumType"></param>
/// <exception cref="ArgumentException"> The current type is not an enumeration.</exception>
/// <returns>
/// A string array with the localized strings representing
/// each of the values of the input enumType.
/// </returns>
public static string[] AllDescription(this Type enumType)
{
ToStringInitialize(enumType);
var descriptions = new List<string>();
var values = enumType.GetEnumValues();
var toStringMap = ToStringEnumLocalizationHolder[enumType];
foreach (var value in values)
{
descriptions.Add(toStringMap.ContainsKey(value) ? toStringMap[value] : value.ToString());
}
return descriptions.ToArray();
}
#endregion
#region Helper methods
/// <summary>
/// Translates an enum value into its localized string equivalent
/// </summary>
/// <remarks>
/// This assumes that the "name" for the localized string in the
/// resources will look like "enum-type-name""value". For example,
/// if I have an enum setup as:
///
/// enum Days {Sat, Sun, Mon, Tue, Wed, Thu, Fri};
///
/// the value "Sun" in the enum must have the name: "DaysSun"
/// in the resources. The localized (translated) string will
/// be in the value field. E.g.,
///
/// <data name="DaysSun" xml:space="preserve">
/// <value>Sunday</value>
/// </data>
///
/// 2nd note: there may be multiple resources to pull from.
/// Will look in each resource until we find a match or
/// return null.
/// </remarks>
/// <typeparam name="T"></typeparam>
/// <param name="enumType">the enum type</param>
/// <param name="value">the specific enum value</param>
/// <returns>
/// If the enum value is found in the resources, then return
/// that string. If not, then return null.
/// </returns>
private static string LocalEnumDescription<T>(Type enumType, T value)
{
foreach (var resourceManager in MapOfResourceManagers)
{
// The following live line uses string interpolation to perform:
//var rk = string.Format("{0}{1}", enumType.Name, value);
var rk = $"{enumType.Name}{value}";
// Given the above string formatting/interpolation, neither the enum.Name
// nor the value will have a '.' so we do not have to remove it.
var result = resourceManager.GetString(rk);
if (!string.IsNullOrEmpty(result))
return result;
}
return null;
}
/// <summary>
/// Initializes the mapping of the enum type to its mapping of localized strings to
/// the enum's values.
/// </summary>
/// <remarks>
/// The reason for each enum type to have a mapping from the localized string back
/// to its values is for ParseEnum and TryParseEnum to quickly return a value rather
/// than doing a lengthy loop comparing each value in the resources.
///
/// Also, we only map the corresponding resource string if it exists in the resources.
/// If not in the resources, then we call the Enum methods Parse() and TryParse() to
/// figure the results and throw the appropriate exception as needed.
/// </remarks>
///
/// <param name="enumType"></param>
private static void ParserInitialize(Type enumType)
{
if (!ParserEnumLocalizationHolder.ContainsKey(enumType))
{
var values = enumType.GetEnumValues(); // See remark for AllDescription().
var enumMap = new Dictionary<string, object>();
foreach (var value in values)
{
var description = LocalEnumDescription(enumType, value);
if (description != null)
enumMap[description] = value;
}
ParserEnumLocalizationHolder[enumType] = enumMap;
}
}
/// <summary>
/// Initializes the mapping of the enum type to its mapping of the enum's values
/// to their localized strings.
/// </summary>
/// <remarks>
/// The reason for each enum type to have a mapping from the localized string to its
/// values is for AllDescription and EnumToString to quickly return a value rather
/// than doing a lengthy loop runing through each of the resources.
///
/// Also, we only map the corresponding resource string if it exists in the resources.
/// See the EnumDescription method for more information.
/// </remarks>
///
/// <param name="enumType"></param>
private static void ToStringInitialize(Type enumType)
{
if (!ToStringEnumLocalizationHolder.ContainsKey(enumType))
{
var values = enumType.GetEnumValues(); // See remark for AllDescription().
var enumMap = new Dictionary<object, string>();
foreach (var value in values)
{
var description = LocalEnumDescription(enumType, value);
if (description != null)
enumMap[value] = description;
}
ToStringEnumLocalizationHolder[enumType] = enumMap;
}
}
#endregion
#region Data
private static readonly List<System.Resources.ResourceManager> MapOfResourceManagers =
new List<System.Resources.ResourceManager>();
private static readonly Dictionary<Type, Dictionary<string, object>> ParserEnumLocalizationHolder =
new Dictionary<Type, Dictionary<string, object>>();
private static readonly Dictionary<Type, Dictionary<object, string>> ToStringEnumLocalizationHolder =
new Dictionary<Type, Dictionary<object, string>>();
#endregion
}
}
これには、各列挙値の前に属性は必要ありませんが、リソース内の翻訳された列挙文字列の名前属性は、enum-type-nameと列挙値の連結になるようにフォーマットする必要があります。詳細については、LocalEnumDescriptionメソッドの上のコメントを参照してください。さらに、列挙値に遭遇するたびに翻訳を検索する必要がないように翻訳をマッピングすることにより、列挙の翻訳(前方および後方の両方)を保存します。
うまくいけば、理解して使用するのに十分簡単です。
以下の拡張方法は私にとってはうまくいきました。
public static string GetDisplayValue(this Enum value)
{
try
{
var fieldInfo = value.GetType().GetField(value.ToString());
var descriptionAttributes = fieldInfo.GetCustomAttributes(typeof(DisplayAttribute), false) as DisplayAttribute[];
if (descriptionAttributes == null || descriptionAttributes.Length == 0) return value.ToString();
if (descriptionAttributes[0].ResourceType != null)
{
var resource = descriptionAttributes[0].ResourceType.GetProperty("ResourceManager").GetValue(null) as ResourceManager;
return resource.GetString(descriptionAttributes[0].Name);
}
else
{
return descriptionAttributes[0].Name;
}
}
catch
{
return value.ToString();
}
}
私は助けます。
@ eluxen's answer のバージョンは、特定のポータブル(PCL)ライブラリ(特にProfile47
)元のソリューションは機能しません。 2つの問題に対処しました:DescriptionAttributeはポータブルライブラリでは使用できません。@ Jitendra Pancholiによって報告された「リソースが見つかりませんでした」というエラーが解決されました。
public class LocalizedDescriptionAttribute : Attribute
{
private readonly string _resourceKey;
private readonly Type _resourceType;
public LocalizedDescriptionAttribute(string resourceKey, Type resourceType)
{
_resourceType = resourceType;
_resourceKey = resourceKey;
}
public string Description
{
get
{
string displayName = String.Empty;
ResourceManager resMan = _resourceType.GetProperty(
@"ResourceManager", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).GetValue(null, null) as ResourceManager;
CultureInfo culture = _resourceType.GetProperty(
@"Culture", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).GetValue(null, null) as CultureInfo;
if (resMan != null)
{
displayName = resMan.GetString(_resourceKey, culture);
}
var ret = string.IsNullOrEmpty(displayName) ? string.Format("[[{0}]]", _resourceKey) : displayName;
return ret;
}
}
}
使用法については、 元の回答 を参照してください。問題が発生していない場合、リフレクションによる回避策が含まれていないため、元の回答を引き続き使用します
さらに別の可能性は、global display name storageをEnumクラスのクラス拡張として作成することです:
// Enum display names
public static class EnumDisplayNames {
// Display name storage
private static Dictionary<Type, Dictionary<Enum, String>> s_Names =
new Dictionary<Type, Dictionary<Enum, String>>();
// Display name for the single enum's option
private static String CoreAsDisplayName(Enum value) {
Dictionary<Enum, String> dict = null;
if (s_Names.TryGetValue(value.GetType(), out dict)) {
String result = null;
if (dict.TryGetValue(value, out result))
return result;
else
return Enum.GetName(value.GetType(), value);
}
else
return Enum.GetName(value.GetType(), value);
}
// Register new display name
public static void RegisterDisplayName(this Enum value, String name) {
Dictionary<Enum, String> dict = null;
if (!s_Names.TryGetValue(value.GetType(), out dict)) {
dict = new Dictionary<Enum, String>();
s_Names.Add(value.GetType(), dict);
}
if (dict.ContainsKey(value))
dict[value] = name;
else
dict.Add(value, name);
}
// Get display name
public static String AsDisplayName(this Enum value) {
Type tp = value.GetType();
// If enum hasn't Flags attribute, just put vaue's name
if (Object.ReferenceEquals(null, Attribute.GetCustomAttribute(tp, typeof(FlagsAttribute))))
return CoreAsDisplayName(value);
// If enum has Flags attribute, enumerate all set options
Array items = Enum.GetValues(tp);
StringBuilder Sb = new StringBuilder();
foreach (var it in items) {
Enum item = (Enum) it;
if (Object.Equals(item, Enum.ToObject(tp, 0)))
continue;
if (value.HasFlag(item)) {
if (Sb.Length > 0)
Sb.Append(", ");
Sb.Append(CoreAsDisplayName(item));
}
}
Sb.Insert(0, '[');
Sb.Append(']');
return Sb.ToString();
}
}
たとえば、次のような使用方法は非常に簡単です。
public enum TestEnum {
None,
One,
Two,
Three
}
[Flags]
public enum TestOptions {
None = 0,
One = 1,
Two = 2,
Three = 4
}
...
// Let them be in German (for demonstration only)...
TestEnum.None.RegisterDisplayName("Nichts");
TestEnum.One.RegisterDisplayName("Eins");
TestEnum.Two.RegisterDisplayName("Zwei");
TestEnum.Three.RegisterDisplayName("Drei");
// Usually, you obtain display names from resources:
// TestEnum.None.RegisterDisplayName(Resources.NoneName);
// ...
TestOptions.None.RegisterDisplayName("-");
TestOptions.One.RegisterDisplayName("bit 0 set");
TestOptions.Two.RegisterDisplayName("bit 1 set");
TestOptions.Three.RegisterDisplayName("bit 2 set");
TestOptions.Four.RegisterDisplayName("bit 3 set");
...
TestEnum v = TestEnum.Two;
String result = v.AsDisplayName(); // <- "Zwei"
TestOptions o = TestOptions.One | TestOptions.Three | TestOptions.Four;
String result2 = o.AsDisplayName(); // <- "[bit 0 set, bit 2 set, bit 3 set]"
public enum RoleEnum
{
Administrator = 4,
Official = 1,
Trader = 3,
HeadOfOffice = 2
}
public static class RoleEnumExtension
{
private static readonly ResourceManager Resource =
new ResourceManager("Project.CommonResource", typeof(CommonResource).Assembly);
public static string Display(this RoleEnum role)
{
return Resource.GetString("RoleType_" + role);
}
}
これを次のように使用できます
RoleEnum.Administrator.Display()
これが誰かに役立つことを願って
LocalizedDescriptionAttributeを使用した上記のソリューションは、プログラムの現在の言語からそれを読み取りました。クライアントに最適ですが、言語をパラメーターとして渡したい場合のサーバー側にはそれほど柔軟性がありません。
そこで、別のメソッドを追加して、LocalizedDescriptionAttributeでeluxenソリューションを拡張しました。
/// <summary>
/// Gets the description, stored in this attribute, reading from the resource using the cultureInfo defined by the language!
/// </summary>
/// <param name="language">The language.</param>
/// <returns>Description for the given language if found; the default Description or ressourceKey otherwise</returns>
public string GetDescription(string language)
{
return resource.GetStringFromResourceForLanguage(resourceKey, language, Description);
}
public static string GetStringFromResourceForLanguage(this ResourceManager resourceManager, string resourceKey, string language, string defaultValue = null)
{
if (string.IsNullOrEmpty(defaultValue))
defaultValue = resourceKey;
try
{
CultureInfo culture = CultureInfo.GetCultureInfo(language);
string displayName = resourceManager.GetString(resourceKey, culture);
return !string.IsNullOrEmpty(displayName) ? displayName : defaultValue;
}
catch // any, not only CultureNotFoundException
{
return defaultValue;
}
}
GetDescription拡張機能と言語パラメーターも使用する場合:
bool hasLanguage = !string.IsNullOrEmpty(language);
if (hasLanguage)
{
var attribute = (LocalizedDescriptionAttribute)Attribute.GetCustomAttribute(fieldInfo, typeof(LocalizedDescriptionAttribute));
if (attribute != null)
{
description = attribute.GetDescription(language);
}
else
hasLanguage = false;
}
if (!hasLanguage)
{
var attribute = (DescriptionAttribute)Attribute.GetCustomAttribute(fieldInfo, typeof(DescriptionAttribute));
if (attribute != null)
{
description = attribute.Description;
}
}
そして最終的に、nameofを使用して、属性の使用法で文字列を避けることを好みます。
public enum QualityPersonInfo
{
Ok = 0,
[LocalizedDescription(nameof(QualityStrings.P1), typeof(QualityStrings))]
Duplicate = 1,
[LocalizedDescription(nameof(QualityStrings.P2), typeof(QualityStrings))]
IdMissing = 2,
}
次のようにコードで使用します:
QualityPersonInfo status = QualityPersonInfo.IdMissing;
var description = status.GetDescription("ch-DE");
私は現在これを使用していますが、それが役立つことを願っています!:
/// <summary>
/// Retrieves a translated value from an enumerated list.
/// </summary>
/// <param name="value">Enum</param>
/// <param name="resource">string</param>
/// <returns>string</returns>
protected string GetTranslatedEnum(Enum value, string resource)
{
string path = String.Format("Resources.{0}", resource);
ResourceManager resources = new ResourceManager(path, global::System.Reflection.Assembly.Load("App_GlobalResources"));
if (resources != null) {
return resources.GetString(value.ToString());
} else {
return value.ToString();
}
}
「App_GlobalResources\ProductNames.resx」という名前の.resxファイルを作成しました。
使用法:
// Convert the ProductId integer on the item to its Enum equivalent.
Products product = (Products) item.ProductId;
string productName = this.GetTranslatedEnum(product, "ProductNames");
受け入れられた回答と同じ回答ですが、コード分析の警告はありません
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1019:DefineAccessorsForAttributeArguments", Justification ="Resourcetype is only needed to instantiate Resource manager, and ResourceManager is exposed")]
[AttributeUsage(AttributeTargets.All)]
public sealed class LocalizedDescriptionAttribute
: DescriptionAttribute
{
private readonly string _resourceKey;
private readonly ResourceManager _resourceManager;
public LocalizedDescriptionAttribute(string resourceKey, Type resourceType)
{
_resourceManager = new ResourceManager(resourceType);
_resourceKey = resourceKey;
}
public string ResourceKey
{
get { return _resourceKey; }
}
public ResourceManager ResourceManager
{
get { return _resourceManager; }
}
public override string Description
{
get
{
string displayName = _resourceManager.GetString(_resourceKey);
return string.IsNullOrEmpty(displayName)? string.Format(CultureInfo.CurrentUICulture ,"[[{0}]]", _resourceKey) : displayName;
}
}
}
public static class EnumExtensions
{
public static string GetDescription(this Enum enumValue)
{
if (enumValue == null)
{
throw new ArgumentNullException("enumValue");
}
FieldInfo fi = enumValue.GetType().GetField(enumValue.ToString());
DescriptionAttribute[] attributes = (DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false);
if (attributes != null && attributes.Length > 0)
{
return attributes[0].Description;
}
else
{
return enumValue.ToString();
}
}
}
Lexical.Localization ¹を使用すると、デフォルト値とカルチャ固有の値をコードに埋め込み、さらにカルチャの外部ローカリゼーションファイル(.json、.resx、.iniなど)で展開できます。
namespace Library
{
enum Permissions
{
Create = 1,
Drop = 2,
Modify = 4,
GrantCreate = 8,
GrantDrop = 16,
GrantModify = 32
}
}
プログラムコード内:
// Load localization.ini
LineRoot.Builder.AddLocalizationFile("localization.ini").Build();
// Create key for enum
ILine key = LineRoot.Global.Assembly("ConsoleApp4").Type<Permissions>().Format("{0}");
// Print
Console.WriteLine(key.Value(Permissions.Create | Permissions.Drop));
Console.WriteLine(key.Value(Permissions.Create | Permissions.Drop).Culture("en"));
Console.WriteLine(key.Value(Permissions.Create | Permissions.Drop).Culture("fi"));
localization.ini:
[Assembly:ConsoleApp4:Type:Library.Permissions:Culture:fi]
Key:Create = Luonti
Key:Drop = Poisto
Key:Modify = Muutos
Key:GrantCreate = Luonnin myöntö
Key:GrantDrop = Poiston myöntö
Key:GrantModify = Muutoksen myöntö
[Assembly:ConsoleApp4:Type:Library.Permissions:Culture:en]
Key:Create = Create
Key:Drop = Drop
Key:Modify = Modify
Key:GrantCreate = Grant Create
Key:GrantDrop = Grant Drop
Key:GrantModify = Grant Modify
¹(私はそのライブラリの管理者です)
最近ではplain easyで、enumをセットアップしています:
public enum ContactOptionType
{
[Display(Description = "ContactOption1", ResourceType = typeof(Globalization.Contact))]
Demo = 1,
[Display(Description = "ContactOption2", ResourceType = typeof(Globalization.Contact))]
Callback = 2,
[Display(Description = "ContactOption3", ResourceType = typeof(Globalization.Contact))]
Quotation = 3,
[Display(Description = "ContactOption4", ResourceType = typeof(Globalization.Contact))]
Other = 4
}
各列挙値は、Display attribute
という名前のリソースアセンブリクラスのエントリであるDescription
値を持つGlobalization.Contact
を取得します。このリソースアセンブリ(プロジェクト)には、さまざまな連絡先オプションタイプ(デモ、コールバック、見積、その他)のさまざまな翻訳が含まれています。 contact.nl.resx
(オランダの場合)およびcontact.resx
(デフォルトはen-US)のようなファイルが含まれており、異なる列挙値にはローカライズされた値(翻訳)があります。
静的列挙型ヘルパークラスには、次のメソッドがあります。
public static string GetDisplayDescription(this Enum enumValue)
{
return enumValue.GetType().GetMember(enumValue.ToString())
.FirstOrDefault()?
.GetCustomAttribute<DisplayAttribute>()
.GetDescription() ?? "unknown";
}
これにより、Description
属性のDisplay
プロパティの値が取得されます。 CurrentUICulture
が設定されている場合にのみ、変換された値になります。これにより、すべてが「接着」されます。
Thread.CurrentThread.CurrentUICulture = new CultureInfo("en-US");
または
Thread.CurrentThread.CurrentUICulture = new CultureInfo("nl-NL");
これで、単純な単体テスト(XUnit)で、期待どおり/望ましい/設計どおりに動作するかどうかを確認できます。
[Theory]
[InlineData("nl-NL", "Terugbelverzoek")]
[InlineData("en-US", "Callback")]
public void TestTranslationOfDescriptionAttribute(string culture, string expectedValue)
{
// Arrange
CultureInfo cultureInfo = new CultureInfo(culture);
Thread.CurrentThread.CurrentCulture = cultureInfo;
Thread.CurrentThread.CurrentUICulture = cultureInfo;
ContactOptionType contactOptionType = ContactOptionType.Callback;
// Act
string description = contactOptionType.GetDisplayDescription();
// Assert
Assert.Equal(expectedValue, description);
}
上記は楽に成功します✅???? ♂️
したがって、このソリューションでは、「複雑な」LocalizedAttribute
を使用せず、Display属性のDescriptionプロパティの(翻訳された)値を取得する単純なヘルパーのみを使用します。 ResourceType
の設定と組み合わせてDisplay属性にCurrentUICulture
の値が存在することは、トリックを行います。