用于 java web-app 的数据库支持的 i18n

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/19295/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-29 10:45:19  来源:igfitidea点击:

Database backed i18n for java web-app

javainternationalization

提问by ScArcher2

I'd like to use a database to store i18n key/value pairs so we can modify / reload the i18n data at runtime. Has anyone done this? Or does anyone have an idea of how to implement this? I've read several threads on this, but I haven't seen a workable solution.

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

I'm specifically refering to something that would work with the jstl tags such as

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

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

I think this will involve extending ResourceBundle, but when I tried this I ran into problems that had to do with the way the jstl tags get the resource bundle.

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

采纳答案by danb

Are you just asking how to store UTF-8/16 characters in a DB? in mysql it's just a matter of making sure you build with UTF8 support and setting that as the default, or specifying it at the column or table level. I've done this in oracle and mysql before. Create a table and cut and paste some i18n data into it and see what happens... you might be set already..

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

or am I completely missing your point?

还是我完全错过了你的观点?

edit:

编辑:

to be more explicit... I usually implement a three column table... language, key, value... where "value" contains potentially foreign language words or phrases... "language" contains some language key and "key" is an english key (i.e. login.error.password.dup)... language and key are indexed...

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

I've then built interfaces on a structure like this that shows each key with all its translations (values)... it can get fancy and include audit trails and "dirty" markers and all the other stuff you need to enable translators and data entry folk to make use of it..

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

Edit 2:

编辑2:

Now that you added the info about the JSTL tags, I understand a bit more... I've never done that myself.. but I found this old info on theserverside...

现在你添加了关于 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));

回答by ScArcher2

I finally got this working with danb's help above.

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

This is my resource bundle class and resource bundle control class.

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

I used this code from @[danb]'s.

我使用了@[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));

and wrote this class.

并编写了这个类。

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

        };
    }

回答by dlinsin

We have a database table with key/language/term where key is a n integer and is a combined primary key together with language.

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

We are using Struts, so we ended up writing our own PropertyMessageResourcesimplementation which allows us to do something like <bean:message key="impressum.text" />.

我们正在使用 Struts,所以我们最终编写了我们自己的PropertyMessageResources实现,它允许我们执行类似<bean:message key="impressum.text" />.

It works very well and gives us the flexibility to do dynamically switch languages in the front-end as well as updating the translations on the fly.

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

回答by krsnik

Actuly what ScArcher2 needed is davids response which is not marked a correct or helpfull.

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

The solution ScArcher2 chose to use is imo terrible mestake:) Loading ALL the translations at one time... in any bigger application its gonna kill it. Loading thousends of translations each request...

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

david's method is more commonly used in real production environments. Sometimes to limit db calls, which is with every message translated, you can create groups of translations by topic, functionality etc. to preload them. But this is little bit more complex and can be substituted with good cache system.

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