IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C# Discussion :

Création d'un ResourceManager à partir d'une assembly satellite


Sujet :

C#

  1. #1
    Rédacteur
    Avatar de Arnaud F.
    Homme Profil pro
    Développeur COBOL
    Inscrit en
    août 2005
    Messages
    5 183
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : France

    Informations professionnelles :
    Activité : Développeur COBOL
    Secteur : Finance

    Informations forums :
    Inscription : août 2005
    Messages : 5 183
    Points : 8 870
    Points
    8 870
    Par défaut Création d'un ResourceManager à partir d'une assembly satellite
    Salut,

    ce que je souhaite faire m'a l'air simple et pourtant.

    Le contexte?
    Une appli multilangue embarquant les libellés générique de base (anglais)
    Une DLL supplémentaire par langue gérée (embarquant donc les traductions nécessaires).

    Ma DLL de langue (qui ne contient qu'un fichier de ressource) :
    • Namespace : MonAppli
    • Nom de la DLL : MonAppli_FR.dll
    • Fichier de resource embarque avec :
      1. Welcome=Bonjour
      2. Goodbye=Au revoir


    Mon programme :
    • Namespace : MonAppli
    • Nom de la DLL : MonAppli.exe
    • Fichier de resource embarque avec :
      1. Welcome=Welcome
      2. Goodbye=Goodbye


    Le code qui ne fonctionne pas :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    Assembly a = Assembly.LoadFrom(@"%PATH%\MonAppli_FR.dll");
    string[] manifests = a.GetManifestResourceNames();
    ResourceManager RM = new ResourceManager(manifests[0], a);
     
    string welcome = RM.GetString("Welcome", null);
    string goodbye = RM.GetString("Goodbye", null);
    J'ai l'exception "MissingManifestResourceException" :
    Impossible de trouver des ressources appropriées pour la culture spécifiée ou la culture neutre. Assurez-vous que "MonAppli.Properties.Resources.resources.resources" a été correctement incorporé ou lié dans l'assembly "MonAppli_FR" au moment de la compilation ou que tous les assemblys satellites requis sont chargeables et complètement signés.
    On prend les mêmes mais différemment :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
     
    Assembly a = Assembly.LoadFrom(@"%PATH%\MonAppli_FR.dll");
    string[] manifests = a.GetManifestResourceNames();
     
    using (ResourceReader reader = new ResourceReader(a.GetManifestResourceStream(manifests[0])))
    {
        // Bienvenue
        string welcome = (from pair in reader.AsEnumerable()
                          where pair.Key == "Welcome"
                          select pair.Value).First();
     
        // Au revoir
        string goodbye = (from pair in reader.AsEnumerable()
                          where pair.Key == "Goodbye"
                          select pair.Value).First();
    }
    (Avec la petite extension pour ResourceReader qui va bien :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
        public static class RRExtension
        {
            public static IEnumerable<KeyValuePair<string, string>> AsEnumerable(this ResourceReader reader)
            {
                IDictionaryEnumerator e = reader.GetEnumerator();
     
                while (e.MoveNext())
                {
                    yield return new KeyValuePair<string, string>((string)e.Key, (string)e.Value);
                }
            }
        }
    )

    Quelqu'un peut m'expliquer ce que je fais mal, pourquoi la première méthode ne fonctionne pas alors que la seconde si?


    beaucoup !
    C'est par l'adresse que vaut le bûcheron, bien plus que par la force. Homère

    Installation de Code::Blocks sous Debian à partir de Nightly Builds

  2. #2
    Rédacteur
    Avatar de Arnaud F.
    Homme Profil pro
    Développeur COBOL
    Inscrit en
    août 2005
    Messages
    5 183
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : France

    Informations professionnelles :
    Activité : Développeur COBOL
    Secteur : Finance

    Informations forums :
    Inscription : août 2005
    Messages : 5 183
    Points : 8 870
    Points
    8 870
    Par défaut
    Bon, j'aurai passé toute la journée et c'est après avoir posé la question que je trouve...

    Je me demande pourquoi je n'y ai pas pensé avant, mais en fait, dans le fichier Resources.Designer.cs de ma DLL, j'ai ça :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    new global::System.Resources.ResourceManager("MonAppli.Properties.Resources", typeof(Resources).Assembly);
    Or le fichier manifeste renvoyé par :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    a.GetManifestResourceNames()[0];
    est :

    "MonAppli.Properties.Resources.resources"
    Y a comme qui dirait un ".resources" en trop

    Du coup :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    Assembly a = Assembly.LoadFrom(@"%PATH%\MonAppli_FR.dll");
    string[] manifests = a.GetManifestResourceNames();
    string manifest = manifests[0].Replace(".resources", string.Empty);
    ResourceManager RM = new ResourceManager(manifest , a);
     
    string welcome = RM.GetString("Welcome", null);
    string goodbye = RM.GetString("Goodbye", null);
    Et je récupère bien mes libellés ...
    C'est par l'adresse que vaut le bûcheron, bien plus que par la force. Homère

    Installation de Code::Blocks sous Debian à partir de Nightly Builds

  3. #3
    Rédacteur/Modérateur


    Homme Profil pro
    Développeur .NET
    Inscrit en
    février 2004
    Messages
    19 875
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : février 2004
    Messages : 19 875
    Points : 39 710
    Points
    39 710
    Par défaut
    Tu te compliques la vie...

    En fait, ce n'est pas comme ça que fonctionnent les assemblies satellites (d'ailleurs ta DLL MonAppli_FR n'est pas un assembly satellite)

    Normalement tu mets les fichiers de ressource de toutes les langues dans ton projet MonAppli, avec la culture dans le nom :

    - Resources.resx (culture par défaut)
    - Resources.fr-FR.resx (Français - France)
    - Resources.fr.resx (Français tout court)
    - Resources.es-MX.resx (Espagnol - Mexique)
    - etc...

    A la compilation, l'assembly principal contient juste la culture par défaut, et des assemblies satellites sont générés pour chaque langue. Ils sont placés dans des sous répertoires par culture :

    - fr-FR\MonAppli.resources.dll
    - fr\MonAppli.resources.dll
    - es-MX\MonAppli.resources.dll
    - etc...

    A l'exécution, les propriétés de la classe Resources renvoient la valeur qui correspond à la culture courante (ou la culture parente la plus proche si la ressource n'existe pas pour la culture courante, par exemple il se rabattra sur fr si la ressource fr-FR n'existe pas, puis sur la culture par défaut si la ressource fr n'existe pas)

    Tu peux aussi choisir explicitement la langue en affectant une valeur à la propriété Culture :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Resources.Culture = CultureInfo.GetCultureInfo("fr-FR");
    (tu obtiendras d'ailleurs le même résultat en affectant la propriété Thread.CurrentUICulture, ce qui est sans doute plus propre)


    Dernière option pour choisir manuellement la langue : passer une culture en paramètre des méthodes du ResourceManager

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    CultureInfo culture = CultureInfo.GetCulture("en-US");
    string welcome = Resources.ResourceManager.GetString("Welcome", culture);
    string goodbye = Resources.ResourceManager.GetString("Goodbye", culture);
    Donc :
    - pas besoin de créer un nouveau projet pour chaque assembly de langue
    - pas besoin de créer un nouveau ResourceManager
    - tout est automatique, si tu ne précises rien ça utilisera les ressources de la culture courante (par défaut c'est celle de l'OS)

  4. #4
    Rédacteur
    Avatar de Arnaud F.
    Homme Profil pro
    Développeur COBOL
    Inscrit en
    août 2005
    Messages
    5 183
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : France

    Informations professionnelles :
    Activité : Développeur COBOL
    Secteur : Finance

    Informations forums :
    Inscription : août 2005
    Messages : 5 183
    Points : 8 870
    Points
    8 870
    Par défaut
    Je suis pas doué.

    J'ai créé une bibliothèque de classe (DLL) que je veux rendre localisable.

    Pour la peine, j'ai recréé un project minimaliste :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
                Class1 c = new Class1();
                string s = c.GetLang();
     
                Thread.CurrentThread.CurrentCulture = new CultureInfo("fr");
                s = c.GetLang();
                Thread.CurrentThread.CurrentCulture = new CultureInfo("en");
                s = c.GetLang();
                Thread.CurrentThread.CurrentCulture = new CultureInfo("en-GB");
                s = c.GetLang();
    Dans tous les cas, j'ai le libellé "FR" qui apparaît et pourtant la MessageBox m'indique j'ai bien changé la culture du thread courant...

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
        public class Class1
        {
            public Class1()
            { }
     
            public string GetLang()
            {
                global::System.Windows.Forms.MessageBox.Show(global::System.Threading.Thread.CurrentThread.CurrentCulture.DisplayName);
                return Resources.Lang;
            }
        }
    Et dans mes fichiers :

    Resources.resx
    Lang=??
    Resources.fr.resx
    Lang=FR
    Resources.en.resx
    Lang=EN
    Resources.en-GB.resx
    Lang=EN-GB

    Donc par défaut, j'ai FR et non ?? (vu que j'ai FR en préférence utilisateur dans Windows je présume), par contre le fait de changer la culture n'a rien changé de fait au niveau de ma variable, pourquoi?
    C'est par l'adresse que vaut le bûcheron, bien plus que par la force. Homère

    Installation de Code::Blocks sous Debian à partir de Nightly Builds

  5. #5
    Rédacteur
    Avatar de Arnaud F.
    Homme Profil pro
    Développeur COBOL
    Inscrit en
    août 2005
    Messages
    5 183
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : France

    Informations professionnelles :
    Activité : Développeur COBOL
    Secteur : Finance

    Informations forums :
    Inscription : août 2005
    Messages : 5 183
    Points : 8 870
    Points
    8 870
    Par défaut
    Bon, je me répond, fallais updater la CurrentUICulture aussi et du coup ça passe aussi.

    Par contre, plusieurs questions:

    • Pour le déploiement ça se passe comment? Je déploie le tout tel quel?
    • Si je veux rajouter un langage, je dois nécessairement compiler le projet?
    • Pourquoi c'est au développeur de créer des fichiers de ressources et de faire attention au nommage? (et déplacer manuellement le fichier créé dans les propriétés du projet, et rajouter l'outil de compilation "ResXFileCodeGenerator")
    • Pourquoi c'est pas automatique comme pour les Forms?
    • Je donne des libellés à traduire à un User quelconque, je dois fais comment, je peux juste lui donner le fichier resx sans rien recompiler?


    Comment ça se passe?

    C'est par l'adresse que vaut le bûcheron, bien plus que par la force. Homère

    Installation de Code::Blocks sous Debian à partir de Nightly Builds

  6. #6
    Rédacteur/Modérateur


    Homme Profil pro
    Développeur .NET
    Inscrit en
    février 2004
    Messages
    19 875
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : février 2004
    Messages : 19 875
    Points : 39 710
    Points
    39 710
    Par défaut
    Citation Envoyé par Arnaud F. Voir le message
    Bon, je me répond, fallais updater la CurrentUICulture aussi et du coup ça passe aussi.
    Pas "aussi", CurrentUICulture suffit... mais en général on change effectivement les deux
    En gros :
    - CurrentUICulture : culture des ressources
    - CurrentCulture : culture utilisée pour les formats de nombre, de date, etc

    Citation Envoyé par Arnaud F. Voir le message
    Pour le déploiement ça se passe comment? Je déploie le tout tel quel?
    Oui, il n'y a rien de particulier à faire. Il faut juste que les assemblies satellites soient dans les sous-dossiers du dossier de l'exe

    Citation Envoyé par Arnaud F. Voir le message
    Si je veux rajouter un langage, je dois nécessairement compiler le projet?
    Pas forcément...
    Tu peux tout recompiler et redéployer seulement le nouvel assembly satellite
    Tu peux aussi utiliser resgen.exe pour générer les ressources binaires (.resources), puis le compilateur (csc.exe) pour générer l'assembly de ressource à partir de ça


    Citation Envoyé par Arnaud F. Voir le message
    Pourquoi c'est au développeur de créer des fichiers de ressources et de faire attention au nommage? (et déplacer manuellement le fichier créé dans les propriétés du projet, et rajouter l'outil de compilation "ResXFileCodeGenerator")
    Pourquoi c'est pas automatique comme pour les Forms?
    Ben pour les Forms, y a le designer qui te permet de changer la langue... si c'est juste du code, y a pas de designer, et donc c'est manuel
    Par contre, seul le fichier de ressource par défaut doit avoir l'outil ResXFileCodeGenerator, pour les autres ce n'est pas nécessaire

    Citation Envoyé par Arnaud F. Voir le message
    Je donne des libellés à traduire à un User quelconque, je dois fais comment, je peux juste lui donner le fichier resx sans rien recompiler?
    Oui, tu lui files le resx et tu l'intègre ensuite dans ton projet
    Il y a un éditeur sympa ici pour pas que le traducteur ait besoin de modifier directement le XML (dans ma boite on convertit en Excel pour envoyer au traducteur, je sais plus quel est l'outil qui fait ça...)

    Un truc sympa aussi, si tu utilises Resharper : quand tu ouvres un resx dans l'éditeur XML, il t'indique les ressources non traduites et autres problèmes éventuels (genre les "{0}" manquants dans les trads...)

  7. #7
    Rédacteur
    Avatar de Arnaud F.
    Homme Profil pro
    Développeur COBOL
    Inscrit en
    août 2005
    Messages
    5 183
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : France

    Informations professionnelles :
    Activité : Développeur COBOL
    Secteur : Finance

    Informations forums :
    Inscription : août 2005
    Messages : 5 183
    Points : 8 870
    Points
    8 870
    Par défaut
    En voilà un Dieu qu'il est beau

    En fait j'ai lu pas mal d'articles sur la localisation mais aucun ne parlait sur la localisation dans les DLL et du coup je m'avais dans la tête que ça devait pas être possible de le faire directement...

    beaucoup Thomas, ça va énormément me simplifier la vie du coup !
    C'est par l'adresse que vaut le bûcheron, bien plus que par la force. Homère

    Installation de Code::Blocks sous Debian à partir de Nightly Builds

  8. #8
    Futur Membre du Club
    Inscrit en
    mars 2011
    Messages
    8
    Détails du profil
    Informations forums :
    Inscription : mars 2011
    Messages : 8
    Points : 6
    Points
    6
    Par défaut
    Bonjour, je relance le sujet ayant un problème identique, et toujours non résolus.

    Je suis en stage et j'expérimente en ce moment la localization avec WPF pour l'intégrer plus tard au framework de mon entreprise.

    J'ai réduit l'application à un projet très minimaliste pour isoler clairement la partie localization.



    Ce que j'aimerais : La combo possédant deux choix, français et english. J'ai besoin que lorsque l'user sélectionne Francais ou English, puis clique sur le bouton, celui-ci prenne automatiquement le content "Appliquer" ou "Apply" en fonction du choix de l'user.


    J'ai pour cela un fichier resources.resx comprenant [Apply][Apply] et un fichier resources.fr.resx comprenant [Apply][Appliquer]. Tout deux en visiblité Public.

    Et voici le code que j'ai :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    namespace LocalizationSample
    {
        /// <summary>
        /// Logique d'interaction pour MainWindow.xaml
        /// </summary>
        public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();
            }
     
            private void ButtonApply_Click(object sender, RoutedEventArgs e)
            {
               if (ComboLang.Text == "Français")
                {
                    Thread.CurrentThread.CurrentUICulture = new CultureInfo("fr");
                    ResourceManager rm = new ResourceManager("LocalizationSample.Properties.Resources.fr", typeof(MainWindow).Assembly);
                    ButtonApply.Content = rm.GetString("Apply", Thread.CurrentThread.CurrentUICulture);
                }
                else if (ComboLang.Text == "English")
                {
                    Thread.CurrentThread.CurrentUICulture = new CultureInfo("en");
                    ResourceManager rm = new ResourceManager("LocalizationSample.Properties.Resources", typeof(MainWindow).Assembly);
                    ButtonApply.Content = rm.GetString("Apply", Thread.CurrentThread.CurrentUICulture);
                }
            } 
        }
    }

    Test :

    Cela fonctionne pour la culture "en", lorsque je clique, le bouton prend comme Content "Apply".

    Puis lorsque je teste avec la culture "fr", voici :

    Impossible de trouver des ressources appropriées pour la culture spécifiée ou la culture neutre. Assurez-vous que "LocalizationSample.Properties.Resources.fr.resources" a été correctement incorporé ou lié dans l'assembly "LocalizationSample" au moment de la compilation ou que tous les assemblys satellites requis sont chargeables et complètement signés.
    J'ai également essayer en pointant sur LocalizationSample.bin.Debug.fr.LocalizationSample.resources.dll ... Mais ça n'a pas fonctionner.

    Et rien n'y fait, j'ai toujours la même erreur. Si quelqu'un pouvait m'expliquer ce que je ne comprend pas, je lui en serais redevable.

    Merci d'avance

  9. #9
    Rédacteur/Modérateur


    Homme Profil pro
    Développeur .NET
    Inscrit en
    février 2004
    Messages
    19 875
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : février 2004
    Messages : 19 875
    Points : 39 710
    Points
    39 710
    Par défaut
    Ben tu pourrais commencer par lire les précédents messages de cette discussion, tout est expliqué...

    Pour ce qui est de la localisation en WPF, jette un oeil à cet article.

  10. #10
    Futur Membre du Club
    Inscrit en
    mars 2011
    Messages
    8
    Détails du profil
    Informations forums :
    Inscription : mars 2011
    Messages : 8
    Points : 6
    Points
    6
    Par défaut
    Bonjour, en effet merci j'ai réussi a mettre en place cette localization.

    Mais j'ai un dernier problème ... Lorsque j'ai tester du premier coup, la localization marchait bien avec les fichier resx et tout.
    Puis au deuxième test ben j'ai cette erreur qui survient et que je n'arrive pas a corriger :

    Erreur de build inconnue, 'MC3074*: la balise 'Loc' n'existe pas dans l'espace de noms XML 'http://schemas.microsoft.com/winfx/2006/xaml/presentation'. Ligne 15 Position 22.'
    J'ai recrée un projet et tout recommencer du début, sans copier coller, mais c'est le même scénario ...

    Cordialement

  11. #11
    Rédacteur/Modérateur


    Homme Profil pro
    Développeur .NET
    Inscrit en
    février 2004
    Messages
    19 875
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : février 2004
    Messages : 19 875
    Points : 39 710
    Points
    39 710
    Par défaut
    Il faudrait que tu montres ton code réel, parce que dans l'article il n'est jamais question d'une balise Loc... il y a LocString et LocImage, mais pas Loc tout court. Est-ce que tu as bien déclaré les namespaces XML ?

  12. #12
    Futur Membre du Club
    Inscrit en
    mars 2011
    Messages
    8
    Détails du profil
    Informations forums :
    Inscription : mars 2011
    Messages : 8
    Points : 6
    Points
    6
    Par défaut
    J'ai trouvé d'où venait mon problème, je ne préfixais pas la balise Loc ... (my:Loc Blabla)

    Merci de ton aide Thomas.

    Aurait-tu des liens utiles concernant le changement de thème en runtime ?


    Cordialement

  13. #13
    Rédacteur/Modérateur


    Homme Profil pro
    Développeur .NET
    Inscrit en
    février 2004
    Messages
    19 875
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : février 2004
    Messages : 19 875
    Points : 39 710
    Points
    39 710
    Par défaut
    Citation Envoyé par Yamigarasu31 Voir le message
    Aurait-tu des liens utiles concernant le changement de thème en runtime ?
    Non... Mais tu devrais ouvrir une nouvelle discussion, parce que ça n'a rien à voir avec la question d'origine. Tu auras plus de réponses dans un nouveau sujet

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Création d'un état à partir d'une requête
    Par keeepcoool dans le forum IHM
    Réponses: 1
    Dernier message: 31/03/2009, 07h44
  2. [A-03] Création d'un tableau à partir d'une requête
    Par helprojet dans le forum VBA Access
    Réponses: 3
    Dernier message: 16/02/2009, 13h41
  3. Création d'un arbre à partir d'une liste contigue
    Par karaz_karaz dans le forum C
    Réponses: 2
    Dernier message: 29/06/2008, 00h51
  4. Création d'un contact à partir d'une VCard
    Par myrddin772 dans le forum VBA Outlook
    Réponses: 8
    Dernier message: 28/05/2008, 16h38
  5. Création d'un vecteur à partir d'une structure
    Par lilyla dans le forum MATLAB
    Réponses: 4
    Dernier message: 13/02/2008, 14h45

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo