Docs

Custom I18N Provider

Implementing a custom I18NProvider for advanced localization needs.

The built-in DefaultI18NProvider loads translations from property files in src/main/resources/vaadin-i18n/. If you need more control — loading translations from a database, using a different file structure, or applying custom fallback logic — implement the I18NProvider interface.

Implementing I18NProvider

The following example enables Finnish and English, with Finnish as the default. Translation files use the translate prefix (e.g., translate.properties, translate_fi_FI.properties, translate_en_GB.properties) and are loaded from the classpath (e.g., src/main/resources/):

Source code
Java
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;
    }
}

Configuring the Provider

The i18n.provider property must be set to the fully qualified class name of your provider. There are several ways to configure it.

System Property

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

@WebServlet Annotation

Source code
Java
@WebServlet(urlPatterns = "/*", name = "slot", asyncSupported = true, loadOnStartup = 1,
   initParams = { @WebInitParam(name = "i18n.provider", value = "com.vaadin.example.ui.TranslationProvider") })
public class ApplicationServlet extends VaadinServlet {
}

web.xml

Source code
XML
<?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>

Spring Bean

For Spring projects, annotate your provider with @Component and it’s automatically detected — no property configuration needed:

Source code
Java
@Component
public class TranslationProvider implements I18NProvider {
    // ...
}
Note
CDI-based projects can use a similar approach with CDI beans.

Adding Right-to-Left Support to Custom Elements

If you have custom elements or custom styles, there are additional steps to enable right-to-left (RTL) support.

DirMixin for Custom Elements

If your element extends Vaadin’s ElementMixin, no changes are needed. Otherwise, have the element extend DirMixin (from @vaadin/component-base):

Source code
JavaScript
import { PolymerElement } from '@polymer/polymer/polymer-element.js';
import { DirMixin } from '@vaadin/component-base/src/dir-mixin.js';

class MyElement extends DirMixin(PolymerElement) {}

DirMixin synchronizes the element’s dir attribute with the document-level dir attribute, allowing CSS and JS code to respond to text direction changes.

Adjusting Styles for RTL

Review properties like padding, margin, text-align, float, and transform. For example, if your styles define directional padding:

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

Add an RTL override:

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

You can replace directional properties with CSS Logical Properties. Flex and Grid containers are handled well by the browser and typically don’t require adjustments.

For help adjusting styles, you can use the tools at RTLCSS. See also this comprehensive right-to-left styling guide.

Icons and Directional Symbols

If your element uses icons or Unicode symbols that indicate direction (e.g., a Back button arrow), use the appropriate icons for RTL mode.

Keyboard Navigation

If keyboard interactions use arrow keys for navigation, adjust the direction based on the dir attribute:

Source code
JavaScript
const dirIncrement = this.getAttribute('dir') === 'rtl' ? -1 : 1;

switch (event.key) {
    // ...
    case 'ArrowLeft':
        idx = currentIdx - dirIncrement;
        break;
    case 'ArrowRight':
        idx = currentIdx + dirIncrement;
        break;
    // ...
}

Custom elements that rely on JavaScript calculations for sizing, position, or horizontal scroll may also need adjustments for RTL.

If you have visual tests, consider adding or updating them to run in RTL mode as well.

Frontend-Only RTL

For frontend-only applications, set document.dir = 'rtl' to enable right-to-left mode.

Updated