我想使用数据库来存储i18n键/值对,这样我们就可以在运行时修改/重新加载i18n数据。有没有人这样做过?或者有没有人知道如何实现这个?我已经阅读了几个主题,但我还没有看到一个可行的解决方案。
我特别指的是可以使用jstl标签的东西,比如
<fmt:setlocale>
<fmt:bundle>
<fmt:setBundle>
<fmt:message>
我认为这将涉及扩展ResourceBundle,但是当我尝试这个时,我遇到了与jstl标签获取资源包的方式有关的问题。
答案 0 :(得分:13)
我终于得到了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;
}
};
}
答案 1 :(得分: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));
答案 2 :(得分:1)
我们有一个带有键/语言/术语的数据库表,其中key是一个n整数,是一个组合的主键和语言。
我们正在使用Struts,所以我们最终编写了自己的 PropertyMessageResources 实现,它允许我们执行类似<bean:message key="impressum.text" />
的操作。
它非常有效,并且我们可以灵活地在前端动态切换语言,以及动态更新翻译。
答案 3 :(得分:0)
实际上,ScArcher2需要的是大卫响应,但没有标记为正确或有帮助。
ScArcher2选择使用的解决方案是非常可怕的mestake :)一次加载所有翻译...在任何更大的应用程序中它将杀死它。每次请求加载翻译文章......
david的方法在实际生产环境中更常用。 有时为了限制每次翻译消息的数据库调用,您可以按主题,功能等创建翻译组来预加载它们。但这有点复杂,可以用好的缓存系统代替。