Question

J'ai posé une question auparavant qui avait qu'une seule réponse. J'ai eu un peu de temps à jouer avec cela maintenant et avoir un plan, mais veulent des commentaires sur si elle est une bonne idée.

Le problème:

Je veux un composant qui a un nom (invariant, utilisé pour identifier le composant) d'avoir son nom localisé dans l'application qui consomme, sans polluer le modèle du composant avec un attribut DisplayName. Le composant peut exister dans une DLL séparée et être chargée au moment de l'exécution.

mon sentiment est que la dll composante devrait être responsable de fournir le nom localisé (cela semble être une bonne encapsulation), mais l'application consomme le composant doit être responsable de l'obtention / en utilisant le nom localisé (le fait que le composant a une nom différent à des fins d'affichage ne sont pas une préoccupation du composant, mais de la « vue » en utilisant ce composant)

La solution:

Ajouter une ressource à la dll de composants avec le même nom que le fichier de la classe de composant est. Ajouter une chaîne à la ressource avec une clé qui est le nom du composant.

Dans l'application obtenir le nom localisé comme ceci:

ExternalObject obj = GetExternalObject ();            
ResourceManager manager = new ResourceManager (obj.GetType ());
string localisedName= manager.GetString (obj.Name);

Ce code sera probablement encapsulé dans une classe LoC, mais transmet le point. Cela semble fonctionner, mais est-ce une bonne idée, ou est-il une meilleure / plus moyen standard de faire cela?

EDIT:. Je tiens à souligner qu'une chose que je ne suis pas sûr de cette solution est que les ressources doivent être dans un fichier .resx qui a le même nom que le fichier que la classe est dans ce fait il fonctionne, comme le fichier de ressources peut être identifié à partir du nom du type. Ceci est la même que la localisation des formes semble fonctionner, et fait visual studio a mis le .resx comme un « sous-composante » du fichier .cs qui tout semble bien. Mais le studio visuel jette alors un avertissement (sur la modification d'une ressource qui fait partie d'un autre élément de projet) si je tente de modifier ce fichier, qui me fait penser que peut-être il y a une autre façon que je suis censé le faire.

Était-ce utile?

La solution

Je pense que vous avez la bonne idée, mais il est meilleur moyen d'y arriver.

On peut supposer que vous avez une interface que les outils de composants connectables. Dites, IPluggable:

interface IPluggable {
    ...
    string LocalizedName {get;}
    ...
}

A partir de votre binaire principal, charger l'ensemble connectable et créer l'instance IPluggable utilisant la réflexion (je suppose que ce que la méthode GetExternalObject() vous avez fait), puis accéder au nom localisé en utilisant la propriété LocalizedName. l'intérieur la mise en œuvre IPluggable, créez un ResourceManager et accéder au LocalizedName du resx de cette assemblée connectable.

Ce que vous obtenez en faisant est une bonne encapsulation du comportement dans l'ensemble connectable - il est responsable de vous fournir le nom localisé, mais il choisit de le faire, sans votre programme de l'homme en supposant qu'un ResourceManager peut être créé pour accéder à une localisée nom.

Autres conseils

J'ai eu un problème il y a quelque temps de localisant valeurs ENUM, je ne sais pas si elle répond à vous poser des questions, mais au moins vous donne une autre approche d'avoir à l'esprit.

Commencé en créant mon propre attribut de localisation des

/// <SUMMARY>
/// Attribute used for localization. Description field should contain a reference to the Resource file for correct localization
/// </SUMMARY>
public class LocalizationAttribute : Attribute
{
    public LocalizationAttribute(string description)
    {
        this._description = description;
    }

    private string _description;
    /// <SUMMARY>
    /// Used to reference a resource key
    /// </SUMMARY>
    public string Description
    {
        get
        {
            return this._description;
        }
    }
}

De là, je crée le ENUM lui-même

[TypeConverter(typeof(EnumToLocalizedString))]
public enum ReviewReason
{
    [LocalizationAttribute("ReviewReasonNewDocument")]
    NewDocument = 1,


    [LocalizationAttribute("ReviewReasonInternalAudit")]
    InternalAudit = 2,


    [LocalizationAttribute("ReviewReasonExternalAudit")]
    ExternalAudit = 3,


    [LocalizationAttribute("ReviewReasonChangedWorkBehaviour")]
    ChangedWorkBehaviour = 4,


    [LocalizationAttribute("ReviewReasonChangedWorkBehaviourBecauseOfComplaints")]
    ChangedWorkBehaviourBecauseOfComplaints = 5,


    [LocalizationAttribute("ReviewReasonMovedFromOlderSystem")]
    MovedFromOlderSystem = 6,


    [LocalizationAttribute("ReviewReasonPeriodicUpdate")]
    PeriodicUpdate = 7,


    [LocalizationAttribute("ReviewReasonDocumentChanged")]
    DocumentChanged = 8
}

Ensuite, je créé un convertisseur de type qui récupérera la clé de description LocalizationAttribute et accéder au fichier de ressources pour obtenir la localisation (la description des attributs doit correspondre à la clé de ressource :))

public class EnumToLocalizedString : TypeConverter
    {
        public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
        {
            return (sourceType.Equals(typeof(Enum)));
        }

        public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
        {
            return (destinationType.Equals(typeof(String)));
        }

        public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
        {
            return base.ConvertFrom(context, culture, value);
        }

        public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType)
        {
            if (!destinationType.Equals(typeof(String)))
            {
                throw new ArgumentException("Can only convert to string.", "destinationType");
            }
            if (!value.GetType().BaseType.Equals(typeof(Enum)))
            {
                throw new ArgumentException("Can only convert an instance of enum.", "value");
            }

            string name = value.ToString();
            object[] attrs = value.GetType().GetField(name).GetCustomAttributes(typeof(LocalizationAttribute), false);
            if (attrs.Length != 1  !(attrs[0] is LocalizationAttribute))
            {
                throw new ArgumentException("Invalid enum argument");
            }
            return Handbok.Code.Resources.handbok.ResourceManager.GetString(((LocalizationAttribute)attrs[0]).Description);
        }
    }

Enfin j'ai créé le client qui utilise le TypeConverter, qui dans ce cas est une collection

public class ReviewReasonCollection
{
    private static Collection<KEYVALUEPAIR<REVIEWREASON,>> _reviewReasons;

    public static Collection<KEYVALUEPAIR<REVIEWREASON,>> AllReviewReasons
    {
        get
        {
            if (_reviewReasons == null)
            {
                _reviewReasons = new Collection<KEYVALUEPAIR<REVIEWREASON,>>();
                TypeConverter t = TypeDescriptor.GetConverter(typeof(ReviewReason));

                foreach (ReviewReason reviewReason in Enum.GetValues(typeof(ReviewReason)))
                {
                    _reviewReasons.Add(new KeyValuePair<REVIEWREASON,>(reviewReason, t.ConvertToString(reviewReason)));
                }
            }
            return _reviewReasons;
        }
    }
}

cette solution sur posté mon blog . Je espère que ça vous aide:)

Le problème avec la façon dont vous avez suggéré est que ça va être difficile de mettre à jour les traductions, et il peut même exiger un programmeur. En outre, comment allez-vous mettre à jour les traductions sans mettre à jour l'ensemble des applications?

Je l'ai fait beaucoup d'applications traduites, et ce que je l'ai fait est un fichier texte séparé avec translatations formated quelque chose comme ceci:

  

[en anglais]
  Fait = Fait

     

[Norvège]
  = Fait Ferdig

Et j'ai une fonction appelée TranslateForm () que j'appelle dans le formulaire Montrer l'événement, qui se traduira par tous les éléments de l'interface utilisateur. La fonction TranslateForm () aura des choses comme

buttonDone.Text = Translate.GetTranslation("Done");

La dernière partie de la TranslateForm n'est pas une solution, je pense au fil du temps, je vais migrer vers une solution que lorsque le contrôle lui-même appelle la classe Traduisez. L'avantage d'utiliser ce système est que son simple pour le programer, vous pouvez avoir d'autres ppl ajouter des traductions sans que vous ayez à faire du travail manuel après (Ceci est importent pour moi que j'ai les traductions axées sur la collectivité), ils sont souvent mis à jour et i ne veulent pas que de passer du temps là-dessus. Je peux aussi mettre à jour les traductions alors que l'application est en cours d'exécution, sans l'application d'avoir à redémarrer ou être mis à jour.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top