架构设置不断变化的文本,属性文件?在Java EE环境中

Ber*_*own 3 java resourcebundle internationalization java-ee

在Java EE环境中,我们通常用于在属性/资源文件中存储文本.并且该属性文件与某些视图HTML标记文件相关联.例如,如果您的标签"名字"在HTML页面上更改为"全名",则可以使用该属性进行更新.

firstName=First Name
someOtherData=This is the data to display on screen, from property file
Run Code Online (Sandbox Code Playgroud)

如果您所处的环境很难定期更新这些属性文件,那么开发人员使用哪种架构来更改通常位于属性文件中的文本/标签内容?或者假设您需要在重新部署属性文件更改之前更改该内容.一个糟糕的解决方案是将其存储在数据库中?开发人员使用memcache吗?这通常用于缓存解决方案吗?

编辑-1数据库实际上不是为这类任务设计的(拉动文本显示在屏幕上),但数据库有用例.我可以添加区域设置列或状态字段,也可以按组添加列过滤器.如果我不使用数据库或属性文件,那么分布式键/值解决方案将允许我添加自定义过滤器?

编辑-2你会在java框架之外使用解决方案吗?像键/值数据存储一样?memcachedb?

Paw*_*yda 11

我想向您保证,如果您需要对本地化文本进行不断更改,例如它们往往因部署而异,那么数据库就是您的选择.好吧,不仅仅是数据库,你需要以某种方式缓存你的字符串.当然,我想你不想完全重建你的资源访问层.

为此我可以建议扩展ResourceBundle类以自动从数据库加载字符串并将其存储WeakHashMap.我WeakHashMap之所以选择它的功能 - 它不再需要从地图中删除一个键来减少内存占用.无论如何,您需要创建一个访问者类.既然你提到了J2EE,这是非常古老的技术,我将为你提供Java SE 1.4兼容的例子(它可以很容易地为新的Java重新工作,只需在需要时添加@Override并在Enumeration中添加一些String泛化):

public class WeakResourceBundle extends ResourceBundle {
    private Map cache = new WeakHashMap();
    protected Locale locale = Locale.US; // default fall-back locale

    // required - Base is abstract
    // @Override
    protected Object handleGetObject(String key) {
        if (cache.containsKey(key))
            return cache.get(key);

        String value = loadFromDatabase(key, locale);
        cache.put(key, value);

        return value;
    }

    // required - Base is abstract
    // @Override
    public Enumeration getKeys() {
        return loadKeysFromDatabase();
    }

    // optional but I believe needed
    // @Override
    public Locale getLocale() {
        return locale;
    }

    // dummy testing method, you need to provide your own
    // should throw MissingResourceException if key does not exist
    private String loadFromDatabase(String key, Locale aLocale) {
        System.out.println("Loading key: " + key
                + " from database for locale:"
                + aLocale );

        return "dummy_" + aLocale.getDisplayLanguage(aLocale);
    }

    // dummy testing method, you need to provide your own
    private Enumeration loadKeysFromDatabase() {
        return Collections.enumeration(new ArrayList());
    }
}
Run Code Online (Sandbox Code Playgroud)

由于一些奇怪的ResourceBundle加载规则,您实际上需要扩展WeakResourceBundle类以为受支持的语言创建一个类:

// Empty Base class for Invariant Language (usually English-US) resources
// Do not need to modify anything here since I already set fall-back language
package com.example.i18n;

public class MyBundle extends WeakResourceBundle {

}
Run Code Online (Sandbox Code Playgroud)

每个支持一种语言(我知道它很糟糕):

// Example class for Polish ResourceBundles
package com.example.i18n;

import java.util.Locale;

public class MyBundle_pl extends WeakResourceBundle {

    public MyBundle_pl() {
        super();
        locale = new Locale("pl");
    }
}
Run Code Online (Sandbox Code Playgroud)

现在,如果你需要实例化你的ResourceBundle,你只需要打电话:

// You probably need to get Locale from web browser
Locale polishLocale = new Locale("pl", "PL");
ResourceBundle myBundle = ResourceBundle.getBundle(
                "com.example.i18n.MyBundle", polishLocale);
Run Code Online (Sandbox Code Playgroud)

并访问密钥:

String someValue = myBundle.getString("some.key");
Run Code Online (Sandbox Code Playgroud)

可能的陷阱:

  1. ResourceBundle 需要完全合格的类名(因此包名称).
  2. 如果省略Locale参数,Locale则将使用default(表示Server).确保Locale在实例化时始终通过ResourceBundle.
  3. myBundle.getString()MissingResourceException如果你按照我的建议,可能会抛出.您需要使用try-catch块来避免问题.相反,如果丢失密钥(例如return "!" + key + "!"),您可能决定从数据库访问层返回一些虚拟字符串,但无论哪种方式,它都应该记录为错误.
  4. 您应该始终尝试创建传递语言和国家/地区代码的Locale对象.这只是因为,例如简体中文(zh_CN)和繁体中文(zh_TW)等语言是完全不同的语言(至少在写作方面),你需要支持它们的两种风格.对于其他国家/地区,ResourceBundle实际上会自动加载正确的语言资源(请注意,我已创建MyBundle_pl.java,而不是MyBundle_pl_PL.java它仍然有效.此外,MyBundle.java如果没有给定语言的资源类,ResourceBundle将自动回退到Enlish-US()这就是我使用这种奇怪的类层次结构的原因).

编辑

关于如何使它更加令人敬畏的一些随机想法.

静态工厂(避免直接使用ResourceBundle)

您可以添加静态工厂方法,而不是使用ResourceBundle直接实例化包.

public static ResourceBundle getInstance(Locale aLocale) {
    return ResourceBundle.getBundle("com.example.i18n.MyBundle", aLocale);
}
Run Code Online (Sandbox Code Playgroud)

如果您决定将WeakResourceBundle类的名称更改为更合适的(我决定使用LocalizationProvider),您现在可以通过使用代码轻松实例化您的bundle:

ResourceBundle myBundle = LocalizationProvider.getInstance(polishLocale);
Run Code Online (Sandbox Code Playgroud)

自动生成的资源类

MyBundle可以通过构建脚本轻松生成本地化类.该脚本可以是配置文件或数据库驱动的 - 它需要知道系统中正在使用哪个Locale.无论哪种方式,类共享非常相似的代码,因此自动生成它们确实有意义.

自动检测区域设置

由于您是实现该类的人,因此您可以完全控制其行为.因此(了解您的应用程序架构)您可以在此处包含区域设置检测并进行修改getInstance()以自动加载适当的语言资源.

实现其他与本地化相关的方法

在本地化应用程序中需要完成常见任务 - 格式化和解析日期,数字,货币等是常见示例.拥有最终用户的Locale后,您只需将此类方法包装在LocalizationProvider中即可.

哎呀,我真的很喜欢我的工作:)