Exception messages in English?

Viewed 104893

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?

17 Answers

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.

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.

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 :)

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;
}

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.

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();

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.

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>
Related