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


6

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

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

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

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

2

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

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

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

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

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

Edit 2:

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

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

1

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

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

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


13

Я, наконец, получил эту работу с помощью danb's help выше.

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

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

     }; 
    } 

0

Применительно к тому, что требуется ScArcher2, это ответ davids, который не отмечен как правильный или полезный.

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

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

  0

Я согласен с тем, что загрузка всех переводов в больших приложениях может вызвать проблемы. В нашем случае это не было проблемой, и это соответствовало нашей потребности. Мой код можно было изменить, чтобы использовать механизм кэширования, который поддерживал только подмножество переводов в памяти в любой момент времени. Код был просто примером того, что сработало, а не то, что было бы лучше всего во всех ситуациях. 05 сен. 122012-09-05 15:01:31