I have an internationalized project with many modules. Each module has its own set of bundles:
- database-module
+ com_naugler_project_database.properties
+ com_naugler_project_database_fr.properties
- mapping-module
+ com_naugler_project_mapping.properties
+ com_naugler_project_mapping_fr.properties
However, many of the internationalized terms are redundant (such as 'OK' or 'Cancel') and I would like have these terms in one place for easier maintenance and development.
I found this helpful explanation of ResourceBundle inheritance, but it appears as though a (not?) common ancestor would not be internationalized properly because:
- common-module
+ com_naugler_project.properties
+ com_naugler_project_fr.properties <-- this is not an ancestor
- database-module
+ com_naugler_project_database.properties
+ com_naugler_project_database_fr.properties <-- of this
Am I way off base with my bundle organization? What is the right way to provide a common internationalized ancestor?
What you want seems to be the hierarchy of Resources, that is, you probably want one class to derive from over (or being composed of some specific part and some common part).
Basically, ResourceBundle was not designed for it, and you are on your own.
But you want some advice, I suppose.
Make sure that common terms are really common. That is things like "OK", "Cancel", "Next >", "< Previous", "Open", "File", etc. will have common translations in their context. I mean it is fairly safe to translate such standard items only once, but if you want to use them in different context, you still need another entry. Why? Because "Open" button translation would be different than "Open" dialog title translation in quite a few languages.
Move all the .properties files to one place (for example a directory called "resources"). Of course module-specific files should be separated to different subdirectories...
Create a resource factory that will return an instance of the ResourceBundle class (or your own Facade - this approach will actually let you share some common bundle).
The good practice for large applications is to create some Language Pack, that is to separate language resources to their own directories (i.e. /resources/en, /resources/fr, /resources/zh-Hans). The problem with this approach, however would be the fact that you would need to implement resource fallback by yourself (with the aid of an article you mention in the question, the hierarchy is actually resource loading hierarchy). That means some special cases like falling back from language tag "nb" to "no" but not falling back from "nn"; falling back from "zh-CN" and "zh-SG" to "zh-Hans" and then to "zh" but falling back from "zh-HK" and "zh-TW" and "zh-MO" to "zh-Hant" and then to your default language, not falling from "pt-BR" to "pt" (falling back to default language instead).
Seems like a lot of work? Well, but the maintenance work afterwards would be minimal.
One thing might come in handy PropertyResourceBundle have two constructors that will let you load whatever properties file you want, namely: PropertyResourceBundle(InputStream stream) and PropertyResourceBundle(Reader reader). Honestly, in large projects standard ResourceBundle mechanism has too many limitations, so you really need your own resource access layer...
As Paweł Dyda indicated, resource bundles on their own do not support class hierarchy resolution. However the Rincl project, which my company just released, should do just what you're looking for---and handle UTF-8 .properties files and XML properties files as well. It even does message formatting for you on the fly.
As explained in the in the Rincl quick start, you can simply implement Rincled and then call getResources(); Rincl will load your resource bundles even if declared in a parent class or interface:
public class MyClass extends BaseClass implements Rincled {
…
final String userName = "Jane Doe";
//Retrieve the formatted user label based upon the current locale.
//en-US: "Settings for user Jane Doe."
//pt-BR: "Definições para usuário Jane Doe."
final String userLabel = getResources().getString("user-label", userName);
…
Rincl is available at http://rincl.io/ with an intro and even a full lesson on Java internationalization. There are many new features coming up, but the latest version should already work for you. Let me know if you have any questions or problems.
Related
I need to create a map of our domain classes simple names to their fully canonical names. I want to do this only for classes that are under our package structure, and that implement Serializable.
In serialization we use the canonical names of classes alot --it's a good default behaviour as its a very conservative approach, but our model objects are going to move around between packages, and I don't want that to represent a breaking change requiring migration scripts, so I'd like this map. I've already tooled our serializer to use this map, now I just need a good strategy for populating it. Its been frustrating.
First alternative: have each class announce itself statically
the most obvious and most annoying: edit each class in question to include the code
static{
Bootstrapper.classAliases.put(
ThisClass.class.getSimpleName(),
ThisClass.class.getCanonicalName()
);
}
I knew I could do this from the get-go, I started on it, and I really hate it. There's no way this is going to be maintained properly, new classes will be introduced, somebody will forget to add this line, and I'll get myself in trouble.
Second alternative: read through the jar
traverse the jar our application is in, load each class, and see if it should be added to this map. This solution smelled pretty bad -- I'm disturbing the normal loading order and I'm coupled tightly to a particular deployment scheme. Gave up on this fairly quickly.
Third alternative: use java.lang.Instrumentation
requires me to run java with a java agent. More specifics about deployment.
Fourth alternative: hijack class loaders
My first idea was to see if I could add a listener to the class loaders, and then listen for my desired classes being loaded, adding them to this map as they're loaded into the JVM. strictly speaking this isn't doing this statically, but its close enough.
After discovering the tree-like nature of class loaders, and the various different schemes used by the different threads and different libraries, I thought that implementing this solution would be both too complicated and lead to bugs.
Fifth alternative: leverage the build system & a properties file
This one seems like one of the better solutions but I don't have the ant skill to do it. My plan would be to search each file for the pattern
//using human readable regex
[whitespace]* package [whitespace]* com.mycompany [char]*;
[char not 'class']*
class [whitespace]+ (<capture:"className">[nameCharacter]+) [char not '{']* implements [char not '{'] Serializable [char not '{'] '{'
//using notepad++'s regex
\s*package\s+([A-Za-z\._]*);.*class\s+(\w+)\s+implements\s+[\w,_<>\s]*Serializable
and then write out each matching entry in the form [pathFound][className]=[className] to a properties file.
Then I add some fairly simple code to load this properties file into a map at runtime.
am I missing something obvious? Why is this so difficult to do? I know that the lazy nature of java classes means that the language is antithetical to code asking the question "what classes are there", and I guess my problem is a derivative of this question, but still, I'm surprised at how much I'm having to scratch my brain to do this.
So I suppose my question is 2 fold:
how would you go about making this map?
If it would be with your build system, what is the ant code needed to do it? Is this worth converting to gradle for?
Thanks for any help
I would start with your fifth alternative. So, there is a byte code manipulation project called - javassist which lets you load .class files and deal with them using java objects. For example, you can load a "Foo.class" and start asking it things like give me your package, public methods etc.
Checkout the ClassPool & CtClass objects.
List<CtClass> classes = new ArrayList<>();
// Using apache commons I/O you can use a glob pattern to populate ALL_CLASS_FILES_IN_PROJECT
for (File file : ALL_CLASS_FILES_IN_PROJECT) {
ClassPool default = ClassPool.getDefault();
classes.add(default.makeClass(new FileInputStream(file.getPath())));
}
The classes list will have all the classes ready for you to now deal with. You can add this to a static block in some entry point class that always gets loaded.
If this doesn't work for you, the next bet is to use the javaagent to do this. Its not that hard to do it, but it will have some implication on your deployment (the agent lib jar should be made available & the -javaagent added to the startup args).
What is the best approach for grouping resources in a resource bundle file? The one I see most common is to group resources by web page, an example would be:
# -- company page
company.name.lbl=Name:
company.address.lbl=Address:
# -- contact page
contact.name.lbl=Name:
contact.email.lbl=Email:
The problem with this is that a lot of fields with the same name are duplicated. Would you then recommend identifying all the common names and group them separately? Something like:
name.lbl=Name:
address.lbl=Address:
email.lbl=Email:
Of course this also has some drawbacks, if you want to change the company name label to 'Company Name' then it is possible you change the contact name label without meaning to. Of course you should create a new resource for this, but it is possible the person making the change might overlook creating a new resource.
I would keep to the former example of grouping by web page, since the text displayed on each page has its own separate context.
You could try to keep things DRY and identify all of the common text, but should the context of any page change, you may find yourself creating new resources that you would have already done if you kept the page resources separate.
Another reason for keeping the resources separated by page is that if you ever need to translate your resources, the context for creating translations will be self-evident. That helps you keep a clean separation of concerns, so your coders will not have to worry about how words might be translated, and your translators will not have to mess with any code.
The first option may mean repeating texts, but is more flexible. What happens if, for example, company name is completely different from personal name? Or if tomorrow you boss decides that the label for the name of the company should change from "Name" to "Company name".
If you use the second option you are losing most of the advantages of using resource bundles.
I'm developing an application to my software engineering class and one of the requisites is that it has 2 or more languages in the interface. I already implemented that. On the other hand, it is required that the user can change the language on its own will by choosing from a combobox or something like that.
As i'm using AWT and Netbeans, I can't edit the initComponents method of the form, as it is automatically generated. I have the option below in mind, but don't know if they would even work, so I'm asking for help.
-Edit the constructor of my class so that it looks like this:
public JFmyConstructor() {
initComponents(); //auto-generated
myInitMethod();
}
The problem I think i'm going to find is that I need to call the constructor one time after the interface is already running (and thus, all objects instantiated. Is there a workaround for this?
Probably my answer comes a little late (too bad you put wrong title)...
Obviously you do not have to call any constructor in order to switch language at runtime. In Java Desktop applications it is as ease as:
Locale brazilian = new Locale("pt", "BR");
Locale.setDefault(brazilian);
Of course if you want to switch it via JComboBox, you would probably do it a bit different. What I would do, I would actually create Model (Swing promotes MVC Design Pattern) that will hold data (different Locales) and I would set default Locale in correct ActionListener. It is not easy though.
Of course I would rather use native names for Locales. If you have them constructed, you can easily get native name Locale name by simply calling getDisplayName() passing itself as a parameter:
String brazilianNativeName = brazilian.getDisplayName(brazilian);
The result might be unsatisfactory for certain Locales (I believe Brazilian Portuguese is unfortunately one of them), but there is not much you can do about it (unless you use your own string).
To be honest, for just two languages, I would probably use some links instead. You can use simple JLabels to do that, just set correct font properties and color and assign common ActionListener. You can then query which one was actually clicked and create and set Locale appropriately. For homework that should be sufficient.
SWT and NetBeans is a weird combination. If you can do things the "normal" way with SWT and use Eclipse, then there's a powerful set of tools for managing localization, all built right in. See, for example, this introduction.
The best way is to use resource injection for all components (rather than getting i18n strings from a Bundle at panel construction time).
Resource Injection is available in a number of open source libraries or frameworks.
Since you use NetBeans, you should be able to generate an application based on Swing Application Framework (SAF), which already supports Resource Injection.
Otherwise, there are also libraries out there that do just that; simply google for "swing" "resource injection" and you'll find, e.g. Fuse library.
Edit:
You should also take a look at this SO question that deals exactly with the same problem, and which has more detailed answers (including my own).
I am currently developing a desktop game as part of my CS course .
Although it is not required I want my application to support Localization
So I browsed the web and found two techniques
- ResourceBundle
- properties file
However I can not decide which is better suited for me , I simply need localized messages and labels for the GUI.
Also I am confused on how such files/classes should be named
for example if FooBar extends ResourceBundle
should be like FooBar_UNIX_en_US
Btw , the assignment is an Entanglement (by gopherwoods studio) clone (Desktop not applet)
~Thanks
You use both - from your program's point of view, they are ResourceBundles, from the translators point of view they are property files. A ResourceBundle can load its strings (only strings, though) from a property file.
So you simply put files like Messages.properties (the default), Messages_eo.properties (the esperanto translation), Messages_de.properties (german translation), Messages_de_AT.properties (special strings for austrian german, overriding the general ones).
In your program you then simply do ResourceBundle bundle = ResourceBundle.getBundle("Messages"), and the runtime builds your resource bundle for the current locale from the property files.
If you need to translate other things than strings, you would need .class files instead, subclassing ResourceBundle. You can even mix both, but I would then better split them to different bundles (i.e. one text bundle and one for the dynamic resources).
You are in the right direction, the convention is typically Name_<language>_<country>.
ResourceBundle itself can use properties file to back the localization string, so you can use that. That way you don't have to compile new classes for each localization you would like to support. Just create a new properties file for the language.
Check out ResourceBundle.getBundle() factory method.
I believe using resource bundles would be the best technique.
Java itself, will attempt to reference your resource bundels by most specific to least specific (most generic).
If you have the following resources:
resource
resource_en
resource_en_GB
resource_fr
resource_fr_CA
And the end users local is en_NZ (New Zeland) he would be shown what is in the resource_en.
However, someone from Quebec, Canada would most likely be shown what is in the resource_fr_CA.
Hope that helps.
In a Java EE environment, we are normally used to storing text in a property/resource file. And that property file is associated with some view HTML markup file. E.g. if your label 'First Name' changes to 'Full Name' on a HTML page, you could use the property to make that update.
firstName=First Name
someOtherData=This is the data to display on screen, from property file
If you are in an environment, where it is difficult to update those property files on a regular basis, what architecture are developers using to change text/label content that would normally reside in a property file? Or let's say you need to change that content before redeploying a property file change. A bad solution is to store that in a database? Are developers using memcache? Is that usually used for caching solutions?
Edit-1 A database is really not designed for this type of task (pulling text to display on the screen), but there are use-cases for a database. I can add a locale column or state field, also add a column filter by group. If I don't use a database or property file, what distributed key/value solution would allow me to add custom filters?
Edit-2 Would you use a solution outside of the java framework? Like a key/value datastore? memcachedb?
I want to assure you that if you need constant changes on localized texts, for example they tend to differ from deployment to deployment, database is the way to go. Well, not just the database, you need to cache your strings somehow. And of course you wouldn't want to totally re-build your resource access layer, I suppose.
For that I can suggest extending ResourceBundle class to automatically load strings from database and store it in WeakHashMap. I choose WeakHashMap because of its features - it removes a key from the map when it is no longer needed reducing memory footprint. Anyway, you need to create an accessor class. Since you mentioned J2EE, which is pretty ancient technology, I will give you Java SE 1.4 compatible example (it could be easily re-worked for newer Java, just put #Override when needed and add some String generalization to Enumeration):
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());
}
}
Because of some strange ResourceBundle's loading rules, you would actually need to extend WeakResourceBundle class to create one class each for supported languages:
// 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 {
}
One supported language each (I know it sucks):
// 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");
}
}
Now, if you need to instantiate your ResourceBundle, you would only call:
// You probably need to get Locale from web browser
Locale polishLocale = new Locale("pl", "PL");
ResourceBundle myBundle = ResourceBundle.getBundle(
"com.example.i18n.MyBundle", polishLocale);
And to access the key:
String someValue = myBundle.getString("some.key");
Possible gotchas:
ResourceBundle requires Fully Qualified Class Name (thus the package name).
If you omit Locale parameter, default (which means Server) Locale would be used. Be sure to always pass Locale while instantiating ResourceBundle.
myBundle.getString() could throw MissingResourceException if you follow my suggestion. You would need to use try-catch block to avoid problems. Instead you may decide on returning some dummy string from database access layer in the event of missing key (like return "!" + key + "!") but either way it should probably be logged as an error.
You should always attempt to create Locale objects passing both language and country code. That is just because, languages like Chinese Simplified (zh_CN) and Chinese Traditional (zh_TW) for example, are totally different languages (at least in terms of writing) and you would need to support two flavors of them. For other countries, ResourceBundle will actually load correct language resource automatically (note that I have created MyBundle_pl.java, not MyBundle_pl_PL.java and it still works. Also, ResourceBundle would automatically fall-back to Enlish-US (MyBundle.java) if there is no resource class for given language (that is why I used such a strange class hierarchy).
EDIT
Some random thoughts about how to make it more awsome.
Static factories (avoid using ResourceBundle directly)
Instead of directly instantiating the bundles with ResourceBundle, you could add static factory method(s):
public static ResourceBundle getInstance(Locale aLocale) {
return ResourceBundle.getBundle("com.example.i18n.MyBundle", aLocale);
}
If you decide to change the name of WeakResourceBundle class to something more appropriate (I decided to use LocalizationProvider), you could now easily instantiate your bundles from consuming code:
ResourceBundle myBundle = LocalizationProvider.getInstance(polishLocale);
Auto-generated resource classes
Localized MyBundle classes could be easily generated via building script. The script could be either configuration file or database driven - it somehow needs to know which Locale are in use within the system. Either way, the classes share very similar code, so generating them automatically really makes sense.
Auto-detecting Locale
Since you are the one that implement the class, you have full control of its behavior. Therefore (knowing your application architecture) you can include Locale detection here and modify getInstance() to actually load appropriate language resources automatically.
Implement additional Localization-related methods
There are common tasks that needs to be done in Localized application - formatting and parsing dates, numbers, currencies, etc. are usual examples. Having end user's Locale in place, you can simply wrap such methods in LocalizationProvider.
Gee, I really love my job :)
You speak about property files, but at execution time, you are likely to have a resource bundle or something that want a list of key/value pairs (maybe even depending of the locale)
You can store data in whatever format and then use it to contruct the right ressource bundle with it. Even if it comes from memory. So database can perfectly do that, because, properties would all be loaded at startup, cached in JVM memory and that's all. (SELECT * FROM LOCALIZATION_DATA)
Don't use distributed cache for that, the data set you have is likely to be small anyway... what ? Maybe a few MB at worst. And access to that data must be instantaneous once loaded because all views will trigger access to it dozen, or even hundred of time per page.
If you want to update the data without restarting the application just add an administration screen somewhere with a "reload localization data", or even a screen that allow to update this type of data (but save to the file/DB/whatever)
From a workflow point of view, it depend of what you are trying to achieve.
The classic property file is the prefered way of doing this. You put it into versionning, together with the source code so you always have the translation up to date with the code. You want to debug V1.3.0.1 ? just get this version, and you'll use the property file that was used at this time. You added new code that require new keys ? Or just changed they key name for whatever reason ? You know that the code and your locatization information are linked into a coherant state. And this is automatic.
If your data is not under version control, you loose automatic versionning and history of your data. When you deploy/redeploy to a new machine, discrepancy can appear and even prevent the application from running propertly (if a new key is required but not added. This is not great, prone to errors and more manual interventions.
If you really need live updates, and really can't release new version for that, what i would do is to have two source for your data. The standard data, under version control, so your sure all is good for a new install from scratch. And the "customised data", in the server that can override standard values. The customized values are not lost when updating from version to version, because this is just the standard values that are updated.
If the change in the server is purely a one shoot customization, then you just go to the right admin webpage, use the customize localization data screen and that's all.
If the change is something that you'll want to keep for any new installation, you add it 2 time. One time in the server, one time in version control.
You could always use JNDI, or even consider a document repository like JCR for this sort of thing.
Not so sure a database couldn't handle this, I think what you are really looking for is a cache that can be invalidated when those properties change. Have you thought about using something like JBoss Infinispan (http://www.jboss.org/infinispan)? It's extremely simple to use, and can be distributed across multiple application servers.
Infinispan, once configured, can be used like a Map; keep in mind you can configure it to be distributed across a cluster!
If you don't mind using a NoSQL solution, I would recommend something like Redis or Memcache. Of course, I would advocate that you keep a local cache (why incur the cost of a network call, especially if these properties are not likely to change often?).
As requested by Berlin Brown, I add another answer, more focussed on it's specific needs :
From the amount of data you need (like a thousand of entries), you just need to load your property file at startup by specifying a remote URL.
Data is cached in JVM memory for maximum performance.
Depending on your workflow you then have a background process that check for update on a regalar basis (let say each minute, hour, whatever is enough for you) or you can have a "button" in administration "refresh localization data" developper use when an update is needed.
No need for database. No need for memcached, no need for NoSQL. a simple URL accessible from production server. In term of security and dev it is easier, faster and more flexible.
Implementation details: if you use the standard format, you'll have a file per language/contry. Don't forget to update for all languages or bundle them together (using a zip for exemple).