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:
- 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
- The user selects a language during login using the locale selector or there is a
fusionauth.locale
cookie present - The HTTP
Accept-Language
header if present on the HTTP request - 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>
OAuth Scope Consent Prompt
This feature is only available in an Essentials or Enterprise plan. Please visit our pricing page to learn more.
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.