那么,我们如何在不改变用户文化的情况下记录任何英语错误消息呢?
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
}
}
t.CurrentUICulture = new System.Globalization.CultureInfo("fr-FR");
和 t.CurrentCulture = new System.Globalization.CultureInfo("fr-FR");
然而,生成的异常仍然是英文的... - VitalyBEnvironment.GetResourceString("...")
实例化的,因此您的解决方案已不再适用。最好的方法是抛出自定义异常,并使用自己的(英文)消息文本并使用InnerException属性来保留旧的异常信息。 - webber2k6或许是一个争议点,但是您可以将文化设置为Invariant
而不是en-US
。在Invariant
文化中,错误消息是用英语显示的。
Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;
Thread.CurrentThread.CurrentUICulture = CultureInfo.InvariantCulture;
它的优点在于不会显得有偏见,特别是对于非美国英语使用区域。 (也就是避免同事的讽刺言辞)
catch
中调用它。 - MPelletier<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
和System.Xml
资源的程序集绑定,以法语(文化设置为“fr
”)重定向到一个不存在的程序集(任意版本999)。
因此,当CLR寻找这两个程序集(mscorlib和System.xml)的法语资源时,它将找不到它们并优雅地回退到英语。根据您的上下文和测试,您可能希望将其他包含本地化资源的程序集添加到这些重定向中。
当然,我认为微软不支持这个功能,所以请自行决定是否使用。如果出现问题,您可以删除此配置并检查它与此无关。
Windows需要安装您想要使用的UI语言。如果没有安装,它就无法神奇地知道所翻译的消息是什么。
在一个安装了pt-PT语言包的en-US Windows 7 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默认(安装?)语言。
我知道这是一个老话题了,但我认为我的解决方案可能对任何在网上搜索时遇到它的人都很相关:
在异常记录器中,您可以记录ex.GetType.ToString,这将保存异常类的名称。我期望类名应该独立于语言,因此始终以英语表示(例如"System.FileNotFoundException"),尽管目前我没有测试这个想法的外语系统。
如果您真的想要错误消息文本,您可以创建一个字典,其中包含所有可能的异常类名称及其在您喜欢的任何语言中的等效消息,但对于英语而言,我认为类名就足够了。
InvalidOperationException
,由 System.Xml.XmlWellFormedWriter
抛出。你可以猜测具体发生了什么错误,而不必阅读消息。可能有成千上万种不同的情况。 - NyergudsCultureInfo 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;
不需要绕过任何问题。
谢谢 :)
设置Thread.CurrentThread.CurrentUICulture
将用于本地化异常。如果您需要两种类型的异常(一种是用户,一种是您自己),则可以使用以下函数来翻译异常消息。它在.NET库资源中搜索原始文本以获取资源键,然后返回翻译值。但有一个弱点,我还没有找到一个好的解决方案:包含{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)
}
基于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;
}
Regex.Match
和String.Join(Environment.NewLine,...)
在结尾处。我还排除了“Globalization.*”资源以避免混淆,你的实现可以从中受益。 - Adam L. S.