Locales and ResourceBundles in a plugin-based program
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.
Asked by: Miranda381 | Posted: 28-01-2022
Answer 1
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:
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);
}
If I have a file named MyResources.properties
on the classpath, then this method will result in:
got bundle: java.util.PropertyResourceBundle@42e816
Value in bundle is: someValue
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)
Answered by: Maddie969 | Posted: 01-03-2022
- First, it attempts to load a class using the candidate bundle name. If such a class can be found and loaded using the specified class loader, is assignment compatible with ResourceBundle, is accessible from ResourceBundle, and can be instantiated, getBundle creates a new instance of this class and uses it as the result resource bundle.
- Otherwise, getBundle attempts to locate a property resource file. It generates a path name from the candidate bundle name by replacing all "." characters with "/" and appending the string ".properties". It attempts to find a "resource" with this name using ClassLoader.getResource. (Note that a "resource" in the sense of getResource has nothing to do with the contents of a resource bundle, it is just a container of data, such as a file.) If it finds a "resource", it attempts to create a new PropertyResourceBundle instance from its contents. If successful, this instance becomes the result resource bundle.
Answer 2
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
ResourceBundle bundle = ResourceBundle.getBundle("MyResources");
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.
Answered by: Lenny631 | Posted: 01-03-2022Answer 3
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 (eg ja
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.
Answered by: Rebecca170 | Posted: 01-03-2022Answer 4
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.
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;
}
}
Answered by: Kelvin260 | Posted: 01-03-2022
Similar questions
java - How to reload ResourceBundles that are used by JSF?
I want to reload the ResourceBundles used by JSF programmatically on a button click. The ResourceBundles in the classpath (i.e. WEB-INF/classes) are modified by an external application and the modification event is known to me.
Java Multiple ResourceBundles
I want to load multiple property files from various packages as ResourceBundle. Can I achieve that in Java
java - JSP and ResourceBundles
Typically resource bundles are loaded and used within a JSP using JSTL and fmt, but this requires to always use the syntax <fmt:message key=""/> to access the value.
When the text to be localized looks like
<a href="url" title="message"> it is awkward (though valid to write):
<a href="url" title="<fmt:message key="key"/>">
and plain...
java.util.logging, ResourceBundles, SAAJ: exception on loading Glassfish OSGi webapp bundle
I'm being driven out of my mind by the following exception:
java.lang.IllegalArgumentException:
com.sun.xml.messaging.saaj.soap.LocalStrings != com.sun.xml.internal.messaging.saaj.soap.LocalStrings
at java.util.logging.Logger.getLogger(Logger.java:357)
at com.sun.xml.internal.messaging.saaj.soap.SAAJMetaFactoryImpl.<clinit>(SAAJMetaFactoryImpl.java:41)
It's occurring when I ...
java - Using multiple ResourceBundles
I'm create a game, and at the start i'm asking the user for a 1/2/3 number input to confirm language selection (1 for dutch, 2 for french, 3 for english). I already have 3 properties files as resourcebundles but now i have no idea how to load them into my game. So when the user puts in 1 in my JOptionPane, there should be a method (or something?) that sets "language" to dutch (cuz nr 1 was inserted), or french if 2 was ins...
Java ResourceBundles does not read utf-8 characters correctly - after upgrading Eclipse
I have a project that runs correctly for quite a long time. Tow days ago, I updated my Eclipse version to Kepler's version and since then - my properties file are not being read correctly.
Letters in Hebrew are being read like this: "××× ×©× ×שת×ש ×ס×ס××".
I though that somehow the files were ruined, so I copy-paste them to the simplest txt file and added to the project again. They are re...
java - Struts reads UTF-8 encoded Resourcebundles as ISO-8859
I have the honorable assignment to change the encoding of our complete workspace, projects and files to the UTF-8 encoding. We have several Resourcebundles which used to code special chars with unicode. We also wanted to get rid of that unicode stuff by switching to UTF-8 so I changed the encoding of the Resourcebundles (.properties) files too and replaced the Unicode characters.
We also have german resourcebundles...
java - How to load two ResourceBundles and inject them separately
I am using Spring for loading localized resource bundles into my application. Here is what I have done.
<bean id="systemMessages" class="o.s.c.s.ResourceBundleMessageSource">
<property name="basename" value="locale/system">
</bean>
<bean id="clientMessages" class="o.s.c.s.ResourceBundleMessageSource">
<property name="basename" value="locale/client">
</bean>
character encoding - Java ResourceBundles umlauts messed up
I have an issue with german "umlauts" in properties files. Now i totally get that i have to escape characters that are out of ISO-8859-1 range and i used native-to-ascii conversion in the past for this.
But why are umlauts like "ü" messed up too? They ARE part of ISO-8859-1 and if read the documentation right ResourceBundles in Java ARE read using ISO-8859-1. Also i use that encoding for my properties files.
Can s...
java - Load all ResourceBundles and pick correct resource based on key and Locale
Okay, so I have been given this challenge to implement a "Service" that loads in all the resources for all locales that we support. Then it should be possible to pick they resource from correct ResourceBundle based on the key and the current Locale. How can I achieve this?
So this is how I have made my solution, I have a Service called TranslationService
public class Transl...
Still can't find your answer? Check out these amazing Java communities for help...
Java Reddit Community | Java Help Reddit Community | Dev.to Java Community | Java Discord | Java Programmers (Facebook) | Java developers (Facebook)