Exception messages in English?
Here is solution that does not require any coding and works even for texts of exceptions that are loaded too early for us to be able to change by code (for example, those in mscorlib).
It may not be always applicable in every case (it depends on your setup as you need to be able to create a .config file aside the main .exe file) but that works for me. So, just create an app.config
in dev, (or a [myapp].exe.config
or web.config
in production) that contains the following lines for example:
<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>
What this does is tell the framework to redirect assembly bindings for mscorlib
's resources and System.Xml
's resources, for versions between 1 and 999, in french (culture is set to "fr
") to an assembly that ... does not exists (an arbitrary version 999).
So when the CLR will look for french resources for these two assemblies (mscorlib and System.xml), it will not find them and fallback to English gracefully. Depending on your context and testings, you might want to add other assemblies to these redirects (assemblies that contains localized resources).
Of course I don't think this is supported by Microsoft, so use at your own risk. Well, in case you detect a problem, you can just remove this configuration and check it's unrelated.
This issue can be partially worked around. The Framework exception code loads the error messages from its resources, based on the current thread locale. In the case of some exceptions, this happens at the time the Message property is accessed.
For those exceptions, you can obtain the full US English version of the message by briefly switching the thread locale to en-US while logging it (saving the original user locale beforehand and restoring it immediately afterwards).
Doing this on a separate thread is even better: this ensures there won't be any side effects. For example:
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();
}
Where the ExceptionLogger class looks something like:
class ExceptionLogger
{
Exception _ex;
public ExceptionLogger(Exception ex)
{
_ex = ex;
}
public void DoLog()
{
Console.WriteLine(_ex.ToString()); //Will display en-US message
}
}
However, as Joe correctly points out in a comment on an earlier revision of this reply, some messages are already (partially) loaded from the language resources at the time the exception is thrown.
This applies to the 'parameter cannot be null' part of the message generated when an ArgumentNullException("foo") exception is thrown, for example. In those cases, the message will still appear (partially) localized, even when using the above code.
Other than by using impractical hacks, such as running all your non-UI code on a thread with en-US locale to begin with, there doesn't seem to be much you can do about that: the .NET Framework exception code has no facilities for overriding the error message locale.
A contentious point perhaps, but instead of setting the culture to en-US
, you can set it to Invariant
. In the Invariant
culture, the error messages are in English.
Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;
Thread.CurrentThread.CurrentUICulture = CultureInfo.InvariantCulture;
It has the advantage of not looking biased, especially for non-American English speaking locales. (a.k.a. avoids snide remarks from colleagues)