Skip to content

The i18n integration provides runtime translations while keeps type-checking, preventing most of the runtime errors in this kind of libraries.

It is intended to be used for projects with a large number of languages or locales and websites with Astro Islands that require some translations. For static or small projects, I’d recommend using the official Astro i18n or any other existing type-safe libraries. In any case, give it a try!

This package uses @astro-tools/transfer-state for managing the state.

The model managed by the integration and the library is an object of type I18nTranslations. This object must be serializable and it will be transferred to the client.

The key of the translation must be type string. The supported types for the translation value is a string or Record<string, string> (plurals only) with interpolation values surrounded by {}, like the following example:

{
"hello": "Hello {name}!"
}

Variables in the translations can be typed to number and string using the following format:

{
"hello": "Hello {name:string}! click here to get {points:number} points!"
}

A plural is a Record<string, string> in which the keys are each plural variant with the string value following the same format as any other non-plural translation.

For example, a plural translation compatible with Intl.PluralRules could be:

{
"animals": {
"zero": "There isn't any animal",
"one": "There is {value} animal",
"other": "There are {value} animals"
}
}

For setting up the internationalization, include the integration in your Astro project:

  1. Install the library and its dependencies using your preferred package manager:
    npm i -D @astro-tools/i18n
  2. Add the integration to your project configuration:
    astro.config.ts
    import { dirname, join } from 'node:path';
    import { fileURLToPath } from 'node:url';
    import { defineConfig } from 'astro/config';
    import { i18n } from '@astro-tools/i18n';
    import { typesLoader } from '@/i18n/types-loader';
    const resolve = (path: string) =>
    join(dirname(fileURLToPath(import.meta.url)), path);
    export default defineConfig({
    integrations: [
    i18n({
    types: typesLoader(),
    providers: {
    translations: resolve('./src/i18n/translations-provider.ts'),
    plural: resolve('./src/i18n/plural-provider.ts'),
    },
    }),
    ],
    });

For more details about the integration options, continue reading.

To extract the typings properly, define the types function which should return an object of type I18nTranslations:

import { readFile } from 'node:fs/promises';
import { join, parse } from 'node:path';
import { fileURLToPath } from 'node:url';
import type { I18nIntegrationTypesLoader } from '@astro-tools/i18n';
export function typesLoader(): I18nIntegrationTypesLoader {
return async () => {
const base = parse(fileURLToPath(import.meta.url)).dir;
const buffer = await readFile(join(base, 'translations', 'en-US.json'));
return JSON.parse(buffer.toString('utf-8'));
};
}

The result is the overload functions of the function t, which can be used to render translations:

types.d.ts
export function t(key: 'home.title'): string;
export function t(key: 'home.description', values: { companyName: string, ownerName: string }): string;
export function t(key: 'rating.title', values: { library: string }): string;
export function t(key: 'rating.stars', count: number, values: { stars: number }): string;

Pluralized translations require an additional argument count: number, which is used to select the proper plural.

The providers allow to configure the behaviors of the library in runtime. For example, a provider could execute a block of code for the server and other block for the client.

The implementation is delegated to the project but the sub-package @astro-tools/i18n/providers provides default providers that should be enough for simple use cases.

This provider loads the proper translations for a locale: string:

import type { I18nTranslationsProvider } from '@astro-tools/i18n';
const translations = new Map();
translations.set('en-US', () =>
import('./translations/en-US.json').then((json) => json.default),
);
translations.set('es-ES', () =>
import('./translations/es-ES.json').then((json) => json.default),
);
const translationsProvider: I18nTranslationsProvider = async (locale) => {
const loader = translations.get(locale);
if (!loader) {
throw new Error(`Missing translations for locale ${locale}`);
}
return await loader();
};
export default translationsProvider;

A context can be recieved as second argument which is arbitrary data coming from the use function explained below.

This provider selects the proper plural from a translation using the count: number argument.

The default provider uses Intl.PluralRules for selecting the plural:

import { pluralProviderFactory } from '@astro-tools/i18n/providers';
const pluralProvider = pluralProviderFactory();
export default pluralProvider;

The integration exposes the virtual module @astro-tools:i18n with the required functions for managing translations.

Before render any translation, configure the locale and fallback locale using the function use(options: I18nUseOptions): Promise<void>. It is recommended to create a wrapper with your own logic following DRY principle:

import { use } from '@astro-tools:i18n';
export async function useTranslations(
locale: string,
fallbackLocale?: string,
): Promise<void> {
await use({
locale,
fallbackLocale: fallbackLocale || 'en-US',
});
}

There is a context option that be used in the use function for pass arbitrary data to the translation provider. For example, the context could be used to load partial translations.

The context won’t be transferred to the client side, so it can be any type of data.

Then, for translating keys, just use the previous useTranslations function with the desired locale for the page being rendered and use the t function to render a translation.

If a translation does not exists in the configured locale, then the fallbackLocale will be used. If the translation still missing, the key will be rendered.

---
import Output from '@/libs/examples/Output.svelte';
import { useTranslations } from '@/i18n/use-translations';
import { t } from '@astro-tools:i18n';
import ClientSideExample from './ClientSideExample.svelte';
interface Props {
id: string;
}
const { id } = Astro.props;
await useTranslations('es-ES');
---
<Output text={t('home.title')}></Output>
<Output text={t('home.description', { ownerName: 'Doc', companyName: 'Astro Tools' })}></Output>
<br />
<button type="button" id="i18n-button">Click me to hydrate!</button>
<br />
<ClientSideExample {id} client:on="click #i18n-button" />
Preview Static
Página de ejemplo
Página de ejemplo de Astro Tools construida por Doc


0

Valora @astro-tools/i18n!
¡Esta librería tiene 0 estrellas!