i18n с поддержкой базы данных для Java web-приложения

StackOverflow https://stackoverflow.com/questions/19295

  •  09-06-2019
  •  | 
  •  

Вопрос

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

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

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

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

Это было полезно?

Решение

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

или я совершенно не понимаю, к чему вы клоните?

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

чтобы быть более откровенным...Обычно я реализую таблицу из трех столбцов...язык, ключ, значение...где "значение" содержит потенциально иноязычные слова или фразы..."language" содержит некоторый языковой ключ, а "key" - это английский ключ (т.е.логин.ошибка.пароль.дублирование)...язык и ключ индексируются...

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

Правка 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));

Другие советы

Наконец-то у меня получилось это сделать с помощью danb, описанной выше.

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

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

        };
    }

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

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

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top