lunes, 15 de abril de 2019

13. Vaadin i18n (1)

updated on April 15, 2019

0. Introduction 

To manage i18n (internationalization) in Vaadin we need to:
  • Inform Vaadin which class will manage i18n. 
  • Create this i18n class (should implement the i18NProvider interface)
  • Create resource bundles for each defined language
  • Optionally create a language enumeration of all the languages used.

1. Informing Vaadin about the i18n responsible class

In the previous post, we saw the class that informed Vaadin about the use of CDI and also about the class that managed the i18n. As a reminder, Vaadin uses a class that extends VaadinServlet or CdiVaadinServlet (only for CDI) and the i18 manager class is defined in the @WebInitParam of the @WebServlet annotation of this class.

Here is again the source code of this class


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package openadmin.listeners;

import javax.servlet.annotation.WebInitParam;
import javax.servlet.annotation.WebServlet;

import com.vaadin.cdi.CdiVaadinServlet;
import com.vaadin.flow.server.Constants;
//import com.vaadin.flow.server.VaadinServlet;
import com.vaadin.flow.server.VaadinServletConfiguration;

/**
 * Information about app servlet 
 * 
 * Necessary for defining i18n Provider
 * @author ximo 
 *
 */
@SuppressWarnings("serial")
@WebServlet(
 urlPatterns = "/*", 
 name = "slot", 
 asyncSupported = true, 
 initParams = {
  // I18N Provider for translation of labels 
        @WebInitParam( 
         name = Constants.I18N_PROVIDER, value = "openadmin.i18n.MyI18nProvider") 
        
 })

@VaadinServletConfiguration(productionMode = false)

//public class ApplicationServlet extends VaadinServlet {
public class ApplicationServlet extends CdiVaadinServlet {
}



2. Creating the i18n managing class


The class for managing i18 is MyI18nProvider, it must implement I18NProvider interface and here is the code. Note how the prefix to access the resource bundle is defined in the key passed as a parameter in the getTranslation procedure.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
package openadmin.i18n;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.ResourceBundle;
import java.util.stream.Collectors;

import com.vaadin.flow.i18n.I18NProvider;

@SuppressWarnings("serial")
public class MyI18nProvider implements I18NProvider {
 //List of all the available locales
 @Override
 public List<Locale> getProvidedLocales() {
  
  return Collections.unmodifiableList(
   Arrays.stream(LangEnum.values())
    .map(item -> new Locale(item.name()))
    .collect(Collectors.toList()));
  
 }

 /** 
  * key -> bundle.key
  */
 @Override
 public String getTranslation(String key, Locale locale, Object... params) {
  String loc=locale.getLanguage();
  String[] bundle_key = key.split("\\."); 
  ResourceBundle rsBundle = 
   ResourceBundle.getBundle(
    "i18n." + bundle_key[0] + "_" + loc);
  if (! rsBundle.containsKey(bundle_key[1])) {
   System.out.println("missing resource key (i18n) " + key);
   //logger().info("missing resource key (i18n) " + key);
      return bundle_key[1] + " - " + locale;
  } else {
   return (rsBundle.containsKey(bundle_key[1])) ? rsBundle.getString(bundle_key[1]) : bundle_key[1];
  }
 }
 
 public MyI18nProvider () {
  
 }
}



3. The resource bundles for the language


The resource bundles are stored in the src/main/resources/i18n folder (in this project, but not necessarily) , and there are several files, each file is named by a prefix followed by an underscore ("_"), the 2 digits of the locale, and the suffix ".properties". The prefixes are used for splitting a big property file into smaller and easier to manage ones. Here are some filename examples:
  • "login_es.properties": Stores keys used for login and the Spanish translation
  • "login_en.properties": Stores keys used for login and the English translation
  • "messages_es.properties": Stores keys used for messages and the Spanish translation
  • "messages_en.properties": Stores keys used for messages and the English translation
Here is the content of the login_es.properties:



user = Usuario
password = Contraseña
login = Acceder
invalid_credentials = Credenciales no válidas


4. The language enumeration


Let's create a Java "enum" with the country (of the Locale) abbreviation and a description.

Methods are provided for getting the LangEnum from the description and getting the LangEnum from the Locale.

Here is the code


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
package openadmin.i18n;

import java.util.Locale;
import lombok.Getter;


public enum LangEnum {
 es ("Español"), 
 ca ("Valencià"),
 en ("English"),
 fr ("Francaise"),
 de ("Deustch"),
 it ("Italiano"),
 ro ("Românesc");
 
 @Getter 
 private final String definition;
 
 
 private LangEnum(String definition) {
  this.definition=definition;
 }
 
  
 /**
  * Return the LangEnum that matches the definition
  * @param loc
  * @return
  */
 public static LangEnum getLangEnum(String definition) {
  LangEnum lEnum=LangEnum.ca;
  for (LangEnum item: LangEnum.values())
   if(item.getDefinition().equalsIgnoreCase(definition))
    lEnum=item;
   
  return lEnum;
 }
 
 /**
  * Return the LangEnum that matches the locale
  * @param loc
  * @return
  */
 public static LangEnum getFromLocale(Locale loc) {
  LangEnum lEnum=LangEnum.ca;
  for (LangEnum item: LangEnum.values())
   if(item.name().equalsIgnoreCase(loc.getLanguage()))
    lEnum=item;
  return lEnum;
 }
 
 public static void main (String[] args) {
  /**
  Arrays.stream(LangEnum.values())
   .map(LangEnum::getDefinition)
   .collect(Collectors.toList());
  */
  LangEnum le=LangEnum.getFromLocale(new Locale("es")) ;
  System.out.println(le.name());
  le=LangEnum.getFromLocale(new Locale("ca")) ;
  System.out.println(le.name());
  le=LangEnum.getFromLocale(new Locale("en")) ;
  System.out.println(le.name());
 }
}

No hay comentarios:

Publicar un comentario