Localization
- Defining the I18n Provider Property
- Locale Selection for New Session
- Provider Sample for Translation
- Supporting Right-to-Left Mode
- Frontend Projects
- Adding RTL Support to Your Custom Elements or Application
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 e.g.:
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 when 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.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 may provide a I18NProvider
as a bean in case you are using Spring. All you need in this case
it’s just annotate your implementation with @Component
so it becomes available as a Spring bean.
Spring add-on will automatically use it in case 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 will then be used, else we
will try to match on only language. If neither is found the locale will be set
to the first 'supported' locale from I18NProvider.getProvidedLocales()
and if
that is empty Locale.getDefault()
will be used.
Provider Sample for Translation
For this example we enable Finnish and English to be used with Finnish being the "default" that is used if the user client doesn’t specify english as an accepted language.
In this sample the language .properties
files start with "translate" e.g.
translate.properties
(for default), translate_fi_FI.properties
and translate_en_GB.properties
The translation properties files are in the example loaded using the class loader
so they should be located on the classpath for example in the resources folder
e.g. src/main/resources
for a default maven setup.
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
Using the internationalization in the application is a combination of using the 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 the LocaleChangeObserver
to receive events
for locale change.
This observer will also be notified on navigation in the attach phase of before navigation after any url parameters are set, so that the state from a url parameter can be used.
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 RTL languages. The components will work out-of-the-box on this mode, but to make your application support both LTR and RTL modes, a few changes are needed.
On top of the last examples, let’s say that your application is now also translated into a RTL Language, such as Arabic. Besides providing the sample for the Arabic translation, at your main layout you can add a code like such:
public class MainLayout extends VerticalLayout {
public MainLayout() {
// ...
final UI ui = UI.getCurrent();
if (ui.getLocale().getLanguage() == "ar") {
ui.setDirection(Direction.RIGHT_TO_LEFT);
}
}
}
This will work if changing of locale is only based on the Accept-Language
coming from the client, but if user can define their language, for instance,
at your application’s setting page, then you can make your main layout implement
the LocaleChangeObserver
interface, so it will receive the changes of locale
and then you can set the direction based on the locale set:
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);
}
}
}
Frontend Projects
For frontend applications only, to set the RTL mode you can call document.dir = 'rtl'
.
Adding RTL Support to Your Custom Elements or Application
If you have your own custom elements or your application has custom styles, there are a few steps needed in order to add RTL support to them:
-
If your element extends Vaadin’s
ElementMixin
, make sure that it depends on the version starting from2.3.0
. If not, you can make the element extends it orDirMixin
only (DirMixin
is part of the@vaadin/vaadin-element-mixin
package).import { PolymerElement } from '@polymer/polymer/polymer-element.js'; import { DirMixin } from '@vaadin/vaadin-element-mixin/vaadin-dir-mixin.js'; class MyElement extends DirMixin(PolymerElement) {}
The
DirMixin
registers the element to changes on thedir
attribute at the document level and keeps it in sync with the element’sdir
attribute. That’s helpful to easily check the RTL status in both CSS and JS code. -
Make sure your styles are adjusted properly for the RTL mode.
For example, if you define values for the padding on the
:host
, like::host { padding-right: 1em; padding-left: 2em; }
You may want to define the proper style for the RTL, like:
:host([dir="rtl"]) { padding-right: 2em; padding-left: 1em; }
You may want to pay attention to declarations such as
padding
,margin
,text-align
,float
andtransform
on your styles. In case your custom element doesn’t have to support old browsers (such as IE11), you can replace some properties with CSS Logical Properties. The MDN web docs has a full list of CSS Logical Properties and Values available along with the browsers support for each property. Flex and Grid containers usually are handled well by the browser and don’t require extra work. More information can be found at this extensive RTL styling guide.To help adjusting the styles for the RTL mode, you can go to the RTL CSS page. There you can paste your original styles and it will generate the code that you can take into usage for your element.
-
If your element uses icons or unicode symbols to define direction (for instance ⬅ for back button) you may need to use the right icons/symbols for RTL.
-
If keyboard interaction are used, such as to navigate between items with arrow keys, make sure to check if
dir
isrtl
and use it 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; // ... }
-
If your custom element relies on some Javascript calculation for sizing, position and/or horizontal scroll, check if it needs some adjustments for RTL.
-
If you have visual tests, you may want to add or update the current ones to also run in RTL.
D5E24461-FB68-4457-B95F-DEFB99F4B41F