【问题标题】:Exception messages in English?英文的异常信息?
【发布时间】:2010-09-17 13:43:06
【问题描述】:

我们通过将 Exception.Message 写入文件来记录系统中发生的任何异常。但是,它们是按照客户的文化编写的。土耳其语错误对我来说意义不大。

那么我们如何在不改变用户文化的情况下用英语记录任何错误消息呢?

【问题讨论】:

  • 你为什么不能这样切换: CultureInfo oldCulture = Thread.CurrentThread.CurrentCulture; Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("en"); // 在这里抛出新异常 => 文化是英文 Thread.CurrentThread.CurrentCulture = oldCulture;
  • @CheGueVerra 如果我可以预测会发生异常的点,那么我可以将线程文化切换到它周围,我也可以修复实际问题发生在那里。这整个问题不适用于您自己抛出的异常。

标签: c# .net exception localization


【解决方案1】:

这对我有用:

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

然后您可以通过调用新方法 ex.EnMessage(); 来使用它

【讨论】:

    【解决方案2】:

    设置Thread.CurrentThread.CurrentUICulture 将用于本地化异常。如果您需要两种异常(一种为用户,一种为您),您可以使用以下函数来翻译异常消息。它在 .NET-Libraries 资源中搜索原始文本以获取资源键,然后返回翻译后的值。但是有一个缺点我还没有找到好的解决方案:找不到资源中包含 {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)
    }
    

    【讨论】:

    • 如果异常包含格式化参数,那将不起作用。
    • 是的,正如我所说:“但有一个弱点我还没有找到一个好的解决方案:消息中包含 {0} 的资源将找不到。如果有人有一个好的解决方案,我将不胜感激。”
    【解决方案3】:

    英文异常信息

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

    然后转到 Localization 文件夹并将其放入 projectName.xml 并添加

    <text name="ExceptionmessagesinEnglish">Exception Message in English</text>
    

    【讨论】:

      【解决方案4】:

      我有同样的情况,我在这里和其他地方找到的所有答案都没有帮助或不令人满意:

      Thread.CurrentUICulture 更改了 .net 异常的语言,但它不用于 Win32Exception,它以 Windows UI 本身的语言使用 Windows 资源。所以我从来没有设法用英语而不是德语打印Win32Exception 的消息,甚至没有使用FormatMessage() 中描述的
      How to get Win32Exception in English?

      因此,我创建了自己的解决方案,它将不同语言的大部分现有异常消息存储在外部文件中。您不会以您想要的语言收到非常准确的信息,但您会收到该语言的信息,这比您目前获得的信息要多得多(这是您可能不懂的语言的信息)。

      这个类的静态函数可以在不同语言的 Windows 安装上执行: CreateMessages() 创建特定于文化的文本
      SaveMessagesToXML() 将它们保存到与创建或加载语言一样多的 XML 文件中
      LoadMessagesFromXML() 加载所有带有特定语言消息的 XML 文件

      在使用不同语言的不同 Windows 安装上创建 XML 文件时,您很快就会拥有所需的所有语言。
      当您安装了多个 MUI 语言包时,也许您可​​以在 1 个 Windows 上创建不同语言的文本,但我还没有测试过。

      经过 VS2008 测试,可以使用。欢迎提出意见和建议!

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

      【讨论】:

      • Thread.CurrentUICulture also 改变了 UI 的语言,使它成为一个糟糕的选择。一个典型的例子是消息框上的 Yes/No/OK/Cancel 按钮。
      【解决方案5】:

      我会想到以下方法之一:

      1. 异常只能由您读取,即它们不是客户端功能,因此您可以使用在土耳其模式下运行时不会更改的硬连线非本地化字符串。

      2. 包含错误代码,例如0x00000001 每个错误,以便您可以在英文表格中轻松查找。

      【讨论】:

      • 当它们是 .net 框架的内部组件抛出的异常时,这将无济于事。这整个问题不适用于您自己抛出的异常;显然,程序员选择要包含在 那些 中的消息。
      【解决方案6】:

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

      【讨论】:

      • 感谢这似乎可以将德语例外翻译成英语。
      • 啊!在我基于@Vortex852456 的实现创建了我的版本后,我注意到了您的回复。我使用了 LiNQ,最后使用了 Regex.MatchString.Join(Environment.NewLine, ...)。我还排除了“全球化。*”资源以避免混淆,您的实现可以从中受益。
      【解决方案7】:

      这里的解决方案不需要任何编码,甚至适用于加载过早而无法通过代码更改的异常文本(例如,mscorlib 中的那些)。

      它可能并不总是适用于所有情况(这取决于您的设置,因为您需要能够在主 .exe 文件之外创建一个 .config 文件)但这对我有用。因此,只需在 dev 中创建一个 app.config(或在生产中创建一个 [myapp].exe.configweb.config),其中包含例如以下行:

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

      这样做是告诉框架重定向 mscorlib 的资源和 System.Xml 的资源的程序集绑定,对于 1 到 999 之间的版本,法语(文化设置为“fr”)到一个...不存在的程序集(任意版本 999)。

      因此,当 CLR 为这两个程序集(mscorlib 和 System.xml)寻找法语资源时,它不会找到它们并优雅地回退到英语。根据您的上下文和测试,您可能希望将其他程序集添加到这些重定向(包含本地化资源的程序集)。

      当然,我认为 Microsoft 不支持此功能,因此使用风险自负。好吧,如果您发现问题,您可以删除此配置并检查它是否不相关。

      【讨论】:

      • 在需要来自测试运行工具的英文输出时工作。
      • 试过这个,但它对我不起作用。 .net 中是否还有其他资源文件?我在哪里可以找到它们?
      • 查看 c:\Windows\Microsoft.NET\Framework\v4.0.30319。每种语言都有一个 2 个字母的文件夹。请记住将上面答案中的“fr”替换为正在使用的实际语言。 “no”表示挪威语,“da”表示丹麦语,“sv”表示瑞典语等。
      • 要创建完整列表,请查看该文件夹。它大约有 120 个资源文件。将它们中的每一个添加到配置中。这似乎是目前 Windows 10 和更新版本的唯一解决方案,因为无法再在较新的 Windows 中卸载 .Net 语言包(它是操作系统的一部分)。它现在甚至放在 GAC 中,因此删除这些语言文件夹似乎不起作用。
      【解决方案8】:

      出于日志记录的目的,某些应用程序可能需要获取英文异常消息(除了在普通客户端的 UICulture 中显示它)。

      为此,下面的代码

      1. 改变当前的 UICulture
      2. 使用“GetType()”和“Activator.CreateInstance(t)”重新创建抛出的异常对象
      3. 在新的 UICuture 中显示新的异常对象的消息
      4. 然后最后将当前的 UICulture 改回之前的 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();
        
             }
        

      【讨论】:

      • 这并不能保证新对象的异常消息与抛出的异常相同。它可能完全不同,而且通常完全不同。这就是我们需要异常消息的原因。
      【解决方案9】:

      这可能是一个有争议的问题,但您可以将其设置为 Invariant,而不是将其设置为 en-US。在Invariant 文化中,错误消息是英文的。

      Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;
      Thread.CurrentThread.CurrentUICulture = CultureInfo.InvariantCulture;
      

      它的优点是看起来没有偏见,尤其是对于非美式英语语言环境。 (a.k.a. 避免同事的冷嘲热讽)

      【讨论】:

      • 我们应该在我们的 ASP.NET 项目中哪里写这些行?谢谢。
      • 我将在顶部的 Application_Start 中提出建议。这将使整个项目以英语运行。如果它只是你想要的错误信息,你可以做一个覆盖函数并在每个catch中调用它。
      • 这不也让标准的消息框按钮变成英文吗?这可能不是我们想要的行为。
      • 如果您的应用程序完全未本地化,当然可以。否则我看不到这种方法的使用,除了在愤世嫉俗的同事的雷达下飞行时削弱所有定位。
      【解决方案10】:

      .NET 框架分为两部分:

      1. .NET 框架本身
      2. .NET 框架语言包

      .NET 框架本身中的所有文本(例如异常消息、MessageBox 上的按钮标签等)都是英文的。语言包具有本地化文本。

      根据您的具体情况,一个解决方案是卸载语言包(即告诉客户这样做)。在这种情况下,例外文本将为英文。但请注意,所有其他框架提供的文本也都是英文的(例如 MessageBox 上的按钮标签、ApplicationCommands 的键盘快捷键)。

      【讨论】:

      • 谢谢!!讽刺的是,卸载对话框使用的是卸载包的语言,而不是本地语言。旁注:语言包似乎每隔几个月就会返回一次。我还没弄清楚为什么,但我猜是更新/升级
      • @ChocoSmith 每次通过 Windows Update 更新 .NET Framework 时,都会再次安装语言包。
      • 要求客户卸载他们自己语言的语言包不是一个可行的解决方案。
      • @Nyerguds 向客户显示系统异常不是一个可行的解决方案。
      • @Icet 不确定这有什么关系。没有什么说你必须展示它;您可以将其登录到文件中并向用户显示一条好消息,告诉他们将日志文件发送给您。这与潜在的问题无关,即如果该用户恰好是中国人或俄罗斯人,那么作为程序员的您最终必须处理中文或俄罗斯人的错误消息。
      【解决方案11】:

      使用扩展方法覆盖catch块中的异常消息,检查抛出的消息是否来自代码,如下所述。

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

      【讨论】:

      • 正如我之前所说...InvalidOperationException。在没有消息本身的情况下弄清楚这意味着什么,玩得开心。新实例不会神奇地拥有它。
      【解决方案12】:

      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 默认(安装?)语言。

      【讨论】:

      • 问题解决了。在我的情况下波兰语 UI,使用 Vistalizator 程序安装了 en MUI 语言包 ~260MB。
      【解决方案13】:

      我知道这是一个老话题,但我认为我的解决方案可能与在网络搜索中偶然发现它的任何人都非常相关:

      您可以在异常记录器中记录 ex.GetType.ToString,这将保存异常类的名称。我希望一个类的名称应该独立于语言,因此总是用英语表示(例如“System.FileNotFoundException”),尽管目前我无法访问外语系统来测试想法。

      如果您真的想要错误消息文本,您可以创建一个包含所有可能的异常类名称及其等效消息的字典,使用您喜欢的任何语言,但对于英语,我认为类名称就足够了。

      【讨论】:

      • 不起作用。我得到了一个InvalidOperationException,由System.Xml.XmlWellFormedWriter 抛出。您尝试在不阅读消息的情况下猜测发生了什么特定错误。可能是一千种不同的东西。
      【解决方案14】:

      您可以在unlocalize.com搜索原始异常消息

      【讨论】:

      • 尝试搜索一些中文异常信息,总是告诉我No records found
      • 错误的选择。当我将我的例外发送到 Google Analytics(或其他云服务)时,对于同一个例外,我会有不同的例外组,但语言不同。而且我将无法按每个异常的计数进行排序,因为它不能反映真实的计数(英文 100,中文 77,韩文 80……等等)
      • 我记得在我只是将本地化异常消息转储到谷歌时多次找到这个漂亮的网站,现在它不再可用了。
      • 虽然此链接可能会回答问题,但最好在此处包含答案的基本部分并提供链接以供参考。如果链接页面发生更改,仅链接答案可能会失效。 - From Review
      • @EJoshuaS-ReinstateMonica:这不是提供进一步说明的文章的链接。它是一个在线实用程序的链接,旨在通过将搜索引擎与翻译查找相结合来解决问题。在这种情况下,“在此处包含答案的基本部分”是不切实际的。
      【解决方案15】:

      您应该记录调用堆栈,而不仅仅是错误消息(IIRC,简单的 exception.ToString() 应该为您执行此操作)。从那里,您可以准确地确定异常的来源,并通常推断出它是哪个异常。

      【讨论】:

      • 我们正在记录消息和堆栈跟踪。但如果信息清晰,那就容易多了。
      【解决方案16】:
      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;
      

      没有变通方法。

      Tks :)

      【讨论】:

      • 你忘记了 ;
      【解决方案17】:

      这个问题可以部分解决。框架异常代码根据当前线程语言环境从其资源中加载错误消息。在某些例外情况下,这会在访问 Message 属性时发生。

      对于这些例外情况,您可以通过在记录消息时将线程区域设置短暂切换到 en-US 来获取消息的完整美国英语版本(预先保存原始用户区域设置并在之后立即恢复)。

      在单独的线程上执行此操作会更好:这样可以确保不会有任何副作用。例如:

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

      但是,正如Joe 在对此回复的早期版本的评论中正确指出的那样,在引发异常时,已经(部分)从语言资源中加载了一些消息。

      这适用于例如引发 ArgumentNullException("foo") 异常时生成的消息的“参数不能为空”部分。在这些情况下,即使使用上述代码,消息仍将显示(部分)本地化。

      除了使用不切实际的 hack,例如在以 en-US 语言环境开始的线程上运行所有非 UI 代码之外,您似乎无能为力:.NET Framework 异常代码没有用于覆盖错误消息区域设置的工具。

      【讨论】:

      • 您的示例适用于 FileNotFoundException,因为在访问 Message 属性时检索消息资源,而不是在引发异常时检索。但这并非适用于所有异常(例如,尝试 throw new ArgumentNullException("paramName"))
      • 我很困惑。我已经尝试按照您的回答进行测试,我希望我的例外是法语,所以我做了t.CurrentUICulture = new System.Globalization.CultureInfo("fr-FR");t.CurrentCulture = new System.Globalization.CultureInfo("fr-FR");,但结果是英语的例外......
      • @VitalyB 本地化的异常文本是 .NET 框架语言包的一部分。因此,如果您没有安装法语语言包,您将无法获得翻译文本。
      • 至少在 .NET 4.5 中,所有异常都使用 Environment.GetResourceString("...") 实例化,因此您的解决方案不再适用。最好的办法是使用您自己的(英文)消息文本引发自定义异常,并使用 InnerException 属性来保留旧的。
      • 反射来获取异常类型名称可能会变得很方便。
      猜你喜欢
      • 1970-01-01
      • 2011-12-12
      • 1970-01-01
      • 2014-02-17
      • 1970-01-01
      • 1970-01-01
      • 2013-10-14
      • 2011-09-14
      相关资源
      最近更新 更多