i18n

HDS comes with a built-in internationalization (i18n) system.

App Internationalization

"App Internationalization" means that the app is translated at the runtime. This is the method to be used in dynamic apps such as the Console.

Step 1: Add JSON Files

First, add the strings in JSON files, preferable in a locales directory. Set the file name to the language code (ex: en.json).

{
    "home": {
        "title": "Welcome to HDS",
        "subtitle": "This is a subtitle",
        "welcome": "Welcome, {name}!"
    },
    "footer": {
        "about": "About us",
        "contact": "Contact us"
    }
}

Step 2: Add the InternationalizationProvider

Next, add the InternationalizationProvider to the +layout.svelte file (highest level possible) and set up the languages you want to support within the provider.

<script>
    import InternationalizationProvider from "@hyvor/design/components";
    import en from './locale/en.json';
</script>

<InternationalizationProvider
    languages={[
        {
            code: 'en',
            flag: '🇺🇸',
            name: 'English',
            region: 'United States',
            strings: en,
            default: true,
        },
        {
            code: 'fr',
            flag: '🇫🇷',
            name: 'Français',
            region: 'France',
            loader: () => import('./locale/fr.json')
        }
    ]}
>
    <slot />
</InternationalizationProvider>
  • The default language strings should be imported statically. Also, set the default property to true.
  • The other languages can be loaded dynamically using the loader property.

Step 3: Set up an export

To avoid repitition and enfore type safety, set up an export in a i18n.ts file. Use the type of the default language JSON file.

import type { InternationalizationService } from "@hyvor/design/components";
import { getContext } from "svelte";
import type enJson from "../locale/en.json";

type I18nType = InternationalizationService<typeof enJson>;

export function getI18n() {
    return getContext<I18nType>("i18n");
}

Step 4: Use the Strings

Now, you can use the strings in your components using the helper method and the t function and the T component.

<script>
    import { getI18n } from '$lib/i18n';
    const I18n = getI18n();
</script>

{I18n.t('home.title')}
{I18n.t('welcome', { name: 'John' })}
<I18n.T key="home.title" />
<I18n.T key="welcome" params={{ name: 'John' }} />

Strings

HDS uses the ICU MessageFormat for string formatting. Strings are parsed using the intl-messageformat library. Here are some examples:

Variables

You can use variables in the strings.

{
    "welcome": "Welcome, {name}!"
}

Then, you can pass the value using the params prop.

{I18n.t('welcome', { name: 'John' })}
<!-- or -->
<I18n.T key="welcome" params={{ name: 'John' }} />

You can also set the value to a reactive variable.

{I18n.t('welcome', { name: myName })}
<!-- or -->
<I18n.T key="welcome" params={{ name: myName }} />

Nested keys

You can use nested keys to organize the strings.

{
    "home": {
        "title": "Welcome to HDS",
        "subtitle": "This is a subtitle"
    }
}

Then, use dot notation to access the nested keys.

<I18n.T key="home.title" />

Formatting

Dates

{
    "start": "Starts on {startDate, date, long}"
}

Then, pass the date as a parameter.

<I18n.T key="start" params={{ startDate: new Date() }} />

Numbers

{
    "price": "The price is {price, number, :: compact-short currency/EUR}"
}

Then, pass the number as a parameter.

<I18n.T key="price" params={{ price: 100 }} />

The result will be The price is €100.

Plural

The plural format is used to handle pluralization. The value is expected to be a number. These plural categories are supported:

  • zero
  • one
  • two
  • few
  • many
  • other
  • =value

Here is an example (both lines produce the same result):

{
    "users": "{count} {count, plural, one {user} other {users}}",
    "usersShort": "{count, plural, one {# user} other {# users}}"
}
<I18n.T key="users" params={{ count: 10 }} />

See the Message Syntax documentation on formatjs.io for more available formatting options.

Components

You can pass components as parameters to the strings. In the following code, <a></a> is a placeholder for a component. The content within it ("this article") will be sent to the component as the children prop.

{
    "withComponent": "For advanced features, check out <a>this article</a>"
}

Then, pass the component as a parameter.

<I18n.T key="withComponent" params={{ 
    a: {component: MyCustomLink} 
}} />

Your MyCustomLink component will look like this:

<script lang="ts">
    export let children: string;
</script>
<a>
    {children} <SomeIcon />
</a>

You may pass additional to the component.

<T key="withComponent" params={{ 
    a: {
        component: MyCustomLink, 
        props: {
            href: '/advanced'
        }
    } 
}} />
Components are rendered only when the frontend is hydrated. In SSR, only the strings are rendered.

Elements

Similar to components, you can also render HTML elements.

{
    "withElement": "Please try again with a <b>different email</b>."
}

The following code maps the <b> in the string to a <strong> HTML element.

<I18n.T key="withElement" params={{b: {element: 'strong'}}} />