Documentation

Documentation versions (currently viewingVaadin 23)

Localization

To use localization and translation strings, the application only needs to implement I18NProvider and define the fully qualified class name in the property i18n.provider.

Defining the i18n.provider Property

The i18n.provider property can be set from the command line as a system property, as a Servlet init parameter in the web.xml, or using the @WebServlet annotation.

As a system property, the parameter needs the vaadin prefix, for example:

mvn jetty:run -Dvaadin.i18n.provider=com.vaadin.example.ui.TranslationProvider

When using the annotation, you could have the servlet class as:

@WebServlet(urlPatterns = "/*", name = "slot", asyncSupported = true, initParams = {
        @WebInitParam(name = Constants.I18N_PROVIDER, value = "com.vaadin.example.ui.TranslationProvider") })
public class ApplicationServlet extends VaadinServlet {
}

Or, using the web.xml file:

<?xml version="1.0" encoding="UTF-8"?>
<web-app
  id="WebApp_ID" version="3.0"
  xmlns="http://java.sun.com/xml/ns/j2ee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
      http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">

  <servlet>
    <servlet-name>myservlet</servlet-name>
    <servlet-class>
        com.vaadin.flow.server.VaadinServlet
    </servlet-class>

    <init-param>
      <param-name>i18n.provider</param-name>
      <param-value>com.vaadin.example.ui.TranslationProvider</param-value>
    </init-param>
  </servlet>

  <servlet-mapping>
    <servlet-name>myservlet</servlet-name>
    <url-pattern>/*</url-pattern>
  </servlet-mapping>
</web-app>

You can provide an I18NProvider as a bean if you are using Spring. In this case, you just need to annotate your implementation with @Component, so that it becomes available as a Spring bean. The Spring add-on will automatically use it if it is available. See the class SimpleI18NProvider.java implemented in the tutorial project as an example.

Locale Selection for New Session

The initial locale is decided by matching the locales provided by the I18NProvider against the Accept-Language header in the initial response from the client.

If an exact match (language + country) is found, this will be used. Otherwise, it tries to match on only language. If neither is found, the locale will be set to the first 'supported' locale from I18NProvider.getProvidedLocales(). If that is empty, Locale.getDefault() will be used.

Example of Using I18NProvider for Translation

For this example, we enable the use of Finnish and English, with Finnish being the "default" that is used if the user client does not specify English as an accepted language.

In this sample, the language .properties files start with "translate"; for example, translate.properties (for the default), translate_fi_FI.properties and translate_en_GB.properties.

In the example, the translation properties files are loaded using the class loader. Hence, they should be located on the classpath, for example in the resources folder. For a default Maven setup, this would be src/main/resources.

public class TranslationProvider implements I18NProvider {

    public static final String BUNDLE_PREFIX = "translate";

    public final Locale LOCALE_FI = new Locale("fi", "FI");
    public final Locale LOCALE_EN = new Locale("en", "GB");

    private List<Locale> locales = Collections
            .unmodifiableList(Arrays.asList(LOCALE_FI, LOCALE_EN));

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

    @Override
    public String getTranslation(String key, Locale locale, Object... params) {
        if (key == null) {
            LoggerFactory.getLogger(TranslationProvider.class.getName())
                    .warn("Got lang request for key with null value!");
            return "";
        }

        final ResourceBundle bundle = ResourceBundle.getBundle(BUNDLE_PREFIX, locale);

        String value;
        try {
            value = bundle.getString(key);
        } catch (final MissingResourceException e) {
            LoggerFactory.getLogger(TranslationProvider.class.getName())
                    .warn("Missing resource", e);
            return "!" + locale.getLanguage() + ": " + key;
        }
        if (params.length > 0) {
            value = MessageFormat.format(value, params);
        }
        return value;
    }
}

Using Localization in the Application

Implementing internationalization in the application is a combination of using I18NProvider and updating the translations on locale change.

To make this simple, the application classes that control the captions and texts that are localized can implement LocaleChangeObserver to receive events related to locale change.

This observer will also be notified on navigation when the component is attached, but before onAttach() is called. Any URL parameters from the navigation are set, so that they can be used to determine the state.

public class LocaleObserver extends Div implements LocaleChangeObserver {

    @Override
    public void localeChange(LocaleChangeEvent event) {
        setText(getTranslation("my.translation", getUserId()));
    }
}

Using Localization Without Using LocaleChangeObserver

public class MyLocale extends Div {

    public MyLocale() {
        setText(getTranslation("my.translation", getUserId()));
    }
}

Supporting Right-to-Left Mode

Vaadin components have support for right-to-left languages. The components will work out of the box in this mode, but to allow your application to support both left-to-right and right-to-left modes, you need to make a few changes.

Continuing from the previous examples, imagine that your application has now also been translated into a right-to-left Language, such as Arabic. In addition to following the I18NProvider example, in your main layout you can add code such as the following:

public class MainLayout extends VerticalLayout {

    public MainLayout() {
        // ...
        final UI ui = UI.getCurrent();
        if (ui.getLocale().getLanguage() == "ar") {
            ui.setDirection(Direction.RIGHT_TO_LEFT);
        }
    }
}

This works if the change of locale is based only on the Accept-Language coming from the client. However, if the user can specify their language, for instance, on your application’s settings page, you can have your main layout implement the LocaleChangeObserver interface. In this way, it will receive changes of locale, so you can then set the text direction based on the specified locale:

public class MainLayout extends VerticalLayout implements LocaleChangeObserver {

    @Override
    public void localeChange(LocaleChangeEvent event) {
        if (event.getLocale().getLanguage() == "ar") {
            event.getUI().setDirection(Direction.RIGHT_TO_LEFT);
        } else {
            event.getUI().setDirection(Direction.LEFT_TO_RIGHT);
        }
    }
}

Front-End Projects

For front-end applications only, to set right-to-left mode, you can specify document.dir = 'rtl'.

Adding RTL Support to Your Custom Elements or Application

If you have your own custom elements, or if your application has custom styles, there are a few steps needed in order to add right-to-left support to them:

  1. If your element extends Vaadin’s ElementMixin, no changes are needed. Otherwise, you can have the element extend it or DirMixin only (DirMixin is part of the @vaadin/component-base package).

    import { PolymerElement } from '@polymer/polymer/polymer-element.js';
    import { DirMixin } from '@vaadin/component-base/src/dir-mixin.js';
    
    class MyElement extends DirMixin(PolymerElement) {}

    The DirMixin registers the element to respond to changes in the dir attribute at the document level and keeps it in sync with the element’s dir attribute. This is helpful in order to easily check the text-direction status in both CSS and JS code.

  2. Make sure your styles are adjusted properly for right-to-left mode.

    For example, if you define values for the padding on the :host, as follows:

    :host {
        padding-right: 1em;
        padding-left: 2em;
    }

    you may want to define the style for right-to-left, as follows:

    :host([dir="rtl"]) {
        padding-right: 2em;
        padding-left: 1em;
    }

    You should also pay attention to settings such as padding, margin, text-align, float and transform in your styles. If your custom element does not need to support old browsers (such as IE11), you can replace some properties with CSS Logical Properties. The MDN web documentation has a full list of CSS Logical Properties and the available values, along with browser support for each property. Flex and Grid containers are usually handled well by the browser and do not require any extra work. You can find more information in this comprehensive RTL styling guide.

    For help with adjusting styles for right-to-left mode, you can go to the RTL CSS page. There, you can paste in your original styles and it will generate code that you can use for your element.

  3. If your element uses icons or Unicode symbols to define direction (for instance, for a "back" button) you may need to use the right icons or symbols for right-to-left mode.

  4. If keyboard interactions are used, such as to navigate between items with arrow keys, be sure to check whether dir is rtl, and use this to define the direction of the movement.

    // somewhere in your code
    const dirIncrement = this.getAttribute('dir') === 'rtl' ? -1 : 1;
    
    switch (event.key) {
        // ...
        case 'ArrowLeft':
            idx = currentIdx - dirIncrement;
            break;
        case 'ArrowRight':
            idx = currentIdx + dirIncrement;
            break;
        // ...
    }
  5. If your custom element relies on some JavaScript calculation for sizing, position and/or horizontal scroll, check whether it needs some adjustments for right-to-left.

  6. If you have visual tests, you may want to add or update the current ones to also run in right-to-left mode.