数据库支持的i18n(适用于Java Web应用)
我想使用数据库存储i18n键/值对,以便我们可以在运行时修改/重新加载i18n数据。有人这样做吗?还是有人对如何实现这一点有想法?我已经阅读了几个线程,但是还没有一个可行的解决方案。
我专门指的是可以与jstl标记一起使用的东西,例如
<fmt:setlocale> <fmt:bundle> <fmt:setBundle> <fmt:message>
我认为这将涉及扩展ResourceBundle,但是当我尝试这样做时,我遇到了与jstl标记获取资源包的方式有关的问题。
解决方案
回答
我们是否只是问如何在数据库中存储UTF-8 / 16字符?在mysql中,只需确保我们使用UTF8支持进行构建并将其设置为默认值,或者在列或者表级别指定它即可。我以前在oracle和mysql中完成过此操作。创建一个表并将其剪切并粘贴一些i18n数据,然后看看会发生什么……我们可能已经设置好了。
还是我完全想念你的意思?
编辑:
更明确的说...我通常实现一个三列表...语言,键,值...其中"值"包含潜在的外语单词或者短语..."语言"包含一些语言键和"键"是英文密钥(即login.error.password.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));
回答
我们有一个带有键/语言/术语的数据库表,其中键是一个n整数,是与语言一起的主键。
我们正在使用Struts,因此最终编写了自己的PropertyMessageResources实现,该实现使我们可以执行类似<bean:message key =" impressum.text" />`的操作。
它工作得很好,使我们能够灵活地在前端动态切换语言以及实时更新翻译。
回答
我终于在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; } }; }