Ausnahmemeldungen in Englisch?

298

Wir protokollieren alle Ausnahmen, die in unserem System auftreten, indem wir die Exception.Message in eine Datei schreiben. Sie sind jedoch in der Kultur des Kunden geschrieben. Und türkische Fehler bedeuten mir nicht viel.

Wie können wir also Fehlermeldungen auf Englisch protokollieren, ohne die Benutzerkultur zu ändern?

Carra
quelle
8
Warum können Sie nicht so wechseln: CultureInfo oldCulture = Thread.CurrentThread.CurrentCulture; Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture ("en"); // hier eine neue Ausnahme auslösen => Kultur ist in Englisch Thread.CurrentThread.CurrentCulture = oldCulture;
CheGueVerra
93
Ich kenne keinen Entwickler, der sich über nicht-englische Ausnahmemeldungen freut: S ..
Zéiksz
3
@ Zéiksz Wenn Sie über englischsprachige Länder hinausblicken, werden Sie viele davon finden: D. Problem ist kein nicht englischer Text, Problem ist eine Sprache, die Sie nicht verstehen können. Nachrichten in Ihrer Muttersprache (unter der Annahme einer ordnungsgemäßen Übersetzung) sind vollkommen in Ordnung.
Alejandro
31
@Alejandro Eine Ausnahmemeldung aus einer Muttersprache ins Englische übersetzen zu müssen, um sie zu googeln, ist ein noch größeres Problem. meiner bescheidenen Meinung nach.
Antoine Meltzheim
18
Welcher Idiot bei Microsoft hatte die Idee, Fehlermeldungen zu übersetzen, die nur für Entwickler sind. Sogar Begriffe, die in der Programmiersprache wie ein Schlüssel in einem Wörterbuch verwendet werden, werden übersetzt. (Schlüssel wird nicht im Wörterbuch gefunden wird Sleutel in niet gevonden in de bibliotheek auf Niederländisch). Ich möchte die Betriebssystemsprache dafür nicht ändern ...
Roel

Antworten:

66

Dieses Problem kann teilweise umgangen werden. Der Framework-Ausnahmecode lädt die Fehlermeldungen aus seinen Ressourcen basierend auf dem aktuellen Thread-Gebietsschema. Bei einigen Ausnahmen geschieht dies zum Zeitpunkt des Zugriffs auf die Message-Eigenschaft.

Für diese Ausnahmen können Sie die vollständige englische Version der Nachricht erhalten, indem Sie das Thread-Gebietsschema während der Protokollierung kurz auf en-US ändern (das ursprüngliche Gebietsschema des Benutzers vorher speichern und unmittelbar danach wiederherstellen).

Dies in einem separaten Thread zu tun ist noch besser: Dadurch wird sichergestellt, dass keine Nebenwirkungen auftreten. Beispielsweise:

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

Wo die ExceptionLogger-Klasse ungefähr so ​​aussieht:

class ExceptionLogger
{
  Exception _ex;

  public ExceptionLogger(Exception ex)
  {
    _ex = ex;
  }

  public void DoLog()
  {
    Console.WriteLine(_ex.ToString()); //Will display en-US message
  }
}

Wie Joe jedoch in einem Kommentar zu einer früheren Überarbeitung dieser Antwort richtig hervorhebt, werden einige Nachrichten zum Zeitpunkt des Auslösens der Ausnahme bereits (teilweise) aus den Sprachressourcen geladen.

Dies gilt für den Teil 'Parameter darf nicht null sein' der Nachricht, der beispielsweise generiert wird, wenn eine ArgumentNullException-Ausnahme ("foo") ausgelöst wird. In diesen Fällen wird die Nachricht auch bei Verwendung des obigen Codes (teilweise) lokalisiert angezeigt.

Abgesehen von der Verwendung unpraktischer Hacks, z. B. dem Ausführen Ihres gesamten Nicht-UI-Codes in einem Thread mit dem Gebietsschema en-US, scheint es nicht viel zu geben, was Sie dagegen tun können: Der .NET Framework-Ausnahmecode hat keine Möglichkeiten zum Überschreiben des Gebietsschemas für Fehlermeldungen.

mdb
quelle
10
Ihr Beispiel funktioniert für eine FileNotFoundException, da die Nachrichtenressource beim Zugriff auf die Message-Eigenschaft abgerufen wird und nicht, wenn die Ausnahme ausgelöst wird. Dies gilt jedoch nicht für alle Ausnahmen (z. B. versuchen Sie, eine neue ArgumentNullException ("paramName") auszulösen)
Joe
3
Ich bin verwirrt. Ich habe versucht, Ihrer Antwort zu folgen und um sie zu testen, wollte ich meine Ausnahme auf Französisch, also tat ich es t.CurrentUICulture = new System.Globalization.CultureInfo("fr-FR");und t.CurrentCulture = new System.Globalization.CultureInfo("fr-FR");doch war die resultierende Ausnahme auf Englisch ...
VitalyB
7
@VitalyB Die lokalisierten Ausnahmetexte sind Teil der .NET Framework-Sprachpakete. Wenn Sie also das französische Sprachpaket nicht installiert haben, erhalten Sie die übersetzten Texte nicht.
Daniel Rose
7
Zumindest mit .NET 4.5 werden alle Ausnahmen instanziiert, Environment.GetResourceString("...")sodass Ihre Lösung nicht mehr funktioniert. Am besten lösen Sie eine benutzerdefinierte Ausnahme mit Ihrem eigenen (englischen) Nachrichtentext aus und verwenden die InnerException-Eigenschaft, um die alte beizubehalten.
webber2k6
1
Das Nachdenken über die Namen der Ausnahmetypen kann nützlich sein.
Guillermo Prandi
67

Sie können unter unlocalize.com nach der ursprünglichen Ausnahmemeldung suchen

user461128
quelle
5
Ich habe versucht, einige chinesische Ausnahmemeldungen zu durchsuchen No records found.
Tyler Long
1
Schlechte Wahl. Wenn ich meine Ausnahmen an Google Analytics (oder einen anderen Cloud-Dienst) sende, habe ich unterschiedliche Gruppen von Ausnahmen für dieselbe Ausnahme, aber unterschiedliche Sprachen. Und ich werde nicht in der Lage sein, nach Anzahl jeder Ausnahme zu sortieren, da sie nicht die tatsächliche Anzahl widerspiegelt (100 auf Englisch, 77 auf Chinesisch, 80 auf Koreanisch ... usw.)
Artemious
Ich erinnere mich, dass ich diese schöne Website oft gefunden habe, als ich einfach lokalisierte Ausnahmemeldungen in Google abgelegt habe. Jetzt ist sie nicht mehr verfügbar.
Martin Braun
40

Ein umstrittener Punkt vielleicht, aber anstatt die Kultur festzulegen en-US, können Sie sie festlegen Invariant. In der InvariantKultur sind die Fehlermeldungen auf Englisch.

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

Es hat den Vorteil, dass es nicht voreingenommen aussieht, insbesondere für nicht amerikanischsprachige englischsprachige Gebiete. (aka vermeidet abfällige Bemerkungen von Kollegen)

MPelletier
quelle
1
Wo sollen wir diese Zeilen in unser ASP.NET-Projekt schreiben? Vielen Dank.
Jason
2
Ich werde oben in Application_Start vorschlagen. Dadurch wird das gesamte Projekt auf Englisch ausgeführt. Wenn Sie nur Fehlermeldungen wünschen, können Sie eine Deckungsfunktion erstellen und in jeder aufrufen catch.
MPelletier
5
Werden dadurch nicht auch Standardschaltflächen für Meldungsfelder in Englisch angezeigt? Das ist möglicherweise kein gewünschtes Verhalten.
Nyerguds
12

Hier ist eine Lösung, die keine Codierung erfordert und auch für Texte von Ausnahmen funktioniert, die zu früh geladen wurden, als dass wir sie per Code ändern könnten (z. B. in mscorlib).

Es ist möglicherweise nicht immer in jedem Fall anwendbar (es hängt von Ihrem Setup ab, da Sie in der Lage sein müssen, eine .config-Datei neben der .exe-Hauptdatei zu erstellen), aber das funktioniert für mich. Erstellen Sie einfach ein app.configin dev (oder ein [myapp].exe.configoder web.configin der Produktion), das beispielsweise die folgenden Zeilen enthält:

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

Dies weist das Framework an, Assembly-Bindungen für mscorlibdie Ressourcen und System.Xmldie Ressourcen für Versionen zwischen 1 und 999 in Französisch (Kultur ist auf " fr" gesetzt) ​​an eine Assembly umzuleiten , die ... nicht existiert (eine beliebige) Version 999).

Wenn die CLR nach französischen Ressourcen für diese beiden Assemblys (mscorlib und System.xml) sucht, findet sie diese nicht und greift ordnungsgemäß auf Englisch zurück. Abhängig von Ihrem Kontext und Ihren Tests möchten Sie diesen Weiterleitungen möglicherweise andere Assemblys hinzufügen (Assemblys, die lokalisierte Ressourcen enthalten).

Natürlich glaube ich nicht, dass dies von Microsoft unterstützt wird. Verwenden Sie es daher auf eigenes Risiko. Wenn Sie ein Problem feststellen, können Sie diese Konfiguration einfach entfernen und überprüfen, ob sie nicht in Zusammenhang steht.

Simon Mourier
quelle
1
Funktioniert, wenn eine englische Ausgabe von Test-Runner-Tools benötigt wird.
smg
Versuchte dies, aber es hat bei mir nicht funktioniert. Gibt es andere Ressourcendateien in .net? Wo finde ich sie?
BluE
1
Suchen Sie in c: \ Windows \ Microsoft.NET \ Framework \ v4.0.30319. Jede Sprache hat dort einen Ordner mit 2 Buchstaben. Denken Sie daran, "fr" in der obigen Antwort durch die tatsächlich verwendete Sprache zu ersetzen. "nein" für norwegisch, "da" für dänisch, "sv" für schwedisch usw.
Wolf5
Schauen Sie sich diesen Ordner an, um eine vollständige Liste zu erstellen. Es sind ungefähr 120 Ressourcendateien. Fügen Sie jeden von ihnen in die Konfiguration ein. Dies scheint derzeit die einzige Lösung für Windows 10 und neuer zu sein, da es keine Möglichkeit mehr gibt, die .NET-Sprachpakete in neueren Fenstern (die Teil des Betriebssystems sind) zu deinstallieren. Es befindet sich jetzt sogar im GAC, sodass das Entfernen dieser Sprachordner nicht zu funktionieren scheint.
Wolf5
10

In Windows muss die gewünschte UI-Sprache installiert sein. Wenn dies nicht der Fall ist, kann es nicht auf magische Weise wissen, was die übersetzte Nachricht ist.

In einem Windows 7 Ultimate in den USA mit installiertem pt-PT wird der folgende Code angezeigt:

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;

Erzeugt Nachrichten in pt-PT, en-US und en-US. Da keine französischen Kulturdateien installiert sind, wird standardmäßig die Windows-Standardsprache (installiert?) Verwendet.

Danobrega
quelle
Damit ist das Problem gelöst. Polnische Benutzeroberfläche in meiner Situation, installiert in MUI-Sprachpaketen ~ 260 MB, mit dem Vistalizator-Programm.
Krzysztof Szynter
5

Ich weiß, dass dies ein altes Thema ist, aber ich denke, dass meine Lösung für jeden relevant sein kann, der bei einer Websuche darauf stößt:

Im Ausnahmeprotokollierer können Sie ex.GetType.ToString protokollieren, wodurch der Name der Ausnahmeklasse gespeichert wird. Ich würde erwarten, dass der Name einer Klasse unabhängig von der Sprache sein sollte und daher immer in Englisch dargestellt wird (z. B. "System.FileNotFoundException"), obwohl ich derzeit keinen Zugriff auf ein Fremdsprachen-System habe, um das zu testen Idee.

Wenn Sie den Text der Fehlermeldung auch wirklich möchten, können Sie ein Wörterbuch aller möglichen Ausnahmeklassennamen und ihrer entsprechenden Nachrichten in einer beliebigen Sprache erstellen, aber für Englisch halte ich den Klassennamen für vollkommen ausreichend.

Barbar
quelle
5
Funktioniert nicht Ich habe eine InvalidOperationException, von geworfen System.Xml.XmlWellFormedWriter. Sie versuchen zu erraten, welcher bestimmte Fehler aufgetreten ist, ohne die Nachricht zu lesen. Könnten tausend verschiedene Dinge sein.
Nyerguds
4
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;

Ohne Workarounds.

Tks :)


quelle
du hast das vergessen;
KansaiRobot
4

Die Einstellung Thread.CurrentThread.CurrentUICulturewird verwendet, um die Ausnahmen zu lokalisieren. Wenn Sie zwei Arten von Ausnahmen benötigen (eine für den Benutzer, eine für Sie), können Sie die Ausnahmemeldung mit der folgenden Funktion übersetzen. In den .NET-Libraries-Ressourcen wird nach dem Originaltext gesucht, um den Ressourcenschlüssel zu erhalten und dann den übersetzten Wert zurückzugeben. Aber es gibt eine Schwäche, für die ich noch keine gute Lösung gefunden habe: Nachrichten, die {0} in Ressourcen enthalten, werden nicht gefunden. Wenn jemand eine gute Lösung hat, wäre ich dankbar.

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)
}
Vortex852456
quelle
Das funktioniert nicht, wenn die Ausnahme einen formatierten Parameter enthält.
Nick Berardi
Ja, wie gesagt: "Aber es gibt eine Schwäche, bei der ich noch keine gute Lösung gefunden habe: Nachrichten, die {0} in Ressourcen enthalten, werden nicht gefunden. Wenn jemand eine gute Lösung hat, wäre ich dankbar."
Vortex852456
3

Das .NET Framework besteht aus zwei Teilen:

  1. Das .NET Framework selbst
  2. Die .NET Framework-Sprachpakete

Alle Texte (z. B. Ausnahmemeldungen, Schaltflächenbeschriftungen in einer MessageBox usw.) sind im .NET Framework selbst in englischer Sprache verfügbar. Die Sprachpakete enthalten die lokalisierten Texte.

Abhängig von Ihrer genauen Situation besteht eine Lösung darin, die Sprachpakete zu deinstallieren (dh den Client dazu aufzufordern). In diesem Fall sind die Ausnahmetexte in englischer Sprache. Beachten Sie jedoch, dass alle anderen vom Framework bereitgestellten Texte ebenfalls englisch sind (z. B. die Schaltflächenbeschriftungen in einer MessageBox, Tastaturkürzel für ApplicationCommands).

Daniel Rose
quelle
Vielen Dank!! Ich finde es ironisch, dass der Deinstallationsdialog in der Sprache des Deinstallationspakets und nicht in der Landessprache ist. Randnotiz: Die Sprachpakete scheinen alle paar Monate zurückzukehren. Ich habe nicht herausgefunden warum, aber ich vermute ein Update / Upgrade
Choco Smith
@ChocoSmith Bei jedem Update von .NET Framework über Windows Update wird das Sprachpaket erneut installiert.
Daniel Rose
5
Kunden zu bitten, Sprachpakete für ihre eigene Sprache zu deinstallieren, ist keine praktikable Lösung.
Nyerguds
2

Ich würde mir einen dieser Ansätze vorstellen:

  1. Die Ausnahmen werden immer nur von Ihnen gelesen, dh sie sind keine Clientfunktion. Sie können also festverdrahtete, nicht lokalisierte Zeichenfolgen verwenden, die sich im türkischen Modus nicht ändern.

  2. Fügen Sie z. B. 0x00000001jedem Fehler einen Fehlercode hinzu, damit Sie ihn problemlos in einer englischen Tabelle nachschlagen können.

morechilli
quelle
9
Das hilft nicht viel, wenn es sich um Ausnahmen handelt, die von internen Komponenten des .net-Frameworks ausgelöst werden . Dieses ganze Problem gilt nicht für Ausnahmen, die Sie selbst auslösen. Offensichtlich wählt der Programmierer aus, welche Nachricht in diese aufgenommen werden soll .
Nyerguds
1

Basierend auf der Undercover1989-Antwort, berücksichtigt jedoch Parameter und wenn Nachrichten aus mehreren Ressourcenzeichenfolgen bestehen (wie Argumentausnahmen).

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;
}
jan
quelle
1

Ich hatte die gleiche Situation und alle Antworten, die ich hier und anderswo fand, halfen nicht oder waren nicht zufriedenstellend:

Das Thread.CurrentUICultureändert die Sprache der .net-Ausnahmen, jedoch nicht für Win32Exception, die Windows-Ressourcen in der Sprache der Windows-Benutzeroberfläche selbst verwendet. Daher habe ich es nie geschafft, die Nachrichten Win32Exceptionauf Englisch statt auf Deutsch zu drucken , auch nicht mit FormatMessage()der Beschreibung unter
Wie bekomme ich Win32Exception auf Englisch?

Aus diesem Grund habe ich meine eigene Lösung erstellt, in der die meisten vorhandenen Ausnahmemeldungen für verschiedene Sprachen in externen Dateien gespeichert sind. Sie erhalten nicht die sehr genaue Nachricht in Ihrer gewünschten Sprache, aber Sie erhalten eine Nachricht in dieser Sprache, die viel mehr ist als derzeit (eine Nachricht in einer Sprache, die Sie wahrscheinlich nicht verstehen).

Die statischen Funktionen dieser Klasse können in Windows-Installationen mit verschiedenen Sprachen ausgeführt werden: Beim CreateMessages()Erstellen der kulturspezifischen Texte werden
SaveMessagesToXML()diese in so vielen XML-Dateien gespeichert, wie Sprachen erstellt oder geladen
LoadMessagesFromXML()werden. Alle XML-Dateien werden mit sprachspezifischen Nachrichten geladen

Wenn Sie die XML-Dateien in verschiedenen Windows-Installationen mit verschiedenen Sprachen erstellen, stehen Ihnen bald alle benötigten Sprachen zur Verfügung.
Vielleicht können Sie die Texte für verschiedene Sprachen unter 1 Windows erstellen, wenn Sie mehrere MUI-Sprachpakete installiert haben, aber das habe ich noch nicht getestet.

Getestet mit VS2008, gebrauchsfertig. Kommentare und Vorschläge sind willkommen!

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");
  }
}
Tobias Knauss
quelle
1
Dadurch wird Thread.CurrentUICulture auch die Sprache der Benutzeroberfläche geändert, was sie zu einer schrecklichen Option macht. Ein klassisches Beispiel sind die Schaltflächen Ja / Nein / OK / Abbrechen im Meldungsfeld.
Nyerguds
0

Ausnahmemeldungen in Englisch

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

Gehen Sie dann zum Lokalisierungsordner, platzieren Sie ihn in projectName.xml und fügen Sie ihn hinzu

<text name="ExceptionmessagesinEnglish">Exception Message in English</text>
Nabeel Haxxan
quelle
-1

Sie sollten den Aufrufstapel statt nur einer Fehlermeldung protokollieren (IIRC, einfache Ausnahme. ToString () sollte dies für Sie tun). Von dort aus können Sie genau bestimmen, woher die Ausnahme stammt, und in der Regel ableiten, um welche Ausnahme es sich handelt.

Branko Dimitrijevic
quelle
3
Wir protokollieren die Nachricht und die Stapelverfolgung. Aber es ist viel einfacher, wenn die Botschaft klar ist.
Carra
-1

Überschreiben Sie die Ausnahmemeldung im catch-Block mithilfe der Erweiterungsmethode. Überprüfen Sie, ob die ausgelöste Nachricht aus dem Code stammt oder nicht, wie unten erwähnt.

    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();
        }
user3472484
quelle
1
Wie ich schon sagte ... InvalidOperationException. Viel Spaß beim Herausfinden, was das ohne die Nachricht selbst bedeutet. Eine neue Instanz wird es nicht auf magische Weise haben.
Nyerguds
-1

Für Protokollierungszwecke müssen bestimmte Anwendungen möglicherweise die englische Ausnahmemeldung abrufen (abgesehen davon, dass sie in der UICulture des üblichen Clients angezeigt wird).

Zu diesem Zweck den folgenden Code

  1. ändert die aktuelle UICulture
  2. erstellt das ausgelöste Exception-Objekt mit "GetType ()" & "Activator.CreateInstance (t)" neu
  3. Zeigt die Nachricht des neuen Ausnahmeobjekts in der neuen UICuture an
  4. und ändert dann schließlich die aktuelle UICulture zurück zu früherer 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();
    
         }
Ron16
quelle
1
Dies garantiert nicht, dass die Ausnahmemeldung des neuen Objekts mit der ausgelösten Ausnahme übereinstimmt. Es kann völlig anders sein, und normalerweise ist es völlig anders. Deshalb brauchen wir die Ausnahmemeldung.
Artemious