Exception.Message をファイルに書き込むことで、システムで発生したすべての例外をログに記録しています。ただし、それらはクライアントの文化で書かれています。トルコ語のエラーは、私にはあまり意味がありません。
では、ユーザーのカルチャを変更せずにエラー メッセージを英語でログに記録するにはどうすればよいでしょうか。
Exception.Message をファイルに書き込むことで、システムで発生したすべての例外をログに記録しています。ただし、それらはクライアントの文化で書かれています。トルコ語のエラーは、私にはあまり意味がありません。
では、ユーザーのカルチャを変更せずにエラー メッセージを英語でログに記録するにはどうすればよいでしょうか。
この問題は部分的に回避できます。フレームワークの例外コードは、現在のスレッド ロケールに基づいて、そのリソースからエラー メッセージを読み込みます。一部の例外の場合、これは Message プロパティへのアクセス時に発生します。
これらの例外については、メッセージのログ記録中にスレッド ロケールを一時的に en-US に切り替えることで、完全な米国英語バージョンのメッセージを取得できます (事前に元のユーザー ロケールを保存し、その後すぐに復元します)。
別のスレッドでこれを行うと、さらに効果的です。これにより、副作用がないことが保証されます。例えば:
try
{
System.IO.StreamReader sr=new System.IO.StreamReader(@"c:\does-not-exist");
}
catch(Exception ex)
{
Console.WriteLine(ex.ToString()); //Will display localized message
ExceptionLogger el = new ExceptionLogger(ex);
System.Threading.Thread t = new System.Threading.Thread(el.DoLog);
t.CurrentUICulture = new System.Globalization.CultureInfo("en-US");
t.Start();
}
ExceptionLogger クラスは次のようになります。
class ExceptionLogger
{
Exception _ex;
public ExceptionLogger(Exception ex)
{
_ex = ex;
}
public void DoLog()
{
Console.WriteLine(_ex.ToString()); //Will display en-US message
}
}
ただし、Joeがこの返信の以前のリビジョンのコメントで正しく指摘しているように、一部のメッセージは、例外がスローされた時点で既に (部分的に) 言語リソースから読み込まれています。
これは、たとえば、ArgumentNullException("foo") 例外がスローされたときに生成されるメッセージの「パラメーターを null にすることはできません」部分に適用されます。このような場合、上記のコードを使用しても、メッセージは (部分的に) ローカライズされたまま表示されます。
最初に en-US ロケールのスレッドですべての非 UI コードを実行するなど、非現実的なハックを使用する以外に、それについてできることはあまりないようです: .NET Framework の例外コードにはエラー メッセージのロケールをオーバーライドするための機能。
unlocalize.comで元の例外メッセージを検索できます。
おそらく論争の的となる点ですが、カルチャを に設定する代わりに、 に設定en-US
できますInvariant
。カルチャではInvariant
、エラー メッセージは英語です。
Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;
Thread.CurrentThread.CurrentUICulture = CultureInfo.InvariantCulture;
特に非アメリカ英語を話すロケールでは、偏見がないように見えるという利点があります。(別名、同僚からの中傷的な発言を避ける)
これは、コーディングを必要とせず、ロードが早すぎてコードで変更できない例外のテキスト (たとえば、mscorlib のもの) に対しても機能するソリューションです。
すべての場合に常に適用できるとは限りません(メインの.exeファイルとは別に.configファイルを作成できるようにする必要があるため、セットアップによって異なります)が、それは私にとってはうまくいきます. したがって、たとえば次の行を含むapp.config
in dev (または a[myapp].exe.config
またはin production) を作成するだけです。web.config
<configuration>
...
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="mscorlib.resources" publicKeyToken="b77a5c561934e089"
culture="fr" /> <!-- change this to your language -->
<bindingRedirect oldVersion="1.0.0.0-999.0.0.0" newVersion="999.0.0.0"/>
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Xml.resources" publicKeyToken="b77a5c561934e089"
culture="fr" /> <!-- change this to your language -->
<bindingRedirect oldVersion="1.0.0.0-999.0.0.0" newVersion="999.0.0.0"/>
</dependentAssembly>
<!-- add other assemblies and other languages here -->
</assemblyBinding>
</runtime>
...
</configuration>
これが行うことは、フランス語 (カルチャは " " に設定) のバージョン 1 ~ 999 のmscorlib
のリソースと のリソースのアセンブリ バインディングを... 存在しないアセンブリ (任意のバージョン 999)。System.Xml
fr
そのため、CLR がこれら 2 つのアセンブリ (mscorlib と System.xml) のフランス語のリソースを検索すると、それらが見つからず、適切に英語にフォールバックします。コンテキストとテストによっては、これらのリダイレクトに他のアセンブリ (ローカライズされたリソースを含むアセンブリ) を追加したい場合があります。
もちろん、これは Microsoft によってサポートされているとは思わないので、自己責任で使用してください。問題を検出した場合は、この構成を削除して、無関係であることを確認できます。
Windowsには、使用するUI言語がインストールされている必要があります。そうではなく、翻訳されたメッセージが何であるかを魔法のように知る方法はありません。
pt-PTがインストールされているen-USWindows7 Ultimateでは、次のコードがあります。
Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo("pt-PT");
string msg1 = new DirectoryNotFoundException().Message;
Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo("en-US");
string msg2 = new FileNotFoundException().Message;
Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo("fr-FR");
string msg3 = new FileNotFoundException().Message;
pt-PT、en-US、およびen-USでメッセージを生成します。フランス語のカルチャファイルがインストールされていないため、デフォルトでWindowsのデフォルト(インストール済み?)言語になります。
私はこれが古いトピックであることを知っていますが、私の解決策は、Web検索でそれを見つけた人に非常に関連している可能性があると思います。
例外ロガーでは、ex.GetType.ToStringをログに記録できます。これにより、例外クラスの名前が保存されます。クラスの名前は言語に依存しないため、常に英語で表されると思います(たとえば、「System.FileNotFoundException」)。ただし、現時点では、外国語システムにアクセスしてテストすることはできません。アイディア。
エラーメッセージテキストも本当に必要な場合は、考えられるすべての例外クラス名とそれに相当するメッセージの辞書を好きな言語で作成できますが、英語の場合、クラス名は完全に適切だと思います。
CultureInfo oldCI = Thread.CurrentThread.CurrentCulture;
Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture ("en-US");
Thread.CurrentThread.CurrentUICulture=new CultureInfo("en-US");
try
{
System.IO.StreamReader sr=new System.IO.StreamReader(@"c:\does-not-exist");
}
catch(Exception ex)
{
Console.WriteLine(ex.ToString())
}
Thread.CurrentThread.CurrentCulture = oldCI;
Thread.CurrentThread.CurrentUICulture = oldCI;
回避策なし。
Tks :)
設定Thread.CurrentThread.CurrentUICulture
は、例外をローカライズするために使用されます。2 種類の例外 (ユーザー用と自分用) が必要な場合は、次の関数を使用して例外メッセージを変換できます。.NET-Libraries リソースで元のテキストを検索して、リソース キーを取得し、翻訳された値を返します。しかし、良い解決策がまだ見つからない弱点が 1 つあります。それは、リソースに {0} を含むメッセージが見つからないということです。誰かが良い解決策を持っているなら、私は感謝します。
public static string TranslateExceptionMessage(Exception ex, CultureInfo targetCulture)
{
try
{
Assembly assembly = ex.GetType().Assembly;
ResourceManager resourceManager = new ResourceManager(assembly.GetName().Name, assembly);
ResourceSet originalResources = resourceManager.GetResourceSet(Thread.CurrentThread.CurrentUICulture, createIfNotExists: true, tryParents: true);
ResourceSet targetResources = resourceManager.GetResourceSet(targetCulture, createIfNotExists: true, tryParents: true);
foreach (DictionaryEntry originalResource in originalResources)
if (originalResource.Value.ToString().Equals(ex.Message.ToString(), StringComparison.Ordinal))
return targetResources.GetString(originalResource.Key.ToString(), ignoreCase: false); // success
}
catch { }
return ex.Message; // failed (error or cause it's not smart enough to find texts with '{0}'-patterns)
}
.NET フレームワークには、次の 2 つの部分があります。
すべてのテキスト (例: 例外メッセージ、MessageBox のボタン ラベルなど) は、.NET フレームワーク自体では英語です。言語パックにはローカライズされたテキストがあります。
正確な状況に応じて、解決策は言語パックをアンインストールすることです (つまり、クライアントにそうするように指示します)。その場合、例外テキストは英語になります。ただし、フレームワークが提供する他のすべてのテキストも英語になることに注意してください (例: MessageBox のボタン ラベル、ApplicationCommands のキーボード ショートカット)。
Undercover1989 の回答に基づいていますが、パラメーターと、メッセージが複数のリソース文字列 (引数の例外など) で構成されている場合を考慮しています。
public static string TranslateExceptionMessage(Exception exception, CultureInfo targetCulture)
{
Assembly a = exception.GetType().Assembly;
ResourceManager rm = new ResourceManager(a.GetName().Name, a);
ResourceSet rsOriginal = rm.GetResourceSet(Thread.CurrentThread.CurrentUICulture, true, true);
ResourceSet rsTranslated = rm.GetResourceSet(targetCulture, true, true);
var result = exception.Message;
foreach (DictionaryEntry item in rsOriginal)
{
if (!(item.Value is string message))
continue;
string translated = rsTranslated.GetString(item.Key.ToString(), false);
if (!message.Contains("{"))
{
result = result.Replace(message, translated);
}
else
{
var pattern = $"{Regex.Escape(message)}";
pattern = Regex.Replace(pattern, @"\\{([0-9]+)\}", "(?<group$1>.*)");
var regex = new Regex(pattern);
var replacePattern = translated;
replacePattern = Regex.Replace(replacePattern, @"{([0-9]+)}", @"${group$1}");
replacePattern = replacePattern.Replace("\\$", "$");
result = regex.Replace(result, replacePattern);
}
}
return result;
}
これらのアプローチのいずれかを想像します:
つまり、例外はクライアント機能ではないため、トルコ語モードで実行しても変更されない、固定されたローカライズされていない文字列を使用できます。
0x00000001
たとえば、英語の表で簡単に検索できるように、各エラーにエラー コードを含めます。
エラーメッセージだけでなく、コールスタックをログに記録する必要があります(IIRC、単純な例外.ToString()がそれを行う必要があります)。そこから、例外の発生元を正確に特定し、通常はそれがどの例外であるかを推測できます。