Propòsit
El mòdul d’internacionalització té com a objectiu principal permetre el desenvolupament d’aplicacions en el que no sigui necessària cap reenginyeria cada vegada que s’incorpori un nou llenguatge a l’aplicació.
En general tota aplicació internacionalitzada pot realitzar-se de dues formes diferenciades:
- Mitjançant la rèplica d’un conjunt de pàgines per a cada idioma.
- Mitjançant un únic conjunt de pàgines que obtenen diferents literals de forma externa segons l’idioma.
La primera solució, fins ara utilitzada en moltes pàgines Web, implica un cost gran de rèplica i manteniment, ja que qualsevol nou requeriment o canvi comporta realitzar més d’un desenvolupament, un per cada idioma.
La segona solució, recomanada per Canigó permet que:
- Es puguin afegir llenguatges sense haver de realitzar canvis al codi.
- Els texts, imatges i missatges s’emmagatzemen de forma externa al codi.
- La informació (per exemple dates) es pot formatejar segons l’idioma de l’usuari
Glossari
i18N (Internationalization)
i18N o Internationalization (el 18 correspon a les 18 lletres que hi ha entre la I inicial i la n final) és el procés d’agafar una aplicació dissenyada i reestructurar-la per a què pugui ser usada en diferents localitats o bé definir el procés de crear-la per a ser totalment flexible per executar-se en qualsevol localitat. Java defineix les classes bàsiques d’ús de la internacionalització.
Instal.lació i configuració
Instal.lació
El mòdul de configuració s’inclou per defecte dins del core de Canigó 3. Durant el procés de creació de l’aplicació, l’eina de suport al desenvolupament inclourà la referència dins del pom.xml. En cas d’una instal·lació manual afegir les següents línies al pom.xml de l’aplicació:
<dependency>
<groupId>cat.gencat.ctti</groupId>
<artifactId>canigo.core</artifactId>
<version>${canigo.core.version}</version>
</dependency>
A la Matriu de Compatibilitats es pot comprovar la versió del mòdul compatible amb la versió de Canigó utilitzada.
Configuració
Per a configurar el mòdul d’internacionalització s’han de manipular els següents fitxers:
- Definició dels fitxers de traduccions (típicament els fitxers dels diferents idiomes: application.properties, application_XX_XX.properties)
- Definició de la integració amb el servei de presentació (web.xml)
- Definició dels idiomes i literals suportats (només JSF).
No requereix configuració per a inicialitzar el mòdul per part del desenvolupador. Els xmls de configuració del servei es troben internalitzats dins del jar del core, i s’inicialitzarà de manera automàtica un cop arrenqui l’aplicació.
Els arxius de configuració d’idiomes de l’aplicació han d’estar en la següent ubicació:
<PROJECT_ROOT>/src/main/resources/config/i18n/*.properties
Automàticament el mòdul d’internacionalització carregarà tots els arxius de configuració d’idiomes perquè posteriorment puguin ser explotats per l’aplicació.
Definició dels fitxers de traducció
Ubicació:
<PROJECT_ROOT>/src/main/resources/config/i18n/*.properties
Explicació:
Els fitxers de traducció són els fitxers que contenen els missatges en els diferents idiomes. Es basen en l’ús de la internacionalització de Java (i18n o Internationalization).
Aquests fitxers s’han de construir tenint en compte que:
- La seva extensió ha de ser ‘.properties’ (es denominen resource bundles).
- Per cada idioma que volguem suportar cal crear un nou fitxer. Aquest fitxer tindrà la següent nomenclatura: ’nomFitxer__lg_pa._properties’, on ’lg’ correspon al codi del llenguatge i ‘pa’ correspon al codi del país pel qual volem definir els literals.
- El seu contingut es basa en l’ús de parells clau-valor tal i com es mostra a continuació:
Fitxer de multiidioma application.properties
clau_1=valor_1
clau_2=valor_2
...
En cas de que l’idioma escollit per l’usuari no correspongui amb cap fitxer, es pot crear un fitxer per defecte. Aquest fitxer tindrà el format ’nomFitxer.properties’.
Exemple:
- ‘applicationResources_en.properties’, per representar els literals en anglès
- ‘applicationResources_es.properties’, per representar els literals en castellà
- ‘applicationResources_ca.properties’, per representar els literals català
- ‘applicationResources.properties’, per representar els literals de qualsevol altre idioma
Més informació dels codis de pais e idioma:
Preguntes freqüents
Accés manual al servei d’internacionalització
Encara que no és una pràctica comú es pot accedir al mòdul d’idiomes desde qualsevol bean de l’aplicació gestionat per Spring. Per recuperar aquest bean el desenvolupador pot realitzar una crida de forma externa mitjançant el patró ‘Dependency Injection’ al mòdul i les seves propietats d’entorn.
- Injecció de dependències mitjançant xml. Per exemple:
Ruta proposada: <PROJECT_ROOT>/src/main/resources/spring/exemple-beans-config.xml
<bean id="myBean" class="cat.gencat.app.exemples.Injection">
<property name="i18nResource" ref="messageSource" />
</bean>
La clase Injection tindria la següent estructura:
import cat.gencat.ctti.canigo.arch.core.i18n.I18nResourceBundleMessageSource;
public class Injection {
I18nResourceBundleMessageSource i18n;
private static final Log LOGGER= LogFactory.getLog(Injection.class);
public void setI18nResource(I18nResourceBundleMessageSource i18nResource){
this.i18n = i18nResource;
}
public void executeCommand(){
if (LOGGER.isDebugEnabled()){
LOGGER.debug(i18n.getMessage("test.label"));
}
}
}
Spring s’encarregarà d’injectar el bean d’internacionalització dins del bean “myBean” executant el mètode setI18nResource.
- Injecció de dependències mitjançant anotacions:
import cat.gencat.ctti.canigo.arch.core.i18n.I18nResourceBundleMessageSource;
public class Injection {
private static final Log LOGGER= LogFactory.getLog(Injection.class);
@Autowired
I18nResourceBundleMessageSource i18n;
article table {
margin:1em 0;
}
public void executeCommand(){
if (LOGGER.isDebugEnabled()){
LOGGER.debug(i18n.getMessage("test.label"));
}
}
}
L’anotació @Autowired injecta en aquest cas, un bean de tipus cat.gencat.ctti.canigo.arch.core.i18n.I18nResourceBundleMessageSource que Spring trobarà en el context de l’aplicació.
Exemples
Exemple de Test unitari
Com a exemple d’utilització es mostra com podem fer una prova unitària:
- Creem la classe de test ‘I18nTest’ i afegim un mètode ’testI18N()’
- Creem els diferents fitxers de literals
@ExtendWith(SpringExtension.class)
@ContextConfiguration(locations = {"../canigo-core.xml"})
public class I18nTest{
@Autowired
I18nResourceBundleMessageSource messageResource;
@Test
public void testI18N() {
try {
Assert.assertEquals("Hola!", messageResource.getMessage("var1"));
Assert.assertEquals("Hi!", messageResource.getMessage("var1", Locale.UK));
Assert.assertEquals("Attaaaaaack!", messageResource.getMessage("var1", Locale.US));
}
catch(NoSuchMessageException ex){
Assert.assertTrue("var1 not found", false);
}
}
}
Fitxers dels diferents idiomes (application_xx_XX.properties):
-
application.properties (fitxer per defecte)
var1=Hola!
-
application_en_US.properties (fitxer pel ‘Locale’ ‘US’)
var1=Attaaaaaack!
-
application_en_GB.properties (fitxer pel ‘Locale’ ‘GB’)
var1=Hi!