Theme Localization

Overview

The FusionAuth theme can be localized to better server your end users. In each theme you may specify one to many language specific message bundles to translate text rendered in a theme into a user’s preferred language.

If you’re just looking into localizing your theme, take a look at our community provided and maintained message bundles.

You may also want to review our localization and internationalization documentation.

Messages

In the Messages tab of your theme editor you may specify one to many languages. Once you have specified a key and value the key may be used in any template to display a localized string.

Consider the following message bundle and theme usage example with English and German messages defined.

English

greeting=Good day

German

greeting=Guten Tag
optional-greeting=Mitmensch

Template

<p>${theme.message('greeting')} ${theme.optionalMessage('optional-greeting')}</p>

If I have selected German as my locale, I will be greeted with Guten Tag Mitmensch rendered on the page.

If I have English selected I will instead find the greeting Good day optional-greeting.

Prior to version 1.53.0, the behavior of theme.message and theme.optionalMessage differed in that an exception would be thrown if you used theme.message and the message key could not be found. While a missing key should be a development time issue, returning the key should provide a better development experience than throwing an exception.

Beginning in version 1.53.0 there is no difference in behavior between these two methods and you should prefer theme.message.

The following example that demonstrates the difference in behavior between theme.message and theme.optionalMessage only pertains to versions of FusionAuth prior to 1.53.0.

The behavior differs between theme.message and theme.optionalMessage only when the key doesn’t exist in any of the messages files, including the default one.

When there is no suitable key found and theme.message is used, an exception is thrown and the template fails to completely render. When there is no suitable key found and theme.optionalMessage is used, the key value is returned: optional-message in the example above.

Here’s an example of a template that will render for a user with a German locale but fail for a user with an English locale, because message fails when there is no key found:

Template Which Will Fail For Users With an English Locale

<p>${theme.message('optional-greeting')}</p>

Here’s a Freemarker function which returns an empty string when there is no value found for an optional message:

Freemarker Function to Return the Empty String When No Value is Found

[#function getOptionalMessage key=""]
  [#if "${theme.optionalMessage(key)}" == "${key}"]
    [#return "" /]
  [/#if]
  [#return theme.optionalMessage(key) /]
[/#function]

If you add this to your _helpers.ftl file, you can call it like this:

Calling getOptionalMessage

[@helpers.getOptionalMessage key="optional-greeting" /]

Locale

The locale is determined by the locale value. The locale is resolved on each request using the following precedence:

  1. The locale request parameter if present on the HTTP request. This is useful if you already know the user’s preferred locale prior to redirecting them to FusionAuth to complete authentication. See example below.

    locale=fr

  2. The user selects a language during login using the locale selector or there is a fusionauth.locale cookie present
  3. The HTTP Accept-Language header if present on the HTTP request
  4. The system default locale as determined by the underlying operating system

The user’s preferredLanguages settings are not used to select a locale for the hosted login pages.

Identity Provider Login Buttons

The button text displayed in the login pages for identity providers such as Google, Facebook or SAML, is retrieved from the identity provider configuration. The API documentation documents how to set and retrieve this value, which is identityProvider.buttonText.

This text is used in the default theme like so:

Login Template Excerpt

<div class="text">${identityProvider.lookupButtonText(clientId)?trim}</div>

The buttonText value stored in the identity provider configuration cannot be localized.

However, you can replace this line in the theme template to pull a localized value from the messages bundle.

First, add the translated text to all messages bundles, including the default bundle:

English

google-login=Login With Google

German

google-login=Mit Google Einloggen

Then, update the relevant templates to display the localized text. Here’s an excerpt of an updated login page:

Updated Login Template Excerpt

<div class="text">${theme.message('google-login')}</div>
FusionAuth Reactor logo

This feature is only available in an Essentials or Enterprise plan. Please visit our pricing page to learn more.

Available since 1.50.0

The OAuth2 consent template at /oauth2/consent has an expanded message lookup policy in order to allow customizing scope consent messages and details without requiring a separate theme.

OAuth scope consent messages and details follow an ordered theme message lookup policy from most-to least-specific. The theme will render the first option that contains a value using the OAuth2 consent template’s resolveScopeMessaging function. For an example data:read scope attached to an Application with the Id a2afb0f5-eb2d-4d8f-a55d-05e978e95fda, below the options for overriding the message are listed from most to least preferred.

Any : or = characters in the scope name need to be escaped in the theme messages definition.

# Most preferred: Override at the application level using the application Id
[{application}a2afb0f5-eb2d-4d8f-a55d-05e978e95fda]{scope-message}data\:read=Application-specific consent message
[{application}a2afb0f5-eb2d-4d8f-a55d-05e978e95fda]{scope-detail}data\:read=Application-specific consent detail

# Override at the tenant level using the tenant Id
[{tenant}e122574f-6ec5-4399-b6b2-04ede9796380]{scope-message}data\:read=Tenant-specific consent message
[{tenant}e122574f-6ec5-4399-b6b2-04ede9796380]{scope-detail}data\:read=Tenant-specific consent detail

# Least preferred: Override at the theme level
{scope-message}data\:read=Themed consent message
{scope-detail}data\:read=Themed consent detail

If a themed message cannot be found using the given application, tenant, or theme, the scope.defaultConsentMessage and scope.defaultConsentDetail values will be used. If scope.defaultConsentMessage is empty, the scope.name will be used as the message. If scope.defaultConsentDetail is empty, no detail value will be displayed.