> For the complete documentation index, see [llms.txt](https://fusionauth.io/docs/llms.txt)

# Theme Localization | FusionAuth Docs

Learn about how FusionAuth Themes work with localization.

# Theme Localization

[Edit on GitHub](https://github.com/FusionAuth/fusionauth-site/blob/main/astro/src/content/docs/customize/look-and-feel/localization.mdx)

[View Markdown](https://fusionauth.io/docs/customize/look-and-feel/localization.md)

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](https://github.com/FusionAuth/fusionauth-localization).

You may also want to review our [localization and internationalization documentation](https://fusionauth.io/docs/get-started/core-concepts/localization-and-internationalization.md).

## 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

```properties
greeting=Good day
```

German

```properties
greeting=Guten Tag
optional-greeting=Mitmensch
```

Template

```html
<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

```html
<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

```plaintext
[#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

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

## Locale

The locale is determined by the [locale](https://fusionauth.io/docs/reference/data-types.md#locales) 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](https://fusionauth.io/docs/apis/identity-providers/google.md) 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

```html
<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

```properties
google-login=Login With Google
```

German

```properties
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

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

## OAuth Scope Consent Prompt

This feature is only available in Essentials or Enterprise plans. To learn more, see [our pricing page](https://fusionauth.io/pricing.md).

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.

```properties
# 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.