Domanda

Ho chiesto a domanda in precedenza che aveva una sola risposta.Ho avuto un po' di tempo per giocarci adesso e ho un piano, ma vorrei un feedback per sapere se è una buona idea.

Il problema:

Voglio che un componente che abbia un nome (invariante, utilizzato per identificare il componente) abbia il suo nome localizzato nell'applicazione che lo sta consumando, senza inquinare il modello del componente con un attributo DisplayName.Il componente può esistere in una DLL separata ed essere caricato dinamicamente in fase di esecuzione.

la mia sensazione è che il componente dll dovrebbe essere responsabile di fornire il nome localizzato (sembra un buon incapsulamento), ma l'applicazione che utilizza il componente dovrebbe essere responsabile di ottenere/utilizzare il nome localizzato (il fatto che il componente abbia un nome diverso per gli scopi di visualizzazione non riguardano il componente, ma la "vista" che utilizza quel componente)

La soluzione:

Aggiungi una risorsa alla dll dei componenti con lo stesso nome del file in cui si trova la classe del componente.Aggiungi una stringa alla risorsa con una chiave che è il nome del componente.

Nell'applicazione ottieni il nome localizzato in questo modo:

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

Questo codice verrà probabilmente incapsulato in una classe Localiser, ma rende bene l'idea.Sembra funzionare, ma è una buona idea o esiste un modo migliore/più standard per farlo?

MODIFICARE:Dovrei sottolineare che una cosa di cui non sono sicuro con questa soluzione è che le risorse devono trovarsi in un file .resx che ha lo stesso nome del file in cui si trova la classe.Questo lo fa funzionare, poiché il file di risorse può essere identificato dal nome del tipo.Questo è lo stesso che la localizzazione dei moduli sembra funzionare e fa sì che Visual Studio inserisca il file .resx come "sottocomponente" del file .cs, il che sembra tutto carino.Ma Visual Studio genera quindi un avviso (sulla modifica di una risorsa che fa parte di un altro elemento del progetto) se provo a modificare questo file, il che mi fa pensare che forse esiste un altro modo in cui dovrei farlo.

È stato utile?

Soluzione

Credo che tu abbia l'idea giusta, ma non c'è modo migliore per raggiungere questo obiettivo.

Presumibilmente, si dispone di un'interfaccia che implementa il componente pluggable. Di ', IPluggable:

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

Dal binario principale, caricare l'assembly plug e creare l'istanza IPluggable utilizzando la riflessione (presumo che è quello che il metodo GetExternalObject() avete lo fa) e poi accedere al nome localizzato utilizzando la proprietà LocalizedName. All'interno l'attuazione IPluggable, creare un ResourceManager e accedere alla LocalizedName dal resx di quell'assemblea pluggable.

Quello che si ottiene facendo è buono incapsulamento del comportamento nel montaggio pluggable - è responsabile della fornitura di voi il nome localizzato, tuttavia sceglie di farlo, senza il tuo programma di uomo assumendo che un ResourceManager può essere creato per accedere a una localizzata nome.

Altri suggerimenti

Qualche tempo fa ho avuto un problema con la localizzazione dei valori enum, non sono sicuro che risponda alla tua domanda, ma almeno ti dà un altro approccio da tenere a mente.

Ho iniziato creando il mio attributo di localizzazione

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

Da lì creo l'enumerazione stessa

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

Quindi ho creato un convertitore di tipi che recupererà la chiave di descrizione LocalizationAttribute e accederà al file di risorse per ottenere la localizzazione (la descrizione dell'attributo deve corrispondere alla chiave della risorsa :))

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

Infine ho creato il client che utilizza TypeConverter, che in questo caso è una raccolta

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

Io originariamente ho pubblicato questa soluzione sul mio blog.Spero che ti aiuti :)

Il problema con il modo in cui avete suggerito è che la sua intenzione di essere difficile da aggiornare le traduzioni, e può anche richiedere un programmatore. Inoltre come hai intenzione di aggiornare le traduzioni senza aggiornare le intere applicazioni?

Ho fatto un sacco di applicazioni tradotte, e quello che ho fatto è di avere un file di testo separato con translatations formattati qualcosa di simile:

  

[English]
  Fatto = Fine

     

[Norwegian]
  Fatto = Ferdig

E ho una funzione chiamata TranslateForm (), che io chiamo all'interno Modulo Visualizza evento, che si tradurrà tutti gli elementi dell'interfaccia utente. La funzione TranslateForm () avrà le cose come

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

L'ultima parte della TranslateForm non è una soluzione ottimale, i pensano nel tempo sarò migrare verso una soluzione in cui il controllo stesso chiama la classe tradurre. Il vantaggio di utilizzare questo sistema è che la sua semplice per il programmatore, è possibile avere altri ppl aggiungere traduzioni, senza dover fare il lavoro manuale dopo (Questo è importent per me come ho guidato dalla comunità traduzioni), in modo che siano aggiornato spesso e ho Non voglio passare il tempo su questo. Posso anche aggiornare le traduzioni mentre l'applicazione è in esecuzione, senza l'applicazione dover riavviare o essere aggiornato.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top