Database supportato i18n per java web-app


6

Mi piacerebbe utilizzare un database per memorizzare coppie chiave/valore i18n in modo che possiamo modificare/ricaricare i dati i18n in fase di esecuzione. Qualcuno ha fatto questo? O qualcuno ha un'idea di come implementarlo? Ho letto diversi thread su questo, ma non ho visto una soluzione praticabile.

Sono in particolare riferendosi a qualcosa che avrebbe funzionato con i tag JSTL quali

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

Penso che questo comporterà l'estensione ResourceBundle, ma quando ho provato questo mi sono imbattuto in problemi che hanno avuto a che fare con la in modo che i tag jstl ottengano il pacchetto di risorse.

2

Stai solo chiedendo come memorizzare i caratteri UTF-8/16 in un DB? in mysql è solo una questione di assicurarsi di creare con il supporto UTF8 e impostarlo come predefinito, o specificandolo a livello di colonna o tabella. Ho già fatto questo in oracle e mysql. Crea una tabella e taglia e incolla alcuni dati i18n e vedi cosa succede ... potresti essere già impostato ..

o mi manca completamente il tuo punto?

edit:

essere più espliciti ... Io di solito implementare un tavolo a tre colonne ... la lingua, chiave, valore ... dove "valore" contiene parole in lingua straniera potenzialmente o frasi ... " lingua "contiene una chiave di lingua e" chiave "è una chiave inglese (es. login.error.password.dup) ... lingua e chiave sono indicizzate ...

Ho quindi creato interfacce su una struttura come questa che mostra ogni chiave con tutte le sue traduzioni (valori) ... può essere fantasiosa e includere tracce di controllo e marcatori "sporchi" e tutte le altre cose necessarie per consentire ai traduttori e agli addetti ai dati di usarle.

Edit 2:

Ora che è stato aggiunto l'informazioni sui tag JSTL, ho capito un po 'di più ... Non ho mai fatto io stesso .. ma ho trovato questa vecchia informazioni 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

Abbiamo una tabella di database con chiave/lingua/termine dove la chiave è un numero intero ed è una chiave primaria combinata insieme alla lingua.

Stiamo utilizzando Struts, così abbiamo finito per scrivere le nostre PropertyMessageResources applicazione che ci permette di fare qualcosa di simile <bean:message key="impressum.text" />.

Funziona molto bene e ci dà la flessibilità di passare dinamicamente le lingue nel front-end e di aggiornare le traduzioni al volo.


13

Finalmente ho funzionato con l'aiuto di danb sopra.

Questa è la classe di controllo del pacchetto di risorse classe e risorsa.

Ho usato questo codice da @ [danb] 's.

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

e ha scritto questa classe.

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

actuly quello ScArcher2 necessario è davids risposta che non è segnalata una corretta o disponibile.

La soluzione ScArcher2 ha scelto di utilizzare è imo terribile mestake :) Caricamento di tutte le traduzioni contemporaneamente ... in qualsiasi applicazione più grande, lo ucciderà. Caricamento di migliaia di traduzioni ogni richiesta ...

Il metodo di david è più comunemente utilizzato negli ambienti di produzione reali. A volte per limitare le chiamate db, che è con ogni messaggio tradotto, è possibile creare gruppi di traduzioni per argomento, funzionalità ecc. Per precaricarle. Ma questo è un po 'più complesso e può essere sostituito con un buon sistema di cache.

  0

Sono d'accordo che caricare tutte le traduzioni in applicazioni più grandi potrebbe causare problemi. Nel nostro caso non è stato affatto un problema e ha soddisfatto il nostro bisogno. Il mio codice potrebbe essere modificato per utilizzare un meccanismo di memorizzazione nella cache che ha mantenuto solo un sottoinsieme delle traduzioni in memoria in un dato momento. Il codice era solo un esempio di ciò che funzionava, non di quello che sarebbe stato il migliore in tutte le situazioni. 05 set. 122012-09-05 15:01:31