Pregunta

le pregunté a pregunta anteriormente que sólo tenía una única respuesta.He tenido algo de tiempo para jugar con esto ahora y tengo un plan, pero quiero recibir comentarios sobre si es una buena idea.

El problema:

Quiero que un componente que tiene un nombre (invariante, usado para identificar el componente) tenga su nombre localizado en la aplicación que lo consume, sin contaminar el modelo del componente con un atributo DisplayName.El componente puede existir en una DLL separada y cargarse dinámicamente en tiempo de ejecución.

Mi sensación es que el dll del componente debería ser responsable de proporcionar el nombre localizado (esto parece una buena encapsulación), pero la aplicación que consume el componente debería ser responsable de obtener/usar el nombre localizado (el hecho de que el componente tenga un nombre diferente para los propósitos de visualización no son una preocupación del componente, sino de la 'vista' que usa ese componente)

La solución:

Agregue un recurso al dll del componente con el mismo nombre que el archivo en el que se encuentra la clase del componente.Agregue una cadena al recurso con una clave que es el nombre del componente.

En la aplicación obtenga el nombre traducido así:

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

Este código probablemente estará encapsulado en una clase Localiser, pero transmite el punto.Esto parece funcionar, pero ¿es una buena idea o existe una forma mejor o más estándar de hacerlo?

EDITAR:Debo señalar que una cosa de la que no estoy seguro con esta solución es que los recursos deben estar en un archivo .resx que tenga el mismo nombre que el archivo en el que se encuentra la clase.Esto lo hace funcionar, ya que el archivo de recursos se puede identificar por el nombre del tipo.Esto es lo mismo que la localización de formularios parece funcionar y hace que Visual Studio coloque el .resx como un 'subcomponente' del archivo .cs, lo cual parece bueno.Pero Visual Studio luego arroja una advertencia (sobre la edición de un recurso que es parte de otro elemento del proyecto) si intento editar este archivo, lo que me hace pensar que tal vez haya alguna otra forma en la que se supone que debo hacer esto.

¿Fue útil?

Solución

Creo que tienes la idea correcta, pero no hay mejor manera de lograr esto.

Es de suponer, que tiene una interfaz que implementa el componente de acoplamiento activo. Decir, IPluggable:

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

A partir de su binario principal, cargar el ensamblado enchufable y crear la instancia IPluggable utilizando la reflexión (supongo que eso es lo que el método tiene GetExternalObject() hace) y luego acceder al nombre localizada mediante la propiedad LocalizedName. Dentro de la aplicación IPluggable, crear un ResourceManager y acceder a la LocalizedName del resx de que el montaje enchufable.

Lo que se obtiene haciendo es una buena encapsulación del comportamiento en el montaje enchufable - es responsable de proporcionarle el nombre localizado, sin embargo, opta por hacerlo, sin su programa man suponiendo que un ResourceManager puede ser creado para acceder a una localizada nombre.

Otros consejos

He tenido un problema hace algún tiempo de la localización de los valores de enumeración, no estoy seguro de que si responde a la pregunta, pero al menos le da otro enfoque para tener en cuenta.

Iniciado por la creación de mi propio atributo de adaptación local

/// <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 de ahí se crea la enumeración en sí

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

A continuación, he creado un convertidor de tipos para que se capturen la tecla Descripción LocalizationAttribute y acceder al archivo de recursos para obtener la localización (atributo de descripción debe coincidir con la clave de recursos :))

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 he creado el cliente que utiliza el TypeConverter, que en este caso es una colección

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

publicado esta solución en mi blog . Espero que le ayuda a cabo:)

El problema con la forma que ha sugerido es que será difícil actualizar las traducciones e incluso puede requerir un programador.Además, ¿cómo vas a actualizar las traducciones sin actualizar todas las aplicaciones?

He hecho muchas aplicaciones traducidas, y lo que he hecho es tener un archivo de texto separado con traducciones con un formato similar a este:

[Inglés]
Hecho = Hecho

[Noruego]
Listo=Ferdig

Y tengo una función llamada TranslateForm() a la que llamo dentro del evento Form Show, que traducirá todos los elementos de la interfaz de usuario.La función TranslateForm() tendrá cosas como

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

La última parte con TranslateForm no es una solución óptima, creo que con el tiempo migraré a una solución en la que el control mismo llame a la clase Translate.La ventaja de usar este sistema es que es simple para el programador, puede hacer que otras personas agreguen traducciones sin tener que hacer trabajo manual después (esto es importante para mí ya que tengo traducciones impulsadas por la comunidad), por lo que se actualizan con frecuencia y No quiero perder tiempo en eso.También puedo actualizar las traducciones mientras la aplicación se está ejecutando, sin que la aplicación tenga que reiniciarse o actualizarse.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top