Baza danych wspierana i18n dla java web-app


6

Chciałbym użyć bazy danych do przechowywania par klucz/wartość i18n, abyśmy mogli modyfikować/ładować dane i18n w czasie wykonywania. Czy ktoś to zrobił? A może ktoś ma pomysł, jak to zaimplementować? Przeczytałem kilka wątków na ten temat, ale nie widziałem praktycznego rozwiązania.

jestem specjalnie odnosząc się do czegoś, co będzie współpracować z tagów JSTL takich jak

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

myślę, że to będzie obejmować rozszerzenie ResourceBundle, ale kiedy próbowałem to wpadłem na problemy, które musiały zrobić z sposób, w jaki znaczniki jstl pobierają pakiet zasobów.

2

Po prostu pytasz, jak przechowywać znaki UTF-8/16 w DB? w mysql to tylko kwestia upewnienia się, że budujesz z obsługą UTF8 i ustawiasz ją jako domyślną lub określasz ją na poziomie kolumny lub tabeli. Robiłem to wcześniej w Oracle i MySQL. Stwórz tabelę i wycinaj i wklejaj dane i18n do niej i zobacz, co się stanie ... możesz już ustawić.

lub czy całkowicie pomijam twój punkt widzenia?

edit:

być bardziej wyraźny ... Ja zwykle realizować trzy kolumny tabeli ... język, klucz, wartość ... gdzie „wartość” zawiera słowa lub wyrażenia języka obcego potencjalnie ... " język "zawiera jakiś klucz języka, a" klucz "jest angielskim kluczem (tzn. login.error.password.dup) ... język i klucz są indeksowane ...

Zbudowałem interfejsy na takiej strukturze który pokazuje każdy klucz wraz ze wszystkimi jego tłumaczeniami (wartościami) ... może się wydawać fantazyjny i zawierać ścieżki audytu i "brudne" znaczniki oraz wszystkie inne rzeczy, które są potrzebne, aby umożliwić tłumaczom i ludziom wprowadzającym dane do korzystania z nich ..

Edit 2:

Teraz, po dodaniu informacji o znacznikach JSTL, rozumiem trochę więcej ... Nigdy nie robiłem to sam .. ale znalazłem ten stary info o 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

Mamy tabelę bazy danych z kluczem/językiem/pojęciem, gdzie klucz jest liczbą całkowitą i jest połączonym kluczem podstawowym wraz z językiem.

Używamy Struts, więc skończyliśmy pisać własną implementację PropertyMessageResources, która pozwala nam zrobić coś takiego jak <bean:message key="impressum.text" />.

Działa bardzo dobrze i daje nam elastyczność, aby dynamicznie przełączać języki w interfejsie, jak również aktualizować tłumaczenia w locie.


13

W końcu udało mi się pracować z pomocą danb powyżej.

To jest moja klasa zasobów i klasa kontroli zasobów.

Użyłem tego kodu z @ [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)); 

i napisał tę klasę.

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

Rzeczywiście tym, czego potrzebował ScArcher2, była odpowiedź davidów, która nie jest oznaczona jako poprawna lub pomocna.

Rozwiązaniem, które ScArcher2 zdecydował się użyć, jest imo terrible mestake :) Ładowanie WSZYSTKICH tłumaczeń za jednym razem ... w większej aplikacji, która go zabije. Ładowanie tysięcy tłumaczeń dla każdego żądania ...

Metoda Davida jest częściej stosowana w rzeczywistych środowiskach produkcyjnych. Czasami, aby ograniczyć wywołania bazy danych, co w przypadku każdej przetłumaczonej wiadomości, można utworzyć grupy tłumaczeń według tematu, funkcjonalności itp., Aby wstępnie je załadować. Jest to jednak nieco bardziej skomplikowane i może zostać zastąpione dobrym systemem cache.

  0

Zgadzam się, że ładowanie wszystkich tłumaczeń w większych aplikacjach może powodować problemy. W naszym przypadku to nie był problem i spełnił naszą potrzebę. Mój kod można zmodyfikować tak, aby korzystał z mechanizmu buforowania, który utrzymywał tylko podzbiór tłumaczeń w pamięci w danym momencie. Kod był tylko przykładem tego, co zadziałało, a nie tym, co byłoby najlepsze we wszystkich sytuacjach. 05 wrz. 122012-09-05 15:01:31