Declare the string constants in the base classes, and define them in language-specific subclasses. To get an idea of what I mean by this, say you're doing an options or settings form. Then you'd have a base Options.java that defined:
and did all the business of adding them to the display, etc. in the appropriate methods. Then you'd have the derived classes that just looked like e.g. this:
protected static String ID_LABEL;
protected static String ID_OK;
protected static String ID_CANCEL;
protected static String ID_MSG;
Then your midlet does:
public class Options_fr extends Options
ID_LABEL = "Param¨¨tres";
ID_OK = "OK";
ID_CANCEL = "Annuler";
ID_MSG = "Oui, ça marche!";
Pros: The string definitions are, more-or-less, where you want them to be. That is, they are near the point they are used. You don't have to have them all in a big, central text file, and start counting offsets, which would be a maintenance nightmare. Reasonably easy to use, quite easy to code: code uses variables that look like constants. It's a good deal less bulky in terms of jar size than the first option.
private Options options;
// ... some time later...
Class cls = Class.forName( "Options_" + locale );
this.options = (Options) cls.newInstance();
catch ( Exception e )
this.options = new Options_en();
this.options.init( this );
Cons: The variables can't be final (but then, you'd never assign to SOMETHING_LIKE_THIS in method code, would you?). You have to mess about with Class.forName, and move construction parameters into initialisation methods, which is a bit flaky. Slightly more involved class hierarchy. Still, potentially, a bit big in terms of jar size -- there could end up being a LOT of classes.
I quite like this one, especially since you could generate the language-specific subclasses from a single source file using Ant's copy task, as in the first option. But I can see that it might strike some people as a bit of an abomination.