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?


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.


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


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)


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.


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.


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.


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


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

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


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.


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

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

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>

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


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

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

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
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.