Documentation

Documentation versions (currently viewingVaadin 23)

You are viewing documentation for Vaadin 23. View latest documentation

Localization

Implementing localization and translation strings using I18NProvider.

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, loadOnStartup = 1,
   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>
    <load-on-startup>1</load-on-startup>

    <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 only need to annotate your implementation with @Component, so that it becomes available as a Spring bean. The Spring add-on automatically uses it if it’s 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, that is used. Otherwise, it tries to match on only language. If neither is found, the locale is set to the first 'supported' locale from I18NProvider.getProvidedLocales(). If that’s empty, Locale.getDefault() is used.

Example of Using I18NProvider for Translation

For this example, you enable the use of Finnish and English, with Finnish being the "default" that’s used if the user client doesn’t 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 is also 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 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. As well as 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 receives 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 Right-to-Left 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 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 to adjust to the text-direction status in both CSS and JS code.

  2. Make sure your styles are adjusted 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 doesn’t 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 don’t require any extra work. You can find more information in this comprehensive right-to-left styling guide.

    For help with adjusting styles for right-to-left mode, you can go to the RTLCSS page. There, you can paste in your original styles and it generates 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, for example, to navigate between items with arrow keys, define the direction of the movement based on the dir attribute:

    // 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. Custom element that rely on JavaScript calculations for sizing, position, and/or horizontal scroll, may need 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.

722E7AE4-191E-4DE8-90F1-CAE8AE6CD3DF