Exception messages in English?
Asked Answered
P

19

344

We are logging any exceptions that happen in our system by writing the Exception.Message to a file. However, they are written in the culture of the client. And Turkish errors don't mean a lot to me.

So how can we log any error messages in English without changing the users culture?

Psychodiagnostics answered 16/10, 2008 at 15:47 Comment(2)
Why can't you swith like this: CultureInfo oldCulture = Thread.CurrentThread.CurrentCulture; Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("en"); // throw new Exception here => Culture is in english Thread.CurrentThread.CurrentCulture = oldCulture;Leatherman
@Leatherman If I can predict a point at which an exception will occur, so I can put the thread culture switch around it, I can also just fix the actual issue that occurs there. This whole problem doesn't apply to exceptions you throw yourself.Roseliaroselin
L
80

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.

Livelong answered 16/10, 2008 at 16:11 Comment(8)
Your example works for a FileNotFoundException, because the message resource is retrieved when the Message property is accessed, not when the exception is thrown. But this is not true for all exceptions (e.g. try throw new ArgumentNullException("paramName"))Perineurium
I am confused. I've tried following your answer and to test it I wanted my exception in french, so I did t.CurrentUICulture = new System.Globalization.CultureInfo("fr-FR"); and t.CurrentCulture = new System.Globalization.CultureInfo("fr-FR"); yet, the resulting exception was in English...Abernon
Also not working for invalid host name exception from socket.Arron
@Abernon The localized exception texts are part of the .NET framework language packs. So if you don't have the French language pack installed, you will not get the translated texts.Obaza
At least with .NET 4.5 all exceptions are instantiated with Environment.GetResourceString("...") so your solution does not work anymore. Best thing is to throw custom exception with your own (english) message text and use InnerException property to keep the old one.Merlynmermaid
Reflection to get the exception type names could become handy.Refraction
Unfortunately this does not translate Win32Exception, because that one takes its texts from Windows itself. And you cannot translate in languages that are not installed. Therefore I have used your solution and enhanced it, see below.Snailpaced
By the way if you are testing it by trying to switch the language to something other than English make sure you have appropriate language pack installed and rebooted after that :)Imine
R
48

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)

Rapine answered 24/7, 2013 at 21:24 Comment(4)
Where should we write these lines in our ASP.NET project? Thanks.Prig
I'm going to suggest at the top, in Application_Start. That will make the whole project run in English. If it's only for error messages that you want it, you can make a cover function and call it in each catch.Rapine
Won't this also make standard message box buttons be in English, though? That may not be desired behaviour.Roseliaroselin
If your application is totally unlocalized, sure. Otherwise I don't see the use of this approach other than crippling all localization while flying under the radar of cynical colleagues.Shauna
G
19

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.

Girlie answered 7/1, 2016 at 15:41 Comment(4)
Works when need english output from test-runner tools.Austro
Tried this, but it did not work for me. Are there other resources files in .net? Where can I find them?Deodand
Look in c:\Windows\Microsoft.NET\Framework\v4.0.30319. Every language has a 2 letter folder there. Remember to replace "fr" in the answer above with the actual language that is being used. "no" for norwegian, "da" for danish, "sv" for swedish etc.Ultrasonic
To create a FULL list, take a look in that folder. Its about 120 resource files. Add each of them into the config. This seems to be the only solution for Windows 10 and newer for now, since there is no way to uninstall the .Net language packs in newer windows anymore (its part of the OS). Its even placed in the GAC now, so removing those language folders doesnt seem to work.Ultrasonic
P
13

Windows needs to have the UI language you want to use installed. It it doesn't, it has no way of magically knowing what the translated message is.

In an en-US windows 7 ultimate, with pt-PT installed, the following code:

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;

Produces messages in pt-PT, en-US and en-US. Since there is no French culture files installed, it defaults to the windows default (installed?) language.

Patristic answered 7/1, 2011 at 16:27 Comment(1)
That's solved the problem. Polish UI in my situation, installed en MUI language packages ~260MB, using Vistalizator program.Curling
P
6
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;

Without WORKAROUNDS.

Tks :)

Preventer answered 15/1, 2009 at 21:36 Comment(1)
you forgot the ;Tray
V
6

I know this is an old topic, but I think my solution may be quite relevant to anyone who stumbles across it in a web search:

In the exception logger you could log ex.GetType.ToString, which would save the name of the exception class. I would expect that the name of a class ought to be independent of language and would therefore always be represented in English (e.g. "System.FileNotFoundException"), though at present I don't have access to a foreign language system to test out the idea.

If you really want the error message text as well you could create a dictionary of all possible exception class names and their equivalent messages in whatever language you prefer, but for English I think the class name is perfectly adequate.

Venola answered 26/8, 2011 at 15:17 Comment(1)
Doesn't work. I got an InvalidOperationException, thrown by System.Xml.XmlWellFormedWriter. You try guessing what specific error occurred without reading the message. Could be a thousand different things.Roseliaroselin
C
5

Setting Thread.CurrentThread.CurrentUICulture will be used to localize the exceptions. If you need two kinds of exceptions (one for the user, one for you) you can use the following function to translate the exception-message. It's searching in the .NET-Libraries resources for the original text to get the resource-key and then return the translated value. But there's one weakness I didn't find a good solution yet: Messages, that contains {0} in resources will not be found. If anyone has a good solution I would be grateful.

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)
}
Conduplicate answered 19/12, 2012 at 15:42 Comment(2)
That is not going to work if the exception contains a formatted parameter.Torbert
Yep, as I said: "But there one weakness I didn't find a good solution yet: Messages, that contains {0} in resources will not be found. If anyone have a good solution I would be grateful."Conduplicate
O
4

The .NET framework comes in two parts:

  1. The .NET framework itself
  2. The .NET framework language packs

All texts (ex. exception messages, button labels on a MessageBox, etc.) are in English in the .NET framework itself. The language packs have the localized texts.

Depending on your exact situation, a solution would be to uninstall the language packs (i.e. tell the client to do so). In that case, the exception texts will be in English. Note however, that all other framework-supplied text will be English as well (ex. the button labels on a MessageBox, keyboard shortcuts for ApplicationCommands).

Obaza answered 28/4, 2014 at 12:34 Comment(5)
Thanks!! I find it ironic that the uninstall dialog is in the language of the uninstalling pack and not the local language. Side note: The language packs seem to returning every few months. i haven't worked out why but i am guessing a update/upgradeTurino
@ChocoSmith With every update of the .NET Framework via Windows Update, the language pack is installed again.Obaza
Asking customers to uninstall language packs for their own language is not a viable solution.Roseliaroselin
@Roseliaroselin displaying to customers system exceptions is not a viable solution.Flack
@Flack Not sure how that's relevant. Nothing says you have to display it; you can just log it in a file and display a nice message to the user telling them to send you the log file. That's irrelevant to the underlying problem that if that user happens to be Chinese or Russian, you, the programmer, ultimately have to deal with a Chinese or Russian error message.Roseliaroselin
P
4

Based on the Undercover1989 answer, but takes into account parameters and when messages are composed of several resource strings (like argument exceptions).

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;
}
Pearce answered 6/11, 2018 at 7:24 Comment(2)
Thanks this appears to work for translation a German exception to an English one.Trotline
Ah! I've noticed your response after I've created my version based on @Vortex852456's implementation. I've used LiNQ, used Regex.Match and String.Join(Environment.NewLine, ...) at the end. I've also excluded "Globalization.*" resources to avoid confusion, which your implementation could benefit from.Keratoid
Y
2

I would imagine one of these approaches:

  1. The exceptions are only ever read by you, i.e. they are not a client feature, so you can use hardwired non localised strings that won't change when you run in Turkish mode.

  2. Include an error code e.g. 0x00000001 with each error so that you can easily look it in up in an English table.

Yaupon answered 16/10, 2008 at 16:4 Comment(1)
That won't help much when they are exceptions thrown by internal components of the .net framework. This whole problem doesn't apply to exceptions you throw yourself; obviously the programmer chooses what message to include with those.Roseliaroselin
S
2

I have had the same situation, and all answers that I found here and elsewhere did not help or were not satisfying:

The Thread.CurrentUICulture changes the language of the .net exceptions, but it does not for Win32Exception, which uses Windows resources in the language of the Windows UI itself. So I never managed to print the messages of Win32Exception in English instead of German, not even by using FormatMessage() as described in
How to get Win32Exception in English?

Therefore I created my own solution, which stores the majority of existing exception messages for different languages in external files. You will not get the very exact message in your desired language, but you will get a message in that language, which is much more than you currently get (which is a message in a language you likely don't understand).

The static functions of this class can be executed on Windows installations with different languages: CreateMessages() creates the culture-specific texts
SaveMessagesToXML() saves them to as many XML files as languages are created or loaded
LoadMessagesFromXML() loads all XML files with language-specific messages

When creating the XML files on different Windows installations with different languages, you will soon have all languages you need.
Maybe you can create the texts for different languages on 1 Windows when you have multiple MUI language packs installed, but I haven't tested that yet.

Tested with VS2008, ready to use. Comments and suggestions are welcome!

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Reflection;
using System.Threading;
using System.Xml;

public struct CException
{
  //----------------------------------------------------------------------------
  public CException(Exception i_oException)
  {
    m_oException = i_oException;
    m_oCultureInfo = null;
    m_sMessage = null;
  }

  //----------------------------------------------------------------------------
  public CException(Exception i_oException, string i_sCulture)
  {
    m_oException = i_oException;
    try
    { m_oCultureInfo = new CultureInfo(i_sCulture); }
    catch
    { m_oCultureInfo = CultureInfo.InvariantCulture; }
    m_sMessage = null;
  }

  //----------------------------------------------------------------------------
  public CException(Exception i_oException, CultureInfo i_oCultureInfo)
  {
    m_oException = i_oException;
    m_oCultureInfo = i_oCultureInfo == null ? CultureInfo.InvariantCulture : i_oCultureInfo;
    m_sMessage = null;
  }

  //----------------------------------------------------------------------------
  // GetMessage
  //----------------------------------------------------------------------------
  public string GetMessage() { return GetMessage(m_oException, m_oCultureInfo); }

  public string GetMessage(String i_sCulture) { return GetMessage(m_oException, i_sCulture); }

  public string GetMessage(CultureInfo i_oCultureInfo) { return GetMessage(m_oException, i_oCultureInfo); }

  public static string GetMessage(Exception i_oException) { return GetMessage(i_oException, CultureInfo.InvariantCulture); }

  public static string GetMessage(Exception i_oException, string i_sCulture)
  {
    CultureInfo oCultureInfo = null;
    try
    { oCultureInfo = new CultureInfo(i_sCulture); }
    catch
    { oCultureInfo = CultureInfo.InvariantCulture; }
    return GetMessage(i_oException, oCultureInfo);
  }

  public static string GetMessage(Exception i_oException, CultureInfo i_oCultureInfo)
  {
    if (i_oException == null) return null;
    if (i_oCultureInfo == null) i_oCultureInfo = CultureInfo.InvariantCulture;

    if (ms_dictCultureExceptionMessages == null) return null;
    if (!ms_dictCultureExceptionMessages.ContainsKey(i_oCultureInfo))
      return CreateMessage(i_oException, i_oCultureInfo);

    Dictionary<string, string> dictExceptionMessage = ms_dictCultureExceptionMessages[i_oCultureInfo];
    string sExceptionName = i_oException.GetType().FullName;
    sExceptionName = MakeXMLCompliant(sExceptionName);
    Win32Exception oWin32Exception = (Win32Exception)i_oException;
    if (oWin32Exception != null)
      sExceptionName += "_" + oWin32Exception.NativeErrorCode;
    if (dictExceptionMessage.ContainsKey(sExceptionName))
      return dictExceptionMessage[sExceptionName];
    else
      return CreateMessage(i_oException, i_oCultureInfo);
  }

  //----------------------------------------------------------------------------
  // CreateMessages
  //----------------------------------------------------------------------------
  public static void CreateMessages(CultureInfo i_oCultureInfo)
  {
    Thread oTH = new Thread(new ThreadStart(CreateMessagesInThread));
    if (i_oCultureInfo != null)
    {
      oTH.CurrentCulture = i_oCultureInfo;
      oTH.CurrentUICulture = i_oCultureInfo;
    }
    oTH.Start();
    while (oTH.IsAlive)
    { Thread.Sleep(10); }
  }

  //----------------------------------------------------------------------------
  // LoadMessagesFromXML
  //----------------------------------------------------------------------------
  public static void LoadMessagesFromXML(string i_sPath, string i_sBaseFilename)
  {
    if (i_sBaseFilename == null) i_sBaseFilename = msc_sBaseFilename;

    string[] asFiles = null;
    try
    {
      asFiles = System.IO.Directory.GetFiles(i_sPath, i_sBaseFilename + "_*.xml");
    }
    catch { return; }

    ms_dictCultureExceptionMessages.Clear();
    for (int ixFile = 0; ixFile < asFiles.Length; ixFile++)
    {
      string sXmlPathFilename = asFiles[ixFile];

      XmlDocument xmldoc = new XmlDocument();
      try
      {
        xmldoc.Load(sXmlPathFilename);
        XmlNode xmlnodeRoot = xmldoc.SelectSingleNode("/" + msc_sXmlGroup_Root);

        string sCulture = xmlnodeRoot.SelectSingleNode(msc_sXmlGroup_Info + "/" + msc_sXmlData_Culture).Value;
        CultureInfo oCultureInfo = new CultureInfo(sCulture);

        XmlNode xmlnodeMessages = xmlnodeRoot.SelectSingleNode(msc_sXmlGroup_Messages);
        XmlNodeList xmlnodelistMessage = xmlnodeMessages.ChildNodes;
        Dictionary<string, string> dictExceptionMessage = new Dictionary<string, string>(xmlnodelistMessage.Count + 10);
        for (int ixNode = 0; ixNode < xmlnodelistMessage.Count; ixNode++)
          dictExceptionMessage.Add(xmlnodelistMessage[ixNode].Name, xmlnodelistMessage[ixNode].InnerText);
        ms_dictCultureExceptionMessages.Add(oCultureInfo, dictExceptionMessage);
      }
      catch
      { return; }
    }
  }

  //----------------------------------------------------------------------------
  // SaveMessagesToXML
  //----------------------------------------------------------------------------
  public static void SaveMessagesToXML(string i_sPath, string i_sBaseFilename)
  {
    if (i_sBaseFilename == null) i_sBaseFilename = msc_sBaseFilename;

    foreach (KeyValuePair<CultureInfo, Dictionary<string, string>> kvpCultureExceptionMessages in ms_dictCultureExceptionMessages)
    {
      string sXmlPathFilename = i_sPath + i_sBaseFilename + "_" + kvpCultureExceptionMessages.Key.TwoLetterISOLanguageName + ".xml";
      Dictionary<string, string> dictExceptionMessage = kvpCultureExceptionMessages.Value;

      XmlDocument xmldoc = new XmlDocument();
      XmlWriter xmlwriter = null;
      XmlWriterSettings writerSettings = new XmlWriterSettings();
      writerSettings.Indent = true;

      try
      {
        XmlNode xmlnodeRoot = xmldoc.CreateElement(msc_sXmlGroup_Root);
        xmldoc.AppendChild(xmlnodeRoot);
        XmlNode xmlnodeInfo = xmldoc.CreateElement(msc_sXmlGroup_Info);
        XmlNode xmlnodeMessages = xmldoc.CreateElement(msc_sXmlGroup_Messages);
        xmlnodeRoot.AppendChild(xmlnodeInfo);
        xmlnodeRoot.AppendChild(xmlnodeMessages);

        XmlNode xmlnodeCulture = xmldoc.CreateElement(msc_sXmlData_Culture);
        xmlnodeCulture.InnerText = kvpCultureExceptionMessages.Key.Name;
        xmlnodeInfo.AppendChild(xmlnodeCulture);

        foreach (KeyValuePair<string, string> kvpExceptionMessage in dictExceptionMessage)
        {
          XmlNode xmlnodeMsg = xmldoc.CreateElement(kvpExceptionMessage.Key);
          xmlnodeMsg.InnerText = kvpExceptionMessage.Value;
          xmlnodeMessages.AppendChild(xmlnodeMsg);
        }

        xmlwriter = XmlWriter.Create(sXmlPathFilename, writerSettings);
        xmldoc.WriteTo(xmlwriter);
      }
      catch (Exception e)
      { return; }
      finally
      { if (xmlwriter != null) xmlwriter.Close(); }
    }
  }

  //----------------------------------------------------------------------------
  // CreateMessagesInThread
  //----------------------------------------------------------------------------
  private static void CreateMessagesInThread()
  {
    Thread.CurrentThread.Name = "CException.CreateMessagesInThread";

    Dictionary<string, string> dictExceptionMessage = new Dictionary<string, string>(0x1000);

    GetExceptionMessages(dictExceptionMessage);
    GetExceptionMessagesWin32(dictExceptionMessage);

    ms_dictCultureExceptionMessages.Add(Thread.CurrentThread.CurrentUICulture, dictExceptionMessage);
  }

  //----------------------------------------------------------------------------
  // GetExceptionTypes
  //----------------------------------------------------------------------------
  private static List<Type> GetExceptionTypes()
  {
    Assembly[] aoAssembly = AppDomain.CurrentDomain.GetAssemblies();

    List<Type> listoExceptionType = new List<Type>();

    Type oExceptionType = typeof(Exception);
    for (int ixAssm = 0; ixAssm < aoAssembly.Length; ixAssm++)
    {
      if (!aoAssembly[ixAssm].GlobalAssemblyCache) continue;
      Type[] aoType = aoAssembly[ixAssm].GetTypes();
      for (int ixType = 0; ixType < aoType.Length; ixType++)
      {
        if (aoType[ixType].IsSubclassOf(oExceptionType))
          listoExceptionType.Add(aoType[ixType]);
      }
    }

    return listoExceptionType;
  }

  //----------------------------------------------------------------------------
  // GetExceptionMessages
  //----------------------------------------------------------------------------
  private static void GetExceptionMessages(Dictionary<string, string> i_dictExceptionMessage)
  {
    List<Type> listoExceptionType = GetExceptionTypes();
    for (int ixException = 0; ixException < listoExceptionType.Count; ixException++)
    {
      Type oExceptionType = listoExceptionType[ixException];
      string sExceptionName = MakeXMLCompliant(oExceptionType.FullName);
      try
      {
        if (i_dictExceptionMessage.ContainsKey(sExceptionName))
          continue;
        Exception e = (Exception)(Activator.CreateInstance(oExceptionType));
        i_dictExceptionMessage.Add(sExceptionName, e.Message);
      }
      catch (Exception)
      { i_dictExceptionMessage.Add(sExceptionName, null); }
    }
  }

  //----------------------------------------------------------------------------
  // GetExceptionMessagesWin32
  //----------------------------------------------------------------------------
  private static void GetExceptionMessagesWin32(Dictionary<string, string> i_dictExceptionMessage)
  {
    string sTypeName = MakeXMLCompliant(typeof(Win32Exception).FullName) + "_";
    for (int iError = 0; iError < 0x4000; iError++)  // Win32 errors may range from 0 to 0xFFFF
    {
      Exception e = new Win32Exception(iError);
      if (!e.Message.StartsWith("Unknown error (", StringComparison.OrdinalIgnoreCase))
        i_dictExceptionMessage.Add(sTypeName + iError, e.Message);
    }
  }

  //----------------------------------------------------------------------------
  // CreateMessage
  //----------------------------------------------------------------------------
  private static string CreateMessage(Exception i_oException, CultureInfo i_oCultureInfo)
  {
    CException oEx = new CException(i_oException, i_oCultureInfo);
    Thread oTH = new Thread(new ParameterizedThreadStart(CreateMessageInThread));
    oTH.Start(oEx);
    while (oTH.IsAlive)
    { Thread.Sleep(10); }
    return oEx.m_sMessage;
  }

  //----------------------------------------------------------------------------
  // CreateMessageInThread
  //----------------------------------------------------------------------------
  private static void CreateMessageInThread(Object i_oData)
  {
    if (i_oData == null) return;
    CException oEx = (CException)i_oData;
    if (oEx.m_oException == null) return;

    Thread.CurrentThread.CurrentUICulture = oEx.m_oCultureInfo == null ? CultureInfo.InvariantCulture : oEx.m_oCultureInfo;
    // create new exception in desired culture
    Exception e = null;
    Win32Exception oWin32Exception = (Win32Exception)(oEx.m_oException);
    if (oWin32Exception != null)
      e = new Win32Exception(oWin32Exception.NativeErrorCode);
    else
    {
      try
      {
        e = (Exception)(Activator.CreateInstance(oEx.m_oException.GetType()));
      }
      catch { }
    }
    if (e != null)
      oEx.m_sMessage = e.Message;
  }

  //----------------------------------------------------------------------------
  // MakeXMLCompliant
  // from https://www.w3.org/TR/xml/
  //----------------------------------------------------------------------------
  private static string MakeXMLCompliant(string i_sName)
  {
    if (string.IsNullOrEmpty(i_sName))
      return "_";

    System.Text.StringBuilder oSB = new System.Text.StringBuilder();
    for (int ixChar = 0; ixChar < (i_sName == null ? 0 : i_sName.Length); ixChar++)
    {
      char character = i_sName[ixChar];
      if (IsXmlNodeNameCharacterValid(ixChar, character))
        oSB.Append(character);
    }
    if (oSB.Length <= 0)
      oSB.Append("_");
    return oSB.ToString();
  }

  //----------------------------------------------------------------------------
  private static bool IsXmlNodeNameCharacterValid(int i_ixPos, char i_character)
  {
    if (i_character == ':') return true;
    if (i_character == '_') return true;
    if (i_character >= 'A' && i_character <= 'Z') return true;
    if (i_character >= 'a' && i_character <= 'z') return true;
    if (i_character >= 0x00C0 && i_character <= 0x00D6) return true;
    if (i_character >= 0x00D8 && i_character <= 0x00F6) return true;
    if (i_character >= 0x00F8 && i_character <= 0x02FF) return true;
    if (i_character >= 0x0370 && i_character <= 0x037D) return true;
    if (i_character >= 0x037F && i_character <= 0x1FFF) return true;
    if (i_character >= 0x200C && i_character <= 0x200D) return true;
    if (i_character >= 0x2070 && i_character <= 0x218F) return true;
    if (i_character >= 0x2C00 && i_character <= 0x2FEF) return true;
    if (i_character >= 0x3001 && i_character <= 0xD7FF) return true;
    if (i_character >= 0xF900 && i_character <= 0xFDCF) return true;
    if (i_character >= 0xFDF0 && i_character <= 0xFFFD) return true;
    // if (i_character >= 0x10000 && i_character <= 0xEFFFF) return true;

    if (i_ixPos > 0)
    {
      if (i_character == '-') return true;
      if (i_character == '.') return true;
      if (i_character >= '0' && i_character <= '9') return true;
      if (i_character == 0xB7) return true;
      if (i_character >= 0x0300 && i_character <= 0x036F) return true;
      if (i_character >= 0x203F && i_character <= 0x2040) return true;
    }
    return false;
  }

  private static string msc_sBaseFilename = "exception_messages";
  private static string msc_sXmlGroup_Root = "exception_messages";
  private static string msc_sXmlGroup_Info = "info";
  private static string msc_sXmlGroup_Messages = "messages";
  private static string msc_sXmlData_Culture = "culture";

  private Exception m_oException;
  private CultureInfo m_oCultureInfo;
  private string m_sMessage;

  static Dictionary<CultureInfo, Dictionary<string, string>> ms_dictCultureExceptionMessages = new Dictionary<CultureInfo, Dictionary<string, string>>();
}

internal class Program
{
  public static void Main()
  {
    CException.CreateMessages(null);
    CException.SaveMessagesToXML(@"d:\temp\", "emsg");
    CException.LoadMessagesFromXML(@"d:\temp\", "emsg");
  }
}
Snailpaced answered 20/4, 2017 at 16:24 Comment(1)
The Thread.CurrentUICulture also changes the language of the UI, making it a terrible option. A classic example are the Yes/No/OK/Cancel buttons on the message box.Roseliaroselin
S
2

Had to make the change on IIS. Go to IIS Manager > Select the Site > .NET Globalization > Set UI Culture to English there.

See a more detailed answer on this other SO post.

Schatz answered 2/7, 2022 at 21:33 Comment(0)
B
1

This worked for me:

 //Exception Class Extensions
    public static class ExceptionExtensions
    {
        public static string EnMessage(this Exception ex)
        {
            CultureInfo oldCI = Thread.CurrentThread.CurrentCulture;
            string englishExceptionMessage = ex.Message;
            Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("en-US");
            Thread.CurrentThread.CurrentUICulture = new CultureInfo("en-US");
            try
            {
                var objectType = Type.GetType(ex.GetType().FullName);
                var instantiatedObject = Activator.CreateInstance(objectType);                
                throw (Exception)instantiatedObject;
            }
            catch (Exception e)
            {
                englishExceptionMessage = e.Message;
            }
            Thread.CurrentThread.CurrentCulture = oldCI;
            Thread.CurrentThread.CurrentUICulture = oldCI;
            return englishExceptionMessage;
        }
    }

You can then use it by calling the the new method ex.EnMessage();

Billbug answered 19/1, 2022 at 12:9 Comment(0)
W
1

Regarding .NET Core and above, the Docs of Thread.CurrentUICulture recommend to use the CultureInfo.CurrentUICulture property to retrieve and set the current culture.

CultureInfo.CurrentUICulture = CultureInfo.InvariantCulture;

On a related GitHub issue, Tarek Mahmoud Sayed recommended to use CultureInfo.DefaultThreadCurrentUICulture to ensure other created threads later will also get the same culture:

CultureInfo.DefaultThreadCurrentUICulture = CultureInfo.InvariantCulture;

The latter is only available since .NET Framework 4.5 (and .NET Core 1.0).

Wayzgoose answered 24/11, 2022 at 12:53 Comment(0)
D
1

This Extension Method can sometimes work, but not in every case.

public static string GetEnglishExceptionMessage(this Exception exception)
    {
        // Get the culture info for the current thread
        CultureInfo currentCulture = Thread.CurrentThread.CurrentCulture;

        // Create a new culture info for en-US
        CultureInfo englishCulture = new CultureInfo("en-US");

        // Change the current thread's culture to en-US
        Thread.CurrentThread.CurrentCulture = englishCulture;

        // Get the english version of the exception message from the c# standard library
        string englishExceptionMessage = System.Runtime.InteropServices.Marshal.GetExceptionForHR(exception.HResult).Message;

        // Reset the current thread's culture to the original culture
        Thread.CurrentThread.CurrentCulture = currentCulture;

        // Return the english version of the exception message
        return englishExceptionMessage;
    }

Sadly there are cases where it does return an invalid result. Example: ArgumentException works but HttpRequestException does not.

It can be used this way:

var error = new NullReferenceException("This is a Test String");
string NonLocalMessage = error.GetEnglishExceptionMessage();
Duhamel answered 22/2, 2023 at 9:58 Comment(0)
D
-1

You should log the call stack instead of just error message (IIRC, simple exception.ToString() should do that for you). From there, you can determine exactly where the exception originated from, and usually deduce which exception it is.

Delegation answered 27/5, 2010 at 18:49 Comment(1)
We're logging the message and stacktrace. But it's a lot easier if the message is clear.Psychodiagnostics
N
-1

Override exception message in catch block using extension method, Check thrown message is from code or not as mentioned below.

    public static string GetEnglishMessageAndStackTrace(this Exception ex)
    {
        CultureInfo currentCulture = Thread.CurrentThread.CurrentUICulture;
        try
        {

            dynamic exceptionInstanceLocal = System.Activator.CreateInstance(ex.GetType());
            string str;
            Thread.CurrentThread.CurrentUICulture = new CultureInfo("en-US");

            if (ex.Message == exceptionInstanceLocal.Message)
            {
                dynamic exceptionInstanceENG = System.Activator.CreateInstance(ex.GetType());

                str = exceptionInstanceENG.ToString() + ex.StackTrace;

            }
            else
            {
                str = ex.ToString();
            }
            Thread.CurrentThread.CurrentUICulture = currentCulture;

            return str;

        }
        catch (Exception)
        {
            Thread.CurrentThread.CurrentUICulture = currentCulture;

            return ex.ToString();
        }
Nummular answered 28/3, 2014 at 11:57 Comment(1)
As I said before... InvalidOperationException. Have fun figuring out what that means without the message itself. A new instance won't magically have it.Roseliaroselin
P
-1

For Logging purposes, certain applications may need to fetch the English exception message (besides displaying it in the usual client's UICulture).

For that purpose, the following code

  1. changes the current UICulture
  2. recreates the thrown Exception object using "GetType()" & "Activator.CreateInstance(t)"
  3. displays the new Exception object's Message in the new UICuture
  4. and then finally changes the current UICulture back to earlier UICulture.

        try
        {
            int[] a = { 3, 6 };
            Console.WriteLine(a[3]); //Throws index out of bounds exception
    
            System.IO.StreamReader sr = new System.IO.StreamReader(@"c:\does-not-exist"); // throws file not found exception
            throw new System.IO.IOException();
    
        }
        catch (Exception ex)
        {
    
            Console.WriteLine(ex.Message);
            Type t = ex.GetType();
    
            CultureInfo CurrentUICulture = System.Threading.Thread.CurrentThread.CurrentUICulture;
    
            System.Threading.Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo("en-US");
    
            object o = Activator.CreateInstance(t);
    
            System.Threading.Thread.CurrentThread.CurrentUICulture = CurrentUICulture; // Changing the UICulture back to earlier culture
    
    
            Console.WriteLine(((Exception)o).Message.ToString());
            Console.ReadLine();
    
         }
    
Prefigure answered 16/3, 2017 at 10:40 Comment(1)
this doesn't guarantee that the new object's exception message is the same that the thrown exception has. It may be totally different, and usually it is totally different. That's why we need the exception message.Farthermost
V
-2

Exception messages in English

try
{
    ......
}
catch (Exception ex)
{
      throw new UserFriendlyException(L("ExceptionmessagesinEnglish"));
}

then go Localization folder and place it in projectName.xml and add

<text name="ExceptionmessagesinEnglish">Exception Message in English</text>
Valerivaleria answered 9/1, 2020 at 8:19 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.