i18n Fields
Dependency :
compile ":i18n-fields:0.6.4"Custom repositories :
mavenRepo "http://snapshots.repository.codehaus.org/" mavenRepo "http://repository.codehaus.org/" mavenRepo "http://download.java.net/maven/2/" mavenRepo "http://repository.jboss.com/maven2/" mavenRepo "http://m2repo.spockframework.org/ext/" mavenRepo "http://m2repo.spockframework.org/snapshots/"
Summary
This plugin provides an easy way of declarativily localize database fields of your content tables.
Installation
grails install-plugin i18n-fields
Description
i18n fields plugin
Overview
This plugin provides a declarative way for localizing domain classes' fields in different languages.The idea is, once you've got your application up and running, adding multiple language support for your database fields must be as easy (or more) than extracting your literals to bundles in your views.The plugin is inspired in the way the Symfony php framework does i18n, even though the technical solution behind the façade is quite different.A simple case
Just assume you have this domain class defined and working:class MyDomainEntity {
def name
def description
}${object.name} : ${object.description}class MyDomainEntity {
def name_en_US
def name_es_ES
def name_pt_BR
def description_en_US
def description_es_ES
def description_pt_BR
}Proposed solution
@i18nfields.I18nFields
class MyDomainEntity {
def name
def description static i18nFields = ['name', 'description']
}i18nFields {
locales = ['en_US', 'es_ES', 'pt_PT', 'pt_BR']
}Localization when not bound to a request thread
Locale is bound to the request thread using LocaleContextHolder.But sometimes, there's no user locale because there's no browser at all, but still you might need to operate in a given language (for example, sending mails to users in their choosen locale, from a quartz job). If you need to work with different languages in a job, or any offline code, you can usewithLocale to setup different locales in your calls:// Show how to set the language for a code block to get it properly, from within a quartz job or similar
withLocale(currentUser.getLocale()) {
myLocaleDependentService.sendMessage(currentUser) // Internally proper language will be selected
}
// Out of the closure, previous locale will be restoredwithLocale closure is dynamically added to domain classes, services, controllers, taglibs, etc… so you can always take an alternative route to the current locale in thread.If you need to dinamically provide access to all languages for an entity (for example, you want to provide access to every language from the content management site), you might use:org.codehaus.groovy.grails.commons.ConfigurationHolder.config.i18nFields.locales.each { loc ->
// do what you need to do with the language…
withLocale(new Locale("xx", "XX")) {
// my stuff… will be in *all* the configured languages…
}
}Direct acces to i18nalized fields
When you use the standard getter for a field, you'll be using the current session's Locale definition to retrieve the correct field contents, as explained above.If you need to access a particular translation of a field in a context where you can't easily perform a withLocale interaction (for example, in the views for l10n content), you can use a variation of the standard getter:object.getField(new Locale("xx", "XX"))
withLocale(new Locale("xx", "XX")) { object.getField() }
object.setField("content", new Locale("xx", "XX"))
object.setField_xx_XX(y)@, @object.getField_xx_XX() or object.field_xx_XX property.Locale handling
As you will probably know, Locales are composed of a language or a language and country combinations. Please, take a look at the Locale class javadoc for more information.You can define in your application any number of different locales for the same language depending on the country of your users. For example, we could use US english (en_US) and british english (en_GB) to reflect their particular differences.Also, you could take the benefit of the hierarchical nature of locale definitions to maybe define a "generic" english (en) for the vast majority of your users and add US or british english for the small differences.Doing so, you will enable your application to fall back to a correct locale definition if, for example, a user uses canadian english (en_CA) configured. The plugin will detect that "en_CA" isn't configured and will fall back to "en" instead of falling back to whatever locale default definition is configured.In other words,object.getField(new Locale("en", "CA")) will really return object.getField(new Locale("en")) in this scenario.Locale validation
The plugin will ignore by default any invalid locale, according to the list of available locales from Locale.getAvailableLocales().However, the returned list is not complete: some valid language or language and country combinations are missing. For example, "eu" locale defintion for Basque wouldn't be valid according to that list.If you come up with this problem you can define a list for extra locales in the plugin's configuration:i18nFields {
locales = ["en", "en_US", "es", "eu"]
extraLocales = ["eu"]
}Caveats
- If you're providing your own getters wrapping property access, it might not work for you. You'll have to take language into account and access properties properly (we will ease your task soon hopefully).
- If you're using @Searchable@, you must ask for all localized fields to be indexed, and you will have to provide your own language filtering in searches if needed (we plan to provide a way to ease that, too).