skapral
skapral

Reputation: 1217

Spring MessageSource fallback to explicit locale

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.5.9.RELEASE</version>
</parent>

In some spring boot application, i18n is used. Here is the config:

@Bean
public MessageSource messageSource() {
    ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
    messageSource.setBasename(translationsPath);
    messageSource.setDefaultEncoding("UTF-8");
    messageSource.setFallbackToSystemLocale(false);
    messageSource.setCacheSeconds(5);
    return messageSource;
}

@Bean
public LocaleResolver localeResolver() {
    AcceptHeaderLocaleResolver localeResolver = new AcceptHeaderLocaleResolver();
    localeResolver.setDefaultLocale(Locale.ENGLISH);
    return localeResolver;
}

There is a requirement to obtain a locale from header Accept-language, falling back to "en" if:

Two first points are resolved by provided LocaleResolver (see config). However, I don't know how to implement point 3: the reloadable list of translation configs is encapsulated in ReloadableResourceBundleMessageSource, and I see no way how to make it fall back to "en" locale on some absent config.

There is setFallbackToSystemLocale option, but system locale != "en" locale. Moreover, MessageSource uses system locale only to resolve the token from translation config, not for localizing the message itself, which causes discrepancies with localizing the numbers.

I might try to limit the list of supported locales by using setSupportedLocales on LocaleResolver, but it is not suitable solution in my case, since the list of translation configs is supposed to change in runtime.

Is there a way to make ReloadableResourceBundleMessageSource fallback to explicit locale?

Upvotes: 3

Views: 4990

Answers (3)

Cassio Seffrin
Cassio Seffrin

Reputation: 8570

There is an useful config spring.messages.use-code-as-default-message. When it's true case the code message cannot be found: <spring:message code="not_found_label"/> the tag will render the code as default message.

Whether to fall back to the system Locale if no files for a specific Locale have been found. if this is turned off, the only fallback will be the default file (e.g. "messages.properties" for basename "messages").

Here is the official complete core properties that can be used with spring application

Here the official docs about <spring:message code="code" text="fallback text"/> It's also possible define a fallback text on text param.

in properties - application.properties

spring.messages.use-code-as-default-message=true

in YML format - application.yml

spring:  
  messages:
    use-code-as-default-message: true

Or a configuration bean:

@Bean
public MessageSource messageSource() {
    ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
    messageSource.setUseCodeAsDefaultMessage(true);
    return messageSource;
}

Upvotes: 1

sonOfRa
sonOfRa

Reputation: 1440

Starting with Spring 5.2.2, this will be possible, see This spring-framework commit

Upvotes: 2

skapral
skapral

Reputation: 1217

Solved that in a bit dirty way by writing a wrapper around MessageSource. It is not exactly what was requested: the fallback locale will be used if the token for requested one is absent (which is close but not the same as "config was absent"). Also, it swallows exceptions and uses them for flow control, which is not good.

final class FallbackMessageSource implements MessageSource {
    private final MessageSource delegate;
    private final Locale fallbackLocale;

    public FallbackMessageSource(MessageSource delegate, Locale fallbackLocale) {
        this.delegate = delegate;
        this.fallbackLocale = fallbackLocale;
    }

    @Override
    public String getMessage(String code, Object[] args, String defaultMessage, Locale locale) {
        try {
            return delegate.getMessage(code, args, defaultMessage, locale);
        } catch(NoSuchMessageException ex) {
            return delegate.getMessage(code, args, defaultMessage, fallbackLocale);
        }
    }

    @Override
    public String getMessage(String code, Object[] args, Locale locale) throws NoSuchMessageException {
        try {
            return delegate.getMessage(code, args, locale);
        } catch(NoSuchMessageException ex) {
            return delegate.getMessage(code, args, fallbackLocale);
        }
    }

    @Override
    public String getMessage(MessageSourceResolvable resolvable, Locale locale) throws NoSuchMessageException {
        try {
            return delegate.getMessage(resolvable, locale);
        } catch(NoSuchMessageException ex) {
            return delegate.getMessage(resolvable, fallbackLocale);
        }
    }
}

Upvotes: 4

Related Questions