In the course of developing test cases for some of my classes,
particularly the classes that are static factories, I've developed some
confusion about localization. Basically, I'm trying to figure out when I
want to have separate constructor and getInstance() methods
First, I hope we can all agree that, in an ideal world, every class which
can produce text output - even if no text is produced per se, most
classed would be capable of producing error messages for Exceptions -
should be written so that it can produce that output in the languages of
the users of those classes. So, if your utility classes are used in
French-speaking countries, text output and/or error messages will be in
French and so forth for other languages. Now, I know that many developers
will not worry about that - after all, the whole IT field seems to be
fairly English-centric - but I aspire to make all of _my_ classes locale-
sensitive.
Now, with the preamble out of the way, let's get down to brass tacks.
Let's say that I want to make a static factory class locale-sensitive but
I don't want to force the user to choose an explicit locale every time
they try to use the class. That suggests to me that I go one of two ways:
1. Provide two private constructors - one that takes a specified locale
and one that uses the default locale - and two corresponding public
getInstance() methods, one of which takes a specified locale and one that
uses the default locale. Then, if the user is comfortable with the
default locale, they use the constructor that doesn't have a locale
parameter, otherwise, they use the constructor that has a locale
parameter and specify the locale of their choice.
2. Provide a single private constructor that has no locale parameter and
a corresponding public getInstance() method. Also provide getLocale() and
setLocale() methods so that a user can instantiate with the single
getInstance() method and then use setLocale() to alter that locale if it
is not to his liking.
I thought I'd have a look at the Java API and see what happens there. I
got something of a mixed bag. A handful of classes have getInstance()
methods that take a Locale parameter, suggesting that they favour
Approach 1. A handful of classes have setLocale() methods, suggesting
that they favour Approach 2. However, the vast, vast majority of the
classes have neither suggesting that they are NOT locale-sensitive and
have no capability for changing the Locale at all.
So I thought I'd ask the learned people on this group for their thoughts
on which approach they'd take when building classes that are meant to be
locale-sensitive. I'm sure there are variations on the two approaches
I've enumerated so I'm open to "none of the above" answers
Personally, I'm leaning towards Approach 2. Approach 1 requires doubling
up of constructors and getInstance() methods and makes you reinstantiate
the class if you want to change Locales. Approach 2 avoids those extra
constructors and getInstance() methods and lets you change Locale by
simply doing setLocale(). There may be negative aspects to that which
aren't occuring to me though so I hope you will point those out if you
see them.
I've learned a lot in this thread about different aspects of i18n/l10n
(internationalization/localization) and I still expect to learn a bit
more as people respond to my scenario about my little Color Converter
program.... I thank you all for supplying considerations I hadn't
thought of in the design decisions that go with i18n/l10n.
But I'd like to try to sum up a bit now. The conversation has gotten
sufficiently fragmented that I'm not sure what we would all agree on
with regards to the situation I am trying to resolve.
In a nutshell, I have some utility-type classes and I am trying to
figure out how they should best handle matters of Locale.
Notice that I said "utility-TYPE classes". I initially described these
as static factory classes but I'm no longer sure that they should be.
Until a few months ago, all of the methods in these classes were
static and there was no constructor for the class. I had some concerns
and asked about them on a thread here. I don't recall how to give a
URL for a Google Groups discussion so I apologize for not being able
to give you one but it was a discussion that started March 15 2010 and
was entitled "Design Question". In a nutshell, because I had a Locale
variable in the class, this was seen as an element of 'state" for the
class and therefore I was advised to change the class so that it had a
private constructor and a public static getInstance() method. I did
that and, in fact, had two of each: a constructor that took no
parameters and one that took Locale as the parameter, and
corresponding getInstance() methods. I understand why the 'state'
necessitated this kind of change now that I've reread that thread.
However, I find myself seriously doubting that Locale SHOULD be a
class variable after all. And if my reasoning is sound, which I hope
you will tell me, I may be able to go back to having all the methods
be static.
As I look at the classes in the Java API, only a tiny handful of them
have constructors that take a Locale as an argument and only a tiny
handful have setLocale methods. Assuming that the Java API is
considered well-designed by the Java community - and I have to hope it
is
- that tells me that the remaining 99.9% of classes are
expecting to provide any locale-specific information in only ONE
Locale. Furthermore, no class invoking the API is typically going to
want to get the information in more than one language at a go.
Essentially, the classes operate on the basis of the Locale defined by
the user.country and user.language properties. Therefore, if my
properties are set to English and Canada, that is the locale that the
JVM is going to try to find - of course it will settle for other
flavours of English if there is no specific en_CA resource bundle -
and use for any other locale-specific text. (DateFormats,
NumberFormats, and their kin handle the localization needs of those
special kinds of data.)
Providing a way to set a specific Locale and even to change that
Locale on the fly so that classes could potentially invoke the class
repeatedly for different Locales is simply overbuilding and is almost
certainly not necessary at all! (I say 'almost' in deference to Lew's
observations about there being no hard and fast rules, which makes a
lot of sense to me.) So I think I can dispose of Locale in
constructors (and their associated getInstance() methods altogether.
Likewise, I can omit and setLocale() and/or getLocale() methods from
my utility classes too. Methods that seek to get ResourceBundles will
simply seek them for the default Locale, as determined via
Locale.getDefault().
Does this all make sense so far? I sure hope it does because I think
I've got myself convinced
The classes will still be fully locale-sensitive; I've proven that by
simply changing user.language and user.country to other values and
information comes out in the desired language just fine. (My
writeLocales() methods writes the display names of the Locales in the
language of the default locale so if my default Locale is en_CA, I get
them in English but if it is de_DE, I them in German, etc.)
As someone interested in the issue of supporting different languages
in a syste, I did some dabbling with the i18n stuff shortly after it
came out. I wrote simple apps that would display a list of locales and
let the user choose their favourite, then go back to the menu and
change them on so that information would be redisplayed in the
replacement Locale. I got in the habit of putting that kind of code in
many of my classes on the theory that I was making them more flexible
but I had never really given a lot of thought to how realistic this
was or how it conformed with the far better designed Java API. Now I
see that it is just unneeded frippery. If a class's methods really do
need to be able to change Locales on the fly, I think I'll be able to
write the code to enable that. But those needs should be few and far
between. For now, I think I should abandon any efforts to have
"dynamic locale switching" (for want of a better term) in my classes.
Am I on the right track here? Or am I throwing out the baby with the
bathwater?
Once we are agreed on that, I would like to follow up with a few
specific detailed questions but I'd like to get the fundamental
principle resolved first.