Home
Home

Translation

Openbakery Translation is an internationalization tool for Java. Unlike standard I18n in Java, openbakery translation uses the Text in the default locale as the key. There is also a tool which checks the whole java code for translations. This tool then provides a list of key/value pairs which have to be added to a certain resource file, and another list of pairs which can be removed.

The translation works by simply calling a static method "translate". The code works out of the box, without writing any properties files. You only write properties files when you really translate the program to a second language.

translate("This string should be translated"); //Simple translation
translate("This string has parameters: {0} and {1}", "Foo", "Bar"); //Translations can be parameterized
translate(File.class, "Open"); //Translations can have a context, which is the name of a Java class

Contents

  • Bug Tracker, Feedback
  • Download
  • Usage
    • Starting a translation project
    • Finding translations in the code
    • Translating the program
    • Contexts
  • Advanced Topics
    • Lists of stuff (JComboBox example)
    • Parameterized messages
    • Translating plurals


Bug Tracker, Feedback

If you find any bugs, please report them to http://bugs.openbakery.org/ in the section for the project "translation".
If you want to give me feedback or if you have questions, don't hesitate to email me: david@openbakery.org. You can also follow me on Twitter: http://twitter.com/dtanzer


Download

Openbakery Translation is distributed under the terms and conditions of the Apache License 2

Version 0.4
Fixed Bugs: Classpath scanning when running in a Tomcat web container, Thread isolation of static method calls with different locales.

  • translation-0.4.tar.bz2
  • translation-0.4.tar.gz
  • translation-0.4.zip

Version 0.3
All properties files for a given locale are now grouped in a folder for the locale. All translations have a context now, the default context is the class where the "translate" statement was written. The java file parser is more sophisticated now. Singular/Plural translations are now supported. Lists of stuff can now be translated and the translation checker can recognize it.

  • translation-0.3.tar.bz2
  • translation-0.3.tar.gz
  • translation-0.3.zip

Yeast: Add the following snippet to the dependencies in your .rs.xml file:

<resource>
	<name>translation</name>
	<group>org.openbakery</group>
	<version>0.3</version>
</resource>

Version 0.2:
Added support for multiple properties files. Better support for hierarchical locales. Multiple calls to initialize are now allowed. Enhanced output of the TranslationChecker.

  • translation-0.2.tar.bz2
  • translation-0.2.tar.gz
  • translation-0.2.zip

Version 0.1:

  • translation-0.1.tar.bz2
  • translation-0.1.tar.gz
  • translation-0.1.zip


Usage

When you start a project you probably don't know if it will ever be translated. Anyway, if you do not add translation support from the beginning, it will be incredibly hard to translate it in the future. With java resource bundles, adding translation support can be quite a hassle: You have to create and maintain properties files during development, and translated strings in the code look ugly, like: resourceBundle.getString("org.openbakery.opendialog.okbutton.label"). Maintaining the properties file can be a problem too: You never know which properties are still used in the code and which ones are missing.


Starting a translation project

With openbakery translation it is quite easy to start a new project: You do not have to write any properties file for the default locale, just use calls to the "translate" function instead of directly using the strings:

package org.openbakery.test.examples;

import static org.openbakery.translation.Translation.*;

public class HelloWorld {
	public static void main(String[] args) {
		System.out.println(translate("Hello World"));
	}
}

This works out of the box: When you run the program it prints "Hello World".


Finding translations in the code

When the development of your project is finished you want to find all the strings which have to be localized. With openbakery translation, there exists a tool for this:

java -cp translation-0.3.jar org.openbakery.translation.CheckTranslation src resources/translations de

This command checks if the translations for the locale "de" (German) are still accurate and reports missing translations and translations which can be removed from the properties files. Before calling this tool, the resource folder "resources/translations" has to exist.

The output of the translation checker is:

Missing: 1 Translations
Indirectly Available: 0 Translations
Remove: 0 Translations

The "Missing" value is the number of translations in the source code which do not appear in the corresponding properties file. The "Remove" is the number of translations from the properties files which are not referenced in the source code. The "Indirectly Available" value is the number of translations which were not found in the given locale (i.e. de_AT), but are available in a higher level locel (i.e. "de").

The translation checker also creates the file "resources/translations/de/org.openbakery.test.examples.HelloWorld.properties.missing":

Hello_World=Hello World


Translating the program

We can now create the properties file "resources/translations/de/org.openbakery.test.examples.HelloWorld.properties":

Hello_World=Hallo Welt

But we also have to initialize the translation engine correclty so it finds the newly generated properites file:

package org.openbakery.test.examples;

import static org.openbakery.translation.Translation.*;
import java.util.Locale;
import org.openbakery.translation.Translation;

public class HelloWorld {
	public static void main(String[] args) {
		Translation.initialize("translations", Locale.GERMAN);
		System.out.println(translate("Hello World"));
	}
}

When we run this program (with the "resources" folder on the classpath) it will output "Hallo Welt".


Contexts

As you can see in the previous example, the translation engine uses one properties file per class. But what if we want two strings in different classes to have the same translation? Or what if we want two strings in one class have different translations? After all, the word "open" might tranlate to different strings when you talk about files or about water bottles.

You can use the context parameter to control this: Before the string you want to translate you can add a class as a parameter which is the context for the translation. This context also defines the properties file where the translation will be found:

translate(File.class, "Open");

will always use the translation found in "java.io.File.properties" (if any), no matter where the actual translate statement was found. Of course, the translation checker will create the "java.io.File.properties.missing" for you when you run it.


Advanced Topics


Lists of stuff (JComboBox example)

When you want to translate a list of stuff (like in the renderer of a JComboBox) you do not have access to the literal string you want to translate, so the translation checker would not recognize it. For this you can use prefixes:

	package org.openbakery.test.examples;

	import static org.openbakery.translation.Translation.*;
	import java.awt.Component;

	import javax.swing.JComboBox;
	import javax.swing.JFrame;
	import javax.swing.JLabel;
	import javax.swing.JList;
	import javax.swing.ListCellRenderer;

	public class ComboExample {
		public static void main(String[] args) {
			JFrame frame = new JFrame("Test");
			JComboBox comboBox = new JComboBox(new String[] {"work", "home", "mobile", "other"});
			comboBox.setRenderer(new ListCellRenderer() {
				public Component getListCellRendererComponent(JList list,
						Object value, int index, boolean isSelected,
						boolean cellHasFocus) {
					return new JLabel(translate("phone_number_type", (String)value));
				}
			});
			frame.add(comboBox);
			frame.pack();
			frame.setVisible(true);
		}
	}

Here the string "phone_number_type" is the prefix to be used. The "missing" properties file created by the translation checker looks like this:

phone_number_type.=There is no translation for this prefix in the properties file.

We can now create a properties file with the following content:

phone_number_type.work=Geschäftlich
phone_number_type.home=Privat
phone_number_type.mobile=Mobil
phone_number_type.other=Sonstige

When we now add the proper initialization code to the example above, the combo box will contain the translated values.

You can also combine prefixes an contexts, for example:

translate(Context.class, "some_prefix", value);


Parameterized messages

You can add MessageFormat style parameters to your messages. To make it possible for the translation checker tool to find such parameters, you have to use the static "format" method to generate them:

package org.openbakery.test.examples;

import static org.openbakery.translation.Translation.*;
import java.util.Locale;
import org.openbakery.translation.Translation;

public class Parameters {
	public static void main(String[] args) {
		Translation.initialize("translations", Locale.GERMAN);
		String yourName = "Frodo Baggins";
		String myName = "Bilbo Baggins";
		System.out.println(translate("Hello {0}, this is {1} " +
				"using a parameterized translation message", 
				format(yourName, myName)));
	}
}

The translation checker generates the following file for this example:

Hello_{0},_this_is_{-1213568192=Hello {0}, this is {1} using a parameterized translation message

As you can see, long strings are abbreviated with their hash code - but only if they do not contain a prefix. Also, spaces and tabs are replaced by "_" characters (this is good to know if you have to write translations for prefix values).

As before, you can combine parameters with a prefix and a context:

translate(Context.class, "prefix", value, format(param1, param2, ...));


Translating plurals

Suppose we want to translate the following message:

translate("{0} Files opened", format(numberOfFiles));

This does not look good when the number is 1, since the message is then "1 Files opened". For this we want to distinguish between singular and plural. Your can do this with openbakery translation with the following call:

translatePlural([context class], [prefix], singular message, plural message, number, [format(...)]);

translatePlural("{0} File opened", "{0} Files opened", numberOfFiles);

This means you specify the singular message and the plural message, and the translation engine chooses the appropriate one. The number parameter is always your parameter "0" (but you don't have to use it), any parameters given to the "format" method start at "1".

  • Blog
  • Translation
  • Yeast
Copyright © 2007-2007 - openbakery.org
Sponsord by ciqua