Info on JMRI:
Development tools
Code Structure
Functional Info
Techniques and Standards
How To
Background Info

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

  1. Field - for a visible field, e.g. label, on the GUI
  2. Button - for a GUI button
  3. Menu - the name of a top-level menu
  4. MenuItem - an item in a menu (may be a nested item)
  5. ToolTip - contents of a tooltip
  6. Error - for an error message displayed as part of the GUI
Other resources are named so as not to conflict with these.

Adapting to a new language

The primary steps to adapt JMRI to a new language are:

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.properties
and so on. 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:

  1. 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.
  2. Start the program and select "Preferences" from the Edit menu
  3. Click the "show advanced preferences" box
  4. Select your language from the "Locale" drop-down box,
  5. Click "Save", quit and restart
  6. 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:

By using this tracker and providing the complete "properties" tree, it's easy for us to merge your new and/or changed files into the code repository.

You may also want to provide additional language-specific features for users:

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.

  1. 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 1.2.5 can be downloaded via http://prdownloads.sourceforge.net/jmri/properties.1.2.5.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:
      1. Create a directory named "classes" in the same directory as your "jmri.jar" file. E.g. on MacOS X or linux:
        	mkdir classes
        
      2. Change to that directory:
        	cd classes
        
      3. Check out the properties module for the version you're interested in:
        	cvs co -r Release-1-2-5 properties
        
  2. 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 line
    FieldRoadName       = 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.
  3. 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.
  4. 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.
  5. Restart a JMRI program, and see if your changes appear in the user interface.
If that doesn't work, please get in touch so that we can help you find the problem and improve the instructions.

Coding for Internationalization

Some web references on how to do this:

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.