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, 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()));
}
}
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:
-
If your element extends Vaadin’s
ElementMixin
, no changes are needed. Otherwise, you can have the element extend it orDirMixin
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 thedir
attribute at the document level and keeps it in sync with the element’sdir
attribute. This is helpful to adjust to the text-direction status in both CSS and JS code. -
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
andtransform
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.
-
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.
-
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; // ... }
-
Custom element that rely on JavaScript calculations for sizing, position, and/or horizontal scroll, may need some adjustments for right-to-left.
-
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