I18N localization
- Defining the i18n provider property
- Locale selection for new session
- Provider sample for translation
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.:
Source code
bash
mvn jetty:run -Dvaadin.i18n.provider=com.vaadin.example.ui.TranslationProviderWhen using the annotation you could have the servlet class as:
Source code
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:
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.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.
Source code
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.
Source code
Java
public class LocaleObserver extends Div implements LocaleChangeObserver {
@Override
public void localeChange(LocaleChangeEvent event) {
setText(getTranslation("my.translation", getUserId()));
}
}