web-dev-qa-db-fra.com

Messages d'exception en anglais?

Nous enregistrons toutes les exceptions qui se produisent dans notre système en écrivant le message Exception.Message dans un fichier. Cependant, ils sont écrits dans la culture du client. Et les erreurs turques ne signifient pas grand chose pour moi.

Alors, comment pouvons-nous consigner des messages d'erreur en anglais sans changer la culture des utilisateurs?

268
Carra

Ce problème peut être partiellement résolu. Le code d'exception du Framework charge les messages d'erreur à partir de ses ressources, en fonction des paramètres régionaux du thread actuel. Dans le cas de certaines exceptions, cela se produit au moment de l'accès à la propriété Message.

Pour ces exceptions, vous pouvez obtenir la version anglaise complète du message en commutant brièvement l'environnement local du thread sur en-US lors de la journalisation (enregistrement préalable de l'environnement local de l'utilisateur et restauration de celui-ci immédiatement après).

Faire cela sur un thread séparé est encore mieux: cela garantit qu'il n'y aura pas d'effets secondaires. Par exemple:

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

Où la classe ExceptionLogger ressemble à quelque chose comme:

class ExceptionLogger
{
  Exception _ex;

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

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

Cependant, comme le fait remarquer Joe dans un commentaire relatif à une précédente révision de cette réponse, certains messages sont déjà (partiellement) chargés depuis les ressources linguistiques au moment où l'exception est levée.

Cela s'applique au paramètre 'ne peut pas être nul' du message généré lorsqu'une exception ArgumentNullException ("foo") est levée, par exemple. Dans ces cas, le message apparaîtra (partiellement) toujours localisé, même si vous utilisez le code ci-dessus.

Hormis l'utilisation de hacks peu pratiques, tels que l'exécution de tout votre code non-UI sur un thread avec une locale en-US pour commencer, il ne semble pas y avoir grand chose à faire à ce sujet: le code d'exception .NET Framework facilités pour remplacer les paramètres régionaux du message d'erreur.

62
mdb

Vous pouvez rechercher le message d’exception d’origine sur unlocalize.com

63
user461128

Un point litigieux peut-être, mais au lieu de définir la culture sur en-US, vous pouvez la définir sur Invariant. Dans la culture Invariant, les messages d'erreur sont en anglais.

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

Il a l'avantage de ne pas sembler partial, en particulier pour les régions non anglophones. (ev.k.a. évite les remarques sarcastiques de collègues)

35
MPelletier

Voici une solution qui n’exige aucun codage et fonctionne même pour les textes d’exceptions chargées trop tôt pour pouvoir être modifiées par code (par exemple, celles de mscorlib).

Cela peut ne pas être toujours applicable dans tous les cas (cela dépend de votre configuration car vous devez être capable de créer un fichier .config en plus du fichier .exe principal), mais cela fonctionne pour moi. Donc, créez simplement un app.config dans dev (ou un [myapp].exe.config ou web.config en production) contenant les lignes suivantes, par exemple:

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

Cela dit au framework de rediriger les liaisons Assembly pour les ressources mscorlib et System.Xml, pour les versions comprises entre 1 et 999, en français (culture a la valeur "fr") vers un Assembly qui ... n'existe pas 999).

Ainsi, lorsque le CLR recherchera des ressources en français pour ces deux assemblys (mscorlib et System.xml), il ne les trouvera pas et retombera en anglais avec grâce. En fonction de votre contexte et de vos tests, vous voudrez peut-être ajouter d'autres assemblys à ces redirections (assemblys contenant des ressources localisées).

Bien sûr, je ne pense pas que cela soit supporté par Microsoft, utilisez donc à vos risques et périls. Eh bien, si vous détectez un problème, vous pouvez simplement supprimer cette configuration et vérifier qu’elle n’est pas liée.

8
Simon Mourier

Windows doit avoir la langue d'interface utilisateur que vous souhaitez utiliser installée. Si ce n'est pas le cas, il n'a aucun moyen de savoir comme par magie quel est le message traduit.

Dans un Windows 7 ultime en-US, avec pt-PT installé, le code suivant:

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;

Produit des messages en pt-PT, en-US et en-US. Puisqu'aucun fichier de culture française n'est installé, la langue par défaut de Windows (installée?) Est utilisée.

8
danobrega

Je sais que c'est un sujet ancien, mais je pense que ma solution peut être tout à fait pertinente pour toute personne qui tombe dans une recherche Web:

Dans le journal des exceptions, vous pouvez enregistrer ex.GetType.ToString, ce qui enregistrerait le nom de la classe des exceptions. Je m'attendrais à ce que le nom d'une classe soit indépendant de la langue et serait donc toujours représenté en anglais (par exemple, "System.FileNotFoundException"), bien qu'à l'heure actuelle, je n'ai pas accès à un système de langue étrangère pour tester la idée.

Si vous voulez vraiment le texte du message d'erreur, vous pouvez créer un dictionnaire de tous les noms de classe d'exception possibles et de leurs messages équivalents dans la langue de votre choix, mais pour l'anglais, je pense que le nom de la classe est parfaitement adéquat.

4
Barbarian
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;

Sans contournements.

Tks :)

3
Fabio Mastelari

Le paramètre Thread.CurrentThread.CurrentUICulture sera utilisé pour localiser les exceptions. Si vous avez besoin de deux types d'exceptions (une pour l'utilisateur, une pour vous), vous pouvez utiliser la fonction suivante pour traduire le message d'exception. Il cherche dans les ressources .NET-Libraries le texte original pour obtenir la clé de ressource, puis renvoie la valeur traduite. Mais il y a une faiblesse pour laquelle je n'ai pas encore trouvé de bonne solution: les messages contenant {0} de ressources ne seront pas trouvés. Si quelqu'un a une bonne solution, je vous en serais reconnaissant.

public static string TranslateExceptionMessage(Exception E, CultureInfo targetCulture)
{
    try
    {
        Assembly a = E.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);
        foreach (DictionaryEntry item in rsOriginal)
            if (item.Value.ToString() == E.Message.ToString())
                return rsTranslated.GetString(item.Key.ToString(), false); // success

    }
    catch { }
    return E.Message; // failed (error or cause it's not intelligent enough to locale '{0}'-patterns
}
3
Undercover1989

Le framework .NET comprend deux parties:

  1. Le framework .NET lui-même
  2. Les packs de langue du framework .NET

Tous les textes (ex. Messages d'exception, étiquettes de bouton sur une MessageBox, etc.) sont en anglais dans le framework .NET même. Les packs de langue ont les textes localisés.

En fonction de votre situation exacte, une solution consisterait à désinstaller les modules de langue (par exemple, dites au client de le faire). Dans ce cas, les textes d’exception seront en anglais. Notez cependant que tous les autres textes fournis par la structure seront également en anglais (par exemple, les libellés de boutons sur une boîte de message, les raccourcis clavier pour les commandes d'applications).

3
Daniel Rose

J'imagine une de ces approches:

1) Les exceptions ne sont lues que par vous, c’est-à-dire qu’elles ne sont pas une fonctionnalité client, vous pouvez donc utiliser des chaînes câblées non localisées qui ne changeront pas lorsque vous exécuterez le mode turc. 

2) Inclure un code d'erreur, par exemple. 0X00000001 avec chaque erreur afin que vous puissiez facilement la rechercher dans un tableau anglais.

2
morechilli

Basé sur la réponse Undercover1989, mais prend en compte les paramètres et lorsque les messages sont composés de plusieurs chaînes de ressources (telles que des exceptions d'arguments).

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
jan

J'ai eu la même situation et toutes les réponses que j'ai trouvées ici et ailleurs n'ont pas aidé ou n'ont pas été satisfaisantes:
Forcer la langue des exceptions en anglais
C # - Obtenir des messages d'exception en anglais lorsque l'application est dans une autre langue?
Comment modifier la langue du message d'exception de Visual Studio en anglais lors du débogage
Comment gérer la traduction d'un message d'exception?
Comment éviter complètement les messages d'exception .NET localisés

Le Thread.CurrentUICulture change la langue des exceptions .net, mais pas le Win32Exception, qui utilise les ressources Windows dans la langue de l'interface utilisateur Windows elle-même. Je n’ai donc jamais réussi à imprimer les messages de Win32Exception en anglais au lieu de l’allemand, pas même en utilisant FormatMessage() comme décrit dans
Comment obtenir Win32Exception en anglais?

J'ai donc créé ma propre solution, qui stocke la majorité des messages d'exception existants pour différentes langues dans des fichiers externes. Vous ne recevrez pas le message très exact dans la langue de votre choix, mais un message dans cette langue, qui est bien plus que ce que vous recevez actuellement (message dans une langue que vous ne comprenez probablement pas). 

Les fonctions statiques de cette classe peuvent être exécutées sur des installations Windows avec différentes langues: CreateMessages() crée les textes propres à la culture.
SaveMessagesToXML() les enregistre dans autant de fichiers XML que de langues créées ou chargées
LoadMessagesFromXML() charge tous les fichiers XML avec des messages spécifiques à la langue 

Lors de la création de fichiers XML sur différentes installations Windows avec différentes langues, vous aurez bientôt toutes les langues dont vous avez besoin.
Peut-être pouvez-vous créer des textes pour différentes langues sur 1 Windows lorsque plusieurs packs de langue MUI sont installés, mais je ne l'ai pas encore testé.

Testé avec VS2008, prêt à l'emploi. Commentaires et suggestions sont les bienvenus!

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