npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@jeffwcx/astro-i18next

v1.2.0

Published

An astro integration of i18next + some utility components to help you translate your astro websites!

Downloads

135

Readme

🧪 astro-i18next

An astro integration of i18next + some utility components to help you translate your astro websites!

npm-badge build-badge codecov-badge license-badge contributions-badge semantic-release-badge stars-badge

Note

Status - 🚧 Beta

👉 Road to v1.0.0

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.

Examples

| Example | Status | | ------------------------------------- | ---------------------------------------------------------------------------------------------------- | | SSG - Basics | example-up-badge | | SSR - Node | example-up-badge | | React | example-up-badge | | SSR - Netlify | example-down-badge (https://github.com/yassinedoghri/astro-i18next/issues/26) | | SSR - Deno | example-down-badge (https://github.com/yassinedoghri/astro-i18next/issues/55) |

🚀 Getting started

1. Install

npm install @jeffwcx/astro-i18next

or

pnpm add @jeffwcx/astro-i18next

or

yarn add @jeffwcx/astro-i18next

2. Configure

  1. Add astro-i18next to your astro.config.mjs:

    import { defineConfig } from "astro/config";
    import astroI18next from "@jeffwcx/astro-i18next";
    
    export default defineConfig({
      integrations: [astroI18next()],
    });
  2. Configure astro-i18next in your astro-i18next.config.mjs file:

    /** @type {import('astro-i18next').AstroI18nextConfig} */
    export default {
      defaultLocale: "en",
      locales: ["en", "fr"],
    };

    ℹ️ 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.

  3. By default, astro-i18next expects your translations to be organized inside your astro's publicDir, in a locales folder:

      public
      └── locales  # create this folder to store your translation strings
          ├── en
          |   └── translation.json
          └── fr
              └── translation.json

    ℹ️ astro-i18next loads your translation files both server-side and client-side using i18next-fs-backend and i18next-http-backend plugins.

    ℹ️ You may choose to organize your translations into multiple files instead of a single file per locale 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:

  1. Use translation keys in your Astro pages

    ---
    // src/pages/index.astro
    import i18next, { t } from "i18next";
    import { Trans, HeadHrefLangs } from "@jeffwcx/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>
    // public/locales/en/translation.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>!"
      }
    }
    // public/locales/fr/translation.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 !"
      }
    }
  2. Create localized pages using the generate command

    npx astro-i18next generate
  3. You're all set! Have fun translating and generate localized pages as you go 🚀

Note

For a real world example, see the website or check out the examples.


💻 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 locales = ["en", "fr", "es"], and "en" being the default locale 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

🔄 Translate Routes

astro-i18next let's you translate your pages routes for each locale!

For instance, with support for 3 locales (en, fr, es), en being the default and the following pages:

src
└── pages
    ├── about.astro
    ├── contact-us.astro
    └── index.astro
  1. Set route mappings in your astro-i18next config:

    /** @type {import('astro-i18next').AstroI18nextConfig} */
    export default {
      defaultLocale: "en",
      locales: ["en", "fr", "es"],
      routes: {
        fr: {
          about: "a-propos",
          "contact-us": "contactez-nous",
          products: {
            index: "produits",
            categories: "categories",
          },
        },
        es: {
          about: "a-proposito",
          "contact-us": "contactenos",
          products: {
            index: "productos",
            categories: "categorias",
          },
        },
      },
    };
  2. Generate your localized pages using the generate CLI command, they will be translated for you!

src
└── pages
    ├── es
    |   ├── productos
    |   |   ├── categorias.astro
    |   |   └── index.astro
    |   ├── a-proposito.astro
    |   ├── contactenos.astro
    |   └── index.astro
    ├── fr
    |   ├── produits
    |   |   ├── categories.astro
    |   |   └── index.astro
    |   ├── a-propos.astro
    |   ├── contactez-nous.astro
    |   └── index.astro
    ├── products
    |   ├── categories.astro
    |   └── index.astro
    ├── about.astro
    ├── contact-us.astro
    └── index.astro

Note

The localizePath and localizeUrl utility functions will retrieve the correct route based on your mappings.


📦 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 "@jeffwcx/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). If omitted, a key is automatically generated using the content of the element. | | 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 "@jeffwcx/astro-i18next/components";
---

<LanguageSelector showFlag={true} class="my-select-class" />

<!-- LanguageSelector with custom language naming -->
<LanguageSelector
  showFlag={true}
  languageMapping={{ en: "Anglais" }}
  class="my-select-class"
/>

LanguageSelector Props

| Prop name | Type (default) | Description | | --------------- | --------------------- | ------------------------------------------------------------------------------------------- | | showFlag | ?boolean (false) | Choose to display the language emoji before language name | | languageMapping | ?object (undefined) | Rewrite language names by setting the locale as key and the wording of your choice as value |

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 "@jeffwcx/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 locales.

For example, if you are on the /about page and support 3 locales (en, fr, es) with en being the default locale, 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 locale 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 locale 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:

public
├-- locales
|   |-- en
|   |   |-- about.json    # "about" namespace
|   |   |-- common.json   # "common" namespace
|   |   └-- home.json     # "home" namespace
|   └-- fr   # same files in other locale folders
src
└-- pages
      |-- about.astro
      └-- index.astro
  1. It can easily be setup using the namespaces and defaultNamespace keys, like so:

    /** @type {import('astro-i18next').AstroI18nextConfig} */
    export default {
      defaultLocale: "en",
      locales: ["en", "fr"],
      namespaces: ["about", "common", "home"],
      defaultNamespace: "common",
    };
  2. Load the namespace globally using i18next.setDefaultNamespace(ns: string) or specify it in the t function or the Trans component:

    ---
    import { t, setDefaultNamespace } from "i18next";
    import { Trans } from "@jeffwcx/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 | | -------------------- | -------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ | | defaultLocale | string (undefined) | The default locale for your website. | | locales | string[] (undefined) | Your website's supported locales. | | namespaces | string or string[] ('translation') | String or array of namespaces to load. | | defaultNamespace | string (translation') | Default namespace used if not passed to the translation function. | | load | Array<"server" or "client"> (["server"]) | Load i18next on server side only, client side only or both. | | resourcesBasePath | ?string | Set base path for i18next resources. Defaults to /locales. | | i18nextServer | ?InitOptions | The i18next server side configuration. See i18next's documentation. | | i18nextServerPlugins | ?{[key: string]: string} ({}) | Set i18next server side plugins. See available plugins. | | i18nextClient | ?InitOptions | The i18next client side configuration . See i18next's documentation. | | i18nextClientPlugins | ?{[key: string]: string} ({}) | Set i18next client side plugins. See available plugins. | | routes | [segment: string]: string or object({}) | The translations mapping for your routes. See translate routes. | | showDefaultLocale | boolean(false) | Whether or not the defaultLocale should show up in the url just as other locales. |

✨ Contributors

Thanks goes to these wonderful people (emoji key):

This project follows the all-contributors specification. Contributions of any kind welcome!

❤️ Acknowledgments

This wouldn't have been possible without the awesome work from the Locize and Astro teams.

Inspired by some of the greatly thought-out i18n implementations:

📜 License

Code released under the MIT License.

Copyright (c) 2022-present, Yassine Doghri (@yassinedoghri)