基于插件的程序中的Locales和ResourceBundle

时间:2008-11-12 03:03:49

标签: java internationalization locale resourcebundle

我们需要开始为我们的计划添加国际化。值得庆幸的是,不仅仅是一点点,但我希望我们这样做的方式可以扩展到可能覆盖整个程序。问题是,我们的程序基于插件,因此并非所有字符串都属于同一个地方。

据我了解,Java的ResourceBundle就像这样工作。您创建了一个扩展ResourceBundle的类,称为MyProgramStrings,以及特定于语言的类MyProgramStrings_frMyProgramStrings_es等。这些类中的每一个都映射键(字符串)值(任何对象)。这些类中的每一个都可以从中获取数据,但是它们的常见位置是属性文件。

您可以分两个阶段查找值:首先获取正确的包,然后查询它以获取所需的字符串。

Locale locale = Locale.getDefault(); // or = new Locale("en", "GB");
ResourceBundle rb = ResourceBundle.getBundle("MyProgramStrings", locale);
String wotsitName = rb.getString("wotsit.name");

但是,我们需要的是将多个语言环境的结果合并到一个资源空间中。例如,插件需要能够覆盖已定义的字符串,并在代码查找字符串时返回新值。

我在这一切中有点迷失。有人可以帮忙吗?


更新:David Waters问道:

  

我已将答案放在最底层,但我很想知道你是如何解决这个问题的。

好吧,我们还没有走得太远 - 长期的WIBNI总是成为最新危机的受害者 - 但我们的基础是插件实现的接口,资源具有相同的完全限定名称的约定作为界面。

因此,接口UsersAPI可能有各种不同的实现。默认情况下,该接口上的方法getBundle()返回等效的ResourceBundle.get("...UsersAPI", locale)。该文件可以替换,或者UsersAPI的实现可以覆盖该方法,如果他们需要更复杂的东西。

到目前为止,我们需要的是,但我们仍然在寻找基于插件的更灵活的解决方案。

4 个答案:

答案 0 :(得分:2)

你没有拥有来将ResourceBundles实现为一系列类,每个语言环境有一个类(即一个名为MyProgramStringsMyProgramStrings_fr,{{1}的类})。如果需要,ResourceBundle类将回退到使用属性文件:

MyProgramStrings_de

如果我在类路径上有一个名为public static void main(String[] args) { ResourceBundle bundle = ResourceBundle.getBundle("MyResources"); System.out.println("got bundle: " + bundle); String valueInBundle = bundle.getString("someKey"); System.out.println("Value in bundle is: " + valueInBundle); } 的文件,则此方法将导致:

MyResources.properties

至于设置捆绑包的层次结构,或者将它们“合并”在一起,我恐怕在那里帮不了多少,除了我知道Spring确实有hierchical MessageSources的概念({{ 3}})在java.util.ResourceBundle之上实现,所以也许你可以使用Spring的功能来实现你想要的东西?

顺便说一句,这是got bundle: java.util.PropertyResourceBundle@42e816 Value in bundle is: someValue javadoc的相关部分,它解释了它的“搜索和实例化策略”:

link to API,java.util.Locale,java.lang.ClassLoader)

  
      
  • 首先,它尝试使用候选包名称加载类。如果可以使用指定的类加载器找到并加载这样的类,与ResourceBundle兼容的赋值,可从ResourceBundle访问,并且可以实例化,则getBundle创建此类的新实例并将其用作结果资源包。
  •   
  • 否则,getBundle会尝试查找属性资源文件。它通过替换所有“。”从候选包名称生成路径名。带有“/”的字符并附加字符串“.properties”。它尝试使用ClassLoader.getResource查找具有此名称的“资源”。 (请注意,getResource意义上的“资源”与资源包的内容无关,它只是数据的容器,例如文件。)如果找到“资源”,它会尝试创建来自其内容的新PropertyResourceBundle实例。如果成功,则此实例将成为结果资源包。
  •   

答案 1 :(得分:1)

我知道Struts和Spring有相关的东西。但是,假设您不能使用Struts或Spring,那么我要做的是创建ResourceBundle的子类并在此ResourceBundle中加载* .properties(每个插件一个)。然后你可以使用

  

ResourceBundle bundle = ResourceBundle.getBundle(“MyResources”);

就像通常使用属性文件一样。

当然,您应该缓存结果,这样您就不必每次都搜索每个属性。

答案 2 :(得分:1)

getBundle加载一组使用指定语言环境(如果有)和默认语言环境生成的候选包文件。

通常,没有语言环境信息的资源文件具有“默认”值(例如,可能en_US值在MyResources.properties中),然后为不同的语言环境创建过度资源值(例如{{1 }}字符串在MyResources_ja.properties中。更具体的文件中的任何资源都会覆盖不太具体的文件属性。

现在,您希望为每个插件添加能力,以提供自己的属性文件集。目前尚不清楚插件是否能够修改主应用程序或其他插件的资源,但听起来你想拥有一个单例类,其中每个插件都可以注册其基本资源文件名。所有对属性值的请求都会通过该单例,然后查看每个插件的资源包(按某种顺序......),最后查看应用程序本身的属性值。

Netbeans NbBundle类也做类似的事情。

答案 3 :(得分:1)

我遇到了类似的问题,请参阅question 653682。 我找到的解决方案是有一个扩展ResourceBundle的类,如果没有委托给从文件生成的PropertyResourceBundle,检查我们是否覆盖了值。

因此,如果您想为UI存储标签,则可以使用com.example.UILabels类和属性文件com.example.UILabelsFiles。

package com.example;

public class UILabels extends ResourceBundle{
    // plugins call this method to register there own resource bundles to override
    public static void addPluginResourceBundle(String bundleName){
        extensionBundles.add(bundleName);
    }

    // Find the base Resources via standard Resource loading
    private ResourceBundle getFileResources(){
        return ResourceBundle.getBundle("com.example.UILabelsFile", this.getLocale());
    }
    private ResourceBundle getExtensionResources(String bundleName){
        return ResourceBundle.getBundle(bundleName, this.getLocale());
    }

    ...
    protected Object handleGetObject(String key){
        // If there is an extension value use that
        Object extensionValue = getValueFromExtensionBundles(key);
        if(extensionValues != null)
            return extensionValues;
        // otherwise use the one defined in the property files
        return getFileResources().getObject(key);
    }

    //Returns the first extension value found for this key, 
    //will return null if not found    
    //will return the first added if there are multiple.
    private Object getValueFromExtensionBundles(String key){
        for(String bundleName : extensionBundles){
            ResourceBundle rb = getExtensionResources(bundleName);
            Object o = rb.getObject(key);
            if(o != null) return o;
        }
        return null;
    }    

}