JMRI: Internationalization
This page discusses how the JMRI libraries handle internationalization.The JMRI libraries are intended to be usable world-wide. To make this possible, they make use of the "internationalization" features built into the Java language and libraries.
You can also use this feature to customize your copy of JMRI. For info on this, please see the customization section.
Use of Locales
JMRI uses the default Locale for locating internationalization information. That means that JMRI will present its user interface in the language Java has defined as the default for that computer.Locals are specified by a language, and optionally a country. The language is a two letter lower-case code; the country is a two letter upper case code. "en" is English, "fr" is French, "de" is German, and "de_CH" is German as spoken in Switzerland.
When Java looks for resources (see below), it searches first for a file with the complete current locale at the end of it's name (e.g. foo_de_CH.properties). If that fails, it tries for a file ending in just the current locale's language: foo_de.properties. And if that fails, it goes to the defaults with no suffix: foo.properties.
By installing appropriate files and allowing the user to select a default locale (as part of the advanced preferences), we can customize the program to different countries and languages.
Use of Resource Bundles
The text for menus, buttons and similiar controls is contained in property files, which are accessed via the Resource Bundle mechanism of java.util.For example, the property file that's used to configure the Roster panel contains lines like:
FieldRoadName = Road Name:To the left of the equal sign is the resource name that the program uses to refer to the string; to the right of the equals sign is the string that will be displayed.
By convention, resouce names for GUI elements start with one of
- Field - for a visible field, e.g. label, on the GUI
- Button - for a GUI button
- Menu - the name of a top-level menu
- MenuItem - an item in a menu (may be a nested item)
- ToolTip - contents of a tooltip
- Error - for an error message displayed as part of the GUI
Adapting to a new language
The primary steps to adapt JMRI to a new language are:- Create new versions of the .properties files to change the language of the GUI controls.
- Optionally, create new versions of GIF images used for logos and icons.
To get a clean copy of the properties files from CVS, do:
cvs checkout properties(For more info on using CVS, please see the page on getting a copy of the code.)
Then, make copies of the files with suffix for your new locale. On a Unix machine, this would be:
cd apps cp AppsBundle.properties AppsBundle_xy.properties cp AppsConfigBundle.properties AppsConfigBundle_xy.propertiesand so on. The easiest way to get the proper suffix is to set the program to your particular language via the advanced preferences, quit and restart the program, and then look at the suffix that it displays on the main window. You can also check the official list of languages (first part of the suffix) and list of countries/regions (optional second part of the suffix).
You can then edit those files to enter text in your own language. Please don't edit the lines in the file that contain things like $Release: $; those are used by CVS to keep track of the history of changeds.
Languages that involve non-roman letters require some extra care; please see the Sun internationalization FAQ for more info on how to include those characters in your property files.
All that adapts the program itself, at least as far as we've "internationalized" it. (That's an ongoing effort, with more and more parts of the program able to be customized in each release).
To check your work:
- Drop the entire "classes" directory into the "JMRI" directory containing your copy of the application. This is the same directory that contains the jmri.jar file.
- Start the program and select "Preferences" from the Edit menu
- Click the "show advanced preferences" box
- Select your language from the "Locale" drop-down box,
- Click "Save", quit and restart
- You should immediately seen the items you've translated.
If there's a problem at this point, check to see what language is listed on the startup screen that the application has displayed. Is it showing the same suffix (e.g. _fr or _cs_CZ) as you gave to your files? The suffix the program uses is determined by the Locale you selected in the preferences above.
To make your work available to other JMRI users, please
use a TAR or ZIP utility to create an archive of the
entire
"properties" directory, including all the files you didn't modify,
and upload it to the "Patches" tracker on sourceforge:
http://sourceforge.net/tracker/?group_id=26788&atid=388315.
On this page:
- Click "Submit new"
- Fill out the title and summary on the new page that appears,
- Click the "check to upload and attach a file" box at the bottom,
- and then select your file "using the choose file" button.
- Click "Submit" to upload the file and notify people that you've done this.
You may also want to provide additional language-specific features for users:
- Create new decoder XML files in the new language. (Although we're currently working on a method to greatly reduce the effort that would take)
- Create a copy of the website and manuals.
- Create new help files.
English-only resources
Some parts of JMRI remain English only due to our developer population. In particular, comments and variable names in the code should remain in English, as should messages sent to the logging system.
As JMRI is open source, it would be OK to take a copy of the source
and translate it. Apart from being a lot of work, this would effectively
isolate that copy from the rapid pace of improvements to the main code.
Customizing JMRI for your own use
You can use the internationalization feature to customize the program
for your own purposes. This section describes how to do that, assuming
that your native language is English.
Basically, you're going to create an "English" version of the program text that gets used in front of the "default" version. We do this so that new versions of the program, which might change the default version, don't overwrite your changes.
-
Obtain a copy of the appropriate set of "properties" files.
Note that you really do need the set of files that's consistent
with the version of JMRI you're using.
There are several ways to do this:
-
Look for a "properties files" download on the
JMRI downloads
page. For example, the files for JMRI version 2.3.6 can
be downloaded via
http://prdownloads.sourceforge.net/jmri/properties.2.3.6.zip?download.
This will give you a "classes" directory, which should be placed in the same directory as the jmri.jar file.
-
You can get these by checking out the "properties" module from
cvs with "cvs checkout properties" (see the
CVS information on the
technical page for background on how to use CVS if you're not
familiar with it. The steps are:
- Create a directory named "classes" in the same directory
as your "jmri.jar" file. E.g. on MacOS X or linux:
mkdir classes
- Change to that directory:
cd classes
- Check out the properties module for the version you're interested in:
cvs co -r Release-1-2-5 properties
- Create a directory named "classes" in the same directory
as your "jmri.jar" file. E.g. on MacOS X or linux:
-
Look for a "properties files" download on the
JMRI downloads
page. For example, the files for JMRI version 2.3.6 can
be downloaded via
http://prdownloads.sourceforge.net/jmri/properties.2.3.6.zip?download.
-
Now, you have to find the correct file to edit, and the correct
string within it.
Some of the more useful files are:
- classes/jmri/jmrit/roster/JmritRosterBundle.properties for the labels on the "Roster" panel and similar.
- classes/jmri/jmrit/JmritToolsBundle.properties for many of the Tools menu names
- classes/jmri/jmrix/JmrixSystemsBundle.properties for the names of the DCC systems that appear in the Systems menu
You're looking for a line that contains the string you want to change. For example, the classes/jmri/jmrit/roster/JmritRosterBundle.properties file contains the lineFieldRoadName = Road Name:
To the left of the equal sign is the name that the program uses to refer to the string; to the right of the equals sign is the string that will be displayed. - Once you've found the right file, make a copy of it with a "_en" appended to the name. For example, make a file classes/jmri/jmrit/roster/JmritRosterBundle_en.properties that's a copy of the classes/jmri/jmrit/roster/JmritRosterBundle.properties file. Because of the way that Java searches these files, this new file will be found before the original.
- Change the strings you want to change, and (this part is important) remove the rest of the lines! This will greatly reduce the changes that you'll have problems with a later version of JMRI conflicting with your changes.
- Restart a JMRI program, and see if your changes appear in the user interface.
Coding for Internationalization
Some web references on how to do this:- Sun internationalization tutorial(highly recommended)
- Sun internationalization FAQ
Briefly, you create a static object in your class that handles the lookup of strings:
static final java.util.ResourceBundle rbx = java.util.ResourceBundle.getBundle("jmri.jmrit.beantable.LogixTableBundle");
The getBundle argument is the complete package name (not file name) for the properties file this class will be using. You can have more than one of these objects if you'd like to look up strings in more than one properties file.
You can then retrieve particular strings like this:
String msg = rbx.getString("ButtonNew");
Some messages need to have specific information inserted into them:
System name LT1 is already in use
Here "LT1" can't be in the properties file, because it's only known which name to display when the program is running. Different languages may put that part of the message in different places, and supporting that is important. To do that, use the message formatting tools that Java provides. First, put a placeholder in the message definition:
Error123 = System name {0} is already in use
(You can have more than one insertion, called {1}, {2}, etc)
Next, format the final message by inserting the content into it:
String msg = java.text.MessageFormat.format(
rbx.getString("Error123"),
new String[]{badName});
The first argument to the "format" call is the message itself; the second is an array of Strings to be inserted into the message.
Different languages may need a different number of lines to express
a message, or may need to break it before or after a particular
value is inserted. It's therefore better to use "\n" within a
single message from the properties file to create line breaks, rather
than providing multiple lines in the code itself.
Testing
You should check that you've properly internationalized
your code. We provide a tool for doing this which creates
and automatically translated version of your properties files,
following the ideas of Harry Robinson and Arne Thormodsen.
(Their
paper on this is recommended reading!)
To use it:
- Make sure your code compiles and builds OK. We'll be modifying the compiled version.
- Run the "translate.sh" script in your java/ build directory. This creates new, temporary properties files in the classes/ directory tree.
- Run the program via "ant locale", which starts the JmriDemo program using the new properties files.