Pregunta

Me gustaría usar una base de datos para almacenar pares clave/valor de i18n para que podamos modificar/recargar los datos de i18n en tiempo de ejecución.¿Alguien ha hecho esto?¿O alguien tiene una idea de cómo implementar esto?He leído varios hilos sobre esto, pero no he visto una solución viable.

Me refiero específicamente a algo que funcionaría con las etiquetas jstl como

<fmt:setlocale>
<fmt:bundle>
<fmt:setBundle>
<fmt:message>

Creo que esto implicará extender ResourceBundle, pero cuando lo intenté me encontré con problemas relacionados con la forma en que las etiquetas jstl obtienen el paquete de recursos.

¿Fue útil?

Solución

¿Estás preguntando cómo almacenar caracteres UTF-8/16 en una base de datos?en mysql es solo cuestión de asegurarse de compilar con soporte UTF8 y configurarlo como predeterminado, o especificarlo a nivel de columna o tabla.He hecho esto en Oracle y MySQL antes.Cree una tabla, corte y pegue algunos datos de i18n en ella y vea qué sucede...puede que ya estés listo...

¿O me estoy perdiendo por completo tu punto?

editar:

para ser más explícito...Normalmente implemento una tabla de tres columnas...idioma, clave, valor...donde "valor" contiene palabras o frases potencialmente extranjeras..."idioma" contiene alguna clave de idioma y "clave" es una clave en inglés (es decir,inicio de sesión.error.contraseña.dup)...el idioma y la clave están indexados...

Luego construí interfaces en una estructura como esta que muestra cada clave con todas sus traducciones (valores)...puede volverse sofisticado e incluir pistas de auditoría y marcadores "sucios" y todo lo demás que necesita para permitir que los traductores y el personal de entrada de datos lo utilicen.

Edición 2:

Ahora que agregaste la información sobre las etiquetas JSTL, entiendo un poco más...Yo nunca he hecho eso..pero encontré esta información antigua en el lado del servidor...

HttpSession session = .. [get hold of the session] 
ResourceBundle bundle = new PropertyResourceBundle(toInputStream(myOwnProperties)) [toInputStream just stores the properties into an inputstream] 
Locale locale = .. [get hold of the locale]
javax.servlet.jsp.jstl.core.Config.set(session, Config.FMT_LOCALIZATION_CONTEXT, new LocalizationContext(bundle ,locale));

Otros consejos

Finalmente logré que esto funcionara con la ayuda de danb arriba.

Esta es mi clase de paquete de recursos y mi clase de control de paquete de recursos.

Utilicé este código de @[danb].

ResourceBundle bundle = ResourceBundle.getBundle("AwesomeBundle", locale, DbResourceBundle.getMyControl());
javax.servlet.jsp.jstl.core.Config.set(actionBeanContext.getRequest(), Config.FMT_LOCALIZATION_CONTEXT, new LocalizationContext(bundle, locale));

y escribí esta clase.

public class DbResourceBundle extends ResourceBundle
{
    private Properties properties;

    public DbResourceBundle(Properties inProperties)
    {
        properties = inProperties;
    }

    @Override
    @SuppressWarnings(value = { "unchecked" })
    public Enumeration<String> getKeys()
    {
        return properties != null ? ((Enumeration<String>) properties.propertyNames()) : null;
    }

    @Override
    protected Object handleGetObject(String key)
    {
        return properties.getProperty(key);
    }

    public static ResourceBundle.Control getMyControl()
    {
        return new ResourceBundle.Control()
        {

            @Override
            public List<String> getFormats(String baseName)
            {
                if (baseName == null)
                {
                    throw new NullPointerException();
                }
                return Arrays.asList("db");
            }

            @Override
            public ResourceBundle newBundle(String baseName, Locale locale, String format, ClassLoader loader, boolean reload) throws IllegalAccessException,
                  InstantiationException, IOException
            {
                if ((baseName == null) || (locale == null) || (format == null) || (loader == null))
                    throw new NullPointerException();
                ResourceBundle bundle = null;
                if (format.equals("db"))
                {
                    Properties p = new Properties();
                    DataSource ds = (DataSource) ContextFactory.getApplicationContext().getBean("clinicalDataSource");
                    Connection con = null;
                    Statement s = null;
                    ResultSet rs = null;
                    try
                    {
                        con = ds.getConnection();
                        StringBuilder query = new StringBuilder();
                        query.append("select label, value from i18n where bundle='" + StringEscapeUtils.escapeSql(baseName) + "' ");

                        if (locale != null)
                        {
                            if (StringUtils.isNotBlank(locale.getCountry()))
                            {
                                query.append("and country='" + escapeSql(locale.getCountry()) + "' ");

                            }
                            if (StringUtils.isNotBlank(locale.getLanguage()))
                            {
                                query.append("and language='" + escapeSql(locale.getLanguage()) + "' ");

                            }
                            if (StringUtils.isNotBlank(locale.getVariant()))
                            {
                                query.append("and variant='" + escapeSql(locale.getVariant()) + "' ");

                            }
                        }
                        s = con.createStatement();
                        rs = s.executeQuery(query.toString());
                        while (rs.next())
                        {
                            p.setProperty(rs.getString(1), rs.getString(2));
                        }
                    }
                    catch (Exception e)
                    {
                        e.printStackTrace();
                        throw new RuntimeException("Can not build properties: " + e);
                    }
                    finally
                    {
                        DbUtils.closeQuietly(con, s, rs);
                    }
                    bundle = new DbResourceBundle(p);
                }
                return bundle;
            }

            @Override
            public long getTimeToLive(String baseName, Locale locale)
            {
                return 1000 * 60 * 30;
            }

            @Override
            public boolean needsReload(String baseName, Locale locale, String format, ClassLoader loader, ResourceBundle bundle, long loadTime)
            {
                return true;
            }

        };
    }

Tenemos una tabla de base de datos con clave/idioma/término donde clave es un número entero y es una clave primaria combinada junto con el idioma.

Usamos Struts, así que terminamos escribiendo el nuestro PropiedadMensajeRecursos implementación que nos permite hacer algo como <bean:message key="impressum.text" />.

Funciona muy bien y nos brinda la flexibilidad de cambiar dinámicamente de idioma en el front-end, así como actualizar las traducciones sobre la marcha.

En realidad, lo que ScArcher2 necesitaba es la respuesta de David, que no está marcada como correcta o útil.

La solución que ScArcher2 eligió usar es, en mi opinión, un error terrible :) Cargar TODAS las traducciones a la vez...en cualquier aplicación más grande lo matará.Cargando miles de traducciones en cada solicitud...

El método de David se utiliza más comúnmente en entornos de producción reales.A veces, para limitar las llamadas a la base de datos, que es con cada mensaje traducido, puedes crear grupos de traducciones por tema, funcionalidad, etc.para precargarlos.Pero esto es un poco más complejo y puede sustituirse por un buen sistema de caché.

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