I18N 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 e.g.:
bash
mvn jetty:run -Dvaadin.i18n.provider=com.vaadin.example.ui.TranslationProvider
When using the annotation you could have the servlet class as:
Java
@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
<?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.
The LoadingCache
used in the implementation is from the Google Guava package.
Sample i18n provider implementation
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));
private static final LoadingCache<Locale, ResourceBundle> bundleCache = CacheBuilder
.newBuilder().expireAfterWrite(1, TimeUnit.DAYS)
.build(new CacheLoader<Locale, ResourceBundle>() {
@Override
public ResourceBundle load(final Locale key) throws Exception {
return initializeBundle(key);
}
});
@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 = bundleCache.getUnchecked(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;
}
private static ResourceBundle initializeBundle(final Locale locale) {
return readProperties(locale);
}
protected static ResourceBundle readProperties(final Locale locale) {
final ClassLoader cl = TranslationProvider.class.getClassLoader();
ResourceBundle propertiesBundle = null;
try {
propertiesBundle = ResourceBundle.getBundle(BUNDLE_PREFIX, locale,
cl);
} catch (final MissingResourceException e) {
LoggerFactory.getLogger(TranslationProvider.class.getName())
.warn("Missing resource", e);
}
return propertiesBundle;
}
}
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.
Java
public class LocaleObserver extends Div implements LocaleChangeObserver {
@Override
public void localeChange(LocaleChangeEvent event) {
setText(getTranslation("my.translation", getUserId()));
}
}
Using localization without using LocaleChangeObserver
I18NProvider without LocaleChangeObserver
public class MyLocale extends Div {
public MyLocale() {
setText(getTranslation("my.translation", getUserId()));
}
}