【问题标题】:Database backed i18n for java web-app用于 java web-app 的数据库支持 i18n
【发布时间】:2008-08-21 03:36:38
【问题描述】:

我想使用数据库来存储 i18n 键/值对,以便我们可以在运行时修改/重新加载 i18n 数据。有人做过吗?或者有没有人知道如何实现这个?我已经阅读了几个关于此的主题,但我还没有看到可行的解决方案。

我特别指的是可以与 jstl 标签一起使用的东西,例如

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

我认为这将涉及扩展 ResourceBundle,但是当我尝试这样做时遇到了与 jstl 标记获取资源包的方式有关的问题。

【问题讨论】:

    标签: java internationalization


    【解决方案1】:

    我终于在上面 danb 的帮助下完成了这项工作。

    这是我的资源包类和资源包控制类。

    我使用了来自@[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;
                }
    
            };
        }
    

    【讨论】:

      【解决方案2】:

      您只是想问如何在数据库中存储 UTF-8/16 字符?在 mysql 中,只需确保使用 UTF8 支持构建并将其设置为默认值,或者在列或表级别指定它。我以前在 oracle 和 mysql 中做过这个。创建一个表格并将一些 i18n 数据剪切并粘贴到其中,看看会发生什么...您可能已经设置好了..

      还是我完全没有理解你的意思?

      编辑:

      更明确地说...我通常实现一个三列表...语言、键、值...其中“值”包含潜在的外语单词或短语...“语言”包含一些语言键和“key”是英文键(即login.error.password.dup)...语言和键被索引...

      然后,我在这样的结构上构建了接口,显示每个键及其所有翻译(值)......它可以变得花哨并包括审计跟踪和“脏”标记以及您需要启用的所有其他内容翻译人员和数据输入人员使用它..

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

      【讨论】:

        【解决方案3】:

        我们有一个包含 key/language/term 的数据库表,其中 key 是一个 n 整数,并且是与语言一起组合的主键。

        我们正在使用 Struts,因此我们最终编写了自己的 PropertyMessageResources 实现,它允许我们执行类似 &lt;bean:message key="impressum.text" /&gt; 的操作。

        它运行良好,让我们可以灵活地在前端动态切换语言以及即时更新翻译。

        【讨论】:

          【解决方案4】:

          实际上,ScArcher2 需要的是没有标记为正确或有用的 davids 响应。

          ScArcher2 选择使用的解决方案是 imo 可怕的错误 :) 一次加载所有翻译......在任何更大的应用程序中它都会杀死它。每个请求都加载数千个翻译...

          david 的方法在实际生产环境中更常用。 有时为了限制数据库调用,即每条消息翻译,您可以按主题、功能等创建翻译组以预加载它们。但这有点复杂,可以用好的缓存系统代替。

          【讨论】:

          • 我同意在较大的应用程序中加载所有翻译可能会导致问题。在我们的案例中,这根本不是问题,它满足了我们的需要。我的代码可以修改为使用缓存机制,该机制在任何给定时间仅在内存中维护翻译的子集。该代码只是一个有效的示例,而不是在所有情况下都是最好的。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-03-14
          • 2022-01-19
          • 1970-01-01
          • 1970-01-01
          • 2018-08-24
          • 1970-01-01
          相关资源
          最近更新 更多