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

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

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

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

有帮助吗?

解决方案

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

或者我完全没有理解你的意思?

编辑:

更明确地说...我通常实现一个三列表...语言、键、值...其中“值”包含潜在的外语单词或短语......“语言”包含一些语言键,“键”是英语键(即登录.错误.密码.dup)...语言和密钥已索引...

然后,我在这样的结构上构建了界面,该结构显示每个键及其所有翻译(值)...它可以变得很奇特,包括审计跟踪和“脏”标记以及使翻译人员和数据输入人员能够使用它所需的所有其他内容。

编辑2:

现在您添加了有关 JSTL 标签的信息,我了解更多了......我自己从来没有这样做过。。但我发现了这个旧信息 服务器端...

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

其他提示

在上面 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;
            }

        };
    }

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

我们使用的是 Struts,所以我们最终编写了自己的 属性消息资源 实现允许我们做类似的事情 <bean:message key="impressum.text" />.

它工作得很好,让我们能够灵活地在前端动态切换语言以及动态更新翻译。

实际上,ScArcher2 需要的是大卫的回应,但没有标记为正确或有帮助。

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

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

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top