🌎Internationalization and Translations

Or i18n for short

Internaltionalization and Translation, refered 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:

src/login/Register.tsx
export default function Register(props: RegisterProps) {
    const { i18n } = props;
    
    const { msg, msgStr, advancedMsg, advancedMsgStr } = i18n;

    return (
        //...
        <a href={url.loginUrl}>
            {msg("backToLogin")}
        </a>
        // ...
    );
}

Overriding the bases message or adding cusom ones

In the theme

If you want to see the base message tranlations you can navigate to the node_modules/keycloakify/src/login/i18n/messages_defaultSet/ directory:

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

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

As a result:

msg("backToLogin") returns the JSX.Element:
<div data-kc-msg="backToLogin">We are <strong>sorry</strong> ...</div>

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 renderd is:

We are sorry ... (with sorry in bold)

msgStr("backToLogin") returns the string:
"We are <strong>sorry</strong> ..."

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

src/login/i18n.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;

The messageBundle that you provide as argument of the createUseI18n function must be statically evaluable. You can't import from external files. All the translations must be declared inline. This is because Keycloakify will analyse 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.

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

In the Keycloak Realm configuration

Some relevent 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 reaml level and it will work as you would expect:

Note that if you try to use:

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 staticaly defined set. More details. See alslo:

πŸ“„Terms and conditions✍️Adding Registration Form Fields

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:

pages/***.stories.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"
            }
        }}
    />
};

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

src/login/KcPageStory.tsx
export const { getKcContextMock } = createGetKcContextMock({
    kcContextExtension,
    kcContextExtensionPerPage,
    overrides: {
        locale: {
            currentLanguageTag: "de"
        },
        
    },
    overridesPerPage: {}
});

What do do if my langage is not in the default set

As of writing theses line Keycloak support 27 languages.

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

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

Last updated