-
Notifications
You must be signed in to change notification settings - Fork 5k
Exception Handling / Error Logging in English #40427
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
This is a duplicate of microsoft/dotnet#474. |
This isn't a language request, but rather a runtime request. I'm going to transfer this over there, and hopefully they'll have an answer or can point you in the right direction. |
I couldn't figure out the best area label to add to this issue. If you have write-permissions please help me learn by adding exactly one area label. |
Does .NET Core runtime itself do any localisation for the exception message? Most of the exception messages I'm seeing doesn't seem to be in my Windows UI language (Korean) and even if they are they seem to originate from Windows API or something. |
@ChirChiara what version of.NET are you using?.NET Core's own resources aren't currently localized. What strings are getting logged and which code do they come from? |
Also you can force English resources if you set CultureInfo.CurrentUICulture to something like en-US Culture. Also, you can use the machine translation to translate the localized string. |
For the record, I'm presently stuck with .NET Framework 3.5 on the app in question. @333fred Thanks for moving it, I wasn't sure. @tarekgh I definitely don't want machine translation because that quickly becomes a game of "operator" where details can be lost in translation. Also, CultureInfo.CurrentUICulture does not work in my case because this did not become read-write until 4.6. |
You can use Thread.CurrentThread.CurrentUICulture instead. Side point, if you are talking about the full framework, then it is very unlikely such request will get added there. .NET core is our focus of future development and the full framework still supported but it is feature complete. |
Yes on the first point, no on the second. CultureInfo.DefaultThreadCurrentUICulture is not available in my version, and Thread.CurrentThread.CurrentUICulture comes with a security warning, but will probably be my only option for now. I agree with you that my code base will inevitably need to be updated, and that is planned for the future, but even in modern versions of .NET I don't see this ability to declare Exceptions be loggable in the language of your choice. I want to maintain that my users see the errors in whatever language they prefer, but the logs are belong to me (intentional grammatical error) and I need them in English. 😋 |
CultureInfo.DefaultThreadCurrentUICulture is supported since version 4.5. are you running on 4.0? if so, definitely you need to upgrade. |
3.5 SMH 🤦🏼♂️ Still trying to convince the PMs to let go of Windows XP support. 😫 I'm holding onto hope that we can finally do that on our next Major rev. |
I hope you'll succeed with your mission :-) do you mind if we close this issue as there is no action required here? feel free to send more questions if you need help with any issue you face. |
... permanently set the UI culture as English, and then explicitly set the culture when doing all formatting/ |
I don't believe I got an answer to my question though; or did I miss it? Does anything in the .NET multiverse natively support logging Exceptions (for Developer use) in English? |
There is nothing you can do without forcing the UI language on the app/running environment to English to guarantee getting English. Does the exception messages you are getting are the framework messages or app messages? |
Framework of course. My app doesn't rely on any other third party APIs etc, so all app messages are written by me and already in English. It's the Framework that is generating Exceptions in the native culture of the client machine. |
One other idea you may try is to uninstall the .NET Framework language packs from the machine. I don't have handy Windows XP machine but I believe you can do that from Control Panel, Add Or Remove programs options. |
🤦♂️ |
@Zymlex my suggestion was specific workaround for @Chiramisu case based on my question #40427 (comment) and not intended to be for any situation. If you have any constructive feedback, please share it. |
Tagging subscribers to this area: @dotnet/area-system-resources Issue DetailsI have an application that is used in other countries. When receiving exception / error logs, they're invariably in other languages that I can't understand. I've looked into this, but had no luck. How do we force our C# .NET programs to log errors in English? If this currently isn't supported, please add it. I've seen requests for this around the Interwebz going back over a decade. The user doesn't care about error logs. They are for developers; the vast majority of whom speak English.
|
~~ For The workaround shared previously did not work well. A new workaround is here https://gist.github.com/ericstj/c72b90b0c12f86b7918850ee276fac3b but will only work if the Windows machine is English or has the English language pack installed. |
@ericstj I am not sure if the proposed code is going to work. Windows depends on the thread locale. Setting |
I see, so instead of that we'd need to PINvoke to |
@tarekgh Did we miss an opportunity here to create a culture overload for that API? public static class Marshal
{
public static string GetPInvokeErrorMessage(int error, CultureInfo cultureInfo);
} |
Adding that overload might be interesting. It would have made this extension method easier to implement. Updated my sample above to use SetThreadLocale (note it requires the new source generator as well). Still not sure if this is actually changing the behavior of |
Yes, I think this will work.
I think such overload will be helpful. |
@tarekgh and @ericstj Filed #72546 for .NET 8 /cc @danmoseley |
I just learned that Windows localized machines is not guaranteed to have English resources. For such situation there will not be anyway getting the English resources with |
This comment was marked as outdated.
This comment was marked as outdated.
This means we may create a request for the Windows development team, aiming at always including English resources. |
However, |
@tfenise Are we expected to know or look up the error codes? If yes, is there a central page of all possible error codes? Even if the answer is yes, that's still way more work to figure out the actual error when you have to read through a log file. |
In the case of |
That would still be a manual step. Not very convenient.
That would certainly be a quick win that should be done. |
Please open an issue if you want to suggest that for SocketException. It does sound useful. I do not see how it would work for Win32Exception though, there is no enum (such an enum would be vast, eg., it would be all of winerror.h, etc) |
BTW, probably everyone knows this, but if it helps anyone --
|
Your assumption is correct, this enum contains about 2840 values: public enum EnumSystemErrorCode
{
[Description ("The operation completed successfully.")]
ERROR_SUCCESS = 0,
[Description ("Incorrect function.")]
ERROR_INVALID_FUNCTION = 1,
[Description ("The system cannot find the file specified.")]
ERROR_FILE_NOT_FOUND = 2,
[Description ("The system cannot find the path specified.")]
ERROR_PATH_NOT_FOUND = 3,
[Description ("The system cannot open the file.")]
ERROR_TOO_MANY_OPEN_FILES = 4,
[Description ("Access is denied.")]
ERROR_ACCESS_DENIED = 5,
...
[Description ("The length of the state manager setting name has exceeded the limit.")]
ERROR_STATE_SETTING_NAME_SIZE_LIMIT_EXCEEDED = 15817,
[Description ("The length of the state manager container name has exceeded the limit.")]
ERROR_STATE_CONTAINER_NAME_SIZE_LIMIT_EXCEEDED = 15818,
[Description ("This API cannot be used in the context of the caller's application type.")]
ERROR_API_UNAVAILABLE = 15841,
} If somebody wants the complete file, I can post it here or upload it to my github account. The problem in using the descriptions from the websites is, that they contain placeholders like: [Description ("{Missing System File} The required system file %hs is bad or missing.")]
ERROR_MISSING_SYSTEMFILE = 573,
[Description ("{Application Error} The exception %s (0x%08lx) occurred in the application at location 0x%08lx.")]
ERROR_UNHANDLED_EXCEPTION = 574, Therefore I had to combine the original exception message, which contains the data that was inserted to the placeholders, with the English exception message from the description. The final message looks like:
The code to create this message is: public static class EnumSystemErrorCodeHelper
{
public static string? GetDescription (this EnumSystemErrorCode i_systemErrorCode)
{
return DescriptionAttributeHelper.GetDescription (i_systemErrorCode);
}
}
public static class DescriptionAttributeHelper
{
public static string? GetDescription (Enum? i_member)
{
string? memberName = i_member?.ToString ();
if (i_member == null
|| string.IsNullOrEmpty (memberName))
throw new ArgumentNullException (nameof (i_member));
var memberInfos = i_member.GetType ().GetMember (memberName);
if (!memberInfos.Any ())
return null;
object[] attributes = memberInfos[0].GetCustomAttributes (typeof (DescriptionAttribute), false);
return attributes.Any ()
? ((DescriptionAttribute)attributes[0]).Description
: null;
}
}
public static class ExceptionHelper
{
/// ------------------------------------------------------------------
/// <summary>
/// Create a message text from the exception.
/// </summary>
/// <param name="i_exception"> The exception from which the message will be created. </param>
/// <param name="i_separator"> The separator text that will be inserted between different parts of the message. </param>
/// <param name="i_withInnerExceptions"> A flag that specifies whether the messages of inner exceptions should be added to the created message. </param>
/// <returns> A message text from the exception. </returns>
/// ------------------------------------------------------------------
public static string? GetMessage (this Exception? i_exception,
string i_separator = ", ",
bool i_withInnerExceptions = true)
{
if (i_exception == null)
return null;
string? newlineSeparator = i_separator.EqualsAny (CONSTS.CR, CONSTS.LF, CONSTS.CRLF)
? i_separator
: null;
string exceptionMessage = i_exception.Message;
var sbMessage = new StringBuilder (exceptionMessage);
if (!string.Equals (System.Threading.Thread.CurrentThread.CurrentCulture.TwoLetterISOLanguageName, "en", StringComparison.InvariantCultureIgnoreCase))
{
if (i_exception is System.ComponentModel.Win32Exception win32Exception)
{
var systemErrorCode = (EnumSystemErrorCode)win32Exception.ErrorCode;
if (!Enum.IsDefined (systemErrorCode))
systemErrorCode = (EnumSystemErrorCode)win32Exception.NativeErrorCode;
if (Enum.IsDefined (systemErrorCode))
{
string? description = DescriptionAttributeHelper.GetDescription (systemErrorCode);
if (!string.IsNullOrEmpty (description))
{
_ = sbMessage.Clear ()
.Append (description)
.Append (newlineSeparator ?? " ")
.Append ("(original message: '")
.Append (exceptionMessage)
.Append ("')");
}
}
}
}
switch (i_exception) // Do NOT change the order of the cases.
{
case System.Net.Sockets.SocketException socketException:
_ = sbMessage.Append ($"{i_separator}Socket Error = {socketException.SocketErrorCode}")
.Append ($"{i_separator}Socket Error Code = {socketException.ErrorCode} (0x{socketException.ErrorCode:X8})");
break;
case System.ComponentModel.Win32Exception win32Exception:
_ = sbMessage.Append ($"{i_separator}Error Code = {win32Exception.ErrorCode} (0x{win32Exception.ErrorCode:X8})")
.Append ($"{i_separator}Native Error Code = {win32Exception.NativeErrorCode} (0x{win32Exception.NativeErrorCode:X8})");
break;
case System.Runtime.InteropServices.ExternalException externalException:
_ = sbMessage.Append ($"{i_separator}Error Code = {externalException.ErrorCode} (0x{externalException.ErrorCode:X8})");
break;
case System.Net.Http.HttpRequestException httpRequestException:
_ = sbMessage.Append ($"{i_separator}Status Code = {httpRequestException.StatusCode}");
break;
}
if (newlineSeparator is null)
_ = sbMessage.Replace (CONSTS.CRLF, CONSTS.CommaSpace)
.Replace (CONSTS.CR, CONSTS.CommaSpace)
.Replace (CONSTS.LF, CONSTS.CommaSpace);
if (i_exception.InnerException != null
&& i_withInnerExceptions)
{
_ = sbMessage.Append (" Inner exception: {")
.Append (i_exception.InnerException.GetMessage (i_separator, i_withInnerExceptions))
.Append ("}");
}
return sbMessage.ToString ();
}
/// ------------------------------------------------------------------
/// <summary>
/// Create a detailed text from the exception. The text contains message, source and stacktrace from the given exception and all inner exceptions.
/// </summary>
/// <param name="i_exception"> The exception from which the text will be created. </param>
/// <param name="i_separator"> The separator text that will be inserted between different parts of the exception text. </param>
/// <returns> A detailed text from the exception. </returns>
/// ------------------------------------------------------------------
public static string GetText (this Exception? i_exception,
string i_separator = ", ")
{
if (i_exception == null)
return s_text_exceptionObjectMissing;
string? newlineSeparator = i_separator.EqualsAny (CONSTS.CR, CONSTS.LF, CONSTS.CRLF)
? i_separator
: null;
var exception = i_exception;
var sb = new StringBuilder ();
int levelOfInnerException = 0;
do
{
if (levelOfInnerException == 0)
{
_ = sb.Append (">>>>> Exception: ");
}
else
{
_ = sb.Append (i_separator);
_ = sb.Append ($">>>>> Inner Exception #{levelOfInnerException}: ");
}
_ = sb.Append (newlineSeparator);
_ = sb.AppendWithSeparator (exception.GetType ().FullName, i_separator);
_ = sb.AppendWithSeparator (exception.GetMessage (i_separator, false), i_separator);
if (exception.Source != null)
{
_ = sb.Append (">>> Source: ");
_ = sb.Append (newlineSeparator);
_ = sb.AppendWithSeparator (exception.Source, i_separator);
}
if (exception.StackTrace != null)
{
string stackTrace = exception.StackTrace;
if (newlineSeparator is null)
stackTrace = stackTrace.Replace (CONSTS.CRLF, CONSTS.CommaSpace)
.Replace (CONSTS.CR, CONSTS.CommaSpace)
.Replace (CONSTS.LF, CONSTS.CommaSpace);
_ = sb.Append (">>> Stack Trace: ");
_ = sb.Append (newlineSeparator);
_ = sb.Append (stackTrace);
}
exception = exception.InnerException;
levelOfInnerException++;
}
while (exception != null);
return sb.ToString ();
}
} |
In case it is useful to others -- what I do is just grep the SDK headers, which you will have if you have installed the C++ workload in Visual Studio, eg
the message is in a comment next to there. Of course, the numbers in those are not always in decimal. However the ERRLOOK.EXE tool that installs with Visual Studio at "C:\Program Files\Microsoft Visual Studio\2022\Preview\Common7\Tools\errlook.exe" can handle hex, etc. I am not sure which headers ERRLOOK.EXE has aggregated. I do not know where the sources are. |
The only concrete step identified in this issue so far is the suggested change to SocketsException. We can pass feedback on to Windows, but I do not thing we will change how they create and deploy Windows for this. |
At least this issue did not get closed yet... They closed mine and this one and this one When can we stop the need to translate something that resembles this (in dutch):
And figure out it meant something like
Or even worse (an example from @macmade)
Which according to him means
Maybe .Net 9 will finally give us |
For our localized app, we want english logs and localized error messages for the user. |
The catch is that you should rarely be displaying "raw" exception messages to the user (and never a stack trace), regardless of the language, but instead be displaying something that would be actionable for them. At that point, you'd be constructing messages and dialogs, so the language/culture of the exception should be irrelevant. |
I have an application that is used in other countries. When receiving exception / error logs, they're invariably in other languages that I can't understand. I've looked into this, but had no luck. How do we force our C# .NET programs to log errors in English? If this currently isn't supported, please add it. I've seen requests for this around the Interwebz going back over a decade.
The user doesn't care about error logs. They are for developers; the vast majority of whom speak English.
The text was updated successfully, but these errors were encountered: