Show Navigation

How to change languages in a Grails app?

Learn how to change the default language used in your application, switch between languages or access the current locale.

Authors: Sergio del Amo

Grails Version: 3.3.1

1 Grails Training

Grails Training - Developed and delivered by the folks who created and actively maintain the Grails framework!.

2 Getting Started

In this guide you are going to learn how to create an app whose interface is translated in four languages.

You will learn how to change the default language used in your application and how to switch between languages while using the app.

2.1 What you will need

To complete this guide, you will need the following:

  • Some time on your hands

  • A decent text editor or IDE

  • JDK 1.7 or greater installed with JAVA_HOME configured appropriately

2.2 How to complete the guide

To get started do the following:

or

The Grails guides repositories contain two folders:

  • initial Initial project. Often a simple Grails app with some additional code to give you a head-start.

  • complete A completed example. It is the result of working through the steps presented by the guide and applying those changes to the initial folder.

To complete the guide, go to the initial folder

  • cd into grails-guides/grails_i18n/initial

and follow the instructions in the next sections.

You can go right to the completed example if you cd into grails-guides/grails_i18n/complete

3 Writing the Application

Grails supports Internationalization (i18n) out of the box by leveraging the underlying Spring MVC internationalization support. With Grails you are able to customize the text that appears in a view based on the user’s Locale

3.1 Message Bundles

Message bundles in Grails are located inside the grails-app/i18n directory and are simple Java properties files.

We want to support English, Spanish, Italian and German in our application. We are going to define the localizations in the different message bundles.

Default Message Bundle

grails-app/i18n/messages.properties
navbar.applicationStatus=Application Status
navbar.artefacts=Artefacts
navbar.installedPlugins=Installed Plugins
navbar.languages=Languages

language.en=English
language.es=Spanish
language.de=German
language.it=Italian

welcome.title=Welcome to Grails
welcome.body=Congratulations, you have successfully started your first Grails application! At the moment this is the default page, feel free to modify it to either redirect to a controller or display whatever content you may choose. Below is a list of controllers that are currently deployed in this application, click on each to execute its default action:

Spanish Message Bundle

grails-app/i18n/messages_es.properties
navbar.applicationStatus=Estado de la aplicación
navbar.artefacts=Artefactos
navbar.installedPlugins=Plugins instalados
navbar.languages=Idiomas

language.en=Inglés
language.es=Español
language.de=Alemán
language.it=Italiano

welcome.title=Bienvenido a Grails
welcome.body=¡Enhorabuena, ha iniciado con éxito su primera aplicación de Grails! En este momento es la página predeterminada, no dude en modificarla para redirigir a un controlador o mostrar el contenido que elija. A continuación se muestra una lista de controladores que se implementan actualmente en esta aplicación, haga clic en cada uno para ejecutar su acción predeterminada:

Italian Message Bundle

grails-app/i18n/messages_it.properties
navbar.applicationStatus=Stato dell'applicazione
navbar.artefacts=Artefatti
navbar.installedPlugins=Plugin installati
navbar.languages=Le lingue

language.en=Inglese
language.es=Spagnolo
language.de=Tedesco
language.it=Italiano

welcome.title=Benvenuti a Grails
welcome.body=Congratulazioni, hai iniziato con successo la tua prima applicazione Grails! Al momento questa è la pagina predefinita, sentitevi liberi di modificarla per reindirizzare a un controller o visualizzare qualsiasi contenuto che puoi scegliere. Di seguito è riportato un elenco di controller attualmente implementati in questa applicazione, fare clic su ciascuno per eseguire l'azione predefinita:

German Message Bundle

grails-app/i18n/messages_de.properties
navbar.applicationStatus=Bewerbungsstatus
navbar.artefacts=Artefakte
navbar.installedPlugins=Installierte Plugins
navbar.languages=Sprachen

language.en=Englisch
language.es=Spanisch
language.de=Deutsche
language.it=Italienisch

welcome.title=Willkommen in Grails
welcome.body=Herzlichen Glückwunsch, Sie haben Ihre erste Grails-Anwendung erfolgreich begonnen! Im Moment ist dies die Standard-Seite, fühlen Sie sich frei, es zu ändern, um entweder auf einen Controller umzuleiten oder anzuzeigen, welche Inhalte Sie wählen können. Unten ist eine Liste von Controllern, die derzeit in dieser Anwendung bereitgestellt werden, klicken Sie auf jeden, um seine Standardaktion auszuführen:

In the home page we use those message codes:

grails-app/views/index.gsp
<h1><g:message code="welcome.title" /></h1>
<p><g:message code="welcome.body" /></p>

3.2 Change Locale

You can switch locales by simply passing a parameter called lang to Grails as a request parameter:

/?lang=es

Grails will automatically switch the user’s locale and store it in a cookie so subsequent requests will have the new header.

We can verify this behaviour with a Functional Test:

/src/integration-test/groovy/demo/ChangeLocaleSpec.groovy
package demo

import geb.spock.GebSpec
import grails.testing.mixin.integration.Integration

@Integration
class ChangeLocaleSpec extends GebSpec {

    def "change locale"() {
        when:
        go '/?lang=en'

        then:
        $('h1').text() == 'Welcome to Grails'

        when:
        go '/?lang=es'

        then:
        $('h1').text() == 'Bienvenido a Grails'
    }
}

3.3 Changing Default Locale

We want our app to startup and by default use Spanish

grails-app/conf/spring/resources.groovy
import org.springframework.web.servlet.i18n.SessionLocaleResolver;

// Place your Spring DSL code here
beans = {
    localeResolver(SessionLocaleResolver) {
        defaultLocale= new java.util.Locale('es');
    }
}

We can verify the default locale is Spanish with a Functional Test:

/src/integration-test/groovy/demo/DefaultLocaleSpec.groovy
package demo

import geb.spock.GebSpec
import grails.testing.mixin.integration.Integration

@Integration
class DefaultLocaleSpec extends GebSpec {

    def "change locale"() {
        when:
        go '/'

        then:
        $('h1').text() == 'Bienvenido a Grails'
    }
}

3.4 Locale Navbar

We define the supported languages in a configuration property:

/grails-app/conf/application.yml
---
guide:
    languages:
        - en
        - es
        - it
        - de
---

We are going to use a Tag Library to render a languages dropdown:

languages drop down
/grails-app/taglib/demo/LocaleNavbarTagLib.groovy
package demo

import grails.config.Config
import grails.core.support.GrailsConfigurationAware
import org.springframework.context.MessageSource
import org.springframework.web.servlet.support.RequestContextUtils

class LocaleNavbarTagLib implements GrailsConfigurationAware {

    static namespace = 'navBar'

    static defaultEncodeAs = [taglib: 'none']

    MessageSource messageSource

    List<String> languages

    @Override
    void setConfiguration(Config co) {
        languages = co.getProperty('guide.languages', List) (1)
    }

    def localeDropdownListItems = { args ->
        String uri = args.uri

        for ( String lang : languages ) {
            String languageCode = "language.$lang"
            def locale = RequestContextUtils.getLocale(request) (2)
            def msg = messageSource.getMessage(languageCode, [] as Object[], null, locale) (3)
            out << "<li><a href='${uri}?lang=${lang}'>${msg}</a></li>"
        }

    }

}
1 Retrieve configuration property
2 Access current locale
3 Retrieving a localized message

We can test the TagLib rendering with a Unit Test.

/src/test/groovy/demo/LocaleNavbarTagLibSpec.groovy
package demo

import grails.testing.web.taglib.TagLibUnitTest
import org.springframework.context.MessageSource
import spock.lang.Specification

class LocaleNavbarTagLibSpec extends Specification implements TagLibUnitTest<LocaleNavbarTagLib> {

    void "LocaleNavbarTagLib method localeDropdown renders"() {
        given:
        def uri = '/books'
        def languages = [[code: 'en', msg: 'English'],
                         [code: 'es', msg: 'Spanish'],
                         [code: 'it', msg: 'Italian'],
                         [code: 'de', msg: 'German']]

        when:
        def expected = ''
        languages.each { Map m ->
            expected += "<li><a href='${uri}?lang=${m.code}'>${m.msg}</a></li>"
        }
        tagLib.languages = languages.collect { it.code }
        tagLib.messageSource = Stub(MessageSource) {
            getMessage('language.en', [] as Object[], null, _) >> languages.find { it.code == 'en'}.msg
            getMessage('language.es', [] as Object[], null, _) >> languages.find { it.code == 'es'}.msg
            getMessage('language.it', [] as Object[], null, _) >> languages.find { it.code == 'it'}.msg
            getMessage('language.de', [] as Object[], null, _) >> languages.find { it.code == 'de'}.msg
        }
        def result = applyTemplate('<navBar:localeDropdownListItems uri="/books"/>')

        then:
        cleanUpString(result) == cleanUpString(expected)
    }

    String cleanUpString(String str) {
        str.replaceAll('\n','').replaceAll(' ', '')
    }
}

We invoke the TagLib inside the <content> element of the default GSP.

/grails-app/views/index.gsp
<li class="dropdown">
    <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"><g:message code="languages" default="Languages"/> <span class="caret"></span></a>
    <ul class="dropdown-menu">
        <navBar:localeDropdownListItems uri="${request.forwardURI}"/>
    </ul>
</li>

We supply to the Tag Lib the request.forwardURI

forwardURI - Useful for obtaining the current request URI since the request object’s requestURI property returns the original URI, not the matched one.

4 Running the Application

To run the tests:

./grailsw
grails> test-app
grails> open test-report

or

./gradlew check
open build/reports/tests/index.html

To run the application use the ./gradlew bootRun command which will start the application on port 8080.

5 Do you need help with Grails?

OCI sponsored the creation of this Guide. OCI offers several Grails services:

Free consultation

The OCI Grails Team includes Grails co-founders, Jeff Scott Brown and Graeme Rocher. Check our Grails courses and learn from the engineers who developed, matured and maintain Grails.

Grails OCI Team