A locale is a language that sign-in screens, one-time-code messages and the consent screen can use. The Locales screen keeps the list, marks one language as the default and activates or deactivates the others. A language can be activated only when all the texts it needs exist.
Before you start
Locales sits under GLOBAL in the sidebar and applies to every tenant. You need locales:read to see the screen and locales:manage to change it. Before you activate a new language, add its text to the things listed under Activating a language.
The list

| Field | Meaning |
|---|---|
| Language code | A code from a fixed list, such as en or ru. Shown in the first column. |
| Name | The language name, filled in from the code. |
| Default | The language used when nothing else matches. Exactly one. It must be active. |
| Active / Inactive | Whether auth offers the language. Only active languages are used. |
A new installation has en (default) and ru, both active.
Edit the list
Click Edit. The page becomes a form that you save as a whole.

- To add a language, pick it in the Language list and click Add. It is added as Inactive.
- Click Inactive on a row to activate it, or Active to deactivate it.
- Click Set default on an active, non-default row to make it the default.
- Click the cross on a row to remove the language.
- Click Save. If the save fails, the reason appears next to the button.
Removing a language deletes its texts from every form and from the OTP and password message templates. It does not delete scope descriptions or client names in that language. Re-adding the language later does not bring the form translations back.
Activating a language
Activating a language that is not active yet fails unless these records all have non-empty text in it:
| Record | What is checked |
|---|---|
| Clients | The name of every client. |
| Scopes | The description of every scope, and the description of every claim in it. See Scope. |
| Forms | Every string of the active version of every form. See Forms. |
| Templates | Every OTP and temporary-password template. |
The console then shows Locale ru is incomplete. Missing: followed by the list of what is missing, for example scope 'payments_read' description or form 'otp' v1. Fill in the texts and save again. The check runs only at activation. A language that is already active is not checked again when you save other changes.
How a language is chosen
ui_locales is an OIDC request parameter: a space-separated list of language codes, most preferred first. It is a hint. A client can send it on /authorize, and an unusable value never fails the request.
On /authorize, auth keeps the codes from ui_locales that are active on this screen, in the order the client sent them. Matching is on the exact code: en-US does not match en. If none is left, auth uses the default language. Then each piece of text is resolved the same way:
- A form uses the first language in the list that exists in that form’s translations. Strings that language lacks fall back to the default language’s strings. The form’s own language menu lists the form’s languages that are active.
- One-time-code and temporary-password messages use the first language in the list that has a template text, then the default language.
- On the consent screen, scope and claim descriptions use the first language in the list that has a text, then the default language. If a scope has no description, the screen shows the raw scope ID.
The Accept-Language header does not choose a language on /authorize. auth reads it only on the account page (the auth-settings form), and only when the request has no ui_locales parameter. There it takes the tags in header order and ignores their q weights.