2010-04-09 21 views
14

Teníamos muchas cadenas que contenían la misma subcadena, desde oraciones sobre la comprobación del registro o cómo contactarse con el soporte, hasta cadenas tipo branding que contienen el nombre de la empresa o del producto. La repetición nos estaba causando algunos problemas (principalmente errores tipográficos o de copiar/pegar) pero también causa problemas, ya que aumenta la cantidad de texto que nuestro traductor tiene que traducir.¿Cómo evito la repetición en cadenas Java ResourceBundle?

La solución que se me ocurrió fue algo como esto:

public class ExpandingResourceBundleControl extends ResourceBundle.Control { 
    public static final ResourceBundle.Control EXPANDING = 
    new ExpandingResourceBundleControl(); 

    private ExpandingResourceBundleControl() { } 

    @Override 
    public ResourceBundle newBundle(String baseName, Locale locale, String format, 
            ClassLoader loader, boolean reload) 
    throws IllegalAccessException, InstantiationException, IOException { 

     ResourceBundle inner = super.newBundle(baseName, locale, format, loader, reload); 
     return inner == null ? null : new ExpandingResourceBundle(inner, loader); 
    } 
} 

ExpandingResourceBundle delegados al paquete de recursos reales, pero realiza la conversión de {{}} this.kind.of.thing para buscar la clave en Los recursos.

Cada vez que desee obtener uno de estos, usted tiene que ir:

ResourceBundle.getBundle("com/acme/app/Bundle", EXPANDING); 

Y esto funciona bien - por un tiempo.

Lo que ocurre es que algún código nuevo (en nuestro caso código autogenerado que fue escupido de Matisse) busca el mismo paquete de recursos sin especificar el control personalizado. Esto parece ser no reproducible si escribe una prueba simple de unidad que lo llama con y luego sin, pero ocurre cuando la aplicación se ejecuta de manera real. De alguna manera, el caché dentro de ResourceBundle expulsa el buen valor y lo reemplaza por el roto. Aún no entiendo por qué y los archivos jar de Sun se compilaron sin información de depuración, por lo que la depuración es una tarea ardua.

Mis preguntas:

  1. ¿Hay alguna manera de establecer a nivel mundial el ResourceBundle.Control por defecto que podría no ser consciente de? Eso resolvería todo de manera bastante elegante.

  2. ¿Hay alguna otra manera de manejar este tipo de cosas elegantemente, quizás sin alterar las clases de ResourceBundle?

+0

Eso es algo que podría manejarse fácilmente mediante metaprogramación, si Java lo tuviera. –

Respuesta

6

Creo que este es un error fundamental en la forma en que se diseñaron los RecursoBundles: las teclas que hacen referencia a otras teclas violan automáticamente el principio DRY (no se repita). La forma en que resolví esto fue similar a su método: crear una clase ReflectiveResourceBundle que le permita especificar claves de Recursos en los mensajes usando la notación EL.

EL CAMINO EQUIVOCADO:

my.name.first=Bob 
my.name.last=Smith 
my.name.full=Bob Smith 

la manera correcta:

my.name.first=Bob 
my.name.last=Smith 
my.name.full=${my.name.first} ${my.name.last} 

he uploaded the code to GitHub para que usted o cualquier otra persona puede descargarlo. Además, he agregado un código de muestra para cualquiera que use el marco de trabajo de Stripes (http://www.stripesframework.org/) para que pueda comenzar a usarlo rápidamente.

El truco para hacer que esto funcione con JSTL estándar taglibs fue configurar un interceptor que reemplazó el recurso HttpServletRequest con el nuestro. El código es como la siguiente:

ResourceBundle bundle = MyStaticResourceHoldingTheBundle.getBundle(); 
Config.set(request, Config.FMT_LOCALIZATION_CONTEXT, new LocalizationContext(bundle, locale)); 

Tome un vistazo al paquete stripes.interceptor en el enlace anterior para más detalles.

+0

Tiene razón, esto es muy similar a nuestra solución, y tiene el mismo inconveniente: el código generado que utiliza ResourceBundle.getBundle() no recibirá la solución alternativa. La principal diferencia es que en nuestro caso usamos la memoria caché de ResourceBundle existente, mientras que en su caso usted conserva la memoria caché por separado, presumiblemente para trabajar alrededor de la caché de Sun ocasionalmente destruyendo los paquetes almacenados en la memoria caché. – Trejkaz

+0

Porque me tropecé con el mismo problema: Básicamente, quería volver a utilizar todas las características de ResourceBundle, pero agregar la expansión de la clave. Así que obtuve de PropertyResourceBundle y elimine handleGetObject() en consecuencia (como se puede ver en las fuentes de Matt Brocks). Luego obtuve de ResourceBundle.Control, reemplacé newBundle() para devolver mi PropertyResourceBundle personalizado (acaba de modificar el origen original de este método) y lo pasé a ResourceBundle.getBundle(). Esto hace posible reutilizar todas las características de ResourceBundle + add key-expansion. – quaylar

+0

@Matt Brock, parece que el enlace está roto. ¿Puedes compartir página? –

0

Si las repeticiones de cadena se localizan en el sentido de que usted conoce a una determinada cadena se repetirá pero sólo dentro de un mismo proyecto de tal manera que los paquetes de recursos para compartir no es una pesadilla de diseño, entonces usted podría considerar romper las cadenas hacia abajo en múltiples partes clave-valor. Separe las partes que se repiten de las que no lo hacen y reutilice las partes repetidas. Por ejemplo, digamos que usted tiene las dos cadenas siguientes que necesita para mostrar: "El Robin cubiertas de rojo es un pequeño natural paseriformes a Australia"

  1. "El Robin cubiertas de rojo se encuentra en regiones más secas en gran parte del continente ".

El paquete de recursos podría ser la siguiente:

robin.name=The Red-capped Robin 
robin.native=is a small passerine bird native to Australia. 
robin.region=is found in dryer regions across much of the continent. 

y luego combinar las piezas necesarias cuando sea necesario bundle.getString("robin.name")+bundle.getString(robin.native).

Una cosa que hay que tener cuidado es que aunque las reglas de la gramática como sujetos orden de predicado, etc. podría no ser el mismo en todos los idiomas. Por lo tanto, tendrías que tener un poco de cuidado al dividir oraciones.

+5

En realidad, eso también es una mala idea para diferentes contextos (por ejemplo, en un menú o en una barra de estado), el género del tema y la pluralidad (1, 2, 27, etc.), ya que también cambian según la cultura. – devstuff

+0

Esta es en realidad la razón exacta por la que elegimos hacer todas las inserciones desde el lado de ResourceBundle. Con nuestro enfoque actual, ese ejemplo se haría colocando {{robin.name}} en el lugar apropiado en los valores debajo de él. Si alguien encuentra un idioma en el que no funcionaría, tiene el poder de eliminar la variable por completo. Pero ... nuestra solución es, por supuesto, difícil de hacer el trabajo correctamente. – Trejkaz

Cuestiones relacionadas