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 🙏

© 2026 – Pkg Stats / Ryan Hefner

astro-loader-i18n

v0.10.9

Published

An Astro content loader for i18n files and folder structures.

Readme

astro-loader-i18n

NPM Downloads npm bundle size

astro-loader-i18n is a content loader for internationalized content in Astro. It builds on top of Astro’s glob() loader and helps manage translations by detecting locales, mapping content, and enriching getStaticPaths.

Features

✅ Automatic locale detection

  • Extracts locale information from file names or folder structures:

    . (project root)
    ├── README.md
    └── src
        └── content
            └── pages
                ├── de-CH
                │   ├── about.mdx
                │   └── projects.mdx
                └── zh-CN
                    ├── about.mdx
                    └── projects.mdx
    . (project root)
    └── src
        └── content
            └── pages
                ├── about.de-CH.mdx
                ├── about.zh-CN.mdx
                ├── projects.de-CH.mdx
                └── projects.zh-CN.mdx
  • Supports nested folders:

    . (project root)
    └── src
        └── content
            └── pages
                ├── de-CH
                │   ├── about.mdx
                │   └── projects
                │       ├── project1.mdx
                │       └── project2.mdx
                └── zh-CN
                    ├── about.mdx
                    └── projects
                        ├── project1.mdx
                        └── project2.mdx

✅ Translation mapping

  • Generates a translation identifier to easily match different language versions of content.

✅ Schema support

  • Helps to define schemas for your localized content.
  • Add translationId and locale to the schema by using extendI18nLoaderSchema. You need this when using i18nLoader or i18nContentLoader.
  • When you have multiple locales in a single file, you can use localized to define the necessary schema. This is useful when using i18nContentLoader.

getStaticPaths() helpers included

  • Includes a helper utility called i18nPropsAndParams
    • Helps to fill and translate URL params like [...locale]/[files]/[slug], whereas [...locale] is the locale, [files] is a translated segment and [slug] is the slug of the title.
    • Adds a translations object to each entry, which contains paths to the corresponding content of all existing translations.

✅ Type safety

  • Keeps Astro.props type-safe.

Usage

  1. Install the package astro-loader-i18n:

    npm install astro-loader-i18n
    yarn add astro-loader-i18n
    pnpm add astro-loader-i18n
  2. Configure locales, a default locale and segments for example in a file called site.config.ts:

    export const C = {
      LOCALES: ["de-CH", "zh-CN"],
      DEFAULT_LOCALE: "de-CH" as const,
      SEGMENT_TRANSLATIONS: {
        "de-CH": {
          files: "dateien",
        },
        "zh-CN": {
          files: "files",
        },
      },
    };
  3. Configure i18n in astro.config.ts:

    import { defineConfig } from "astro/config";
    import { C } from "./src/site.config";
    
    export default defineConfig({
      i18n: {
        locales: C.LOCALES,
        defaultLocale: C.DEFAULT_LOCALE,
      },
    });
  4. Define collections using astro-loader-i18n in content.config.ts. Don't forget to use extendI18nLoaderSchema or localized to extend the schema with the i18n specific properties:

    import { defineCollection, z } from "astro:content";
    import { extendI18nLoaderSchema, i18nContentLoader, i18nLoader, localized } from "astro-loader-i18n";
    import { C } from "./site.config";
    
    const filesCollection = defineCollection({
      loader: i18nLoader({ pattern: "**/[^_]*.{md,mdx}", base: "./src/content/files" }),
      schema: extendI18nLoaderSchema(
        z.object({
          title: z.string(),
        })
      ),
    });
    const folderCollection = defineCollection({
      loader: i18nLoader({ pattern: "**/[^_]*.{md,mdx}", base: "./src/content/folder" }),
      schema: extendI18nLoaderSchema(
        z.object({
          title: z.string(),
        })
      ),
    });
    
    /*
     Example of a content file:
     navigation:
       de-CH:
         - path: /projekte
           title: Projekte
         - path: /ueber-mich
           title: Über mich
       zh-CN:
         - path: /zh/projects
           title: 项目
         - path: /zh/about-me
           title: 关于我
    */
    const infileCollection = defineCollection({
      loader: i18nContentLoader({ pattern: "**/[^_]*.{yml,yaml}", base: "./src/content/infile" }),
      schema: extendI18nLoaderSchema( // `extendI18nLoaderSchema` defines `translationId` and `locale` for you in the schema.
        z.object({
          navigation: localized( // `localized` defines an object with the locale as key and applies the schema you provide to the value.
            z.array(
              z.object({
                path: z.string(),
                title: z.string(),
              })
            ),
            C.LOCALES
          ),
        })
      ),
    });
    
    export const collections = {
      files: filesCollection,
      folder: folderCollection,
      infile: infileCollection,
    };
  5. Create content files in the defined structure:

    ⚠️ WARNING The content files need to be structured according to the locales defined in astro.config.ts.

    . (project root)
    └── src
        └── content
            └── pages
                ├── about.de-CH.mdx
                ├── about.zh-CN.mdx
                ├── projects.de-CH.mdx
                └── projects.zh-CN.mdx
  6. Retrieve the locale and translationId identifier during rendering:

    import { getCollection } from "astro:content";
    
    const pages = await getCollection("files");
    console.log(pages["data"].locale); // e.g. de-CH
    console.log(pages["data"].translationId); // e.g. src/content/files/about.mdx
  7. Use i18nPropsAndParams to provide params and get available translations paths via the page props:

    import { i18nPropsAndParams } from "astro-loader-i18n";
    import sluggify from "limax"; // sluggify is used to create a slug from the title
    
    export const getStaticPaths = async ({ routePattern }) => {
      // ⚠️ If you are using Astro <5.14.0, you need to hardcode the routePattern here.
      // see https://github.com/withastro/astro/pull/13520
      // const routePattern = "[...locale]/[files]/[slug]";
      const filesCollection = await getCollection("files");
    
      return i18nPropsAndParams(filesCollection, {
        defaultLocale: C.DEFAULT_LOCALE,
        routePattern,
        segmentTranslations: C.SEGMENT_TRANSLATIONS,
        // `generateSegments` is a function that generates per entry individual segments.
        generateSegments: (entry) => ({ slug: sluggify(entry.data.title) }),
      });
    };
  8. Use Astro.props.translations to provide a same site language switcher.

In-file localized content

Sometimes to have multilingual content in a single file is more convenient. For example data for menus or galleries. This allows sharing untranslated content across locales.

Use the i18nContentLoader loader to load in-file localized content. If you want to load a single file, you can use the i18nFileLoader.

  1. Create a collection:

    . (project root)
    └── src
       └── content
           └── navigation
               ├── footer.yml
               └── main.yml
    # src/content/navigation/main.yml
    navigation:
      de-CH:
        - path: /projekte
          title: Projekte
        - path: /ueber-mich
          title: Über mich
      zh-CN:
        - path: /zh/projects
          title: 项目
        - path: /zh/about-me
          title: 关于我
  2. Use extendI18nLoaderSchema and localized to define the schema:

    const infileCollection = defineCollection({
      loader: i18nContentLoader({ pattern: "**/[^_]*.{yml,yaml}", base: "./src/content/infile" }),
      schema: extendI18nLoaderSchema( // `extendI18nLoaderSchema` defines `translationId` and `locale` for you in the schema.
        z.object({
          navigation: localized( // `localized` defines an object with the locale as key and applies the schema you provide to the value.
            z.array(
              z.object({
                path: z.string(),
                title: z.string(),
              })
            ),
            C.LOCALES
          ),
        })
      ),
    });
  3. When you get the collection, you will receive for each locale the localized content. For example if you have two locales de-CH and zh-CN with two files main.yml and footer.yml, you will get four entries in the collection:

    import { getCollection } from "astro:content";
    
    const navigation = await getCollection("infile");
    console.log(navigation[0].data.locale); // e.g. de-CH
    console.log(navigation[0].data.translationId); // e.g. src/content/infile/main.yml
    console.log(navigation[0].data.navigation); // e.g. [{ path: "/projekte", title: "Projekte" }, ...]

Virtual i18n collections

Sometimes you want to translate a page that is not based on i18n content. For example an index page or a 404 page.

createI18nCollection allows you to create a virtual collection that is not based on any content:

export const getStaticPaths = async () => {
  const routePattern = "[...locale]/[files]";
  const collection = createI18nCollection({ locales: C.LOCALES, routePattern });

  return i18nPropsAndParams(collection, {
    defaultLocale: C.DEFAULT_LOCALE,
    routePattern,
    segmentTranslations: C.SEGMENT_TRANSLATIONS,
  });
};

API

Below you can find a description of all exported functions and types.

i18nLoader

i18nLoader parses i18n information from file names or folder structures.

As this is a wrapper around the glob() loader, you can use all options from the glob() loader. See the Astro documentation for more information.

It adds the following properties to an entrys data object:

  • locale: The locale of the entry. This is either the folder name or the file name suffix.
  • translationId: The translation identifier. This helps to identify the same content in different languages.
  • contentPath: The path to the file relative to the content base path. This is useful if you want to add the folder names into the path. For example src/content/pages/de-CH/deeply/nested/about.mdx it would be deeply/nested.
  • basePath: The base path from the Astro config. This is a workaround, because from getStaticPaths() you don't have access to the base path and you need it for generating paths.

i18nContentLoader

i18nContentLoader creates multiple entries based on yaml or json files that contain i18n text fields.

See i18nLoader for more information.

i18nFileLoader

i18nFileLoader creates multiple entries based on a single yaml or json that contain i18n text fields.

It is a wrapper around the file() loader. See the Astro documentation for more information.

localized

localized is a helper function to define a schema for in-file localized content. It takes a schema and an array of locales and returns a schema that is an object with the locale as key and the schema as value.

Parameters:

  • schema: The schema to apply to the value of the object.
  • locales: An array of locales to use as keys for the object.
  • partial: Optional. If true, not all locales need to be defined in the schema.

extendI18nLoaderSchema

extendI18nLoaderSchema is a helper function to extend the schema of the i18nLoader and i18nContentLoader. It adds the translationId, locale, contentPath and basePath properties to the schema.

i18nLoaderSchema

i18nLoaderSchema is a schema that is used by the i18nLoader and i18nContentLoader. It defines the properties that are added to the entrys data object.

i18nPropsAndParams

i18nPropsAndParams is a helper function to generate the params and props object for getStaticPaths() and to provide the translations object to the page props.

Parameters:

  • collection: The collection to use. This can be a collection from getCollection() or a virtual collection created with createI18nCollection().
  • options: An object with the following properties:
    • defaultLocale: The default locale to use.
    • routePattern: The route pattern to use. This is the pattern that is used in the getStaticPaths() function. Unfortunately there is no way to access the routePattern, that's why we need to define it here again.
    • segmentTranslations: An object with the segment translations. This is used to translate the segments in the URL.
    • generateSegments: (Optional) A function that generates the segments for each entry. This is useful if you want to generate slugs or other segments.
    • localeParamName: (Optional) The name of the locale parameter in the URL. This is used to generate the URL for the translations object.
    • prefixDefaultLocale: (Optional) If true, the default locale will be prefixed to the URL. This is useful if you want to have a clean URL for the default locale.

It returns an object with params and props. props contains additionally a translations object with the paths to the corresponding content of all existing translations. The translatedPath is the current entry path.

createI18nCollection

createI18nCollection creates a virtual collection that is not based on any content. This is useful if you want to create a collection for a page that is not based on i18n content.

resolvePath

resolvePath is a helper function that connects path segments and deals with slashes.

Examples

Made by the author of astro-loader-i18n:

  • Test project (Source): Minimal example of how to use astro-loader-i18n with Astro.
  • Astro Theme International (Demo / Source): A demo theme with the goal to be as international as possible.
  • r.obin.ch (Demo / Source): A personal website with a blog and projects, built with Astro and astro-loader-i18n.

Made by the community:

  • eCamp3 (Demo / Source): A website of an application for camp planning.

Roadmap

  • [x] Add i18nFileLoader that is based on Astros file() loader
  • [ ] Improve types of params returned by i18nPropsAndParams
  • [ ] Include a language switcher Astro component

Wish list

To make internationalization easier, Astro could offer the following features:

  • [x] Provide routing information to getStaticPaths() such as the routePattern to avoid manual repetition. Also see this pull request: https://github.com/withastro/astro/pull/13520
  • [ ] Allow to define custom parameters for getStaticPaths() like paginate from integrations and loaders. This makes integrating additional helpers for building getStaticPaths() way easier.
  • [ ] Allow to define different schemas for input (this already exists, today) and output of a loader. This is useful if a loader transforms the data. Currently the schema wouldn't match the output of the loader anymore.
  • [ ] Allow to define additional custom properties from loaders apart from the data object, that are available inside getStaticPaths() and while rendering. This is useful if a loader calculates additional properties that later used in the template and are not necessarily part of the data object to avoid collisions with the user provided data.