Skip to content
This repository has been archived by the owner on Nov 25, 2020. It is now read-only.

Using I18N4Vaadin with CDI

Petter Holmström edited this page Sep 6, 2013 · 11 revisions

Using I18N4Vaadin 2.0 with CDI

This page will explain how to install and use I18N4Vaadin 2.0 in a Vaadin 7 project that uses CDI. Although I18N4Vaadin itself does not require it, I recommend you use the Vaadin CDI Add-on to configure your UIs and views.

Get the JARs

You will need to add the following JARs to your project's compile time and runtime classpaths:

  • i18n4vaadin-api-2.x.x.jar - the annotations and interfaces you will be using in your application
  • i18n4vaadin-cdi-2.x.x.jar - the classes that provide CDI support

In addition, the annotation processor JAR i18n4vaadin-cdi-ap-2.x.x.jar needs to be available on the classpath during compilation. It is not, however, required during runtime. Also note that the annotation processor requires some additional libraries. Maven will download them automatically.

Configure your UI

The first thing you need to do is to enable I18N4Vaadin in your Vaadin UI. You do this by injecting an instance of the I18N interface. This instance contains information about the supported locales and the currently selected locale. It is also used to change the locale.

The I18N instance is session scoped, meaning that multiple UI instances will use the same I18N instance (and thus the same locale).

It is possible to define supported locales in two ways: by using an annotation or by using code (the I18N.setSupportedLocales(Collection<Locale>) method).

@VaadinUI
@Root
public class MyUI extends UI {

    @I18nSupportedLocales({@Locale(language = "sv"), @Locale(language = "fi")})
    @Inject
    private I18N i18n;

    @Override
    protected void init(VaadinRequest request) {
        // ...
    }
    
    // ...
}

In the example above, the supported locales will be sv and fi and the current locale will be automatically set to the first locale in the list, i.e. sv.

Add some messages

Now, it is time to add some messages by using the @Message or @Messages annotations. These annotations can be placed on any class - it does not have to be a Vaadin class.

@VaadinView(value = "myView")
public class MyView extends CustomComponent implements View {

    @Message(key = "resourceNotAvailable", value = "Resursen {0} är inte tillgänglig för alarmering")
    private void aMethod(Resource resource) {
        // ...
    }

    @Messages({
        @Message(key = "ticketModifiedException", value = "Uppdraget har ändrats av en annan användare. Informationen har nu uppdaterats."),
        @Message(key = "ticketClosedException", value = "Uppdraget har avslutats och kan inte längre ändras."),
        @Message(key = "noSuchTicketException", value = "Uppdraget kunde inte hittas i databasen."),
        @Message(key = "ticketSaved", value = "Uppdragsinformationen har sparats i databasen.")
    })
    private void anotherMethod() {
        // ...
    }
    
    // ...
}

The messages should be written in the default language of your application - in this case Swedish. Also note, that you can use indexed parameters ({0}, {1}, etc) in the message strings.

Configure the annotation processor

The annotation processor takes one configuration parameter:

  • bundleperclass=true|false: If true, one bundle per annotated class will be generated. If false, one bundle per package will be generated (the default).

Please note, that it is best to always specify the parameters explicitly since the default values might change before the final release. For example, to generate one bundle per class, pass the following option to the Java compiler: -Abundleperclass=true

If you are using Maven, you can use the CDI demo application as an example.

Inject your bundle

After you have compiled your project, you will now find a few auto-generated files in the same package as the annotated class (your IDE has probably placed them in a separate directory, though, since you should not commit them into your source code repository).

First, you will find a message bundle file MyViewmessages.properties (i.e. the class name appended with messages.properties) that looks something like this:

#Auto-generated by com.github.peholmst.i18n4vaadin.ap.BundleFileGenerator
#Thu Feb 28 15:28:45 EET 2013
ticketModifiedException=Uppdraget har ändrats av en annan användare. Informationen har nu uppdaterats.
ticketSaved=Uppdragsinformationen har sparats i databasen.
ticketClosedException=Uppdraget har avslutats och kan inte längre ändras.
resourceNotAvailable=Resursen {0} är inte tillgänglig för alarmering
noSuchTicketException=Uppdraget kunde inte hittas i databasen.

Second, you will find a Java class file MyViewBundle.java (i.e. the class name appended with Bundle.java) that looks something like this:

Generated(value = "com.github.peholmst.i18n4vaadin.ap.JavaFileGenerator", date = "2013-02-28T15:28:45+0200")
@SessionScoped
public class MyViewBundle implements java.io.Serializable {

    // ...

    public String resourceNotAvailable(Object... args) {
        return getMessage("resourceNotAvailable", args);
    }
    public String ticketClosedException(Object... args) {
        return getMessage("ticketClosedException", args);
    }
    public String ticketModifiedException(Object... args) {
        return getMessage("ticketModifiedException", args);
    }
    public String noSuchTicketException(Object... args) {
        return getMessage("noSuchTicketException", args);
    }
    public String ticketSaved(Object... args) {
        return getMessage("ticketSaved", args);
    }

    public static final class Keys {

        private Keys() {
        }
        public static final String resourceNotAvailable = "resourceNotAvailable";
        public static final String ticketClosedException = "ticketClosedException";
        public static final String ticketModifiedException = "ticketModifiedException";
        public static final String noSuchTicketException = "noSuchTicketException";
        public static final String ticketSaved = "ticketSaved";
    }

    public String getMessage(String key, Object... args) {
    	// ...
    }

    public static String getMessage(String key, Locale locale, Object... args) {
        // ...
    }
}

Now you can inject this session scoped bundle instance into your view like this:

@Inject
MyViewBundle bundle;

... and start using your translated strings:

Notification.show(bundle.noSuchTicketException(), Notification.Type.WARNING_MESSAGE);
Notification.show(bundle.resourceNotAvailable("MyResource"));

Translate your bundles

Once your application is working in the default language, make copies of all the generated text bundle files and add the proper locale suffixes (i.e. MyViewmessages_fi.properties). Then translate the contents of your copied files. Also remember to commit your translations into your source code repository.

Change the language on the fly or upon login

You change the language of your application by invoking the setLocale(Locale) method of the I18N instance. If you are doing this on the fly, you have to remember to update all your UI components since this is not something I18N4Vaadin can do automatically.

The easiest way of doing this is probably to have your views listen for the LocaleChangedEvent fired by the I18N instance. This event is fired both as a CDI event and to registered LocaleChangedListeners, but due to how the Vaadin CDI add-on works, you will have to use the latter approach since for example the views in Vaadin CDI are dependent scoped. See the CDI demo application for details.