Localization
- Locale Selection for New Session
- Defining I18n Provider Property
- Using I18NProvider for Translation
- Supporting Right-to-Left Mode
- Frontend Projects
- Adding Right-to-Left Support
To use localization and translation strings, the application needs only to have the translation properties available on the classpath under the directory, vaadin-i18n
with the filename prefix, translations
(e.g., src/main/resources/vaadin-i18n/translations.properties
).
When using localization in an application, calling for a translation, or when the I18NProvider is used for the first time, the folder resource, vaadin-i18n
is checked if it contains any translations.properties
or translations_[langcode].properties
files. Any language codes are collected from the available property files and added as provided locales in the DefaultI18NProvider
.
The file translations.properties
is a default translation file that’ll be used for any Locale
that doesn’t have a specific translations file. Locale translation files are named, for example, like translations_fi_FI.properties
or translations_de.properties
. The automatic Locale
creation supports from one to three parts (e.g., translations_language_country_variant.properties
).
Note
|
DefaultI18NProvider is available as of version 24.3 of Vaadin Flow. For an earlier version, you’ll need to implement your own I18NProvider , as documented in Defining I18n Provider Property section.
|
Locale Selection for New Session
The initial locale is determined by matching the locales provided by the I18NProvider
against the Accept-Language
header in the initial response from the client.
If an exact match (i.e., language and country) is found, it’ll be used. Otherwise, it tries to match only on 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.
Using Localization in Application
Implementing internationalization in an 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()));
}
}
Defining I18n Provider Property
To use a custom I18N provider with more features than available by the default one, the application needs only to implement I18NProvider
and define the fully qualified class name in the property, i18n.provider
.
Note
|
For a Spring project, the property is not needed if the custom provider is a Bean of type I18NProvider .
|
The i18n.provider
property can be set from the command-line as a system property, as a Servlet initial parameter in the web.xml
file, or using the @WebServlet
annotation.
As a system property, the parameter needs the vaadin
prefix like this:
mvn jetty:run -Dvaadin.i18n.provider=com.vaadin.example.ui.TranslationProvider
When using the annotation, you could have the servlet class as something such as this:
@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, if you prefer to use the web.xml
file, you might set it as this:
<?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’re using Spring. In that case, you need only annotate your implementation with @Component
, so that it’s available as a Spring bean. The Spring add-on uses it if it’s available. See the class SimpleI18NProvider.java
implemented in the tutorial project as an example.
Using I18NProvider for Translation
For this example, the use of Finnish and English is enabled, with Finnish being the default that’s used if the user client doesn’t specify English as an accepted language. The language .properties
files start with "translate": for example, translate.properties
for the default, as well as translate_fi_FI.properties
, and translate_en_GB.properties
.
Here 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;
}
}
Supporting Right-to-Left Mode
Vaadin components have support for right-to-left languages. The components work out-of-the-box in this mode. However, to allow your application to support both left-to-right and right-to-left modes, you’ll need to make a few changes.
Continuing from the previous examples, suppose your application now has 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);
}
}
}
Frontend Projects
For frontend applications only, to set right-to-left mode, you can specify, document.dir = 'rtl'
.
Adding Right-to-Left Support
If you have custom elements, or if your application has custom styles, there are a few steps needed to add right-to-left support to them.
First, if your element extends Vaadin’s ElementMixin
, no changes are needed. Otherwise, you can have the element extend it or DirMixin
only (i.e., 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 synchronized with the element’s dir
attribute. This is helpful to adjust to the text-direction status in both CSS and JS code.
Second, 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;
}
Third, you should also review settings such as padding
, margin
, text-align
, float
and transform
in your styles. If your custom element doesn’t need to support old browsers, you can replace some properties with CSS Logical Properties. The MDN web documentation has a full list of CSS Logical Properties and their available values, along with browser support for each property. Flex and Grid containers are usually handled well by the browser and don ’t require anything extra. 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 use the tools available on the RTLCSS page. There, you can paste original styles and it’ll generate code that you can use for your element.
If your element uses icons or Unicode symbols to define direction (e.g., 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 like so:
// 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 elements that rely on JavaScript calculations for sizing, position, 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 run also in right-to-left mode.
722E7AE4-191E-4DE8-90F1-CAE8AE6CD3DF