これは、プラグイン可能なコンポーネントのローカライズに適したソリューションですか?
-
21-09-2019 - |
質問
私は以前に一つだけの答えを持っていたの質問をしました。私は今、これで遊んで計画を持っているが、それは良い考えである場合にはいくつかのフィードバックをしたいためにいくつかの時間を持っていた。
問題:
私は名前(不変、コンポーネントを識別するために使用される)、その名前がDisplayName属性を持つコンポーネントのモデルを汚染せずに、それを消費しているアプリケーションに局在持つことのあるコンポーネントをしたいです。コンポーネントは、別のDLL内に存在してもよいし、実行時に動的にロードされる。
私の気持ちは(これは良いカプセルのように思える)コンポーネントDLLがローカライズされた名前を提供する責任を負うべきであるということですが、コンポーネントを消費するアプリケーションは、ローカライズされた名前を使用して/取得するための責任を負わなければならない(成分a持っているという事実表示目的のために異なる名前は、コンポーネントの関心事ではなく、そのコンポーネントを使用して「ビュー」)の
ソリューション:
コンポーネントクラスが含まれているファイルと同じ名前を持つコンポーネントのDLLにリソースを追加します。コンポーネントの名前であるキーを持つリソースに文字列を追加します。
アプリケーションではそれほどのようにローカライズされた名前を取得します:
ExternalObject obj = GetExternalObject ();
ResourceManager manager = new ResourceManager (obj.GetType ());
string localisedName= manager.GetString (obj.Name);
このコードはおそらく、ローカライザークラスでカプセル化されたが、ポイントを伝えることになります。これが動作しているようですが、それは良いアイデアである、またはこれを行うのより良い/より標準的な方法は何ですか?
EDITは:私は、このソリューションでわからないんだけど、一つのことは、リソースがクラスであるファイルと同じ名前を持つの.resxファイルでなければならないことであることを指摘すべきである。これは作りますリソースファイルは、タイプ名から識別できるようには、動作します。これは、フォームのローカライズが動作しているようですと同じであり、Visual Studioは、すべてがいいらしいは.csファイル、の「サブコンポーネント」としての.resxを入れます。しかし、Visual Studioは、その後、私はおそらく私がこれを行うことになってる他のいくつかの方法があること。考えさせる私がしようとすると、このファイルを編集する場合は、(別のプロジェクト項目の一部であるリソースの編集について)警告をスローします
解決
私はあなたが正しい考えを持っていると思いますが、これを達成するためのより良い方法はあります。
おそらく、あなたはプラグイン可能なコンポーネントが実装インタフェースを持っています。 、と言うIPluggableます:
interface IPluggable {
...
string LocalizedName {get;}
...
}
あなたのメインのバイナリからは、プラグイン可能なアセンブリをロードし、リフレクションを使用してIPluggableインスタンスを作成します(私はそれはあなたが持っているGetExternalObject()
メソッドが何をするかだと仮定)、その後LocalizedName
プロパティを使用して、ローカライズされた名前にアクセスします。 のインサイドのIPluggable実装、ResourceManager
を作成し、そのプラグイン可能なアセンブリのRESXからLocalizedName
にアクセスします。
あなたが実行して取得することは、プラグイン可能なアセンブリでの行動の良いカプセル化したものです - しかし、それはあなたの男のプログラムがResourceManager
ローカライズにアクセスするために作成することができると仮定することなく、それを行うには、あなたにローカライズされた名前を提供する責任が選択されますされ名前ます。
他のヒント
私は列挙値をローカライズするいくつかの時間前に問題を抱えていた、私はそれがあなたの質問に答えるかどうかわからないんだけど、少なくとも心を持っているために、あなたに別のアプローチを提供します。
私自身のローカライズ属性を作成することによって開始
/// <SUMMARY>
/// Attribute used for localization. Description field should contain a reference to the Resource file for correct localization
/// </SUMMARY>
public class LocalizationAttribute : Attribute
{
public LocalizationAttribute(string description)
{
this._description = description;
}
private string _description;
/// <SUMMARY>
/// Used to reference a resource key
/// </SUMMARY>
public string Description
{
get
{
return this._description;
}
}
}
私は列挙型自体を作成し、そこから
[TypeConverter(typeof(EnumToLocalizedString))]
public enum ReviewReason
{
[LocalizationAttribute("ReviewReasonNewDocument")]
NewDocument = 1,
[LocalizationAttribute("ReviewReasonInternalAudit")]
InternalAudit = 2,
[LocalizationAttribute("ReviewReasonExternalAudit")]
ExternalAudit = 3,
[LocalizationAttribute("ReviewReasonChangedWorkBehaviour")]
ChangedWorkBehaviour = 4,
[LocalizationAttribute("ReviewReasonChangedWorkBehaviourBecauseOfComplaints")]
ChangedWorkBehaviourBecauseOfComplaints = 5,
[LocalizationAttribute("ReviewReasonMovedFromOlderSystem")]
MovedFromOlderSystem = 6,
[LocalizationAttribute("ReviewReasonPeriodicUpdate")]
PeriodicUpdate = 7,
[LocalizationAttribute("ReviewReasonDocumentChanged")]
DocumentChanged = 8
}
それから私は(属性記述がリソースキーを:)一致する必要があります)LocalizationAttribute記述キーを取得し、ローカライズを取得するには、リソースファイルにアクセスする型コンバータを作成した。
public class EnumToLocalizedString : TypeConverter
{
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
return (sourceType.Equals(typeof(Enum)));
}
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
return (destinationType.Equals(typeof(String)));
}
public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
{
return base.ConvertFrom(context, culture, value);
}
public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType)
{
if (!destinationType.Equals(typeof(String)))
{
throw new ArgumentException("Can only convert to string.", "destinationType");
}
if (!value.GetType().BaseType.Equals(typeof(Enum)))
{
throw new ArgumentException("Can only convert an instance of enum.", "value");
}
string name = value.ToString();
object[] attrs = value.GetType().GetField(name).GetCustomAttributes(typeof(LocalizationAttribute), false);
if (attrs.Length != 1 !(attrs[0] is LocalizationAttribute))
{
throw new ArgumentException("Invalid enum argument");
}
return Handbok.Code.Resources.handbok.ResourceManager.GetString(((LocalizationAttribute)attrs[0]).Description);
}
}
最後に、私は、この場合には、
コレクションでのTypeConverterを使用するクライアントを作成しましたpublic class ReviewReasonCollection
{
private static Collection<KEYVALUEPAIR<REVIEWREASON,>> _reviewReasons;
public static Collection<KEYVALUEPAIR<REVIEWREASON,>> AllReviewReasons
{
get
{
if (_reviewReasons == null)
{
_reviewReasons = new Collection<KEYVALUEPAIR<REVIEWREASON,>>();
TypeConverter t = TypeDescriptor.GetConverter(typeof(ReviewReason));
foreach (ReviewReason reviewReason in Enum.GetValues(typeof(ReviewReason)))
{
_reviewReasons.Add(new KeyValuePair<REVIEWREASON,>(reviewReason, t.ConvertToString(reviewReason)));
}
}
return _reviewReasons;
}
}
}
私はもともと私のブログの上でこのソリューションを投稿しました。ホープそれはあなたを支援します。)
あなたが示唆されている方法の問題点は、そのが翻訳を更新することは難しいことになるだろうということで、それも、プログラマが必要な場合があります。また、どのようにアプリケーション全体を更新せずに翻訳を更新するつもりですか?
私は翻訳されたアプリケーションの多くを行って、そして私がやったことは、このような何かをフォーマットさtranslatationsと別々のテキストファイルを持っているしています
[英語]
完了=完了[ノルウェー]
完了= Ferdig
と私は、すべてのUI要素を変換しますTranslateForm()私は、フォームを表示するイベント内で呼び出すことと呼ばれる機能を、持っています。 TranslateForm()関数は、
のようなものを持っていますbuttonDone.Text = Translate.GetTranslation("Done");
TranslateFormとの最後の部分は最適解ではないですが、私は時間をかけて私がコントロール自体が翻訳クラスを呼び出すことソリューションに移行すると思います。 このシステムを使用する利点は、プログラマのためのシンプルな、あなたは(私はコミュニティ主導の翻訳を持っているので、これは私にとってにとって必須である)他の皆さんは、あなたが後で手作業を行うことなく、翻訳を追加することができ、したがって、彼らは頻繁に更新され、私ということですその上で時間を過ごしたいいけません。 アプリケーションが実行されている間、私はまた、アプリケーションを再起動するか、更新されなくても、翻訳を更新することができます。