java


Where to place i18n key strings in Java


When doing internationalization in Java, you assign a string key to each message. What's the best practice, on where to place those string keys. Goal is to allow easy refactoring (eg. key name changes), clean and readable code, separation of concerns but still no duplication of keys/messages even if called from different parts of the code.
//bad way, strings directly in code
messages.getString("hello_key");
-
// better way, use String constants
public static final String HELLO_KEY = "hello_key";
...
messages.getString(HELLO_KEY);
-
// other (better?) way, put all keys in one huge central class
public class AllMessageKeys {
public static final String HELLO_KEY = "hello_key";
...
}
public class Foo {
...
messages.getString(AllMessageKeys.HELLO_KEY);
}
-
// other (better?) way, put all keys in neighbor class
public class FooMessageKeys {
public static final String HELLO_KEY = "hello_key";
}
public class Foo {
...
messages.getString(FooMessageKeys.HELLO_KEY);
}
Any other proposals? Which is best? I'm on Eclipse IDE, if that makes the refactoring part any clearer.
Clarification: in the above examples "messages" is of type ResourceBundle.
i always using for such stuff an interface where my keys are listed. The name of the Interace is mostly DESC=Issue/short describtion/topic and the keys values.
This way you can make some nice Interface and some general Interface e.g. for OK or Abort keys.
// other (better?) way, put all keys in neighbor class
public interface DESCMessage {
public static final String HELLO_KEY = "hello_key";
}
public class Foo {
...
messages.getString(DESCMessage.HELLO_KEY);
}
Basically, it seems that we all agree that some kind of constant is needed. When it comes to constants, I strongly prefer Enums. Java Enums are very powerful and definitely underused:
String title = Messages.getString(RunDialogMessages.TITLE);
OK but what I had to do to make it look like this? A simple interface, an enum and slight modification to standard message access routine. Let's start with the interface:
public interface MessageKeyProvider {
String getKey();
}
The enum:
public enum RunDialogMessages implements MessageKeyProvider {
TITLE("RunDialog.Title"),
PROMPT("RunDialog.Prompt.Label"),
RUN("RunDialog.Run.Button"),
CANCEL("RunDialog.Cancel.Button");
private RunDialogMessages(String key) {
this.key = key;
}
private String key;
#Override
public String getKey() {
return key;
}
}
And modified getString() method:
public static String getString(MessageKeyProvider provider) {
String key = provider.getKey();
try {
return RESOURCE_BUNDLE.getString(key);
} catch (MissingResourceException e) {
return '!' + key + '!';
}
}
Just to complete the picture, let us see RunDialog.properties (I will make a point about it soon):
RunDialog.Title=Run
RunDialog.Prompt.Label=Enter the name of the program to run:
RunDialog.Run.Button=Run
RunDialog.Cancel.Button=Cancel
Obviously, you could use Enum to read from properties file (by embedding ResourceBundle), however it would probably violate Single Responsibility Principle (as well as Don't Repeat Yourself, as access code would need to be repeated).
Going back to properties file, I had a feeling (I might be wrong here), that one of your goals was to avoid duplicating the translations. That's why I put two Runs in example above. You see, this word would be translated in a different way depending on the context (which is actually quite common). In this example, if I were to translate that to Polish it would look like this:
RunDialog.Title=Uruchamianie
RunDialog.Prompt.Label=Wpisz nazwÄ™ programu do uruchomienia:
RunDialog.Run.Button=Uruchom
RunDialog.Cancel.Button=Anuluj
That is unfortunate problem of some strange language that have a concept of conjugation...
I also think the first is the worst choice. In most cases (the key is only used by one class) I would prefer the second solution with String constants.
If the key is referenced from more than one class, the neighbor class is a better way (using an interface like #moohkooh mentioned).
The solution with one central class creates a dependency magnet which is a bad design in my opinion. Neighbor interfaces with constants per package would be a better one.
If you do not want a interface to hold the constants, you can use an enriched enum:
public enum DESCMessage {
HELLO("hello_key"),
OTHER("other_key");
private final String key;
private DESCMessage(String key) {
this.key = key;
}
public String key() {
return key;
}
}
This can be used as:
messages.getString(DESCMessage.HELLO.key());
String constants are the way to go. Where you define them, it really depends on your code structure and usage of keys. For example:
If you only use the keys in one class it's best to put them there.
If you re-use the same keys across your code, it's best to put them in a helper class (a global or per-package class depending on key usage and number of keys)
From refactoring point of view it's a bit more complicated (requires more changes) to move the constants from one class to another than to rename them or to change their value.
When changing their value you have no way of automatically change the defined resource.
One of the good way of doing this is explained here: Short article on a new approach using NLS with some advantages over ResourceBundle approach.
IMHO ResourceBundle facilitates usage of Locale specific properties files. In order to use ResourceBundle, properties files should be named according to following convention :-
BaseName_langCode.properties
OR
BaseName_langCode_countryCode.properties
You can use the properties files.
IMHO The best place to define these keys, and their related strings, are NLS files.n And you must saved them at ResourceBundle files

Related Links

How to setup game to be multiplayer? [closed]
Java - Recover bytes from string [duplicate]
Java compiler - several methods, same behaviour, different output [duplicate]
Cropping an image from URL to InputStream
IDs specified in Eclipse plugin
installing R package openNLP in R
Too many LOGS getting generated for Hystrix-AMQP
Logback: SizeAndTimeBasedRollingPolicy not honoring totalSizeCap
AnyLogic Agent-Based simulation agents value transfer
Java array accessing it from another class and using as a combobox
How to store 5 lines of data as 1 element in an ArrayList?
Android JSON Exception when parsing
Getting a random value from an array
How to print multiples of a given variable in java?
Synchronizing a Cipher
Hide Action bar In a fragment

Categories

HOME
vim
variables
bluetooth
reflection
graphql
gps
portia
pheatmap
virtualization
elasticsearch-hadoop
cloudkit
google-translate
vifm
ssl-client-authentication
here-api
offline
quickfix
carthage
opentracing
foselasticabundle
fatal-error
google-static-maps
serilog
pugjs
text-rendering
beyondcompare
grails3
conemu
paging
opencover
cloudhub
linkerd
elasticsearch-net
large-file-upload
file-rename
instant-messaging
media-queries
bosh
jspresso
copying
avcapturesession
gammu
picasso
dartium
web-mining
gesture
impersonation
grid.mvc
http-live-streaming
firebase-admin
nxlog
reactive-cocoa-5
qcombobox
executenonquery
scrollable
prolog-setof
typescript1.8
recursive-datastructures
nodebb
quartz-composer
root-framework
medium.com
instant
mathematica-frontend
etsy
bgp
captivenetwork
livequery
independentsoft
freedesktop.org
cartesian-product
thredds
pundit
riak-cs
service-accounts
operation
device-orientation
tween
gui-test-framework
asp.net-dynamic-data
mesa
navigationservice
bundles
ms-project-server-2010
commoncrypto
inbox
dbconnection
hungarian-algorithm
yui-compressor
html-editor
android-hardware
free-variable
referrer
e4x
objective-c-2.0
jquery-ui-layout
work-stealing
custom-backend
nsdatecomponents
pydot
data-loss
f#-powerpack
rfc1123
yetanotherforum
remember-me
web-application-design
.nettiers
sproutcore-2
getresponsestream
microsoft-virtualization
avatar
phonon
zune
genealogy
signal-handling
lzh

Resources

Mobile Apps Dev
Database Users
javascript
java
csharp
php
android
MS Developer
developer works
python
ios
c
html
jquery
RDBMS discuss
Cloud Virtualization
Database Dev&Adm
javascript
java
csharp
php
python
android
jquery
ruby
ios
html
Mobile App
Mobile App
Mobile App