База данных поддерживается i18n для веб-приложения Java

Я хотел бы использовать базу данных для хранения пар ключ / значение i18n, чтобы мы могли изменять / перезагружать данные i18n во время выполнения. Кто-нибудь делал это? Или у кого-нибудь есть идеи как это реализовать? Я прочитал несколько веток по этому вопросу, но я не видел работоспособного решения.

Я специально ссылаюсь на то, что будет работать с тегами jstl, такими как

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

Я думаю, что это будет связано с расширением ResourceBundle, но когда я попробовал это, я столкнулся с проблемами, связанными с тем, как теги jstl получают пакет ресурсов.

21.08.2008 03:36:38
4 ОТВЕТА
РЕШЕНИЕ

Вы просто спрашиваете, как хранить символы UTF-8/16 в БД? в mysql нужно просто убедиться, что вы строите с поддержкой UTF8 и установить его по умолчанию или указать его на уровне столбца или таблицы. Я сделал это в Oracle и MySQL раньше. Создайте таблицу, вырежьте и вставьте в нее некоторые данные i18n и посмотрите, что произойдет ... возможно, вы уже настроены ..

или я полностью упускаю твою точку зрения?

редактировать:

чтобы быть более явным ... я обычно реализую таблицу из трех столбцов ... язык, ключ, значение ... где "значение" содержит потенциально иностранные слова или фразы ... "язык" содержит некоторый языковой ключ и "ключ" это английский ключ (т.е. login.error.password.dup) ... язык и ключ индексируются ...

Затем я построил интерфейсы на подобной структуре, которая показывает каждый ключ со всеми его переводами (значениями) ... он может стать причудливым и включать контрольные журналы и «грязные» маркеры и все остальное, что нужно для включения переводчиков и данных. народ, чтобы использовать это ..

Изменить 2:

Теперь, когда вы добавили информацию о тегах JSTL, я понимаю немного больше ... Я сам никогда этого не делал ... но я нашел эту старую информацию на сервере ...

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));
2
21.08.2008 04:21:55

У нас есть таблица базы данных с ключом / языком / термином, где ключ является целым числом и представляет собой комбинированный первичный ключ вместе с языком.

Мы используем Struts, поэтому в итоге мы написали собственную реализацию PropertyMessageResources, которая позволяет нам делать что-то подобное <bean:message key="impressum.text" />.

Это работает очень хорошо и дает нам гибкость для динамического переключения языков во внешнем интерфейсе, а также обновления переводов на лету.

1
21.08.2008 05:36:50

Я наконец получил это с помощью Данба выше.

Это мой класс комплекта ресурсов и класс управления комплектом ресурсов.

Я использовал этот код из @ [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));

и написал этот класс.

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

        };
    }
13
8.09.2008 21:56:09

На самом деле ScArcher2 необходим ответ Дэвидса, который не помечен как правильный или полезный.

Решение, которое ScArcher2 выбрал для использования, - это ужасное место для меня :) Загрузка ВСЕХ переводов за один раз ... в любом более крупном приложении это убьет его. Загрузка тысячи переводов каждый запрос ...

Метод Дэвида чаще используется в реальных производственных средах. Иногда, чтобы ограничить количество вызовов в БД, что при переводе каждого сообщения, вы можете создавать группы переводов по темам, функциям и т. Д. Для их предварительной загрузки. Но это немного сложнее и может быть заменено хорошей системой кэширования.

0
31.08.2012 07:34:45
Я согласен, что загрузка всех переводов в больших приложениях может вызвать проблемы. В нашем случае это не было проблемой вообще, и это отвечало нашим потребностям. Мой код может быть изменен для использования механизма кэширования, который поддерживает только подмножество переводов в памяти в любой момент времени. Код был просто примером того, что сработало, а не того, что было бы лучше во всех ситуациях.
ScArcher2 5.09.2012 15:01:31