Wiadomości wyjątkowe w języku angielskim?

298

Rejestrujemy wszelkie wyjątki, które zdarzają się w naszym systemie, pisząc do pliku wyjątek. Są one jednak zapisane w kulturze klienta. A błędy tureckie niewiele dla mnie znaczą.

Jak więc możemy rejestrować komunikaty o błędach w języku angielskim bez zmiany kultury użytkowników?

Carra
źródło
8
Dlaczego nie możesz tak zrobić: CultureInfo oldCulture = Thread.CurrentThread.CurrentCulture; Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture ("en"); // wyrzuć nowy wyjątek tutaj => Kultura jest w języku angielskim Thread.CurrentThread.CurrentCulture = oldCulture;
CheGueVerra
93
Nie znam żadnego programisty, który
byłby
3
@ Zéiksz Spójrz poza kraje anglojęzyczne, a znajdziesz ich wiele: D. Problem nie jest tekstem w języku innym niż angielski, problemem jest język, którego nie rozumiesz. Wiadomości w twoim ojczystym języku (zakładając prawidłowe tłumaczenie) są w porządku.
Alejandro
31
@Alejandro Konieczność przetłumaczenia komunikatu wyjątku z jednego języka ojczystego na angielski w celu google to jeszcze większy problem w dupie. moim zdaniem.
Antoine Meltzheim,
18
Który idiota w Microsoft wpadł na pomysł przetłumaczenia komunikatów o błędach, które są przeznaczone tylko dla programistów. Tłumaczone są nawet terminy używane w języku programowania jak klucz w słowniku. (Klucz nie został znaleziony w Słowniku staje się Sleutel in niet gevonden in de bibliotheek w języku niderlandzkim). Nie chcę zmieniać języka systemu operacyjnego dla tego ...
Roel

Odpowiedzi:

66

Ten problem można częściowo obejść. Kod wyjątku środowiska ładuje komunikaty o błędach ze swoich zasobów w oparciu o bieżące ustawienia regionalne wątku. W przypadku niektórych wyjątków dzieje się to w momencie uzyskania dostępu do właściwości Message.

W przypadku tych wyjątków możesz uzyskać pełną wersję wiadomości w języku angielskim, krótko zmieniając ustawienia regionalne wątku na en-US podczas logowania (wcześniej zapisując oryginalne ustawienia narodowe użytkownika i natychmiast przywracając je).

Robienie tego w osobnym wątku jest jeszcze lepsze: zapewnia to, że nie będzie żadnych skutków ubocznych. Na przykład:

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

Gdzie klasa ExceptionLogger wygląda mniej więcej tak:

class ExceptionLogger
{
  Exception _ex;

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

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

Jednak jak Joe słusznie wskazuje w komentarzu do wcześniejszej wersji tej odpowiedzi, niektóre wiadomości są już (częściowo) ładowane z zasobów językowych w momencie zgłoszenia wyjątku.

Dotyczy to części „parametr nie może być zerowy” komunikatu generowanego, gdy na przykład zgłoszony jest wyjątek ArgumentNullException („foo”). W takich przypadkach komunikat będzie nadal (częściowo) zlokalizowany, nawet przy użyciu powyższego kodu.

Poza wykorzystaniem niepraktycznych włamań, takich jak uruchomienie całego kodu innego niż interfejs użytkownika w wątku z ustawieniami narodowymi en-US, na początku nie wydaje się, aby można było wiele z tym zrobić: kod wyjątku .NET Framework nie ma narzędzia do zastępowania ustawień regionalnych komunikatów o błędach.

mdb
źródło
10
Twój przykład działa dla wyjątku FileNotFoundException, ponieważ zasób wiadomości jest pobierany, gdy uzyskiwany jest dostęp do właściwości Message, a nie po zgłoszeniu wyjątku. Nie dotyczy to jednak wszystkich wyjątków (np. Spróbuj zgłosić nowy wyjątek ArgumentNullException („paramName”))
Joe,
3
Jestem zdezorientowany. Próbowałem podążać za twoją odpowiedzią i przetestować ją. Chciałem mój wyjątek po francusku, więc zrobiłem to, t.CurrentUICulture = new System.Globalization.CultureInfo("fr-FR");a t.CurrentCulture = new System.Globalization.CultureInfo("fr-FR");mimo to powstały wyjątek po angielsku ...
VitalyB
7
@VitalyB Zlokalizowane teksty wyjątków są częścią pakietów językowych środowiska .NET. Więc jeśli nie masz zainstalowanego pakietu języka francuskiego, nie otrzymasz przetłumaczonych tekstów.
Daniel Rose
7
Przynajmniej .NET 4.5 tworzą wszystkie wyjątki, dzięki Environment.GetResourceString("...")czemu Twoje rozwiązanie nie działa. Najlepiej jest rzucić niestandardowy wyjątek z własnym tekstem wiadomości (w języku angielskim) i użyć właściwości InnerException, aby zachować stary.
webber2k6
1
Refleksja w celu uzyskania nazw typów wyjątków może się przydać.
Guillermo Prandi
67

Możesz wyszukać oryginalną wiadomość wyjątku na unlocalize.com

użytkownik461128
źródło
5
Próbowałem wyszukać jakieś chińskie wiadomości o wyjątkach, zawsze mi to mówiłem No records found.
Tyler Long,
1
Zły wybór. Kiedy przesyłam moje wyjątki do Google Analytics (lub innej usługi w chmurze), będę mieć różne grupy wyjątków dla tego samego wyjątku, ale w różnych językach. Nie będę mógł sortować według liczby każdego wyjątku, ponieważ nie odzwierciedla on rzeczywistej liczby (100 po angielsku, 77 po chińsku, 80 po koreańsku ... itd.)
Artemious
Pamiętam, że wiele razy znajdowałem tę piękną stronę internetową, gdy po prostu zrzuciłem do Google zlokalizowane wiadomości wyjątków, teraz nie są już dostępne.
Martin Braun
40

Być może kwestia sporna, ale zamiast ustawić kulturę en-US, możesz ją ustawić Invariant. W Invariantkulturze komunikaty o błędach są w języku angielskim.

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

Ma to tę zaletę, że nie wygląda na stronnicze, szczególnie w przypadku nieamerykańskich lokalizacji anglojęzycznych. (alias unika złośliwych uwag kolegów)

MPelletier
źródło
1
Gdzie powinniśmy napisać te wiersze w naszym projekcie ASP.NET? Dzięki.
jason
2
Mam zamiar zasugerować na górze, w Application_Start. Dzięki temu cały projekt będzie prowadzony w języku angielskim. Jeśli potrzebujesz tylko komunikatów o błędach, możesz utworzyć funkcję okładki i wywołać ją w każdym z nich catch.
MPelletier
5
Czy nie spowoduje to jednak, że standardowe przyciski w oknie wiadomości będą w języku angielskim? To może nie być pożądane zachowanie.
Nyerguds
12

Oto rozwiązanie, które nie wymaga żadnego kodowania i działa nawet w przypadku tekstów wyjątków, które są ładowane zbyt wcześnie, abyśmy mogli zmienić kod (na przykład te w mscorlib).

Nie zawsze może mieć zastosowanie w każdym przypadku (zależy to od konfiguracji, ponieważ musisz mieć możliwość utworzenia pliku .config oprócz głównego pliku .exe), ale to działa dla mnie. Więc po prostu stwórz app.configin dev, (lub a [myapp].exe.configlub web.configw produkcji), który zawiera na przykład następujące wiersze:

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

Mówi to frameworkowi, aby przekierowywał wiązania zestawu dla mscorlibzasobów i System.Xmlzasobów, w wersjach od 1 do 999, w języku francuskim (kultura jest ustawiona na „fr ”) na zestaw, który ... nie istnieje (arbitralny wersja 999).

Kiedy więc CLR będzie szukał francuskich zasobów dla tych dwóch zestawów (mscorlib i System.xml), nie znajdzie ich i z wdziękiem powróci do angielskiego. W zależności od kontekstu i testów, możesz chcieć dodać inne zestawy do tych przekierowań (zestawy, które zawierają zlokalizowane zasoby).

Oczywiście nie sądzę, że jest to obsługiwane przez Microsoft, więc używaj na własne ryzyko. Cóż, w przypadku wykrycia problemu możesz po prostu usunąć tę konfigurację i sprawdzić, czy nie jest ona powiązana.

Simon Mourier
źródło
1
Działa, gdy potrzebujesz angielskich danych wyjściowych z narzędzi testowych.
smg
Próbowałem tego, ale to nie działało dla mnie. Czy w .net są inne pliki zasobów? Gdzie mogę je znaleźć?
BluE
1
Zajrzyj do c: \ Windows \ Microsoft.NET \ Framework \ v4.0.30319. Każdy język ma dwuliterowy folder. Pamiętaj, aby zastąpić „fr” w powyższej odpowiedzi faktycznym językiem, który jest używany. „nie” dla norweskiego, „da” dla duńskiego, „sv” dla szwedzkiego itp.
Wolf5
Aby utworzyć PEŁNĄ listę, zajrzyj do tego folderu. Ma około 120 plików zasobów. Dodaj każdy z nich do konfiguracji. Wydaje się, że jest to obecnie jedyne rozwiązanie dla systemu Windows 10 i nowszych, ponieważ nie ma już możliwości odinstalowania pakietów językowych .Net w nowszych oknach (stanowiących część systemu operacyjnego). Jest teraz nawet umieszczony w GAC, więc usunięcie tych folderów językowych nie działa.
Wolf5
10

System Windows musi mieć zainstalowany język interfejsu użytkownika. Tak nie jest, nie ma magicznej wiedzy na temat przetłumaczonej wiadomości.

W en-US Windows 7 Ultimate z zainstalowanym pt-PT następujący kod:

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;

Tworzy wiadomości w pt-PT, en-US i en-US. Ponieważ nie ma zainstalowanych plików kultury francuskiej, domyślnie jest to domyślny (zainstalowany?) Język systemu Windows.

danobrega
źródło
To rozwiązało problem. Polski interfejs w mojej sytuacji, zainstalowałem pakiety językowe en MUI ~ 260 MB, używając programu Vistalizator.
Krzysztof Szynter,
5

Wiem, że to stary temat, ale myślę, że moje rozwiązanie może być odpowiednie dla każdego, kto natknie się na to podczas wyszukiwania w Internecie:

W loggerze wyjątków możesz zalogować np. GetType.ToString, który zapisałby nazwę klasy wyjątku. Spodziewałbym się, że nazwa klasy powinna być niezależna od języka i dlatego zawsze będzie reprezentowana w języku angielskim (np. „System.FileNotFoundException”), chociaż obecnie nie mam dostępu do systemu języka obcego, aby przetestować pomysł.

Jeśli naprawdę chcesz również tekst komunikatu o błędzie, możesz utworzyć słownik wszystkich możliwych nazw klas wyjątków i odpowiadających im komunikatów w dowolnym języku, ale dla angielskiego uważam, że nazwa klasy jest całkowicie odpowiednia.

Barbarzyńca
źródło
5
Nie działa Zostałem InvalidOperationExceptionrzucony System.Xml.XmlWellFormedWriter. Próbujesz zgadnąć, jaki konkretny błąd wystąpił, nie czytając komunikatu. Może być tysiąc różnych rzeczy.
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;

Bez obejść.

Tks :)


źródło
zapomniałeś;
KansaiRobot
4

Ustawienie Thread.CurrentThread.CurrentUICulturezostanie wykorzystane do zlokalizowania wyjątków. Jeśli potrzebujesz dwóch rodzajów wyjątków (jeden dla użytkownika, drugi dla Ciebie), możesz użyć poniższej funkcji do przetłumaczenia komunikatu wyjątku. Przeszukuje w zasobach bibliotek .NET oryginalny tekst, aby uzyskać klucz zasobu, a następnie zwrócić przetłumaczoną wartość. Ale jest jedna słabość, której nie znalazłem jeszcze dobrego rozwiązania: Wiadomości, które zawierają {0} zasobów, nie zostaną znalezione. Jeśli ktoś ma dobre rozwiązanie, byłbym wdzięczny.

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
źródło
To nie zadziała, jeśli wyjątek zawiera sformatowany parametr.
Nick Berardi,
Tak, jak powiedziałem: „Ale jest jedna słabość, której nie znalazłem jeszcze dobrego rozwiązania: Wiadomości, które zawierają {0} zasobów, nie zostaną znalezione. Jeśli ktoś ma dobre rozwiązanie, będę wdzięczny”.
Vortex852456
3

Struktura .NET składa się z dwóch części:

  1. Sam system .NET
  2. Pakiety językowe .NET Framework

Wszystkie teksty (np. Komunikaty o wyjątkach, etykiety przycisków na MessageBox itp.) Są w języku angielskim w samym środowisku .NET. Pakiety językowe zawierają zlokalizowane teksty.

W zależności od konkretnej sytuacji rozwiązaniem byłoby odinstalowanie pakietów językowych (tj. Powiadomienie klienta). W takim przypadku teksty wyjątków będą w języku angielskim. Należy jednak pamiętać, że wszystkie inne teksty dostarczone w ramach frameworka będą również w języku angielskim (np. Etykiety przycisków w MessageBox, skróty klawiaturowe dla poleceń aplikacji).

Daniel Rose
źródło
Dzięki!! Ironiczne jest dla mnie to, że okno dialogowe odinstalowywania jest w języku pakietu odinstalowującego, a nie w języku lokalnym. Uwaga dodatkowa: pakiety językowe wydają się wracać co kilka miesięcy. Nie wiem, dlaczego, ale zgaduję aktualizację / aktualizację
Choco Smith
@ChocoSmith Przy każdej aktualizacji systemu .NET Framework za pośrednictwem usługi Windows Update pakiet językowy jest instalowany ponownie.
Daniel Rose
5
Poproszenie klientów o odinstalowanie pakietów językowych dla ich własnego języka nie jest realnym rozwiązaniem.
Nyerguds
2

Wyobrażam sobie jedno z tych podejść:

  1. Wyjątki są zawsze odczytywane tylko przez Ciebie, tzn. Nie są funkcją klienta, więc możesz używać przewodowych nie zlokalizowanych ciągów, które nie zmienią się, gdy uruchomisz w trybie tureckim.

  2. Dołącz kod błędu, np. 0x00000001Przy każdym błędzie, abyś mógł łatwo sprawdzić go w angielskiej tabeli.

morechilli
źródło
9
To niewiele pomoże, gdy są wyjątkami zgłaszanymi przez wewnętrzne komponenty frameworka .net . Cały ten problem nie dotyczy wyjątków, które sam sobie rzucasz; oczywiście programista wybiera, jaką wiadomość ma dołączyć do nich .
Nyerguds
1

Na podstawie odpowiedzi Undercover1989, ale uwzględnia parametry i kiedy komunikaty składają się z kilku ciągów zasobów (takich jak wyjątki argumentów).

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
źródło
1

Miałem tę samą sytuację i wszystkie odpowiedzi, które znalazłem tutaj i gdzie indziej, nie pomogły lub nie były satysfakcjonujące:

Thread.CurrentUICultureZmienia język wyjątków .NET, ale nie za Win32Exception, który korzysta z zasobów systemu Windows w języku Windows UI samego. Więc nigdy nie udało mi się wydrukować wiadomości Win32Exceptionw języku angielskim zamiast niemieckim, nawet przy użyciu FormatMessage()opisanego w
Jak uzyskać wyjątek Win32Ex po angielsku?

Dlatego stworzyłem własne rozwiązanie, które przechowuje większość istniejących komunikatów wyjątków dla różnych języków w plikach zewnętrznych. Nie dostaniesz bardzo dokładnej wiadomości w wybranym języku, ale dostaniesz wiadomość w tym języku, czyli o wiele więcej niż obecnie (która jest wiadomością w języku, którego prawdopodobnie nie rozumiesz).

Funkcje statyczne tej klasy można wykonywać w instalacjach Windows w różnych językach: CreateMessages()tworzy teksty specyficzne dla kultury,
SaveMessagesToXML()zapisuje je w tylu plikach XML, ile języków jest tworzonych lub ładowanych
LoadMessagesFromXML() ładuje wszystkie pliki XML z komunikatami specyficznymi dla języka

Podczas tworzenia plików XML w różnych instalacjach systemu Windows w różnych językach wkrótce dostępne będą wszystkie potrzebne języki.
Być może możesz utworzyć teksty dla różnych języków w 1 systemie Windows, jeśli masz zainstalowanych wiele pakietów językowych MUI, ale jeszcze tego nie przetestowałem.

Testowany z VS2008, gotowy do użycia. Komentarze i sugestie są mile widziane!

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
źródło
1
Thread.CurrentUICulture Także zmianę języka interfejsu użytkownika, dzięki czemu jest straszny rozwiązaniem. Klasycznym przykładem są przyciski Tak / Nie / OK / Anuluj w oknie komunikatu.
Nyerguds
0

Komunikaty wyjątków w języku angielskim

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

następnie przejdź do folderu Lokalizacja i umieść go w projectName.xml i dodaj

<text name="ExceptionmessagesinEnglish">Exception Message in English</text>
Nabeel Haxxan
źródło
-1

Powinieneś zarejestrować stos wywołań zamiast samego komunikatu o błędzie (IIRC, prosty wyjątek. ToString () powinno to zrobić za Ciebie). Stamtąd możesz dokładnie określić, skąd pochodzi wyjątek, i zazwyczaj wydedukować, który to wyjątek.

Branko Dimitrijevic
źródło
3
Rejestrujemy wiadomość i stacktrace. Ale jest o wiele łatwiej, jeśli przesłanie jest jasne.
Carra
-1

Zastąp komunikat wyjątku w bloku catch przy użyciu metody rozszerzenia. Sprawdź, czy zgłoszony komunikat pochodzi z kodu lub nie, jak wspomniano poniżej.

    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();
        }
użytkownik3472484
źródło
1
Jak powiedziałem wcześniej ... InvalidOperationException. Baw się dobrze, zastanawiając się, co to znaczy bez samej wiadomości. Nowa instancja nie będzie tego magicznie mieć.
Nyerguds
-1

Do celów rejestrowania niektóre aplikacje mogą wymagać pobrania komunikatu o wyjątku w języku angielskim (oprócz wyświetlania go w standardowej wersji UICulture klienta).

W tym celu następujący kod

  1. zmienia bieżącą UICulture
  2. odtwarza rzucony obiekt wyjątku za pomocą „GetType ()” i „Activator.CreateInstance (t)”
  3. wyświetla komunikat nowego obiektu wyjątku w nowym UICuture
  4. a następnie ostatecznie zmienia bieżącą UICulture z powrotem na wcześniejszą 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
źródło
1
nie gwarantuje to, że komunikat wyjątku nowego obiektu jest taki sam, jak zgłoszony wyjątek. Może być zupełnie inny i zwykle jest zupełnie inny. Dlatego potrzebujemy komunikatu wyjątku.
Artemious