Internationalization is an opt-in feature. Without any configuration, the site behaves as a single-language site with unprefixed URLs (/about, etc.). AstroTable is designed to work for non-ecommerce sites too (portfolios, for example), so multilingual support isn't a core assumption.

This site itself opts into two locales: ja (Japanese) and en (English). The rest of this page describes the opted-in setup.

Configuration

Add Astro's built-in i18n routing to astro.config.mjs.

// astro.config.mjs
export default defineConfig({
  i18n: {
    defaultLocale: 'ja',
    locales: ['ja', 'en'],
    routing: { prefixDefaultLocale: true },
  },
});

With routing.prefixDefaultLocale: true, every page is locale-prefixed (/ja/..., /en/...), including pages in the default locale (ja). There is no unprefixed URL. Requests to the root / are redirected to /ja/ by the deployment platform (this site uses Cloudflare's public/_redirects, supported on both Pages and Workers static assets — see the deployment page).

Content layout

When i18n is enabled, content is split into locale-named directories.

Directory structure

  • content/pages/ja/*.md / content/pages/en/*.md — page bodies
  • content/nav/ja.md / content/nav/en.md — header navigation
  • content/footer/ja.md / content/footer/en.md — footer

Page content is a completely separate file per locale. There's no automatic sync mechanism (machine translation or otherwise), so keeping translations up to date is an operational task.

How routing works

For content pages (content/pages/), src/pages/[...slug].astro maps the file path (a locale-prefixed id like ja/about) directly to a URL. A file at ja/index routes to /ja/.

Pages that exist as fixed templates (such as member pages, which live as real files rather than going through Content Collections) use an actual src/pages/[locale]/... directory structure, generating static paths per locale via getStaticPaths().

Language switcher UI

src/components/LanguageSwitcher.astro generates the URL for each available locale using Astro's official getRelativeLocaleUrl() helper. The shared layout (Base.astro) places it next to the header navigation automatically.

import { getRelativeLocaleUrl } from 'astro:i18n';

const LOCALES = ['ja', 'en'] as const; // keep in sync with i18n.locales in astro.config.mjs

The LOCALES constant duplicates i18n.locales from astro.config.mjs. Both currently need to be kept in sync by hand, so update both when adding or removing a locale.

Adding a translated page

To translate an existing page into another locale, create a file at the same relative path under the corresponding locale directory. For example, to translate content/pages/ja/about.md into English, create content/pages/en/about.md and translate both the frontmatter (title/description/pageType) and the body's Blocks. Keeping file names and directory structure aligned across locales ensures the URLs LanguageSwitcher generates actually resolve to real files.

Adding a new locale

  1. Add it to i18n.locales in astro.config.mjs
  2. Add it to the LOCALES constant in src/components/LanguageSwitcher.astro
  3. Create content/pages/<locale>/, content/nav/<locale>.md, and content/footer/<locale>.md

Removing i18n

Remove the i18n setting from astro.config.mjs, flatten the locale directories under content/pages/<locale>/ back into content/pages/*.md, and merge nav.md/footer.md back into single files instead of per-locale ones. Also remove the LanguageSwitcher component usage from the shared layout.