基于插件的程序中的语言环境和资源包
我们需要开始为我们的计划添加国际化。 值得庆幸的是,还不是全部,只是一些部分,但我希望我们的方式能够扩大规模,以覆盖整个计划。 问题是,我们的程序基于插件,因此并非所有字符串都属于同一个位置。
据我了解,Java的ResourceBundle是这样工作的。 您创建一个扩展 ResourceBundle
的类,名为 MyProgramStrings
之类,以及名为 MyProgramStrings_fr
、MyProgramStrings_es
的特定于语言的类> 等等。每个类都将键(字符串)映射到值(任何对象)。 从哪里获取数据取决于每个类,但它们的共同位置是属性文件。
您可以分两个阶段查找值:首先获得正确的包,然后在其中查询所需的字符串。
Locale locale = Locale.getDefault(); // or = new Locale("en", "GB");
ResourceBundle rb = ResourceBundle.getBundle("MyProgramStrings", locale);
String wotsitName = rb.getString("wotsit.name");
然而,我们需要的是将多个语言环境的结果组合到一个资源空间中。 例如,插件需要能够覆盖已定义的字符串,并在代码查找该字符串时返回新值。
我对这一切有点迷失。 有人可以帮忙吗?
更新:大卫·沃特斯询问:
我已将答案放在底部,但我有兴趣听听您如何解决这个问题。
好吧,我们还没有走得太远 - 长期 WIBNI 总是成为最新危机的受害者 - 但我们将其基于插件实现的接口,并约定资源具有与接口相同的完全限定名称。
因此,接口UsersAPI
可能有各种不同的实现。 默认情况下,该接口上的方法 getBundle()
返回与 ResourceBundle.get("...UsersAPI", locale)
等效的内容。 该文件可以被替换,或者如果需要更复杂的东西,UsersAPI 的实现可以覆盖该方法。
到目前为止,这满足了我们的需要,但我们仍在寻找基于插件的更灵活的解决方案。
We need to start adding internationalisation to our program. Thankfully not the whole thing yet, just a few bits, but I want the way we do it to scale up to potentially cover the whole program. The thing is, our program is based on plugins, so not all strings belong in the same place.
As far as I understand it, Java's ResourceBundle
work like this. You create a class that extends ResourceBundle
, called something like MyProgramStrings
, and also language-specific classes called MyProgramStrings_fr
, MyProgramStrings_es
etc. Each of these classes maps keys (strings) to values (any object). It's up to each of these classes where to get its data from, but a common place for them is a properties file.
You look up values in two stages: first you get the correct bundle, then you query it for the string you want.
Locale locale = Locale.getDefault(); // or = new Locale("en", "GB");
ResourceBundle rb = ResourceBundle.getBundle("MyProgramStrings", locale);
String wotsitName = rb.getString("wotsit.name");
However, what we need is to combine the results of several locales into a single resource space. For example, a plugin needs to be able to override a string that's already defined, and have that new value returned whenever code looks up the string.
I'm a little lost in all this. Can anybody help?
Update: David Waters asked:
I have put my answer at the bottom but I would be interested in hearing how you solved this problem.
Well, we haven't got very far yet - long term WIBNIs always fall victim to the latest crisis - but we're basing it on the interface that a plugin implements, with the convention that resources have the same fully qualified name as the interface.
So an interface UsersAPI
may have various different implementations. A method getBundle()
on that interface by default returns the equivalent of ResourceBundle.get("...UsersAPI", locale)
. That file can be replaced, or implementations of UsersAPI can override the method if they need something more complicated.
So far that does what we need, but we're still looking at more flexible solutions based on the plugins.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
您不必将 ResourceBundles 实现为一系列类,每个区域设置一个类(即名为
MyProgramStrings
、MyProgramStrings_fr
、<代码>MyProgramStrings_de)。 如果需要,ResourceBundle 类将回退到使用属性文件:如果我在类路径上有一个名为
MyResources.properties
的文件,那么此方法将导致:至于设置包的层次结构,或者将它们“合并”在一起,恐怕我在那里帮不了什么忙,除了我知道 Spring 确实有一个 分层消息源 (链接到 API) 是在 java.util.ResourceBundle 之上实现的,所以也许你可以使用 Spring 的功能来实现你想要什么?
顺便说一句,这里是
ResourceBundle.getBundle()
javadoc 的相关部分,解释了它的“搜索和实例化策略”:http://java.sun.com/j2se/1.5.0/docs/api/ java/util/ResourceBundle.html#getBundle(java.lang.String, java.util.Locale, java.lang.ClassLoader)
You don't have to implement ResourceBundles as a series of classes, with one class per locale (i.e. a class named
MyProgramStrings
,MyProgramStrings_fr
,MyProgramStrings_de
). The ResourceBundle class will fall back to using properties files if need be:If I have a file named
MyResources.properties
on the classpath, then this method will result in:As for setting up a hierarchy of bundles, or "merging" them together, I'm afraid I can't help much there, except that I know that Spring does have a concept of hierchical MessageSources (link to API) which are implemented on top of java.util.ResourceBundle, so perhaps you can use Spring's functionality to achieve what you want?
BTW, here is the relevant part of the
ResourceBundle.getBundle()
javadoc that explains it's "search and instantiation strategy":http://java.sun.com/j2se/1.5.0/docs/api/java/util/ResourceBundle.html#getBundle(java.lang.String, java.util.Locale, java.lang.ClassLoader)
我知道 Struts 和 Spring 对此有一些东西。 但假设您不能使用 Struts 或 Spring,那么我要做的就是创建 ResourceBundle 的子类并在该 ResourceBundle 中加载 *.properties(每个插件一个)。 然后你可以使用
就像您通常对属性文件所做的那样。
当然,您应该缓存结果,这样您就不必每次都搜索每个属性。
I know Struts and Spring have something for that. But let's say you can't use Struts or Spring then what I would do is to create a subclass of ResourceBundle and load the *.properties (one per plugin) in this ResourceBundle. Then you can use
Like you would normally do with a property file.
Of course, you should cached the result so you don't have to search each Properties every time.
getBundle 加载使用指定区域设置(如果有)和默认区域设置生成的一组候选捆绑包文件。
通常,没有区域设置信息的资源文件具有“默认”值(例如,
en_US
值可能位于 MyResources.properties 中),然后为不同的区域设置创建覆盖资源值(例如ja
字符串位于 MyResources_ja.properties 中)更具体的文件中的任何资源都会覆盖不太具体的文件属性。现在您想要为每个插件添加提供其自己的属性文件集的功能。 目前尚不清楚该插件是否能够修改主应用程序或其他插件的资源,但听起来您想要一个单例类,其中每个插件都可以注册其基本资源文件名。 对属性值的所有请求都会通过该单例,然后该单例将查看每个插件的资源包(按某种顺序......),最后查看应用程序本身以获取属性值。
Netbeans NbBundle 类执行类似的操作。
getBundle loads a set of candidate bundle files generated using the specified locale (if any) and the default locale.
Typically, a resource file with no locale info has the "default" values (eg, perhaps
en_US
values are in MyResources.properties), then over riding resource values are created for different locales (egja
strings are in MyResources_ja.properties) Any resource in the more specific file overrides the less specific file properties.Now you want to add in the ability for each plug-in to provide it's own set of properties files. It isn't clear whether the plug-in would be able to modify the resources of the main app or other plug-ins, but it sounds like you want to have a singleton class where each plug-in can register its base resource file name. All requests for property values go through that singleton, which would then look through the resource bundles of each plug in (in some order ...) and finally the app itself for the property value.
The Netbeans NbBundle class does similar stuff.
我遇到了一个稍微类似的问题,请参阅 问题 653682 。
我找到的解决方案是拥有一个扩展 ResourceBundle 的类,并检查我们是否重写该值(如果不委托给从文件生成的 PropertyResourceBundle)。
因此,如果您想存储 UI 的标签,您将有一个类 com.example.UILabels 和一个属性文件 com.example.UILabelsFiles。
I have had a slightly similar problem see question 653682 .
The solution I found was to have a class that extends ResourceBundle and, checks if we are overriding the value if not delegate to the PropertyResourceBundle generated from files.
So if you wanted to store Labels for the UI you would have a class com.example.UILabels and a properties file com.example.UILabelsFiles.