Sven Ruppert

Internationalization in Vaadin Flow

In this tutorial, we will learn how to implement an I18NProvider that is used for translating text content in a Vaadin application

Preparations for this tutorial

For this tutorial, we assume to have the following piece of code

@Route("")
public class VaadinApp extends Composite<Div> {

  private final Button         btnClickMe   = new Button("click me");
  private final Span           lbClickCount = new Span("0");
  private final VerticalLayout layout       = new VerticalLayout(btnClickMe, lbClickCount);

  private int clickcount = 0;

  public VaadinApp() {
    btnClickMe.addClickListener(event -> lbClickCount.setText(valueOf(++clickcount)));
    getContent().add(layout);
  }
}

For this tutorial interesting part is the following line of code.

private final Button btnClickMe = new Button("click me");

ResourceBundles

The description on the Button is given in plain text. If we want to deal with different languages we need something that will give us a mechanism to deliver the requested translation of the message click me.

But first, we will go a step back and remember what is already included inside the JDK. The mechanism/element I have in mind is called ResourceBundle. ResourceBundles are part of the JDK since version 1.1 The JDK itself will provide a few implementations of the abstract class ResourceBundle. One implementation is called PropertyResourceBundle. This implementation will be used for this tutorial.

This implementation is based on property files.

The short version: A PropertyResourceBundle is based on a bunch of property-files that are containing the same keys but with different values for the same key.

The longer version: The PropertyResourceBundle needs a bunch of property-files with the same name in the beginning but with different endings. In this tutorial, the resource files are located inside the resource folder and the main name of the files is vaadinapp. The main name is followed by locale shortcuts. Here we are providing English (en) and German (de) The full name of the file with the English resource definitions is vaadinapp_en.properties

There is one file allowed with the same main name but without any locale in the end. This is the resource that will be used as fall back if there is nothing else that matches.

Some languages, like German, are a little bit different if they are used in different countries. Let’s assume we want to support Germany and Switzerland. The German properties files would get the name vaadinapp_de and the properties file for Switzerland would have the name vaadinapp_de_CH The last file would only contain the properties that would have a different value, compared to the original German language file.

The longest version: If you need more information’s in detail, have a look at the tutorial about ResourceBundles

How to use?

The usage of this implementation depends on one method, with a few different signatures. With the method getBundle(..) the corresponding properties file will be loaded from the classpath.

  public static final String RESOURCE_BUNDLE_NAME = "vaadinapp";

  private static final ResourceBundle RESOURCE_BUNDLE_EN = getBundle(RESOURCE_BUNDLE_NAME , ENGLISH);
  private static final ResourceBundle RESOURCE_BUNDLE_DE = getBundle(RESOURCE_BUNDLE_NAME , GERMAN);

In this tutorial we a loading two bundles. An English and a German one. Both are held in a static class attribute. At this point, we are assuming that the resources are static during the runtime of the program.

I18NProvider implementation

The implementation of the I18NProvider itself is straight forward. There are only two methods that must be implemented.

public interface I18NProvider extends Serializable {
  List<Locale> getProvidedLocales();
  String getTranslation(String var1, Locale var2, Object... var3);
}

The first one getProvidedLocales() will be used to decide if the requested locale is provided by this implementation or not. Based on the fact that this implementation will not support changes during the runtime of the application, the result can be a static unmodifiable list of locales.

  private static final List providedLocales = unmodifiableList(asList(ENGLISH , GERMAN));

  @Override
  public List<Locale> getProvidedLocales() {
    return providedLocales;
  }

The second method will be used to translate the key into the value of the requested locale or if needed and supported, into the translation based on the default locale.

  @Override
  public String getTranslation(String key, Locale locale, Object... params) {
       ResourceBundle resourceBundle = RESOURCE_BUNDLE_EN;
    if (GERMAN.equals(locale)) {
        resourceBundle = RESOURCE_BUNDLE_DE;
    }

    if (! resourceBundle.containsKey(key)) {
      logger().info("missing resource key (i18n) " + key);
      return key + " - " + locale;
    } else {
      return (resourceBundle.containsKey(key)) ? resourceBundle.getString(key) : key;
    }
  }

The method getTranslation will get the key itself,the requested locale and an optional list of params. The workflow is based on the following steps:

1) check if the requested locale is available and select the corresponding resource bundle. If the requested locale is not supported decide what to do. In this implementation, the fall back is the English locale.

2) check if the requested key is available inside the resource bundle and convert the key into the value. If not, decide what should be done. This implementation will convert the key into a value based on the key and the request locale. This makes it easy for the developer to detect what is missing.

Activation of the Implementation

The last step that is needed, is the activation of the I18NProvider implementation itself. If the method getTranslation from the component will be used, indirectly the framework will try to identify the corresponding implementation. For this, the system property vaadin.i18n.provider will be checked. Flow is expecting the class name of the I18NProvider implementation and will start with the instantiation.

There are different ways to set the system property. For this tutorial, we are setting the property inside the main method of the class BasicTestUIRunner.

    setProperty("vaadin.i18n.provider", VaadinI18NProvider.class.getName());

How to use in your code?

After we activated the I18NProvider Implementation we are ready to use this in our code. Setting the label will be done with the result from the method getTranslation(..).

private final Button btnClickMe = new Button(getTranslation("btn.click-me"));

The implementation will now check if the key btn.click-me is available. To define this key for the English language you have to add the key plus value to your properties file vaadinapp_en.properties, and for the German version into the file vaadinapp_de.properties.

    btn.click-me=click me
    btn.click-me=drücke mich

Next

In this tutorial, we learned how to implement a simple I18NProvider. To learn more in practice check out the latest source code @github.

Vaadin is an open-source framework offering the fastest way to build web apps on Java backends
GET STARTED

Comments (6)

Jonatan Jönsson
1 year ago Jun 22, 2020 6:15pm
thomas pieronczyk
1 year ago Aug 31, 2020 1:03pm
Luis Gutierrez
2 years ago Aug 30, 2019 5:14am
Jason Siemens
2 years ago Aug 30, 2019 10:31am