Pergunta

Eu perguntei a pergunta anteriormente que tinha apenas uma resposta. Eu tive algum tempo para brincar com isso agora e tenho um plano, mas quero algum feedback sobre se for uma boa ideia.

O problema:

Eu quero um componente que tenha um nome (invariante, usado para identificar o componente) para ter seu nome localizado no aplicativo que o está consumindo, sem poluir o modelo do componente com um atributo DisplayName. O componente pode existir em uma DLL separada e ser carregado dinamicamente em tempo de execução.

Meu sentimento é que a DLL do componente deve ser responsável por fornecer o nome localizado (isso parece um bom encapsulamento), mas o aplicativo que consome o componente deve ser responsável por obter/usar o nome localizado (o fato de o componente ter um nome diferente para fins de exibição não são uma preocupação do componente, mas da 'visão' usando esse componente)

A solução:

Adicione um recurso à DLL dos componentes com o mesmo nome que o arquivo em que a classe de componente está. Adicione uma string ao recurso com uma chave que é o nome do componente.

No aplicativo, obtenha o nome localizado como assim:

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

Esse código provavelmente será encapsulado em uma classe localiser, mas transmite o ponto. Isso parece funcionar, mas é uma boa ideia ou existe uma maneira melhor/mais padrão de fazer isso?

EDIT: Devo ressaltar que uma coisa que não tenho certeza com esta solução é que os recursos devem estar em um arquivo .resx com o mesmo nome do arquivo em que a classe está. Isso faz com que funcione, como o arquivo de recursos pode ser identificado no nome do tipo. É o mesmo que a localização para os formulários parece funcionar e faz com que o Visual Studio coloque o .Resx como um 'subcomponente' do arquivo .CS, o que parece bom. Mas o Visual Studio então lança um aviso (sobre a edição de um recurso que faz parte de outro item do projeto) se eu tentar editar esse arquivo, o que me faz pensar que talvez haja outra maneira de eu estar fazendo isso.

Foi útil?

Solução

Eu acho que você tem a ideia certa, mas há melhor maneira de conseguir isso.

Presumivelmente, você tem uma interface que o componente flash implementa. Diga, iPlugable:

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

No seu principal binário, carregue a assembléia travável e crie a instância iPluggable usando a reflexão (presumo que seja o que GetExternalObject() método que você tem) e depois acesse o nome localizado usando o LocalizedName propriedade. Lado de dentro a implementação iPlugable, crie um ResourceManager e acesse o LocalizedName a partir do resx dessa assembléia flash.

O que você recebe ao fazer é um bom encapsulamento de comportamento na assembléia reluzente - é responsável por fornecer o nome localizado, no entanto, ele escolhe fazê -lo, sem o seu programa de homem assumindo que um ResourceManager pode ser criado para acessar um nome localizado.

Outras dicas

Eu tive um problema há algum tempo de localizar valores de enum, não tenho certeza se isso responde à pergunta, mas pelo menos lhe dá outra abordagem para ter em mente.

Comecei criando meu próprio atributo de localização

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

A partir daí eu crio a própria enumeração

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

Em seguida, criei um conversor de tipo que buscará a tecla LocalizationAttribute Descrição e acesse o arquivo de recursos para obter a localização (descrição do atributo deve corresponder à chave de recurso :))

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

Finalmente, criei o cliente que usa o TypeConverter, que neste caso é uma coleção

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

Eu originalmente postou esta solução no meu blog. Espero que te ajude :)

O problema com a maneira como você sugeriu é que será difícil atualizar as traduções e pode até exigir um programador. Além disso, como você vai atualizar as traduções sem atualizar os aplicativos inteiros?

Eu fiz muitas aplicações traduzidas, e o que fiz é ter um arquivo de texto separado com traduções formou algo assim:

Inglês
Feito = feito

Norueguês
Feito = Ferdig

E eu tenho uma função chamada transllateForm () que eu chamo de evento Inside Form Show, que traduzirá todos os elementos da interface do usuário. A função tradlateForm () terá coisas como

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

A última parte do TradlateForm não é uma solução ideal, acho que com o tempo migrarei para uma solução que o próprio controle chama a classe traduzida. A vantagem de usar este sistema é que é simples para o programador, você pode ter outras traduções adicionais sem ter que fazer o trabalho manual depois (isso é importante para mim, pois tenho traduções orientadas pela comunidade), então elas são atualizadas com frequência e eu Não quero gastar tempo nisso. Também posso atualizar as traduções enquanto o aplicativo estiver em execução, sem que o aplicativo precise reiniciar ou ser atualizado.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top