# Internationalization and Translations

Internationalization and Translation, referred as i18n is the set of feature that enables you to make your pages available in multiple languages.

## Base principles

When in your components you see instructions like:

<pre class="language-tsx" data-title="src/login/Register.tsx"><code class="lang-tsx">export default function Register(props: RegisterProps) {
    const { i18n } = props;
    
    const { msg, msgStr, advancedMsg, advancedMsgStr } = i18n;

    return (
        //...
        &#x3C;a href={url.loginUrl}>
<strong>            {msg("backToLogin")}
</strong>        &#x3C;/a>
        // ...
    );
}
</code></pre>

<figure><img src="https://723341942-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F1pGOu8uVH5l8KqPEPmKi%2Fuploads%2FwISLTyqL3K6yR4QRdYWy%2Fimage.png?alt=media&#x26;token=da6bc4ed-2167-42ec-8f28-ed56e41bce37" alt="" width="374"><figcaption><p><code>msg("backToLogin")</code> gets rendered as <strong>« Back to Login</strong></p></figcaption></figure>

## Overriding the base message or adding custom ones

### In the theme

If you want to see the base message translations you can navigate to the **node\_modules/keycloakify/src/login/i18n/messages\_defaultSet/** directory:

<figure><img src="https://723341942-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F1pGOu8uVH5l8KqPEPmKi%2Fuploads%2Fl8a2wTAsUMYJx9k1oePi%2FScreenshot%202024-07-14%20at%2018.08.03.png?alt=media&#x26;token=eb06d0a3-cdae-4be5-8d3b-b753dce2d588" alt=""><figcaption><p>Don't edit this file directly, it's just for seeing what are the default set of i18n messages.</p></figcaption></figure>

As you can see, the translation message for the key backToLogin in English (**en.ts**) is:

`We are <strong>sorry</strong> ...`

\
As a result:

{% code title="msg(" %}

```html
<div data-kc-msg="backToLogin">We are <strong>sorry</strong> ...</div>
```

{% endcode %}

{% hint style="info" %}
The `data-kc-msg` attribute is only here to help you find the source code that generates this node when inspecting with the browser dev tools.\
Note also that the text that is going to be rendered is:

We are **sorry** ... (with sorry in bold)
{% endhint %}

{% code title="msgStr(" %}

```javascript
"We are <strong>sorry</strong> ...";
```

{% endcode %}

Keycloakify let's you overwrite the values of the translations messages and define new ones.\
This is how to do it:

{% code title="src/login/i18n.tsx" %}

```tsx
import { createUseI18n } from "keycloakify/login";

export const { useI18n, ofTypeI18n } = createUseI18n({
  en: {
    backToLogin: "⏪ Back to <strong>Login page</strong>",
    myCustomKey: "My custom message",
  },
  fr: {
    backToLogin: "⏪ Retour à la <strong>page de Login</strong>",
    myCustomKey: "Mon message personalisé",
  },
});

export type I18n = typeof ofTypeI18n;
```

{% endcode %}

{% hint style="warning" %}
The messageBundle that you provide as argument of the `createUseI18n` function must be statically valuable. You can't import from external files. All the translations must be declared inline.\
This is because Keycloakify will analyze your code at build time to make Keycloak aware of your modifications of the base messages so that server side generated feedback messages can use your translations.

![](https://723341942-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F1pGOu8uVH5l8KqPEPmKi%2Fuploads%2FWmGvGwmY4Km4YQLxUD5j%2Fimage.png?alt=media\&token=02edd3c2-1b18-47da-9fad-64fbe60c1aaa)\
![](https://723341942-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F1pGOu8uVH5l8KqPEPmKi%2Fuploads%2F6ZvuTJZZpqU29SdodjIf%2Fimage.png?alt=media\&token=a74032a0-5f35-449f-b0c6-66633cdb844b)

<img src="https://723341942-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F1pGOu8uVH5l8KqPEPmKi%2Fuploads%2FIohiiwvU4PJUpPLFsM1W%2FScreenshot%202024-06-22%20at%2021.36.53.png?alt=media&#x26;token=b1e4843d-540c-47e1-8689-5a2f7cb66efe" alt="" data-size="original">
{% endhint %}

If you don't provide translation for all the language that are enabled in the realm configuration it will fallback to english (or your first translation declared).

### In the Keycloak Realm configuration

Some relevant messages, namely `termsText` and all the messages used in the User Profile Attributes like for example the Display name, the helper text or the select option labels can be defined at the realm level and it will work as you would expect:

<figure><img src="https://723341942-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F1pGOu8uVH5l8KqPEPmKi%2Fuploads%2F2bttuHkA3yWaxMrqnPNb%2Fimage.png?alt=media&#x26;token=3a76e08d-d898-40d9-a61a-404c8e08d683" alt=""><figcaption><p>The custom user attribute favourite_pet has for Display Name the message key "profile.attributes.favourite_pet"</p></figcaption></figure>

<figure><img src="https://723341942-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F1pGOu8uVH5l8KqPEPmKi%2Fuploads%2Fj15AUSNZP8ayFdsGZx2Z%2Fimage.png?alt=media&#x26;token=94fcafbf-9071-4dc7-9813-78efdec56faa" alt=""><figcaption><p>A translation for the message key "profile.attributes.favourite_pet" has been defined for the English language: "Favourite Pet"</p></figcaption></figure>

<figure><img src="https://723341942-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F1pGOu8uVH5l8KqPEPmKi%2Fuploads%2FN6bQKD52SwE0X5sqPohc%2Fimage.png?alt=media&#x26;token=508db357-aa7e-4998-a261-8785284b6709" alt="" width="312"><figcaption><p>"Favourite Pet" is correctly used as Display Name for the input field in the register page</p></figcaption></figure>

Note that if you try to use:

```tsx
msg("profile.attributes.favourite_pet");
```

It will work at runtime, you'll get `Favourite Pet` but typescript will complain because `"profile.attributes.favourite_pet"` or `string` isn't a known i18n message key, it makes sense as it's only defined on the server.

This is why you'll see in some place in the code the usage of `advancedMsg(attribute.displayName)`, `advancedMsg()` is basically equivalent to `msg()` except that TypeScript won't complain if the key isn't part of the statically defined set.\
[More details](https://github.com/keycloakify/keycloakify/blob/60aaa03202763307a82991c38997d166f8f44d65/src/login/i18n/i18n.tsx#L58-L72).\
\
See also:

{% content-ref url="terms-and-conditions" %}
[terms-and-conditions](https://doc-old.keycloakify.dev/documentation/v10/terms-and-conditions)
{% endcontent-ref %}

{% content-ref url="broken-reference" %}
[Broken link](https://doc-old.keycloakify.dev/documentation/v10/broken-reference)
{% endcontent-ref %}

## Stories in different languages

Changing the language directly using the dropdown select in Storybook isn't supported.\
To preview your component in different languages, create separate stories for each language.\
Example:

{% code title="pages/\*\*\*.stories.tsx" %}

```tsx
export const Default: Story = {
  render: () => <KcPageStory />,
};

export const French: Story = {
  render: () => (
    <KcPageStory
      kcContext={{
        locale: {
          currentLanguageTag: "fr",
        },
      }}
    />
  ),
};

export const Spanish: Story = {
  render: () => (
    <KcPageStory
      kcContext={{
        locale: {
          currentLanguageTag: "es",
        },
      }}
    />
  ),
};
```

{% endcode %}

If you want all your story to by by default in an other language you can edit:

{% code title="src/login/KcPageStory.tsx" %}

```tsx
export const { getKcContextMock } = createGetKcContextMock({
  kcContextExtension,
  kcContextExtensionPerPage,
  overrides: {
    locale: {
      currentLanguageTag: "de",
    },
  },
  overridesPerPage: {},
});
```

{% endcode %}

## What do do if my language is not in the default set

{% hint style="warning" %}
Support for adding extra language will be added soon.\
In the meantime see <https://github.com/keycloakify/keycloakify/issues/599>
{% endhint %}

As of writing theses line Keycloak support 27 languages.

<figure><img src="https://723341942-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F1pGOu8uVH5l8KqPEPmKi%2Fuploads%2FXm5taQPG7YEwQVqTzOXG%2Fimage.png?alt=media&#x26;token=d4faf740-81af-41d6-8b41-dbe020963b63" alt="" width="96"><figcaption><p>The 27 languages in supported by Keycloak</p></figcaption></figure>

What to do is your language isn't one of them? Like Hebrew for example.

Unfortunately, Keycloak doesn't provide a way to easily add a new language however there's a workaround with Keycloakify. You can overrides the translation of an other language to add your translations. Here is an example:

{% embed url="<https://github.com/keycloakify/keycloakify-starter/commit/6cce74f516c25005da39e8612132712db1723894>" %}
