🧪 astro-i18next
An astro integration of i18next + some utility components to help you translate your astro websites!
Note
Status - 🚧 Beta
You can use it, and feedback is more than welcome! Note that some breaking changes may still be introduced during this phase as the goal for v1 is to get the best possible DX for translating your Astro pages.
- 🚀 Getting started
- 💻 CLI commands
- 📦 Utility components
- 📦 Utility functions
- 👀 Going further
- ✨ Contributors
- 📜 License
🚀 Getting started
1. Install
npm install astro-i18next
2. Configure
-
Add
astro-i18next
to yourastro.config.mjs
:import { defineConfig } from "astro/config"; import astroI18next from "astro-i18next"; export default defineConfig({ integrations: [astroI18next()], });
-
Configure
astro-i18next
in yourastro-i18next.config.mjs
file:/** @type {import('astro-i18next').AstroI18nextConfig} */ export default { defaultLanguage: "en", supportedLanguages: ["en", "fr"], i18next: { debug: true, // convenient during development to check for missing keys resources: { en: { translation: { key: "hello world", }, }, fr: { translation: { key: "bonjour le monde", }, }, }, }, };
ℹ️ Your
astro-i18next
config file can be a javascript (.js
|.mjs
|.cjs
) or typescript (.ts
|.mts
|.cts
) file.ℹ️ For a more advanced configuration, see the AstroI18nextConfig props.
-
(recommended) Load translation keys using an i18next backend plugin. For instance with the
i18next-fs-backend
plugin to load translation keys from the filesystem:npm install i18next-fs-backend
src ├-- locales # create this folder to store your translation strings | |-- en.json # english translation strings | └-- fr.json # french translation strings └-- pages └-- index.astro # route for default language
/** @type {import('astro-i18next').AstroI18nextConfig} */ export default { defaultLanguage: "en", supportedLanguages: ["en", "fr"], i18next: { debug: true, initImmediate: false, backend: { loadPath: "./src/locales/{{lng}}.json", }, }, i18nextPlugins: { fsBackend: "i18next-fs-backend" }, };
ℹ️ You may choose to organize your translations into multiple files instead of a single file per language using namespaces.
3. Start translating
You may now start translating your pages by using
i18next's t
function or the
Trans component depending on your needs.
Here's a quick tutorial to get you going:
-
Use translation keys in your Astro pages
--- // src/pages/index.astro import i18next, { t } from "i18next"; import { Trans, HeadHrefLangs } from "astro-i18next/components"; --- <html lang={i18next.language}> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width" /> <title>{t("site.title")}</title> <meta name="description" content={t("site.description")} /> <HeadHrefLangs /> </head> <body> <h1>{t("home.title")}</h1> <p> <Trans i18nKey="home.subtitle"> This is a <em>more complex</em> string to translate, mixed with <strong >html elements </strong> such as <a href="https://example.com/">a cool link</a>! </Trans> </p> </body> </html>
// src/locales/en.json { "site": { "title": "My awesome website!", "description": "Here is the description of my awesome website!" }, "home": { "title": "Welcome to my awesome website!", "subtitle": "This is a <0>more complex</0> string to translate, mixed with <1>html elements</1>, such as a <2>a cool link</2>!" } }
// src/locales/fr.json { "site": { "title": "Mon super site web !", "description": "Voici la description de mon super site web !" }, "home": { "title": "Bienvenue sur mon super site web !", "subtitle": "Ceci est une chaine de charactères <0>plus compliquée</0> à traduire, il y a des <1>éléments html</1>, comme <2>un super lien</2> par exemple !" } }
-
Create localized pages using the generate command
npx astro-i18next generate
-
You're all set! Have fun translating and generate localized pages as you go 🚀
Note
For a real world example, see the demo project or try the Astro i18n basics example on StackBlitz:
💻 CLI commands
generate
npx astro-i18next generate
This command will generate localized pages depending on your config and set i18next's language change on each page.
For instance, with supportedLanguages = ["en", "fr", "es"]
, and "en"
being
the default language and having:
src
└-- pages
|-- about.astro
└-- index.astro
👇 Running npx astro-i18next generate
will create the following pages
src
└-- pages
|-- es
| |-- about.astro
| └-- index.astro
|-- fr
| |-- about.astro
| └-- index.astro
|-- about.astro
└-- index.astro
📦 Utility components
Trans component
A component that takes care of interpolating its children with the translation strings. Inspired by react-i18next's Trans component.
---
import { Trans } from "astro-i18next/components";
---
<Trans i18nKey="superCoolKey">
An <a href="https://astro.build" title="Astro website">astro</a> integration of
<a href="https://www.i18next.com/" title="i18next website">i18next</a> and utility
components to help you translate your astro websites!
</Trans>
// fr.json
{
"superCoolKey": "Une intégration <0>astro</0> d'<1>i18next</1> + quelques composants utilitaires pour vous aider à traduire vos sites astro !"
}
Trans Props
Prop name | Type (default) | Description |
---|---|---|
i18nKey | string (undefined) | Internationalization key to interpolate to. Can contain the namespace by prepending it in the form 'ns:key' (depending on i18next.options.nsSeparator) |
ns | ?string (undefined) | Namespace to use. May also be embedded in i18nKey but not recommended when used in combination with natural language keys. |
LanguageSelector component
Unstyled custom select component to choose amongst supported locales.
---
import { LanguageSelector } from "astro-i18next/components";
---
<LanguageSelector showFlag={true} class="my-select-class" />
LanguageSelector Props
Prop name | Type (default) | Description |
---|---|---|
showFlag | ?boolean (false ) |
Choose to display the language emoji before language name |
HeadHrefLangs component
HTML tags to include in your page's <head>
section to let search engines know
about its language and region variants. To know more, see
Google's advanced localized versions.
---
import i18next from "i18next";
import { HeadHrefLangs } from "astro-i18next/components";
---
<html lang={i18next.language}>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
<title>...</title>
<meta name="description" content="..." />
<HeadHrefLangs />
</head>
<body>...</body>
</html>
The HeadHrefLangs component will generate all of the alternate links depending on the current url and supported languages.
For example, if you are on the /about
page and support 3 languages (en
,
fr
, es
) with en
being the default language, this will render:
<link rel="alternate" hreflang="en" href="https://www.example.com/about/" />
<link rel="alternate" hreflang="fr" href="https://www.example.com/fr/about/" />
<link rel="alternate" hreflang="es" href="https://www.example.com/es/about/" />
📦 Utility functions
interpolate function
interpolate(i18nKey: string, reference: string, namespace: string | null): string
astro-i18next
exposes the logic behind the Trans component, you may want to
use it directly.
import { interpolate } from "astro-i18next";
const interpolated = interpolate(
"superCoolKey",
'An <a href="https://astro.build" title="Astro website">astro</a> integration of <a href="https://www.i18next.com/" title="i18next website">i18next</a> and utility components to help you translate your astro websites!'
);
localizePath function
localizePath(path: string, locale: string | null = null, base: string = import.meta.env.BASE_URL): string
Sets a path within a given locale. If the locale param is not specified, the current language will be used.
Note: This should be used instead of hard coding paths to other pages. It will take care of setting the right path depending on the locale you set.
---
import { localizePath } from "astro-i18next";
import i18next from "i18next";
i18next.changeLanguage("fr");
---
<a href={localizePath("/about")}>...</a>
<!-- renders: <a href="/fr/about">...</a> -->
localizeUrl function
localizeUrl(url: string, locale: string | null = null, base: string = import.meta.env.BASE_URL): string
Sets a url within a given locale. If the locale param is not specified, the current language will be used.
Note: This should be used instead of hard coding urls for internal links. It will take care of setting the right url depending on the locale you set.
---
import { localizeUrl } from "astro-i18next";
import i18next from "i18next";
i18next.changeLanguage("fr");
---
<a href={localizeUrl("https://www.example.com/about")}>...</a>
<!-- renders: <a href="https://www.example.com/fr/about">...</a> -->
👀 Going further
Namespaces
i18next allows you to organize your translation keys into namespaces.
You can have as many namespaces as you wish, have one per page and one for common translation strings for example:
src
├-- locales
| |-- en
| | |-- about.json # "about" namespace
| | |-- common.json # "common" namespace
| | └-- home.json # "home" namespace
| └-- fr # same files other language folders
└-- pages
|-- about.astro
└-- index.astro
-
Using the
i18next-fs-backend
plugin, it can easily be setup in yourbackend.loadPath
alongside thens
anddefaultNS
keys, like so:/** @type {import('astro-i18next').AstroI18nextConfig} */ export default { defaultLanguage: "en", supportedLanguages: ["en", "fr"], i18next: { debug: true, initImmediate: false, ns: ["common", "home", "about"], defaultNS: "common", backend: { loadPath: "./src/locales/{{lng}}/{{ns}}.json", // will look for json files inside language folders }, }, i18nextPlugins: { fsBackend: "i18next-fs-backend" }, };
ℹ️ See i18next-fs-backend's documentation for more info.
-
Load the namespace globally using
i18next.setDefaultNamespace(ns: string)
or specify it in thet
function or theTrans
component:--- import { t, setDefaultNamespace } from "i18next"; import { Trans } from "astro-i18next/components"; setDefaultNamespace("home"); --- <h1>{t("myHomeTitle")}</h1> <p> <Trans i18nKey="myHomeDescription"> This translation is loaded from the default <strong>home</strong> namespace! </Trans> </p> <p> <Trans i18nKey="myCommonCTA" ns="common"> This translation is loaded from the <strong>common</strong> namespace! </Trans> </p> <!-- t function loads the "buttonCTA" key from the "common" namespace --> <button>{t("common:buttonCTA")}</button>
AstroI18nextConfig Props
astro-i18next
's goal is to abstract most of the configuration for you so that
you don't have to think about it. Just focus on translating!
Though if you'd like to go further in customizing i18next, feel free to tweak your config!
Prop name | Type (default) | Description |
---|---|---|
defaultLanguage | string (undefined) |
The default language for your website |
supportedLanguages | string[] (undefined) |
Your website's supported languages |
i18next | ?InitOptions |
The i18next configuration. See i18next's documentation. |
i18nextPlugins | ?{[key: string]: string} |
Set i18next plugins. See i18next's available plugins. |
✨ Contributors
Thanks goes to these wonderful people (emoji key):
Yassine Doghri 💻 📖 🤔 🎨 💡 🚧 |
Davide Ceschia 💻 🐛 |
preetamslot 🐛 |
Dmytro 🐛 |
Campbell He 🐛 |
MelKam 💻 |
L1lith 🐛 🤔 |
Anomander43 📖 |
Dominik Schöni 💻 |
Dalibor Hon 💻 🐛 |
Oleksii Lozoviahin 💻 |
Alessandro Talamona 🐛 |
Josh Kramer 🐛 |
This project follows the all-contributors specification. Contributions of any kind welcome!
📜 License
Code released under the MIT License.
Copyright (c) 2022-present, Yassine Doghri (@yassinedoghri)