2008-08-21 14 views
6

Ich möchte eine Datenbank verwenden, um i18n Schlüssel/Wert-Paare zu speichern, so dass wir die i18n Daten zur Laufzeit ändern/neu laden können. Hat jemand das getan? Oder hat jemand eine Idee, wie man das umsetzt? Ich habe mehrere Threads zu diesem Thema gelesen, aber ich habe keine praktikable Lösung gesehen.Datenbank gesichert i18n für Java Web-App

Ich beziehe speziell auf etwas, das mit dem jstl Tags wie

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

Ich denke, diese beinhalten werden erstrecken Resource funktionieren würde, aber wenn ich das habe ich versucht, lief in Probleme, die mit dem zu tun hatten, So erhalten die JSTL-Tags das Ressourcenpaket.

Antwort

2

Fragen Sie nur, wie UTF-8/16 Zeichen in einer DB gespeichert werden? In Mysql ist es nur eine Frage der Sicherstellung, dass Sie mit UTF8-Unterstützung erstellen und diese als Standard festlegen oder auf Spalten- oder Tabellenebene angeben. Ich habe dies in Oracle und MySQL zuvor getan. Erstellen Sie eine Tabelle und schneiden Sie einige i18n Daten aus und fügen Sie sie ein und sehen, was passiert ... Sie könnten bereits eingestellt sein ..

oder verpasse ich Ihren Punkt vollständig?

edit:

expliziter zu sein ... ich in der Regel implementieren eine dreispaltige Tabelle ... Sprache, Schlüssel, Wert ... in den „Wert“ potenziell Fremdsprache Worte oder Phrasen enthält ... " sprache "enthält einen sprachschlüssel und" schlüssel "ist ein englischer schlüssel (dh login.error.password.dup) ... sprache und schlüssel sind indexiert ...

Ich habe dann Schnittstellen auf eine Struktur wie diese gebaut das zeigt jeden Schlüssel mit all seinen Übersetzungen (Werten) ... er kann interessant werden und Prüfpfade und "schmutzige" Markierungen und all die anderen Dinge enthalten, die man benötigt, um Übersetzer und Dateneinsteiger zu aktivieren.

Edit 2:

Jetzt, wo Sie die Informationen über die JSTL-Tags hinzugefügt, verstehe ich ein bisschen mehr ... Ich habe noch nie getan, dass ich .. aber ich diese alte Informationen über theserverside ...

gefunden
1

Wir haben eine Datenbanktabelle mit Schlüssel/Sprache/Begriff, wobei Schlüssel eine ganze Zahl ist und ein kombinierter Primärschlüssel zusammen mit Sprache ist.

Wir Struts verwenden, so dass wir schließlich unsere eigene PropertyMessageResources Umsetzung Schreiben auf, die uns so etwas wie <bean:message key="impressum.text" /> tun können.

Es funktioniert sehr gut und gibt uns die Flexibilität, Sprachen im Front-End dynamisch zu wechseln sowie die Übersetzungen im laufenden Betrieb zu aktualisieren.

13

Ich habe endlich das funktioniert mit danbs Hilfe oben.

Dies ist meine Ressourcenpaketklasse und Ressourcenbündel-Steuerelementklasse.

Ich habe diesen Code von @ [danb] verwendet.

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

und schrieb diese Klasse.

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

Wirklich was ScArcher2 benötigt ist David Antwort, die nicht als richtig oder hilfreich markiert ist.

Die Lösung, die ScArcher2 verwendet, ist imo terrible mestake :) Alle Übersetzungen gleichzeitig laden ... in jeder größeren Anwendung wird es sie töten. Das Laden tausender Übersetzungen für jede Anfrage ...

Die Methode von David wird häufiger in realen Produktionsumgebungen verwendet. Manchmal, um db-Aufrufe zu begrenzen, die mit jeder übersetzten Nachricht sind, können Sie Gruppen von Übersetzungen nach Thema, Funktionalität usw. erstellen, um sie vorher zu laden. Dies ist jedoch etwas komplexer und kann durch ein gutes Cache-System ersetzt werden.

+0

Ich stimme zu, dass das Laden aller Übersetzungen in größere Anwendungen Probleme verursachen kann. In unserem Fall war das überhaupt kein Problem und es entsprach unserem Bedarf. Mein Code könnte geändert werden, um einen Caching-Mechanismus zu verwenden, der nur eine Teilmenge der Übersetzungen im Speicher zu einem bestimmten Zeitpunkt verwaltet. Der Code war nur ein Beispiel dafür, was funktioniert hat, nicht das Beste in allen Situationen. – ScArcher2