Internationalization Guide

Astro Plain i18n guide: translation functions, configuration conventions, and steps to add new languages.

Reading time: 3 min
By:

NOTE

This article was translated using AI.

Feature Overview

  • Smart Redirect: Automatically routes visitors to their preferred language homepage based on cookies or browser language headers.
  • Zero-prop Context: Powered by ts-intl-astro, Astro components can call useTranslations or useLocale directly without passing lang down props.
  • Multilingual Post Linking: Links posts across languages using translationKey in frontmatter without requiring nested subdirectories, automatically generating localized SEO tags (canonical / hreflang).

File Structure

  • Directoryi18n/
    • index.ts Core i18n initialization and exported helpers
    • Directorymessages/ Language dictionaries
      • zh-Hans.ts Chinese dictionary
      • en-US.ts English dictionary
      • ja-JP.ts Japanese dictionary
  • Directorysrc/content/
    • Directoryblog/ Blog posts (linked via translationKey)
      • …
    • Directoryauthor/ Author profiles (organized by language directories)
      • …
    • Directoryproject/ Project showcases (organized by language directories)
      • …
    • Directorymdx/ Other MDX content (organized by language directories)
      • Directory[lang]/
        • hero.mdx Home page hero section
  • astro.config.mjs Astro configuration (registers tsIntl() integration)
  • site.config.ts Site configuration

Translation Functions

Internationalization in this project is powered by ts-intl-astro (opens in a new tab), automatically injecting the current locale context across Astro components.

Basic Usage (Binding a Namespace)

Specify a namespace when initializing useTranslations to access keys directly:

src/components/Header.astro
---
import { useTranslations } from "~/i18n";
// Bind the `common` namespace
const t = useTranslations("common");
---
<header>
<h1>{t("header_title")}</h1>
<a href="/">{t("home")}</a>
</header>

Global Namespace Access

Call useTranslations without arguments to query keys across namespaces using dot notation (namespace.key):

---
import { useTranslations } from "~/i18n";
const t = useTranslations();
---
<p>{t("common.home")}</p>
<p>{t("search.placeholder")}</p>

Getting the Current Locale

When constructing localized paths or passing the locale code to third-party modules, call useLocale():

---
import { useLocale } from "~/i18n";
const lang = useLocale();
---
<a href={`/${lang}/about`}>About</a>

Parameter Interpolation

Message templates support {param} placeholders:

---
import { useTranslations } from "~/i18n";
const t = useTranslations("common");
// Dictionary entry: link_to: "Link to {title}"
---
<p>{t("link_to", { title: "Astro" })}</p>
{/* Output: Link to Astro */}

Usage Outside Components

In contexts without Astro rendering state (e.g. getStaticPaths or API endpoints like rss.xml.ts), call getTranslations with an explicit language code:

import { getTranslations } from "~/i18n";
const t = getTranslations("en-US", "common");
const siteTitle = t("site_title");

Adding a New Language

Adding a new language (e.g. French fr-FR) requires only 4 steps:

  1. Add the language mapping in site configuration

    Add the language code and display label to languageNameMap in site.config.ts:

    site.config.ts
    const SiteConfig = {
    // ...
    languageNameMap: {
    "en-US": "English",
    "ja-JP": "日本語",
    "zh-Hans": "简体中文",
    "fr-FR": "Français",
    },
    };
  2. Create the translation dictionary

    Create i18n/messages/fr-FR.ts. You can copy an existing file as a baseline:

    i18n/messages/fr-FR.ts
    const frFR = {
    common: {
    home: "Accueil",
    about: "À propos",
    link_to: "Lien vers {title}",
    page_num: "Page {num}",
    // ... other dictionary keys
    },
    } as const;
    export default frFR;
  3. Register the new dictionary

    Import and register the dictionary in i18n/index.ts:

    i18n/index.ts
    import zhHans from "./messages/zh-Hans";
    import enUS from "./messages/en-US";
    import jaJP from "./messages/ja-JP";
    import frFR from "./messages/fr-FR";
    export const {
    useTranslations,
    useLocale,
    useFormatter,
    getTranslations,
    // ...
    } = createAstroI18n({
    defaultLanguage: "zh-Hans",
    messages: {
    "zh-Hans": zhHans,
    "en-US": enUS,
    "ja-JP": jaJP,
    "fr-FR": frFR,
    },
    });
  4. Add author profile and hero content

    Create the corresponding directories and files under src/content/author/ and src/content/mdx/:

    • src/content/author/fr-FR/default.mdx
    • src/content/mdx/fr-FR/hero.mdx

Writing Multilingual Posts

All blog posts are kept under src/content/blog/ without nested language folders. Posts in different languages are automatically linked via translationKey:

---
title: "Astro Plain 介绍"
summary: "博客模板介绍"
translationKey: "my-first-post"
language: "zh-Hans"
date: "2026-09-01T12:00:00Z"
isCanonical: true
---
文章中文内容...

NOTE

  • translationKey: All translations of the same article must use an identical key.
  • isCanonical: Recommended to set to true on the default language version to produce authoritative canonical and x-default alternate links.
Last modified: