Prepare a Java Source File for Localization

This guide explains how to prepare a Java source file in Jenkins core or a plugin for localization.

Step 1: Identify messages

First, we need to identify the localizable strings that should be translated. In general, everything shown on any user interface should be internationalized (i.e. prepared for localization into the user’s language).

Historically, log messages to be written to the Jenkins server log file from a logger aren’t translated, but could also be.

Let’s consider the following example, taken from the sample HelloWorldBuilder:

HelloWorldBuilder.java (excerpt)
public FormValidation doCheckName(@QueryParameter String value)
        throws IOException, ServletException {
    if (value.length() == 0)
        return FormValidation.error("Please set a name"); (1)
    if (value.length() < 4)
        return FormValidation.warning("Isn't the name too short?"); (2)
    return FormValidation.ok();
}

...

/**
 * This human readable name is used in the configuration screen.
 */
public String getDisplayName() {
    return "Say hello world"; (3)
}

@Override
public boolean configure(StaplerRequest req, JSONObject formData) throws FormException {
    // To persist global configuration information,
    // set that to properties and call save().
    useFrench = formData.getBoolean("useFrench"); (4)
    // ^Can also use req.bindJSON(this, formData);
    //  (easier when there are many fields; need set* methods for this, like setUseFrench)
    save();
    return super.configure(req,formData);
}
1 This string should be internationalized, as it is used as a form validation response.
2 This string should also be internationalized, as it is another form validation response.
3 This is the display name of the build step on the UI, and should also be internationalized.
4 This is an internal identifier for a form configuration option and must not be internationalized.

Step 2: Prepare the Messages.properties file

See IDE Configuration for plugins that make this and the following steps easier.

The localized strings for the default language (typically English), unless they are reused within the same component, are typically added to a Messages.properties file in the same package as the class that uses it.

Do not reference other components (e.g. core’s) Messages classes. They typically do not consider their localized messages stable API, so your plugin might break if they change their messages.

Using the default Maven project layout, the HelloWorldBuilder class stored in the source file src/main/java/org/jenkinsci/plugins/example/HelloWorldBuilder.java would use the localized strings in the resources file src/main/resources/org/jenkinsci/plugins/example/Messages.properties (basically, in the same package). Create that file if it doesn’t exist.

Now, let’s add entries for the localizable strings identified above to that file:

Messages.properties
HelloWorldBuilder.NoName=Please set a name (1)
HelloWorldBuilder.ShortName=\(2)
  Isn''t the name too short? (3)
# Unicode variant of the line above: (4)
# Isn\u2019t the name too short? (5)
HelloWorldBuilder.DisplayName=Say hello world
1 This sets the value for the key HelloWorldBuilder.NoName. While the key can be freely chosen, it’s typically a good idea to indicate where it’s used, as multiple files may share the same Messages.properties file. This is also the format generated by the Stapler Plugins for IntelliJ IDEA and NetBeans.
2 Line breaks, mostly useful with very long values, e.g. usage instructions, can be escaped with a back slash. Make sure to not add whitespace after it.
3 In properties files, single quotes need to be escaped with another single quote. Alternatively, a Unicode apostrophe could be used here as well.
4 Lines starting with the # character are comments.
5 The file encoding for .properties files is ISO-8859-1 (Latin 1), so characters not in that set need to be specified using their Unicode code point, e.g. \u2019 for the apostrophe.

Step 3: Build the project once

After adding new entries to any Messages.properties file (or renaming existing ones), the project needs to be rebuilt so the localizer can generate source code from it. While we can refer to them in source code without that, we only get autocompletion once the Messages class has been generated.

Step 4: Reference Messages strings

After code generation has run, we can add references to the generated class to the source code. Example:

HelloWorldBuilder.java (excerpt)
public FormValidation doCheckName(@QueryParameter String value)
        throws IOException, ServletException {
    if (value.length() == 0)
        return FormValidation.error(Messages.HelloWorldBuilder_NoName());
    if (value.length() < 4)
        return FormValidation.warning(Messages.HelloWorldBuilder_ShortName());
    return FormValidation.ok();
}

If your IDE doesn’t offer autocompletion even after building the project once, make sure the output folder of the localizer, target/generated-sources/localizer, is configured and recognized as a directory containing generated source code.

References