El mejor enfoque para Java Enum multilingüe

7

Estoy teniendo problemas con un enfoque que estoy tomando y ahora me pregunto si acabo de comenzar por el camino equivocado y debo repensar mi enfoque. Esto es lo que estoy intentando.

Me gustaría usar un Enum para representar diferentes estados de cuenta posibles (hay otros conjuntos de datos finitos que configuraré utilizando un enfoque duplicado):

  • A, activo
  • Yo, inactivo
  • P, pendiente

Esos estados de cuenta deben presentarse al usuario en su idioma si así lo prefiere.

Ejemplo: el estado de la cuenta es A, mostrarlo como Activo para personas que hablan inglés y Activo para español.

También hay situaciones en las que me gustaría mostrar una lista desplegable de todos los Estados de cuenta posibles en la preferencia de ubicación apropiada.

Enfoque actual

Utilice una clase AccountStatusEnum con valores de (A, I, P).

En una variable miembro privada estática, almacene un EnumMap de estados de cuenta localizados dentro de un EnumMap de posibles ubicaciones.

Ejemplo:

private static EnumMap<LocaleEnum, EnumMap<AccountStatusEnum, String>> localeAccountStatuses = new EnumMap<LocaleEnum, EnumMap<AccountStatusEnum, String>>(LocaleEnum.class);

Esto se cargaría dentro de un bloque de inicialización estática.

Luego podría usar los siguientes métodos para obtener todos los valores de un menú desplegable:

public static EnumMap<AccountStatusEnum, String> getAccountStatuses(LocaleEnum locale) {
        return localeAccountStatuses.get(locale);
    }

O podría usar este método para obtener una sola descripción de un Enum dado:

public String getDescription(LocaleEnum locale){
            return getAccountStatuses(locale).get(this);
        }

Ejemplo de uso: AccountStatusEnum.A.getDescription (LocaleEnum.es_MX);

Tengo curiosidad acerca de sus pensamientos sobre este enfoque y posibles mejores enfoques para lograr lo mismo.

EDIT La lógica para obtener los valores para rellenar las Enums se centralizaría dentro de una única clase auxiliar. Por lo tanto, si la fuente de las descripciones cambia para provenir de una nueva fuente, el cambio de código se minimiza.

Gracias

    
pregunta forevernewb123 20.09.2014 - 18:35

3 respuestas

14

Enfocaría este problema como abordaría cualquier problema de localización: ResourceBundle . Uso una clase llamada I18n que tiene un método estático llamado getMessage que toma una clave y, opcionalmente, una lista de argumentos. La clave se busca en un ResourceBundle configurado para usar el Locale predeterminado o lo que prefiera (los detalles aquí no son importantes, siempre que la persona que llama a I18n no tenga que saber qué es el Locale es).

La clase I18n es la siguiente:

public final class I18n {
    private I18n() {
    }

    private static ResourceBundle bundle;

    public static String getMessage(String key) {
        if(bundle == null) {
            bundle = ResourceBundle.getBundle("path.to.i18n.messages");
        }
        return bundle.getString(key);
    }

    public static String getMessage(String key, Object ... arguments) {
        return MessageFormat.format(getMessage(key), arguments);
    }
}

En su proyecto, tendría en la ruta del paquete.to.i18n, archivos que contienen messages.properties (predeterminado), messages_en.properties (Locale es), messages_it.properties (Locale it), etc.

Cuando necesite traducir un valor de enumeración, no debería tener que tratarlo de manera diferente a cualquier otra clave, excepto que la clave es el término de enumeración.

En otras palabras, tienes:

public enum AccountStatusEnum {
  Active,
  Inactive,
  Pending
}

Cuando llama a I18n.getMessage , pasa con AccountStatusEnum.Active.toString() y la traducción se encontrará en un archivo de propiedades con la clave "Activo". Si eres como yo, y prefieres usar minúsculas, entonces debes realizar toLowerCase() en la cadena. Aún mejor, crea un nuevo I18n.getMessage que toma un Enum en lugar de una clave, y ese método llama a la versión de String de I18n.getMessage con el valor enum convertido en cadena y en minúscula.

Entonces, agregarías esto a la clase I18n :

    public static String getMessage(Enum<?> enumVal) {
        return getMessage(enumVal.toString().toLowerCase());
    }

Prefiero usar este método porque incorpora la misma lógica de traducción en la misma parte del programa sin afectar su diseño. Es posible que incluso desee diferenciar las claves de enumeración de las otras claves, en cuyo caso puede comenzar cada clave de enumeración con "enumeración", por lo que debería anteponer "enumeración". al llamar a getMessage . Solo recuerde nombrar sus claves en sus archivos de propiedades en consecuencia.

Espero que ayude!

    
respondido por el Neil 20.09.2014 - 19:19
1

ResourceBundle es el camino a seguir, pero me gustaría aprovechar el hecho de que las enumeraciones son objetos y ocultar los detalles de i19n dentro

public enum MyEnum {
  A,
  I,
  P;

  private String localeText = null;

  private MyEnum() {
    // Load resource bundle
    this.localeText = // get message;
  }

  public String getLocaleText() {
    return this.localeText();
  }
}

Eso te permitiría hacer:

MyEnum.A.getLocaleText();

directamente, en todas partes

Una optimización sería cargar el paquete de recursos como un atributo estático, en un inicializador estático.

Y en JEE, puntos de bonificación por hacer que el paquete de recursos sea inyectable (para que pueda cambiar las cadenas de localización fácilmente).

    
respondido por el SJuan76 21.09.2014 - 12:22
1

Resumiendo los pros y los contras de las respuestas propuestas:

El problema con el archivo de ayuda global es la falta de flexibilidad, por ejemplo, en una aplicación tengo varias enumeraciones con una constante de enumeración específica que me gustaría tener una clave muy específica, no siguiendo el formato común.

El problema con la ubicación de la lógica en las enumeraciones es la replicación de código, lo que a su vez dificultaría el mantenimiento a largo plazo

Con Java 8 tenemos a nuestra disposición una característica muy útil que puede ser muy útil para resolver los problemas mencionados anteriormente: métodos predeterminados.

Mi propuesta es tener una interfaz con un solo método para obtener las claves:

public interface I18nEnum {
   default String getMessageKey(Enum<?> e) {
      return e.getClass().getSimpleName() + '.' + e.name().toLowerCase();
   }
}

De esa manera, nuestros enumerados simplemente tienen que declarar que implementan esa interfaz, sin implementarla realmente, y tenemos los beneficios de tener nuestro código en un solo lugar, pero aún tenemos la flexibilidad de manejar casos específicos al anular el método predeterminado cuando sea necesario, por ejemplo:

public enum Sex implements I18nEnum {
    MALE,
    FEMALE,
    UNKNOWN {
        @Override
        public String getMessageKey(Enum<?> e) {
            return "Common.UNKNOWN";
        }
    }
}
    
respondido por el magomar 25.07.2016 - 23:26

Lea otras preguntas en las etiquetas

Comentarios Recientes

Metadatos El código anterior utiliza JavaBean para obtener los registros JNDI (Java Keydilies Inventory) que son compatibles con Java o para los cuales hay IDI para cada idioma de destino. El enfoque presenta JavaBean como una unidad que agrega a Oracle RPRO o UniResources locales y supone que la distribución de Java con la versión> = 1.3.0 está disponible (y se está actualizando) en su plataforma. El primer desafío que enfrentamos ahora es determine las ubicaciones de los objetos JNI únicos y devuelva una enumeración,... Lee mas