Hop internationalisation
Introduction
Hop is already being used in many countries across the world where different languages are spoken. As such "internationalisation" represents the support for multiple languages in our software. Since it is such a long word it is typically abbreviated to i18n.
Translating changes source code
The message bundles that contains the translations for the various parts of Hop are part of the source code. If you want to start any i18n efforts we recommend that you set up your development environment first as described here:
How is it configured?
In the Hop configuration file
there is an entry called hop-config.json
which points to the locale string that you want to use. Such a locale string is always in the format: language code, underscore and country. For example, the default is en_US for English in the US.LocaleDefault
You can change the locale you want to use in the configuration file or in the Hop GUI under the Tools / options menu.
Translating strings
In Java code
Suppose we’re working on a class in file
What we want to do is have an easy way to get our hands on a translated String. The easiest way to do this is with the standard BaseMessages class. For example, you can see the following pop up all over the place in the Apache Hop source code:src/main/java/org/apache/hop/p1/Foo.java
org.apache.hop.i18n.BaseMessages.getString(PKG, "Foo.MyMessage");
If references a PKG variable typically defined at the top of the class like this:
private static final Class<?> PKG = Foo.class; // For Translator
By doing this we know the location of the message bundles that will be used for translations. In this case the message bundles will be looked for in
src/main/resources/org/apache/hop/p1/messages/
For the default locale
it will point to a file called messages_en_US.properties in that folder:en_US
src/main/resources/org/apache/hop/p1/messages/messages_en_US.properties
In that file we can place an entry:
Foo.MyMessage = My own personal message
In annotations
Many plugin and GUI annotations support i18n as well. Unfortunately it is not possible to use the method explained above to resolve keys into strings. Because of this we use the following format:
i18n:package:key
For example, the main File menu in the Hop GUI is defined with an annotation:
@GuiMenuElement(
root = ID_MAIN_MENU,
id = ID_MAIN_MENU_FILE,
label = "i18n::HopGui.Menu.File",
parentId = ID_MAIN_MENU)
With the
prefix the Hop GUI registry knows that it can translate this using the given package and key. If there is no package defined it simply means: use the package from the current class. In our example it is i18n:
which means that we can find the message bundles for this class in org.apache.hop.ui.hopgui
and for locale ui/src/main/resources/org/apache/hop/ui/hopgui/messages/
in :en_US
ui/src/main/resources/org/apache/hop/ui/hopgui/messages/messages_en_US.properties
In there we can find the English translation:
...
HopGui.Menu.File=&File
....
Translator concerns
If you think that it’s quite a bit of a pain to figure out the keys, locations, locale and so on: we agree! To make i18n easy and translating fun we created a tool called Hop Translator. Translator allows you to provide strings for the keys that you use in your source code. It will create the message bundles in the right location, in the right format (in UTF-8) and with the right Apache license header.
To run translator simply build the code and then:
cd assemblies/client/target/
unzip -q hop-*.zip
cd hop
sh hop-translator.sh translator.xml ../../../../
In other words, run Translator and point it to the location of the source code you want to analyze.
Do not be too smart
Translator uses a simple Java code scanner to look for patterns like
-
PKG
-
BaseMessages.getString
-
"i18:
So please give Translator a chance to pick up the i18n keys as well as the package in which they’re being used. If you think that it would be "better" to use things like clever prefixes and so on please remember that all you’re doing is making sure your keys can’t be found by Translator and as such are hard to translate.
As much as possible, define the PKG variable to the same class as the one you’re working in. This will prevent message bundles from becoming too large and perhaps difficult to merge later on.
Using Translator
To use Translator you need 2 things: the Hop source code and a recent build of Hop. With the latter you can start Translator like this:
sh hop-translator.sh translator.xml /path/to/hop/source/code
After a few seconds of code-scanning you’ll see the following interface:
The default locale
will be selected by default.en_US
Translating keys
As an example, here is how we can translate the Hop GUI menu to Dutch:
-
Select the hopgui package:
org.apache.hop.ui.hopgui
` -
Select locale
at the very top leftnl_NL
-
Select any of the missing
keysHopGui.Menu…
In the following screenshot we selected
:HopGui.Menu.Edit.Cut
Now we can type in the translation for "Cut selected to clipboard" and hit the Apply button. Finally, when we’re done with all keys and packages we can use the "Save" button to make sure our efforts are not for nought:
Once the files are saved you re-build Hop and test them by using the translated keys. Finally, don’t forget to commit and push changes as described here: Setting up your development environment
Please reference a Github Issue and see if there aren’t any already for the locale you’re covering.
Highlighting codes
As you can see in the screenshot above many packages for nl_NL are highlighted. That is because there are a lot of non-translated keys in those packages:
-
light red: the message bundle is missing
-
the darkest gray: over 50 keys are not translated
-
dark gray: over 25 keys are not translated
-
gray: over 10 keys are not translated
-
light gray: over 5 keys are not translated
-
the lightest gray: at least one key is not translated